FileDocCategorySizeDatePackage
ID3v23Frame.javaAPI DocJaudiotagger 2.0.431525Wed Jun 08 12:05:40 BST 2011org.jaudiotagger.tag.id3

ID3v23Frame

public class ID3v23Frame extends AbstractID3v2Frame
Represents an ID3v2.3 frame.
author
: Paul Taylor
author
: Eric Farng
version
$Id: ID3v23Frame.java 976 2011-06-08 10:05:34Z paultaylor $

Fields Summary
private static Pattern
validFrameIdentifier
protected static final int
FRAME_ID_SIZE
protected static final int
FRAME_FLAGS_SIZE
protected static final int
FRAME_SIZE_SIZE
protected static final int
FRAME_COMPRESSION_UNCOMPRESSED_SIZE
protected static final int
FRAME_ENCRYPTION_INDICATOR_SIZE
protected static final int
FRAME_GROUPING_INDICATOR_SIZE
protected static final int
FRAME_HEADER_SIZE
private int
encryptionMethod
If the frame is encrypted then the encryption method is stored in this byte
private int
groupIdentifier
If the frame belongs in a group with other frames then the group identifier byte is stored
Constructors Summary
public ID3v23Frame()
Creates a new ID3v23 Frame

    
public ID3v23Frame(String identifier)
Creates a new ID3v23 Frame of type identifier.

An empty body of the correct type will be automatically created. This constructor should be used when wish to create a new frame from scratch using user data.

param
identifier

        super(identifier);
        statusFlags = new StatusFlags();
        encodingFlags = new EncodingFlags();
    
public ID3v23Frame(ID3v23Frame frame)
Copy Constructor

Creates a new v23 frame based on another v23 frame

param
frame

        super(frame);
        statusFlags = new StatusFlags(frame.getStatusFlags().getOriginalFlags());
        encodingFlags = new EncodingFlags(frame.getEncodingFlags().getFlags());
    
public ID3v23Frame(AbstractID3v2Frame frame)
Creates a new ID3v23Frame based on another frame of a different version.

param
frame
throws
org.jaudiotagger.tag.InvalidFrameException

        logger.finer("Creating frame from a frame of a different version");
        if (frame instanceof ID3v23Frame)
        {
            throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
        }
        if (frame instanceof ID3v24Frame)
        {
            statusFlags = new StatusFlags((ID3v24Frame.StatusFlags) frame.getStatusFlags());
            encodingFlags = new EncodingFlags(frame.getEncodingFlags().getFlags());
        }

        if (frame instanceof ID3v24Frame)
        {
            //Unknown Frame e.g NCON, also protects when known id but has unsupported frame body
            if (frame.getBody() instanceof FrameBodyUnsupported)
            {
                this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
                this.frameBody.setHeader(this);
                identifier = frame.getIdentifier();
                logger.config("UNKNOWN:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                return;
            }
            // Deprecated frame for v24
            else if (frame.getBody() instanceof FrameBodyDeprecated)
            {
                //Was it valid for this tag version, if so try and reconstruct
                if (ID3Tags.isID3v23FrameIdentifier(frame.getIdentifier()))
                {
                    this.frameBody = ((FrameBodyDeprecated) frame.getBody()).getOriginalFrameBody();
                    this.frameBody.setHeader(this);
                    this.frameBody.setTextEncoding(ID3TextEncodingConversion.getTextEncoding(this,this.frameBody.getTextEncoding()));
                    identifier = frame.getIdentifier();
                    logger.config("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                }
                //or was it still deprecated, if so leave as is
                else
                {
                    this.frameBody = new FrameBodyDeprecated((FrameBodyDeprecated) frame.getBody());
                    this.frameBody.setHeader(this);
                    this.frameBody.setTextEncoding(ID3TextEncodingConversion.getTextEncoding(this,this.frameBody.getTextEncoding()));

                    identifier = frame.getIdentifier();
                    logger.config("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                    return;
                }
            }
            else if (ID3Tags.isID3v24FrameIdentifier(frame.getIdentifier()))
            {
                logger.finer("isID3v24FrameIdentifier");
                //Version between v4 and v3
                identifier = ID3Tags.convertFrameID24To23(frame.getIdentifier());
                if (identifier != null)
                {
                    logger.finer("V4:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                    this.frameBody = (AbstractTagFrameBody) ID3Tags.copyObject(frame.getBody());
                    this.frameBody.setHeader(this);
                    this.frameBody.setTextEncoding(ID3TextEncodingConversion.getTextEncoding(this,this.frameBody.getTextEncoding()));
                    return;
                }
                else
                {
                    //Is it a known v4 frame which needs forcing to v3 frame e.g. TDRC - TYER,TDAT
                    identifier = ID3Tags.forceFrameID24To23(frame.getIdentifier());
                    if (identifier != null)
                    {
                        logger.finer("V4:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                        this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
                        this.frameBody.setHeader(this);
                        this.frameBody.setTextEncoding(ID3TextEncodingConversion.getTextEncoding(this,this.frameBody.getTextEncoding()));
                        return;
                    }
                    //It is a v24 frame that is not known and cannot be forced in v23 e.g TDRL,in which case
                    //we convert to a frameBody unsupported by writing contents as a byte array and feeding
                    //it into FrameBodyUnsupported
                    else
                    {
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ((AbstractID3v2FrameBody) frame.getBody()).write(baos);

                        identifier = frame.getIdentifier();
                        this.frameBody = new FrameBodyUnsupported(identifier, baos.toByteArray());
                        this.frameBody.setHeader(this);
                        logger.finer("V4:Orig id is:" + frame.getIdentifier() + ":New Id Unsupported is:" + identifier);
                        return;
                    }
                }
            }
            // Unable to find a suitable frameBody, this should not happen
            else
            {
                logger.severe("Orig id is:" + frame.getIdentifier() + "Unable to create Frame Body");
                throw new InvalidFrameException("Orig id is:" + frame.getIdentifier() + "Unable to create Frame Body");
            }
        }
        else if (frame instanceof ID3v22Frame)
        {
            if (ID3Tags.isID3v22FrameIdentifier(frame.getIdentifier()))
            {
                identifier = ID3Tags.convertFrameID22To23(frame.getIdentifier());
                if (identifier != null)
                {
                    logger.config("V3:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                    this.frameBody = (AbstractTagFrameBody) ID3Tags.copyObject(frame.getBody());
                    this.frameBody.setHeader(this);
                    return;
                }
                //Is it a known v2 frame which needs forcing to v23 frame e.g PIC - APIC
                else if (ID3Tags.isID3v22FrameIdentifier(frame.getIdentifier()))
                {
                    //Force v2 to v3
                    identifier = ID3Tags.forceFrameID22To23(frame.getIdentifier());
                    if (identifier != null)
                    {
                        logger.config("V22Orig id is:" + frame.getIdentifier() + "New id is:" + identifier);
                        this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
                        this.frameBody.setHeader(this);
                        return;
                    }
                    //No mechanism exists to convert it to a v23 frame
                    else
                    {
                        this.frameBody = new FrameBodyDeprecated((AbstractID3v2FrameBody) frame.getBody());
                        this.frameBody.setHeader(this);
                        identifier = frame.getIdentifier();
                        logger.config("Deprecated:V22:orig id id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                        return;
                    }
                }
            }
            // Unknown Frame e.g NCON
            else
            {
                this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
                this.frameBody.setHeader(this);
                identifier = frame.getIdentifier();
                logger.config("UNKNOWN:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                return;
            }
        }

        logger.warning("Frame is unknown version:"+frame.getClass());
    
public ID3v23Frame(ByteBuffer byteBuffer, String loggingFilename)
Creates a new ID3v23Frame dataType by reading from byteBuffer.

param
byteBuffer to read from
param
loggingFilename
throws
org.jaudiotagger.tag.InvalidFrameException

        setLoggingFilename(loggingFilename);
        read(byteBuffer);
    
public ID3v23Frame(ByteBuffer byteBuffer)
Creates a new ID3v23Frame dataType by reading from byteBuffer.

param
byteBuffer to read from
deprecated
use {@link #ID3v23Frame(ByteBuffer,String)} instead
throws
org.jaudiotagger.tag.InvalidFrameException

        this(byteBuffer, "");
    
Methods Summary
public voidcreateStructure()
Return String Representation of body

        MP3File.getStructureFormatter().openHeadingElement(TYPE_FRAME, getIdentifier());
        MP3File.getStructureFormatter().addElement(TYPE_FRAME_SIZE, frameSize);
        statusFlags.createStructure();
        encodingFlags.createStructure();
        frameBody.createStructure();
        MP3File.getStructureFormatter().closeHeadingElement(TYPE_FRAME);
    
public booleanequals(java.lang.Object obj)
Compare for equality To be deemed equal obj must be a IDv23Frame with the same identifier and the same flags. containing the same body,dataType list ectera. equals() method is made up from all the various components

param
obj
return
if true if this object is equivalent to obj

        if ( this == obj ) return true;

        if (!(obj instanceof ID3v23Frame))
        {
            return false;
        }
        ID3v23Frame that = (ID3v23Frame) obj;


        return
              EqualsUtil.areEqual(this.statusFlags, that.statusFlags) &&
              EqualsUtil.areEqual(this.encodingFlags, that.encodingFlags) &&
              super.equals(that);

    
public AbstractID3v2Frame.EncodingFlagsgetEncodingFlags()

        return encodingFlags;
    
public intgetEncryptionMethod()

        return encryptionMethod;
    
protected intgetFrameHeaderSize()

        return FRAME_HEADER_SIZE;
    
protected intgetFrameIdSize()


      
    
        return FRAME_ID_SIZE;
    
protected intgetFrameSizeSize()

        return FRAME_SIZE_SIZE;
    
public intgetGroupIdentifier()

        return groupIdentifier;
    
public intgetSize()
Return size of frame

return
int frame size

        return frameBody.getSize() + ID3v23Frame.FRAME_HEADER_SIZE;
    
public AbstractID3v2Frame.StatusFlagsgetStatusFlags()

        return statusFlags;
    
public booleanisBinary()

return
true if considered a common frame

        return ID3v23Frames.getInstanceOf().isBinary(getId());
    
public booleanisCommon()

return
true if considered a common frame

        return ID3v23Frames.getInstanceOf().isCommon(getId());
    
public booleanisValidID3v2FrameIdentifier(java.lang.String identifier)
Does the frame identifier meet the syntax for a idv3v2 frame identifier. must start with a capital letter and only contain capital letters and numbers

param
identifier to be checked
return
whether the identifier is valid

        Matcher m = ID3v23Frame.validFrameIdentifier.matcher(identifier);
        return m.matches();
    
public voidread(java.nio.ByteBuffer byteBuffer)
Read the frame from a byteBuffer

param
byteBuffer buffer to read from

        String identifier = readIdentifier(byteBuffer);
        if (!isValidID3v2FrameIdentifier(identifier))
        {
            logger.config(getLoggingFilename() + ":Invalid identifier:" + identifier);
            byteBuffer.position(byteBuffer.position() - (getFrameIdSize() - 1));
            throw new InvalidFrameIdentifierException(getLoggingFilename() + ":" + identifier + ":is not a valid ID3v2.30 frame");
        }
        //Read the size field (as Big Endian Int - byte buffers always initialised to Big Endian order)
        frameSize = byteBuffer.getInt();
        if (frameSize < 0)
        {
            logger.warning(getLoggingFilename() + ":Invalid Frame Size:" + identifier);
            throw new InvalidFrameException(identifier + " is invalid frame");
        }
        else if (frameSize == 0)
        {
            logger.warning(getLoggingFilename() + ":Empty Frame Size:" + identifier);
            //We don't process this frame or add to frameMap because contains no useful information
            //Skip the two flag bytes so in correct position for subsequent frames
            byteBuffer.get();
            byteBuffer.get();
            throw new EmptyFrameException(identifier + " is empty frame");
        }
        else if (frameSize > byteBuffer.remaining())
        {
            logger.warning(getLoggingFilename() + ":Invalid Frame size of " +frameSize +" larger than size of" + byteBuffer.remaining() + " before mp3 audio:" + identifier);
            throw new InvalidFrameException(identifier + " is invalid frame");
        }

        //Read the flag bytes
        statusFlags = new StatusFlags(byteBuffer.get());
        encodingFlags = new EncodingFlags(byteBuffer.get());
        String id;

        //If this identifier is a valid v24 identifier or easily converted to v24
        id = ID3Tags.convertFrameID23To24(identifier);

        // Cant easily be converted to v24 but is it a valid v23 identifier
        if (id == null)
        {
            // It is a valid v23 identifier so should be able to find a
            //  frame body for it.
            if (ID3Tags.isID3v23FrameIdentifier(identifier))
            {
                id = identifier;
            }
            // Unknown so will be created as FrameBodyUnsupported
            else
            {
                id = UNSUPPORTED_ID;
            }
        }
        logger.fine(getLoggingFilename() + ":Identifier was:" + identifier + " reading using:" + id + "with frame size:" + frameSize);

        //Read extra bits appended to frame header for various encodings
        //These are not included in header size but are included in frame size but won't be read when we actually
        //try to read the frame body data
        int extraHeaderBytesCount = 0;
        int decompressedFrameSize = -1;
        if (((EncodingFlags) encodingFlags).isCompression())
        {
            //Read the Decompressed Size
            decompressedFrameSize = byteBuffer.getInt();
            extraHeaderBytesCount = FRAME_COMPRESSION_UNCOMPRESSED_SIZE;
            logger.fine(getLoggingFilename() + ":Decompressed frame size is:" + decompressedFrameSize);
        }

        if (((EncodingFlags) encodingFlags).isEncryption())
        {
           //Consume the encryption byte
            extraHeaderBytesCount += FRAME_ENCRYPTION_INDICATOR_SIZE;
            encryptionMethod = byteBuffer.get();
        }

        if (((EncodingFlags) encodingFlags).isGrouping())
        {
            //Read the Grouping byte, but do nothing with it
            extraHeaderBytesCount += FRAME_GROUPING_INDICATOR_SIZE;
            groupIdentifier = byteBuffer.get();
        }

        //Work out the real size of the frameBody data
        int realFrameSize = frameSize - extraHeaderBytesCount;

        ByteBuffer frameBodyBuffer;
        //Read the body data
        try
        {
            if (((EncodingFlags) encodingFlags).isCompression())
            {
                frameBodyBuffer = ID3Compression.uncompress(identifier,getLoggingFilename(),byteBuffer, decompressedFrameSize, realFrameSize);
                frameBody = readBody(id, frameBodyBuffer, decompressedFrameSize);
            }
            else if (((EncodingFlags) encodingFlags).isEncryption())
            {
                frameBodyBuffer = byteBuffer.slice();
                frameBodyBuffer.limit(realFrameSize);
                frameBody = readEncryptedBody(identifier, byteBuffer,frameSize);
            }
            else
            {
                //Create Buffer that only contains the body of this frame rather than the remainder of tag
                frameBodyBuffer = byteBuffer.slice();
                frameBodyBuffer.limit(realFrameSize);
                frameBody = readBody(id, frameBodyBuffer, realFrameSize);
            }
            //TODO code seems to assume that if the frame created is not a v23FrameBody
            //it should be deprecated, but what about if somehow a V24Frame has been put into a V23 Tag, shouldn't
            //it then be created as FrameBodyUnsupported
            if (!(frameBody instanceof ID3v23FrameBody))
            {
                logger.config(getLoggingFilename() + ":Converted frameBody with:" + identifier + " to deprecated frameBody");
                frameBody = new FrameBodyDeprecated((AbstractID3v2FrameBody) frameBody);
            }
        }
        finally
        {
            //Update position of main buffer, so no attempt is made to reread these bytes
            byteBuffer.position(byteBuffer.position() + realFrameSize);
        }
    
public voidsetEncoding(java.lang.String encoding)
Sets the charset encoding used by the field.

param
encoding charset.

        Integer encodingId = TextEncoding.getInstanceOf().getIdForValue(encoding);
        if(encoding!=null)
        {
            if(encodingId <2)
            {
                this.getBody().setTextEncoding(encodingId.byteValue());
            }
        }
    
public voidwrite(java.io.ByteArrayOutputStream tagBuffer)
Write the frame to bufferOutputStream

throws
IOException

        logger.config("Writing frame to buffer:" + getIdentifier());
        //This is where we will write header, move position to where we can
        //write body
        ByteBuffer headerBuffer = ByteBuffer.allocate(FRAME_HEADER_SIZE);

        //Write Frame Body Data
        ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream();
        ((AbstractID3v2FrameBody) frameBody).write(bodyOutputStream);
        //Write Frame Header write Frame ID
        if (getIdentifier().length() == 3)
        {
            identifier = identifier + ' ";
        }
        headerBuffer.put(Utils.getDefaultBytes(getIdentifier(), "ISO-8859-1"), 0, FRAME_ID_SIZE);
        //Write Frame Size
        int size = frameBody.getSize();
        logger.fine("Frame Size Is:" + size);
        headerBuffer.putInt(frameBody.getSize());

        //Write the Flags
        //Status Flags:leave as they were when we read
        headerBuffer.put(statusFlags.getWriteFlags());

        //Remove any non standard flags
        ((EncodingFlags) encodingFlags).unsetNonStandardFlags();

        //Unset Compression flag if previously set because we uncompress previously compressed frames on write.
        ((EncodingFlags)encodingFlags).unsetCompression();
        headerBuffer.put(encodingFlags.getFlags());

        try
        {
            //Add header to the Byte Array Output Stream
            tagBuffer.write(headerBuffer.array());

            if (((EncodingFlags) encodingFlags).isEncryption())
            {
               tagBuffer.write(encryptionMethod);
            }

            if (((EncodingFlags) encodingFlags).isGrouping())
            {
                tagBuffer.write(groupIdentifier);
            }

            //Add body to the Byte Array Output Stream
            tagBuffer.write(bodyOutputStream.toByteArray());
        }
        catch (IOException ioe)
        {
            //This could never happen coz not writing to file, so convert to RuntimeException
            throw new RuntimeException(ioe);
        }