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

ID3v22Frame

public class ID3v22Frame extends AbstractID3v2Frame
Represents an ID3v2.2 frame.
author
: Paul Taylor
author
: Eric Farng
version
$Id: ID3v22Frame.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_SIZE_SIZE
protected static final int
FRAME_HEADER_SIZE
Constructors Summary
public ID3v22Frame()


     
    

    
public ID3v22Frame(AbstractID3v2Frame frame)
Creates a new ID3v22 Frame from another frame of a different tag version

param
frame to construct the new frame from
throws
org.jaudiotagger.tag.InvalidFrameException

        logger.config("Creating frame from a frame of a different version");
        if (frame instanceof ID3v22Frame)
        {
            throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
        }

        // If it is a v24 frame is it possible to convert it into a v23 frame, anmd then convert from that
        if (frame instanceof ID3v24Frame)
        {
            ID3v23Frame v23Frame = new ID3v23Frame(frame);
            createV22FrameFromV23Frame(v23Frame);
        }
        //If it is a v23 frame is it possible to convert it into a v22 frame
        else if (frame instanceof ID3v23Frame)
        {
            createV22FrameFromV23Frame((ID3v23Frame) frame);
        }
        this.frameBody.setHeader(this);
        logger.config("Created frame from a frame of a different version");
    
public ID3v22Frame(ByteBuffer byteBuffer, String loggingFilename)
Creates a new ID3v22Frame datatype by reading from byteBuffer.

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

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

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

        this(byteBuffer, "");
    
public ID3v22Frame(org.jaudiotagger.tag.id3.framebody.AbstractID3v2FrameBody body)
Creates a new ID3v22 Frame with given body

param
body New body and frame is based on this

        super(body);
    
public ID3v22Frame(String identifier)
Creates a new ID3v22 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 values

param
identifier


        logger.config("Creating empty frame of type" + identifier);
        String bodyIdentifier = identifier;
        this.identifier = identifier;

        //If dealing with v22 identifier (Note this constructor is used by all three tag versions)
        if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
        {
            //Does it have its own framebody (PIC,CRM) or are we using v23/v24 body (the normal case)
            if (ID3Tags.forceFrameID22To23(bodyIdentifier) != null)
            {
                //Do not convert
            }
            else if(bodyIdentifier.equals("CRM"))
            {
                //Do not convert.
                //TODO we don't have a way of converting this to v23 which is why its not in the ForceMap
            }
            //TODO Improve messy fix for datetime
            //TODO need to check in case v22 body does exist before using V23 body(e.g PIC)
            else if ((bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TYER)) || (bodyIdentifier.equals(ID3v22Frames.FRAME_ID_V2_TIME)))
            {
                bodyIdentifier = ID3v24Frames.FRAME_ID_YEAR;
            }
            // Have to check for v22 because most don't have own body they use v23 or v24
            // body to hold the data, the frame is identified by its identifier, the body identifier
            // is just to create a body suitable for writing the data to
            else if (ID3Tags.isID3v22FrameIdentifier(bodyIdentifier))
            {
                bodyIdentifier = ID3Tags.convertFrameID22To23(bodyIdentifier);
            }
        }

        // Use reflection to map id to frame body, which makes things much easier
        // to keep things up to date.
        try
        {
            Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody>) Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + bodyIdentifier);
            frameBody = c.newInstance();
        }
        catch (ClassNotFoundException cnfe)
        {
            logger.log(Level.SEVERE, cnfe.getMessage(), cnfe);
            frameBody = new FrameBodyUnsupported(identifier);
        }
        //Instantiate Interface/Abstract should not happen
        catch (InstantiationException ie)
        {
            logger.log(Level.SEVERE, ie.getMessage(), ie);
            throw new RuntimeException(ie);
        }
        //Private Constructor shouild not happen
        catch (IllegalAccessException iae)
        {
            logger.log(Level.SEVERE, iae.getMessage(), iae);
            throw new RuntimeException(iae);
        }
        frameBody.setHeader(this);
        logger.config("Created empty frame of type" + this.identifier + "with frame body of" + bodyIdentifier);

    
public ID3v22Frame(ID3v22Frame frame)
Copy Constructor

Creates a new v22 frame based on another v22 frame

param
frame

        super(frame);
        logger.config("Creating frame from a frame of same version");
    
Methods Summary
public voidcreateStructure()
Return String Representation of body

        MP3File.getStructureFormatter().openHeadingElement(TYPE_FRAME, getIdentifier());
        MP3File.getStructureFormatter().addElement(TYPE_FRAME_SIZE, frameSize);
        frameBody.createStructure();
        MP3File.getStructureFormatter().closeHeadingElement(TYPE_FRAME);
    
private voidcreateV22FrameFromV23Frame(ID3v23Frame frame)

        identifier = ID3Tags.convertFrameID23To22(frame.getIdentifier());
        if (identifier != null)
        {
            logger.config("V2:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
            this.frameBody = (AbstractID3v2FrameBody) ID3Tags.copyObject(frame.getBody());
        }
        // Is it a known v3 frame which needs forcing to v2 frame e.g. APIC - PIC
        else if (ID3Tags.isID3v23FrameIdentifier(frame.getIdentifier()))
        {
            identifier = ID3Tags.forceFrameID23To22(frame.getIdentifier());
            if (identifier != null)
            {
                logger.config("V2:Force:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
                this.frameBody = this.readBody(identifier, (AbstractID3v2FrameBody) frame.getBody());
            }
            // No mechanism exists to convert it to a v22 frame
            else
            {
                throw new InvalidFrameException("Unable to convert v23 frame:" + frame.getIdentifier() + " to a v22 frame");
            }
        }
        //Deprecated frame for v23
        else if (frame.getBody() instanceof FrameBodyDeprecated)
        {
            //Was it valid for this tag version, if so try and reconstruct
            if (ID3Tags.isID3v22FrameIdentifier(frame.getIdentifier()))
            {
                this.frameBody = frame.getBody();
                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());
                identifier = frame.getIdentifier();
                logger.config("DEPRECATED:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
            }
        }
        // Unknown Frame e.g NCON
        else
        {
            this.frameBody = new FrameBodyUnsupported((FrameBodyUnsupported) frame.getBody());
            identifier = frame.getIdentifier();
            logger.config("v2:UNKNOWN:Orig id is:" + frame.getIdentifier() + ":New id is:" + identifier);
        }
    
private intdecodeSize(byte[] buffer)
Read Frame Size, which has to be decoded

param
buffer
return

        BigInteger bi = new BigInteger(buffer);
        int tmpSize = bi.intValue();
        if (tmpSize < 0)
        {
            logger.warning("Invalid Frame Size of:" + tmpSize + "Decoded from bin:" + Integer.toBinaryString(tmpSize) + "Decoded from hex:" + Integer.toHexString(tmpSize));
        }
        return tmpSize;
    
private voidencodeSize(java.nio.ByteBuffer headerBuffer, int size)
Write Frame Size (can now be accurately calculated, have to convert 4 byte int to 3 byte format.

param
headerBuffer
param
size

        headerBuffer.put((byte) ((size & 0x00FF0000) >> 16));
        headerBuffer.put((byte) ((size & 0x0000FF00) >> 8));
        headerBuffer.put((byte) (size & 0x000000FF));
        logger.fine("Frame Size Is Actual:" + size + ":Encoded bin:" + Integer.toBinaryString(size) + ":Encoded Hex" + Integer.toHexString(size));
    
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 ID3v22Frame))
        {
            return false;
        }
        ID3v22Frame that = (ID3v22Frame) obj;


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

    
protected intgetFrameHeaderSize()

        return FRAME_HEADER_SIZE;
    
protected intgetFrameIdSize()

        return FRAME_ID_SIZE;
    
protected intgetFrameSizeSize()

        return FRAME_SIZE_SIZE;
    
public intgetSize()
Return size of frame

return
int size of frame

        return frameBody.getSize() + getFrameHeaderSize();
    
public booleanisBinary()

return
true if considered a common frame

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

return
true if considered a common frame

        return ID3v22Frames.getInstanceOf().isCommon(getId());
    
protected booleanisPadding(byte[] buffer)

        if(
                (buffer[0]=='\0")&&
                (buffer[1]=='\0")&&
                (buffer[2]=='\0")
           )
        {
            return true;
        }
        return false;
    
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
return

        Matcher m = ID3v22Frame.validFrameIdentifier.matcher(identifier);
        return m.matches();
    
public voidread(java.nio.ByteBuffer byteBuffer)
Read frame from file. Read the frame header then delegate reading of data to frame body.

param
byteBuffer

        String identifier = readIdentifier(byteBuffer);

        byte[] buffer = new byte[getFrameSizeSize()];

        // Is this a valid identifier?
        if (!isValidID3v2FrameIdentifier(identifier))
        {
            logger.config("Invalid identifier:" + identifier);
            byteBuffer.position(byteBuffer.position() - (getFrameIdSize() - 1));
            throw new InvalidFrameIdentifierException(getLoggingFilename() + ":" + identifier + ":is not a valid ID3v2.20 frame");
        }
        //Read Frame Size (same size as Frame Id so reuse buffer)
        byteBuffer.get(buffer, 0, getFrameSizeSize());
        frameSize = decodeSize(buffer);
        if (frameSize < 0)
        {
            throw new InvalidFrameException(identifier + " has invalid size of:" + frameSize);
        }
        else if (frameSize == 0)
        {
            //We dont process this frame or add to framemap becuase contains no useful information
            logger.warning("Empty Frame:" + identifier);
            throw new EmptyFrameException(identifier + " is empty frame");
        }
        else if (frameSize > byteBuffer.remaining())
        {
            logger.warning("Invalid Frame size larger than size before mp3 audio:" + identifier);
            throw new InvalidFrameException(identifier + " is invalid frame");
        }
        else
        {
            logger.fine("Frame Size Is:" + frameSize);
            //Convert v2.2 to v2.4 id just for reading the data
            String id = ID3Tags.convertFrameID22To24(identifier);
            if (id == null)
            {
                //OK,it may be convertable to a v.3 id even though not valid v.4
                id = ID3Tags.convertFrameID22To23(identifier);
                if (id == null)
                {
                    // Is it a valid v22 identifier so should be able to find a
                    // frame body for it.
                    if (ID3Tags.isID3v22FrameIdentifier(identifier))
                    {
                        id = identifier;
                    }
                    // Unknown so will be created as FrameBodyUnsupported
                    else
                    {
                        id = UNSUPPORTED_ID;
                    }
                }
            }
            logger.fine("Identifier was:" + identifier + " reading using:" + id);

            //Create Buffer that only contains the body of this frame rather than the remainder of tag
            ByteBuffer frameBodyBuffer = byteBuffer.slice();
            frameBodyBuffer.limit(frameSize);

            try
            {
                frameBody = readBody(id, frameBodyBuffer, frameSize);
            }
            finally
            {
                //Update position of main buffer, so no attempt is made to reread these bytes
                byteBuffer.position(byteBuffer.position() + frameSize);
            }
        }
    
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 Frame raw data

throws
IOException

        logger.config("Write Frame to Buffer" + getIdentifier());
        //This is where we will write header, move position to where we can
        //write body
        ByteBuffer headerBuffer = ByteBuffer.allocate(getFrameHeaderSize());

        //Write Frame Body Data
        ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream();
        ((AbstractID3v2FrameBody) frameBody).write(bodyOutputStream);

        //Write Frame Header
        //Write Frame ID must adjust can only be 3 bytes long
        headerBuffer.put(Utils.getDefaultBytes(getIdentifier(), "ISO-8859-1"), 0, getFrameIdSize());
        encodeSize(headerBuffer, frameBody.getSize());

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

            //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);
        }