FileDocCategorySizeDatePackage
MetadataDescriptor.javaAPI DocJaudiotagger 2.0.431403Wed Mar 30 16:11:50 BST 2011org.jaudiotagger.audio.asf.data

MetadataDescriptor

public class MetadataDescriptor extends Object implements Comparable, Cloneable
This structure represents metadata objects in ASF {@link MetadataContainer}.
The values are {@linkplain ContainerType#assertConstraints(String, byte[], int, int, int) checked} against the capability introduced by the given {@link ContainerType} at construction.

Limitation: Even though some container types do not restrict the data size to {@link Integer#MAX_VALUE}, this implementation does it (due to java nature).
2 GiB of data should suffice, and even be to large for normal java heap.
author
Christian Laireiter

Fields Summary
public static final long
DWORD_MAXVALUE
Maximum value for WORD.
private static final Logger
LOGGER
Logger instance.
public static final int
MAX_LANG_INDEX
The maximum language index allowed. (exclusive)
public static final int
MAX_STREAM_NUMBER
Maximum stream number. (inclusive)
public static final BigInteger
QWORD_MAXVALUE
Maximum value for a QWORD value (64 bit unsigned).
public static final int
TYPE_BINARY
Constant for the metadata descriptor-type for binary data.
public static final int
TYPE_BOOLEAN
Constant for the metadata descriptor-type for booleans.
public static final int
TYPE_DWORD
Constant for the metadata descriptor-type for DWORD (32-bit unsigned).
public static final int
TYPE_GUID
Constant for the metadata descriptor-type for GUIDs (128-bit).
public static final int
TYPE_QWORD
Constant for the metadata descriptor-type for QWORD (64-bit unsinged).
public static final int
TYPE_STRING
Constant for the metadata descriptor-type for Strings.
public static final int
TYPE_WORD
Constant for the metadata descriptor-type for WORD (16-bit unsigned).
public static final int
WORD_MAXVALUE
Maximum value for WORD.
private final ContainerType
containerType
Stores the containerType of the descriptor.
private byte[]
content
The binary representation of the value.
private int
descriptorType
This field shows the type of the metadata descriptor.
private int
languageIndex
the index of the language in the {@linkplain LanguageList language list} this descriptor applies to.
private final String
name
The name of the metadata descriptor.
private int
streamNumber
The number of the stream, this descriptor applies to.
Constructors Summary
public MetadataDescriptor(ContainerType type, String propName, int propType)
Creates an Instance.

param
type the container type, this descriptor is resctricted to.
param
propName Name of the MetadataDescriptor.
param
propType Type of the metadata descriptor. See {@link #descriptorType}


                                                                        
          
               
        this(type, propName, propType, 0, 0);
    
public MetadataDescriptor(ContainerType type, String propName, int propType, int stream, int language)
Creates an Instance.

param
type The container type the values (the whole descriptor) is restricted to.
param
propName Name of the MetadataDescriptor.
param
propType Type of the metadata descriptor. See {@link #descriptorType}
param
stream the number of the stream the descriptor refers to.
param
language the index of the language entry in a {@link LanguageList} this descriptor refers to.
Consider: No checks performed if language entry exists.

        assert type != null;
        type.assertConstraints(propName, new byte[0], propType, stream,
                language);
        this.containerType = type;
        this.name = propName;
        this.descriptorType = propType;
        this.streamNumber = stream;
        this.languageIndex = language;
    
public MetadataDescriptor(String propName)
Creates an instance.
Capabilities are set to {@link ContainerType#METADATA_LIBRARY_OBJECT}.

param
propName name of the metadata descriptor.

        this(propName, TYPE_STRING);
    
public MetadataDescriptor(String propName, int propType)
Creates an Instance.
Capabilities are set to {@link ContainerType#METADATA_LIBRARY_OBJECT}.

param
propName Name of the MetadataDescriptor.
param
propType Type of the metadata descriptor. See {@link #descriptorType}

        this(ContainerType.METADATA_LIBRARY_OBJECT, propName, propType, 0, 0);
    
Methods Summary
public java.math.BigIntegerasNumber()
Converts the descriptors value into a number if possible.
A boolean will be converted to "1" if true, otherwise "0".
String will be interpreted as number with radix "10".
Binary data will be interpreted as the default WORD,DWORD or QWORD binary representation, but only if the data does not exceed 8 bytes. This precaution is done to prevent creating a number of a multi kilobyte image.
A GUID cannot be converted in any case.

return
number representation.
throws
NumberFormatException If no conversion is supported.

        BigInteger result = null;
        switch (this.descriptorType) {
        case TYPE_BOOLEAN:
        case TYPE_WORD:
        case TYPE_DWORD:
        case TYPE_QWORD:
        case TYPE_BINARY:
            if (this.content.length > 8) {
                throw new NumberFormatException(
                        "Binary data would exceed QWORD");
            }
            break;
        case TYPE_GUID:
            throw new NumberFormatException(
                    "GUID cannot be converted to a number.");
        case TYPE_STRING:
            result = new BigInteger(getString(), 10);
            break;
        default:
            throw new IllegalStateException();
        }
        if (result == null) {
            final byte[] copy = new byte[this.content.length];
            for (int i = 0; i < copy.length; i++) {
                copy[i] = this.content[this.content.length - (i + 1)];
            }
            result = new BigInteger(1, copy);
        }
        return result;
    
public java.lang.Objectclone()
(overridden)

see
java.lang.Object#clone()

        return super.clone();
    
public intcompareTo(org.jaudiotagger.audio.asf.data.MetadataDescriptor other)
{@inheritDoc}

        return getName().compareTo(other.getName());
    
public org.jaudiotagger.audio.asf.data.MetadataDescriptorcreateCopy()
This method creates a copy of the current object.
All data will be copied, too.

return
A new metadata descriptor containing the same values as the current one.

        final MetadataDescriptor result = new MetadataDescriptor(
                this.containerType, this.name, this.descriptorType,
                this.streamNumber, this.languageIndex);
        result.content = getRawData();
        return result;
    
public booleanequals(java.lang.Object obj)
(overridden)

see
java.lang.Object#equals(java.lang.Object)

        boolean result = false;
        if (obj instanceof MetadataDescriptor) {
            if (obj == this) {
                result = true;
            } else {
                final MetadataDescriptor other = (MetadataDescriptor) obj;
                result = other.getName().equals(getName())
                        && other.descriptorType == this.descriptorType
                        && other.languageIndex == this.languageIndex
                        && other.streamNumber == this.streamNumber
                        && Arrays.equals(this.content, other.content);
            }
        }
        return result;
    
public booleangetBoolean()
Returns the value of the MetadataDescriptor as a Boolean.
If no Conversion is Possible false is returned.
true if first byte of {@link #content}is not zero.

return
boolean representation of the current value.

        return this.content.length > 0 && this.content[0] != 0;
    
public byte[]getBytes()
This method will return a byte array, which can directly be written into an "Extended Content Description"-chunk.

return
byte[] with the data, that occurs in ASF files.
deprecated
{@link #writeInto(OutputStream,ContainerType)} is used

        final ByteArrayOutputStream result = new ByteArrayOutputStream();
        try {
            writeInto(result, this.containerType);
        } catch (final IOException e) {
            LOGGER.warning(e.getMessage());
        }
        return result.toByteArray();
    
public ContainerTypegetContainerType()
Returns the container type this descriptor ist restricted to.

return
the container type

        return this.containerType;
    
public intgetCurrentAsfSize(ContainerType type)
Returns the size (in bytes) this descriptor will take when written to an ASF file.

param
type the container type for which the size is calculated.
return
size of the descriptor in an ASF file.

        /*
         * 2 bytes name length, 2 bytes name zero term, 2 bytes type, 2 bytes
         * content length
         */
        int result = 8;

        if (type != ContainerType.EXTENDED_CONTENT) {
            // Stream number and language index (respectively reserved field).
            // And +2 bytes, because data type is 32 bit, not 16
            result += 6;
        }
        result += getName().length() * 2;

        if (this.getType() == TYPE_BOOLEAN) {
            result += 2;
            if (type == ContainerType.EXTENDED_CONTENT) {
                // Extended content description boolean values are stored with
                // 32-bit
                result += 2;
            }
        } else {

            result += this.content.length;
            if (TYPE_STRING == this.getType()) {
                result += 2; // zero term of content string.
            }
        }
        return result;
    
public GUIDgetGuid()
Returns the GUID value, if content could represent one.

return
GUID value

        GUID result = null;
        if (getType() == TYPE_GUID && this.content.length == GUID.GUID_LENGTH) {
            result = new GUID(this.content);
        }
        return result;
    
public intgetLanguageIndex()
Returns the index of the language that is referred (see {@link LanguageList}):

return
the language index

        return this.languageIndex;
    
public java.lang.StringgetName()
This method returns the name of the metadata descriptor.

return
Name.

        return this.name;
    
public longgetNumber()
This method returns the value of the metadata descriptor as a long.
Converts the needed amount of byte out of {@link #content}to a number.
Only possible if {@link #getType()}equals on of the following:
  • return
    integer value.
    see
    #TYPE_BOOLEAN
  • see
    #TYPE_DWORD
  • see
    #TYPE_QWORD
  • see
    #TYPE_WORD

            int bytesNeeded;
            switch (getType()) {
            case TYPE_BOOLEAN:
                bytesNeeded = 1;
                break;
            case TYPE_DWORD:
                bytesNeeded = 4;
                break;
            case TYPE_QWORD:
                bytesNeeded = 8;
                break;
            case TYPE_WORD:
                bytesNeeded = 2;
                break;
            default:
                throw new UnsupportedOperationException(
                        "The current type doesn't allow an interpretation as a number. ("
                                + getType() + ")");
            }
            if (bytesNeeded > this.content.length) {
                throw new IllegalStateException(
                        "The stored data cannot represent the type of current object.");
            }
            long result = 0;
            for (int i = 0; i < bytesNeeded; i++) {
                result |= (((long) this.content[i] & 0xFF) << (i * 8));
            }
            return result;
        
  • public byte[]getRawData()
    This method returns a copy of the content of the descriptor.

    return
    The content in binary representation, as it would be written to asf file.

            final byte[] copy = new byte[this.content.length];
            System.arraycopy(this.content, 0, copy, 0, this.content.length);
            return copy;
        
    public intgetRawDataSize()
    Returns the size (in bytes) the binary representation of the content uses. (length of {@link #getRawData()})

    return
    size of binary representation of the content.

            return this.content.length;
        
    public intgetStreamNumber()
    Returns the stream number this descriptor applies to.

    return
    the stream number.

            return this.streamNumber;
        
    public java.lang.StringgetString()
    Returns the value of the MetadataDescriptor as a String.

    return
    String - Representation Value

            String result = null;
            switch (getType()) {
            case TYPE_BINARY:
                result = "binary data";
                break;
            case TYPE_BOOLEAN:
                result = String.valueOf(getBoolean());
                break;
            case TYPE_GUID:
                result = getGuid() == null ? "Invalid GUID" : getGuid().toString();
                break;
            case TYPE_QWORD:
            case TYPE_DWORD:
            case TYPE_WORD:
                result = String.valueOf(getNumber());
                break;
            case TYPE_STRING:
                try {
                    result = new String(this.content, "UTF-16LE");
                } catch (final UnsupportedEncodingException e) {
                    LOGGER.warning(e.getMessage());
                }
                break;
            default:
                throw new IllegalStateException("Current type is not known.");
            }
            return result;
        
    public intgetType()
    Returns the type of the metadata descriptor.

    return
    the value of {@link #descriptorType}
    see
    #TYPE_BINARY
    see
    #TYPE_BOOLEAN
    see
    #TYPE_DWORD
    see
    #TYPE_GUID
    see
    #TYPE_QWORD
    see
    #TYPE_STRING
    see
    #TYPE_WORD

            return this.descriptorType;
        
    public inthashCode()
    {@inheritDoc}

            return this.name.hashCode();
        
    public booleanisEmpty()
    This method checks if the binary data is empty.
    Disregarding the type of the descriptor its content is stored as a byte array.

    return
    true if no value is set.

            return this.content.length == 0;
        
    public voidsetBinaryValue(byte[] data)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_BINARY}.

    param
    data Value to set.
    throws
    IllegalArgumentException if data is invalid for {@linkplain #getContainerType() container}.

            this.containerType.assertConstraints(this.name, data,
                    this.descriptorType, this.streamNumber, this.languageIndex);
            this.content = data.clone();
            this.descriptorType = TYPE_BINARY;
        
    public voidsetBooleanValue(boolean value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_BOOLEAN}.

    param
    value Value to set.

            this.content = new byte[] { value ? (byte) 1 : 0 };
            this.descriptorType = TYPE_BOOLEAN;
        
    public voidsetDWordValue(long value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_DWORD}.

    param
    value Value to set.

            if (value < 0 || value > DWORD_MAXVALUE) {
                throw new IllegalArgumentException("value out of range (0-"
                        + DWORD_MAXVALUE + ")");
            }
            this.content = Utils.getBytes(value, 4);
            this.descriptorType = TYPE_DWORD;
        
    public voidsetGUIDValue(GUID value)
    Sets the value of the metadata descriptor.
    Using this method will change {@link #descriptorType} to {@link #TYPE_GUID}

    param
    value value to set.

            this.containerType.assertConstraints(this.name, value.getBytes(),
                    TYPE_GUID, this.streamNumber, this.languageIndex);
            this.content = value.getBytes();
            this.descriptorType = TYPE_GUID;
        
    public voidsetLanguageIndex(int language)
    Sets the index of the referred language (see {@link LanguageList}).
    Consider: The {@linkplain #containerType requirements} must be held.

    param
    language the language index to set

            this.containerType.assertConstraints(this.name, this.content,
                    this.descriptorType, this.streamNumber, language);
            this.languageIndex = language;
        
    public voidsetQWordValue(java.math.BigInteger value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_QWORD}

    param
    value Value to set.
    throws
    NumberFormatException on null values.
    throws
    IllegalArgumentException on illegal values or values exceeding range.

            if (value == null) {
                throw new NumberFormatException("null");
            }
            if (BigInteger.ZERO.compareTo(value) > 0) {
                throw new IllegalArgumentException(
                        "Only unsigned values allowed (no negative)");
            }
            if (MetadataDescriptor.QWORD_MAXVALUE.compareTo(value) < 0) {
                throw new IllegalArgumentException(
                        "Value exceeds QWORD (64 bit unsigned)");
            }
            this.content = new byte[8];
            final byte[] valuesBytes = value.toByteArray();
            if (valuesBytes.length <= 8) {
                for (int i = valuesBytes.length - 1; i >= 0; i--) {
                    this.content[valuesBytes.length - (i + 1)] = valuesBytes[i];
                }
            } else {
                /*
                 * In case of 64-Bit set
                 */
                Arrays.fill(this.content, (byte) 0xFF);
            }
            this.descriptorType = TYPE_QWORD;
        
    public voidsetQWordValue(long value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_QWORD}

    param
    value Value to set.

            if (value < 0) {
                throw new IllegalArgumentException("value out of range (0-"
                        + MetadataDescriptor.QWORD_MAXVALUE.toString() + ")");
            }
            this.content = Utils.getBytes(value, 8);
            this.descriptorType = TYPE_QWORD;
        
    public voidsetStreamNumber(int stream)
    Sets the stream number the descriptor applies to.
    Consider: The {@linkplain #containerType requirements} must be held.

    param
    stream the stream number to set

            this.containerType.assertConstraints(this.name, this.content,
                    this.descriptorType, stream, this.languageIndex);
            this.streamNumber = stream;
        
    public voidsetString(java.lang.String value)
    This method converts the given string value into the current {@linkplain #getType() data type}.

    param
    value value to set.
    throws
    IllegalArgumentException If conversion was impossible.

            try {
                switch (getType()) {
                case TYPE_BINARY:
                    throw new IllegalArgumentException(
                            "Cannot interpret binary as string.");
                case TYPE_BOOLEAN:
                    setBooleanValue(Boolean.parseBoolean(value));
                    break;
                case TYPE_DWORD:
                    setDWordValue(Long.parseLong(value));
                    break;
                case TYPE_QWORD:
                    setQWordValue(new BigInteger(value, 10));
                    break;
                case TYPE_WORD:
                    setWordValue(Integer.parseInt(value));
                    break;
                case TYPE_GUID:
                    setGUIDValue(GUID.parseGUID(value));
                    break;
                case TYPE_STRING:
                    setStringValue(value);
                    break;
                default:
                    // new Type added but not handled.
                    throw new IllegalStateException();
                }
            } catch (final NumberFormatException nfe) {
                throw new IllegalArgumentException(
                        "Value cannot be parsed as Number or is out of range (\""
                                + value + "\")", nfe);
            }
        
    public voidsetStringValue(java.lang.String value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_STRING}.

    param
    value Value to set.
    throws
    IllegalArgumentException If byte representation would take more than 65535 Bytes.

            if (value == null) {
                this.content = new byte[0];
            } else {
                final byte[] tmp = Utils.getBytes(value, AsfHeader.ASF_CHARSET);
                if (getContainerType().isWithinValueRange(tmp.length)) {
                    // Everything is fine here, data can be stored.
                    this.content = tmp;
                } else {
                    // Normally a size violation, check if JAudiotagger my truncate
                    // the string
                    if (TagOptionSingleton.getInstance()
                            .isTruncateTextWithoutErrors()) {
                        // truncate the string
                        final int copyBytes = (int) getContainerType()
                                .getMaximumDataLength().longValue();
                        this.content = new byte[copyBytes % 2 == 0 ? copyBytes
                                : copyBytes - 1];
                        System.arraycopy(tmp, 0, this.content, 0,
                                this.content.length);
                    } else {
                        // We may not truncate, so its an error
                        throw new IllegalArgumentException(
                                ErrorMessage.WMA_LENGTH_OF_DATA_IS_TOO_LARGE
                                        .getMsg(tmp.length, getContainerType()
                                                .getMaximumDataLength(),
                                                getContainerType()
                                                        .getContainerGUID()
                                                        .getDescription()));
                    }
                }
            }
            this.descriptorType = TYPE_STRING;
        
    public voidsetWordValue(int value)
    Sets the Value of the current metadata descriptor.
    Using this method will change {@link #descriptorType}to {@link #TYPE_WORD}

    param
    value Value to set.
    throws
    IllegalArgumentException on negative values. ASF just supports unsigned values.

            if (value < 0 || value > WORD_MAXVALUE) {
                throw new IllegalArgumentException("value out of range (0-"
                        + WORD_MAXVALUE + ")");
            }
            this.content = Utils.getBytes(value, 2);
            this.descriptorType = TYPE_WORD;
        
    public java.lang.StringtoString()
    (overridden)

    see
    java.lang.Object#toString()

            return getName()
                    + " : "
                    + new String[] { "String: ", "Binary: ", "Boolean: ",
                            "DWORD: ", "QWORD:", "WORD:", "GUID:" }[this.descriptorType]
                    + getString() + " (language: " + this.languageIndex
                    + " / stream: " + this.streamNumber + ")";
        
    public intwriteInto(java.io.OutputStream out, ContainerType contType)
    Writes this descriptor into the specified output stream.

    param
    out stream to write into.
    param
    contType the container type this descriptor is written to.
    return
    amount of bytes written.
    throws
    IOException on I/O Errors

            final int size = getCurrentAsfSize(contType);
            /*
             * Booleans are stored as one byte, if a boolean is written, the data
             * must be converted according to the container type.
             */
            byte[] binaryData;
            if (this.descriptorType == TYPE_BOOLEAN) {
                binaryData = new byte[contType == ContainerType.EXTENDED_CONTENT ? 4
                        : 2];
                binaryData[0] = (byte) (getBoolean() ? 1 : 0);
            } else {
                binaryData = this.content;
            }
            // for Metadata objects the stream number and language index
            if (contType != ContainerType.EXTENDED_CONTENT) {
                Utils.writeUINT16(getLanguageIndex(), out);
                Utils.writeUINT16(getStreamNumber(), out);
            }
            Utils.writeUINT16(getName().length() * 2 + 2, out);
    
            // The name for the metadata objects come later
            if (contType == ContainerType.EXTENDED_CONTENT) {
                out.write(Utils.getBytes(getName(), AsfHeader.ASF_CHARSET));
                out.write(AsfHeader.ZERO_TERM);
            }
    
            // type and content len follow up are identical
            final int type = getType();
            Utils.writeUINT16(type, out);
            int contentLen = binaryData.length;
            if (TYPE_STRING == type) {
                contentLen += 2; // Zero Term
            }
    
            if (contType == ContainerType.EXTENDED_CONTENT) {
                Utils.writeUINT16(contentLen, out);
            } else {
                Utils.writeUINT32(contentLen, out);
            }
    
            // Metadata objects now write their descriptor name
            if (contType != ContainerType.EXTENDED_CONTENT) {
                out.write(Utils.getBytes(getName(), AsfHeader.ASF_CHARSET));
                out.write(AsfHeader.ZERO_TERM);
            }
    
            // The content.
            out.write(binaryData);
            if (TYPE_STRING == type) {
                out.write(AsfHeader.ZERO_TERM);
            }
            return size;