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

ID3v22Tag

public class ID3v22Tag extends AbstractID3v2Tag
Represents an ID3v2.2 tag.
author
: Paul Taylor
author
: Eric Farng
version
$Id: ID3v22Tag.java 976 2011-06-08 10:05:34Z paultaylor $

Fields Summary
protected static final String
TYPE_COMPRESSION
protected static final String
TYPE_UNSYNCHRONISATION
public static final int
MASK_V22_UNSYNCHRONIZATION
Bit mask to indicate tag is Unsychronization
public static final int
MASK_V22_COMPRESSION
Bit mask to indicate tag is compressed, although compression is not actually defined in v22 so just ignored
protected boolean
compression
The tag is compressed, although no compression scheme is defined in ID3v22
protected boolean
unsynchronization
If set all frames in the tag uses unsynchronisation
public static final byte
RELEASE
public static final byte
MAJOR_VERSION
public static final byte
REVISION
Constructors Summary
public ID3v22Tag()
Creates a new empty ID3v2_2 tag.

        frameMap = new LinkedHashMap();
        encryptedFrameMap = new LinkedHashMap();
    
public ID3v22Tag(ID3v22Tag copyObject)
Copy Constructor, creates a new ID3v2_2 Tag based on another ID3v2_2 Tag

param
copyObject

        //This doesnt do anything.
        super(copyObject);
        logger.config("Creating tag from another tag of same type");
        copyPrimitives(copyObject);
        copyFrames(copyObject);
    
public ID3v22Tag(AbstractTag mp3tag)
Constructs a new tag based upon another tag of different version/type

param
mp3tag

        frameMap = new LinkedHashMap();
        encryptedFrameMap = new LinkedHashMap();
        logger.config("Creating tag from a tag of a different version");
        //Default Superclass constructor does nothing
        if (mp3tag != null)
        {
            ID3v24Tag convertedTag;
            //Should use the copy constructor instead
            if ((!(mp3tag instanceof ID3v23Tag)) && (mp3tag instanceof ID3v22Tag))
            {
                throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
            }
            //If v2.4 can getFields variables from this
            else if (mp3tag instanceof ID3v24Tag)
            {
                convertedTag = (ID3v24Tag) mp3tag;
            }
            //Any tag (e.g lyrics3 and idv1.1,idv2.3 can be converted to id32.4 so do that
            //to simplify things
            else
            {
                convertedTag = new ID3v24Tag(mp3tag);
            }
            this.setLoggingFilename(convertedTag.getLoggingFilename());
            //Set the primitive types specific to v2_2.
            copyPrimitives(convertedTag);
            //Set v2.2 Frames
            copyFrames(convertedTag);
            logger.config("Created tag from a tag of a different version");
        }
    
public ID3v22Tag(ByteBuffer buffer, String loggingFilename)
Creates a new ID3v2_2 datatype.

param
buffer
param
loggingFilename
throws
TagException

        setLoggingFilename(loggingFilename);
        this.read(buffer);
    
public ID3v22Tag(ByteBuffer buffer)
Creates a new ID3v2_2 datatype.

param
buffer
throws
TagException
deprecated
use {@link #ID3v22Tag(ByteBuffer,String)} instead

        this(buffer, "");
    
Methods Summary
protected voidaddFrame(AbstractID3v2Frame frame)

        try
        {
            //Special case to handle TDRC frame from V24 that needs breaking up into separate frame in V23
            if ((frame.getIdentifier().equals(ID3v24Frames.FRAME_ID_YEAR)) && (frame.getBody() instanceof FrameBodyTDRC))
            {
                translateFrame(frame);
            }
            else if (frame instanceof ID3v22Frame)
            {
                 copyFrameIntoMap(frame.getIdentifier(),frame);
            }
            else
            {
                ID3v22Frame newFrame = new ID3v22Frame(frame);
                copyFrameIntoMap(newFrame.getIdentifier(), newFrame);
            }
        }
        catch (InvalidFrameException ife)
        {
            logger.log(Level.SEVERE, "Unable to convert frame:" + frame.getIdentifier());
        }
    
protected voidcopyPrimitives(AbstractID3v2Tag copyObj)
Copy primitives applicable to v2.2

        logger.config("Copying primitives");
        super.copyPrimitives(copyObj);

        //Set the primitive types specific to v2_2.
        if (copyObj instanceof ID3v22Tag)
        {
            ID3v22Tag copyObject = (ID3v22Tag) copyObj;
            this.compression = copyObject.compression;
            this.unsynchronization = copyObject.unsynchronization;
        }
        else if (copyObj instanceof ID3v23Tag)
        {
            ID3v23Tag copyObject = (ID3v23Tag) copyObj;
            this.compression = copyObject.compression;
            this.unsynchronization = copyObject.unsynchronization;
        }
        else if (copyObj instanceof ID3v24Tag)
        {
            ID3v24Tag copyObject = (ID3v24Tag) copyObj;
            this.compression = false;
            this.unsynchronization = copyObject.unsynchronization;
        }
    
public TagFieldcreateArtworkField(byte[] data, java.lang.String mimeType)

        AbstractID3v2Frame frame = createFrame(getFrameAndSubIdFromGenericKey(FieldKey.COVER_ART).getFrameId());
        FrameBodyPIC body = (FrameBodyPIC) frame.getBody();
        body.setObjectValue(DataTypes.OBJ_PICTURE_DATA, data);
        body.setObjectValue(DataTypes.OBJ_PICTURE_TYPE, PictureTypes.DEFAULT_ID);
        body.setObjectValue(DataTypes.OBJ_IMAGE_FORMAT, ImageFormats.getFormatForMimeType(mimeType));
        body.setObjectValue(DataTypes.OBJ_DESCRIPTION, "");
        return frame;
    
public TagFieldcreateField(ID3v22FieldKey id3Key, java.lang.String value)
Create Frame for Id3 Key

Only textual data supported at the moment, should only be used with frames that support a simple string argument.

param
id3Key
param
value
return
throws
KeyNotFoundException
throws
FieldDataInvalidException

        if (id3Key == null)
        {
            throw new KeyNotFoundException();
        }
        return super.doCreateTagField(new FrameAndSubId(id3Key.getFrameId(), id3Key.getSubId()), value);
    
public TagFieldcreateField(org.jaudiotagger.tag.images.Artwork artwork)
{@inheritDoc}

        AbstractID3v2Frame frame = createFrame(getFrameAndSubIdFromGenericKey(FieldKey.COVER_ART).getFrameId());
        FrameBodyPIC body = (FrameBodyPIC) frame.getBody();
        if(!artwork.isLinked())
        {
            body.setObjectValue(DataTypes.OBJ_PICTURE_DATA, artwork.getBinaryData());
            body.setObjectValue(DataTypes.OBJ_PICTURE_TYPE, artwork.getPictureType());
            body.setObjectValue(DataTypes.OBJ_IMAGE_FORMAT, ImageFormats.getFormatForMimeType(artwork.getMimeType()));
            body.setObjectValue(DataTypes.OBJ_DESCRIPTION, "");
            return frame;
        }
        else
        {
            try
            {
                body.setObjectValue(DataTypes.OBJ_PICTURE_DATA,artwork.getImageUrl().getBytes("ISO-8859-1"));
            }
            catch(UnsupportedEncodingException uoe)
            {
                throw new RuntimeException(uoe.getMessage());
            }
            body.setObjectValue(DataTypes.OBJ_PICTURE_TYPE, artwork.getPictureType());
            body.setObjectValue(DataTypes.OBJ_IMAGE_FORMAT, FrameBodyAPIC.IMAGE_IS_URL);
            body.setObjectValue(DataTypes.OBJ_DESCRIPTION, "");
            return frame;
        }
    
public ID3v22FramecreateFrame(java.lang.String id)
Create Frame

param
id frameid
return

        return new ID3v22Frame(id);
    
public voidcreateStructure()

        MP3File.getStructureFormatter().openHeadingElement(TYPE_TAG, getIdentifier());

        super.createStructureHeader();

        //Header
        MP3File.getStructureFormatter().openHeadingElement(TYPE_HEADER, "");
        MP3File.getStructureFormatter().addElement(TYPE_COMPRESSION, this.compression);
        MP3File.getStructureFormatter().addElement(TYPE_UNSYNCHRONISATION, this.unsynchronization);
        MP3File.getStructureFormatter().closeHeadingElement(TYPE_HEADER);
        //Body
        super.createStructureBody();

        MP3File.getStructureFormatter().closeHeadingElement(TYPE_TAG);
    
public voiddeleteField(ID3v22FieldKey id3v22FieldKey)
Delete fields with this id3v22FieldKey

param
id3v22FieldKey
throws
org.jaudiotagger.tag.KeyNotFoundException

        if (id3v22FieldKey == null)
        {
            throw new KeyNotFoundException();
        }
        super.doDeleteTagField(new FrameAndSubId(id3v22FieldKey.getFrameId(), id3v22FieldKey.getSubId()));
    
public voiddeleteField(java.lang.String id)
Delete fields with this (frame) id

param
id

        super.doDeleteTagField(new FrameAndSubId(id,null));
    
public booleanequals(java.lang.Object obj)

param
obj
return
equality

        if (!(obj instanceof ID3v22Tag))
        {
            return false;
        }
        ID3v22Tag object = (ID3v22Tag) obj;
        if (this.compression != object.compression)
        {
            return false;
        }
        return this.unsynchronization == object.unsynchronization && super.equals(obj);
    
public java.util.ListgetArtworkList()
{@inheritDoc}

        List<TagField> coverartList = getFields(FieldKey.COVER_ART);
        List<Artwork> artworkList   = new ArrayList<Artwork>(coverartList.size());

        for (TagField next : coverartList)
        {
            FrameBodyPIC coverArt = (FrameBodyPIC) ((AbstractID3v2Frame) next).getBody();
            Artwork artwork = ArtworkFactory.getNew();
            artwork.setMimeType(ImageFormats.getMimeTypeForFormat(coverArt.getFormatType()));
            artwork.setPictureType(coverArt.getPictureType());
            if (coverArt.isImageUrl())
            {
                artwork.setLinked(true);
                artwork.setImageUrl(coverArt.getImageUrl());
            }
            else
            {
                artwork.setBinaryData(coverArt.getImageData());
            }
            artworkList.add(artwork);
        }
        return artworkList;
    
public java.lang.StringgetFirst(ID3v22FieldKey id3v22FieldKey)
Retrieve the first value that exists for this id3v22key

param
id3v22FieldKey
return
throws
org.jaudiotagger.tag.KeyNotFoundException

        if (id3v22FieldKey == null)
        {
            throw new KeyNotFoundException();
        }

        FrameAndSubId frameAndSubId = new FrameAndSubId(id3v22FieldKey.getFrameId(), id3v22FieldKey.getSubId());
        if (id3v22FieldKey == ID3v22FieldKey.TRACK)
        {
            AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
            return String.valueOf(((FrameBodyTRCK)frame.getBody()).getTrackNo());
        }
        else if (id3v22FieldKey == ID3v22FieldKey.TRACK_TOTAL)
        {
            AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
            return String.valueOf(((FrameBodyTRCK)frame.getBody()).getTrackTotal());
        }
        else if (id3v22FieldKey == ID3v22FieldKey.DISC_NO)
        {
            AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
            return String.valueOf(((FrameBodyTPOS)frame.getBody()).getDiscNo());
        }
        else if (id3v22FieldKey == ID3v22FieldKey.DISC_TOTAL)
        {
            AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
            return String.valueOf(((FrameBodyTPOS)frame.getBody()).getDiscTotal());
        }
        else
        {
            return super.doGetValueAtIndex(frameAndSubId, 0);
        }
    
protected FrameAndSubIdgetFrameAndSubIdFromGenericKey(FieldKey genericKey)

        ID3v22FieldKey id3v22FieldKey = ID3v22Frames.getInstanceOf().getId3KeyFromGenericKey(genericKey);
        if (id3v22FieldKey == null)
        {
            throw new KeyNotFoundException();
        }
        return new FrameAndSubId(id3v22FieldKey.getFrameId(), id3v22FieldKey.getSubId());
    
protected ID3FramesgetID3Frames()

        return ID3v22Frames.getInstanceOf();
    
public java.lang.StringgetIdentifier()

return
an indentifier of the tag type

        return "ID3v2_2.20";
    
public bytegetMajorVersion()
Retrieve the Major Version

        return MAJOR_VERSION;
    
public java.util.ComparatorgetPreferredFrameOrderComparator()

return
comparator used to order frames in preffrred order for writing to file so that most important frames are written first.

        return ID3v22PreferredFrameOrderComparator.getInstanceof();
    
public bytegetRelease()
Retrieve the Release


            
      
    
        return RELEASE;
    
public bytegetRevision()
Retrieve the Revision

        return REVISION;
    
public intgetSize()
Return frame size based upon the sizes of the frames rather than the size including padding recorded in the tag header

return
size

        int size = TAG_HEADER_LENGTH;
        size += super.getSize();
        return size;
    
public booleanisCompression()

return
is tag compressed

        return compression;
    
public booleanisUnsynchronization()

return
is tag unsynchronized

        return unsynchronization;
    
public voidread(java.nio.ByteBuffer byteBuffer)
{@inheritDoc}

        int size;
        if (!seek(byteBuffer))
        {
            throw new TagNotFoundException("ID3v2.20 tag not found");
        }
        logger.config(getLoggingFilename() + ":" + "Reading tag from file");

        //Read the flags
        readHeaderFlags(byteBuffer);

        // Read the size
        size = ID3SyncSafeInteger.bufferToValue(byteBuffer);

        //Slice Buffer, so position markers tally with size (i.e do not include tagheader)
        ByteBuffer bufferWithoutHeader = byteBuffer.slice();

        //We need to synchronize the buffer
        if (unsynchronization)
        {
            bufferWithoutHeader = ID3Unsynchronization.synchronize(bufferWithoutHeader);
        }
        readFrames(bufferWithoutHeader, size);
        logger.config(getLoggingFilename() + ":" + "Loaded Frames,there are:" + frameMap.keySet().size());
    
protected voidreadFrames(java.nio.ByteBuffer byteBuffer, int size)
Read frames from tag

param
byteBuffer
param
size

        //Now start looking for frames
        ID3v22Frame next;
        frameMap = new LinkedHashMap();
        encryptedFrameMap = new LinkedHashMap();

        //Read the size from the Tag Header
        this.fileReadSize = size;
        logger.finest(getLoggingFilename() + ":" + "Start of frame body at:" + byteBuffer.position() + ",frames sizes and padding is:" + size);
        /* todo not done yet. Read the first Frame, there seems to be quite a
         ** common case of extra data being between the tag header and the first
         ** frame so should we allow for this when reading first frame, but not subsequent frames
         */
        // Read the frames until got to upto the size as specified in header
        while (byteBuffer.position() < size)
        {
            try
            {
                //Read Frame
                logger.finest(getLoggingFilename() + ":" + "looking for next frame at:" + byteBuffer.position());
                next = new ID3v22Frame(byteBuffer, getLoggingFilename());
                String id = next.getIdentifier();
                loadFrameIntoMap(id, next);
            }
            //Found Padding, no more frames
            catch (PaddingException ex)
            {
                logger.config(getLoggingFilename() + ":Found padding starting at:" + byteBuffer.position());
                break;
            }
            //Found Empty Frame
            catch (EmptyFrameException ex)
            {
                logger.warning(getLoggingFilename() + ":" + "Empty Frame:" + ex.getMessage());
                this.emptyFrameBytes += ID3v22Frame.FRAME_HEADER_SIZE;
            }
            catch (InvalidFrameIdentifierException ifie)
            {
                logger.config(getLoggingFilename() + ":" + "Invalid Frame Identifier:" + ifie.getMessage());
                this.invalidFrames++;
                //Dont try and find any more frames
                break;
            }
            //Problem trying to find frame
            catch (InvalidFrameException ife)
            {
                logger.warning(getLoggingFilename() + ":" + "Invalid Frame:" + ife.getMessage());
                this.invalidFrames++;
                //Dont try and find any more frames
                break;
            }
            //Failed reading frame but may just have invalid data but correct length so lets carry on
            //in case we can read the next frame
            catch(InvalidDataTypeException idete)
            {
                logger.warning(getLoggingFilename() + ":Corrupt Frame:" + idete.getMessage());
                this.invalidFrames++;
                continue;
            }
        }
    
private voidreadHeaderFlags(java.nio.ByteBuffer byteBuffer)
Read tag Header Flags

param
byteBuffer
throws
TagException

        //Flags
        byte flags = byteBuffer.get();
        unsynchronization = (flags & MASK_V22_UNSYNCHRONIZATION) != 0;
        compression = (flags & MASK_V22_COMPRESSION) != 0;

        if (unsynchronization)
        {
            logger.config(ErrorMessage.ID3_TAG_UNSYNCHRONIZED.getMsg(getLoggingFilename()));
        }

        if (compression)
        {
            logger.config(ErrorMessage.ID3_TAG_COMPRESSED.getMsg(getLoggingFilename()));
        }

        //Not allowable/Unknown Flags
        if ((flags & FileConstants.BIT5) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT5));
        }
        if ((flags & FileConstants.BIT4) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT4));
        }
        if ((flags & FileConstants.BIT3) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT3));
        }
        if ((flags & FileConstants.BIT2) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT2));
        }
        if ((flags & FileConstants.BIT1) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT1));
        }
        if ((flags & FileConstants.BIT0) != 0)
        {
            logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT3));
        }
    
protected voidtranslateFrame(AbstractID3v2Frame frame)
This is used when we need to translate a single frame into multiple frames, currently required for TDRC frames.

param
frame

        FrameBodyTDRC tmpBody = (FrameBodyTDRC) frame.getBody();
        ID3v22Frame newFrame;
        if (tmpBody.getYear().length() != 0)
        {
            //Create Year frame (v2.2 id,but uses v2.3 body)
            newFrame = new ID3v22Frame(ID3v22Frames.FRAME_ID_V2_TYER);
            ((AbstractFrameBodyTextInfo) newFrame.getBody()).setText(tmpBody.getYear());
            frameMap.put(newFrame.getIdentifier(), newFrame);
        }
        if (tmpBody.getTime().length() != 0)
        {
            //Create Time frame (v2.2 id,but uses v2.3 body)
            newFrame = new ID3v22Frame(ID3v22Frames.FRAME_ID_V2_TIME);
            ((AbstractFrameBodyTextInfo) newFrame.getBody()).setText(tmpBody.getTime());
            frameMap.put(newFrame.getIdentifier(), newFrame);
        }
    
public voidwrite(java.io.File file, long audioStartLocation)
{@inheritDoc}

        setLoggingFilename(file.getName());
        logger.config("Writing tag to file:"+getLoggingFilename());

        // Write Body Buffer
        byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();

        // Unsynchronize if option enabled and unsync required
        unsynchronization = TagOptionSingleton.getInstance().isUnsyncTags() && ID3Unsynchronization.requiresUnsynchronization(bodyByteBuffer);
        if (isUnsynchronization())
        {
            bodyByteBuffer = ID3Unsynchronization.unsynchronize(bodyByteBuffer);
            logger.config(getLoggingFilename() + ":bodybytebuffer:sizeafterunsynchronisation:" + bodyByteBuffer.length);
        }

        int sizeIncPadding = calculateTagSize(bodyByteBuffer.length + TAG_HEADER_LENGTH, (int) audioStartLocation);
        int padding = sizeIncPadding - (bodyByteBuffer.length + TAG_HEADER_LENGTH);
        logger.config(getLoggingFilename() + ":Current audiostart:" + audioStartLocation);
        logger.config(getLoggingFilename() + ":Size including padding:" + sizeIncPadding);
        logger.config(getLoggingFilename() + ":Padding:" + padding);

        ByteBuffer headerBuffer = writeHeaderToBuffer(padding, bodyByteBuffer.length);
        writeBufferToFile(file,headerBuffer, bodyByteBuffer,padding,sizeIncPadding,audioStartLocation);
    
public voidwrite(java.nio.channels.WritableByteChannel channel)
{@inheritDoc}

        logger.config(getLoggingFilename() + ":Writing tag to channel");

        byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();
        logger.config(getLoggingFilename() + ":bodybytebuffer:sizebeforeunsynchronisation:" + bodyByteBuffer.length);

        //Unsynchronize if option enabled and unsync required
        unsynchronization = TagOptionSingleton.getInstance().isUnsyncTags() && ID3Unsynchronization.requiresUnsynchronization(bodyByteBuffer);
        if (isUnsynchronization())
        {
            bodyByteBuffer = ID3Unsynchronization.unsynchronize(bodyByteBuffer);
            logger.config(getLoggingFilename() + ":bodybytebuffer:sizeafterunsynchronisation:" + bodyByteBuffer.length);
        }
        ByteBuffer headerBuffer = writeHeaderToBuffer(0, bodyByteBuffer.length);

        channel.write(headerBuffer);
        channel.write(ByteBuffer.wrap(bodyByteBuffer));
    
private java.nio.ByteBufferwriteHeaderToBuffer(int padding, int size)
Write the ID3 header to the ByteBuffer.

param
padding
param
size
return
ByteBuffer
throws
IOException

        compression = false;

        //Create Header Buffer
        ByteBuffer headerBuffer = ByteBuffer.allocate(TAG_HEADER_LENGTH);

        //TAGID
        headerBuffer.put(TAG_ID);
        //Major Version
        headerBuffer.put(getMajorVersion());
        //Minor Version
        headerBuffer.put(getRevision());

        //Flags
        byte flags = (byte) 0;
        if (unsynchronization)
        {
            flags |= (byte) MASK_V22_UNSYNCHRONIZATION;
        }
        if (compression)
        {
            flags |= (byte) MASK_V22_COMPRESSION;
        }

        headerBuffer.put(flags);

        //Size As Recorded in Header, don't include the main header length
        headerBuffer.put(ID3SyncSafeInteger.valueToBuffer(padding + size));
        headerBuffer.flip();
        return headerBuffer;