Coverage Report - org.jaudiotagger.tag.id3.ID3v24Tag
 
Classes in this File Line Coverage Branch Coverage Complexity
ID3v24Tag
59%
256/427
36%
67/186
4.235
 
 1  
 /*
 2  
  *  MusicTag Copyright (C)2003,2004
 3  
  *
 4  
  *  This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
 5  
  *  General Public  License as published by the Free Software Foundation; either version 2.1 of the License,
 6  
  *  or (at your option) any later version.
 7  
  *
 8  
  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 9  
  *  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 10  
  *  See the GNU Lesser General Public License for more details.
 11  
  *
 12  
  *  You should have received a copy of the GNU Lesser General Public License along with this library; if not,
 13  
  *  you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
 14  
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 15  
  */
 16  
 package org.jaudiotagger.tag.id3;
 17  
 
 18  
 import org.jaudiotagger.FileConstants;
 19  
 import org.jaudiotagger.audio.mp3.MP3File;
 20  
 import org.jaudiotagger.logging.ErrorMessage;
 21  
 import org.jaudiotagger.tag.*;
 22  
 import org.jaudiotagger.tag.datatype.Artwork;
 23  
 import org.jaudiotagger.tag.datatype.DataTypes;
 24  
 import org.jaudiotagger.tag.id3.framebody.*;
 25  
 import org.jaudiotagger.tag.lyrics3.AbstractLyrics3;
 26  
 import org.jaudiotagger.tag.lyrics3.Lyrics3v2;
 27  
 import org.jaudiotagger.tag.lyrics3.Lyrics3v2Field;
 28  
 import org.jaudiotagger.tag.reference.GenreTypes;
 29  
 import org.jaudiotagger.tag.reference.PictureTypes;
 30  
 
 31  
 import java.io.File;
 32  
 import java.io.IOException;
 33  
 import java.nio.ByteBuffer;
 34  
 import java.nio.channels.WritableByteChannel;
 35  
 import java.util.*;
 36  
 import java.util.logging.Level;
 37  
 
 38  
 /**
 39  
  * Represents an ID3v2.4 tag.
 40  
  *
 41  
  * @author : Paul Taylor
 42  
  * @author : Eric Farng
 43  
  * @version $Id: ID3v24Tag.java 845 2009-11-13 14:10:57Z paultaylor $
 44  
  */
 45  209
 public class ID3v24Tag extends AbstractID3v2Tag
 46  
 {
 47  
     protected static final String TYPE_FOOTER = "footer";
 48  
     protected static final String TYPE_IMAGEENCODINGRESTRICTION = "imageEncodingRestriction";
 49  
     protected static final String TYPE_IMAGESIZERESTRICTION = "imageSizeRestriction";
 50  
     protected static final String TYPE_TAGRESTRICTION = "tagRestriction";
 51  
     protected static final String TYPE_TAGSIZERESTRICTION = "tagSizeRestriction";
 52  
     protected static final String TYPE_TEXTENCODINGRESTRICTION = "textEncodingRestriction";
 53  
     protected static final String TYPE_TEXTFIELDSIZERESTRICTION = "textFieldSizeRestriction";
 54  
     protected static final String TYPE_UPDATETAG = "updateTag";
 55  
     protected static final String TYPE_CRCDATA = "crcdata";
 56  
     protected static final String TYPE_EXPERIMENTAL = "experimental";
 57  
     protected static final String TYPE_EXTENDED = "extended";
 58  
     protected static final String TYPE_PADDINGSIZE = "paddingsize";
 59  
     protected static final String TYPE_UNSYNCHRONISATION = "unsyncronisation";
 60  
 
 61  
 
 62  4
     protected static int TAG_EXT_HEADER_LENGTH = 6;
 63  4
     protected static int TAG_EXT_HEADER_UPDATE_LENGTH = 1;
 64  4
     protected static int TAG_EXT_HEADER_CRC_LENGTH = 6;
 65  4
     protected static int TAG_EXT_HEADER_RESTRICTION_LENGTH = 2;
 66  4
     protected static int TAG_EXT_HEADER_CRC_DATA_LENGTH = 5;
 67  4
     protected static int TAG_EXT_HEADER_RESTRICTION_DATA_LENGTH = 1;
 68  4
     protected static int TAG_EXT_NUMBER_BYTES_DATA_LENGTH = 1;
 69  
 
 70  
     /**
 71  
      * ID3v2.4 Header bit mask
 72  
      */
 73  
     public static final int MASK_V24_UNSYNCHRONIZATION = FileConstants.BIT7;
 74  
 
 75  
     /**
 76  
      * ID3v2.4 Header bit mask
 77  
      */
 78  
     public static final int MASK_V24_EXTENDED_HEADER = FileConstants.BIT6;
 79  
 
 80  
     /**
 81  
      * ID3v2.4 Header bit mask
 82  
      */
 83  
     public static final int MASK_V24_EXPERIMENTAL = FileConstants.BIT5;
 84  
 
 85  
     /**
 86  
      * ID3v2.4 Header bit mask
 87  
      */
 88  
     public static final int MASK_V24_FOOTER_PRESENT = FileConstants.BIT4;
 89  
 
 90  
     /**
 91  
      * ID3v2.4 Extended header bit mask
 92  
      */
 93  
     public static final int MASK_V24_TAG_UPDATE = FileConstants.BIT6;
 94  
 
 95  
     /**
 96  
      * ID3v2.4 Extended header bit mask
 97  
      */
 98  
     public static final int MASK_V24_CRC_DATA_PRESENT = FileConstants.BIT5;
 99  
 
 100  
     /**
 101  
      * ID3v2.4 Extended header bit mask
 102  
      */
 103  
     public static final int MASK_V24_TAG_RESTRICTIONS = FileConstants.BIT4;
 104  
 
 105  
     /**
 106  
      * ID3v2.4 Extended header bit mask
 107  
      */
 108  
     public static final int MASK_V24_TAG_SIZE_RESTRICTIONS = (byte) FileConstants.BIT7 | FileConstants.BIT6;
 109  
 
 110  
     /**
 111  
      * ID3v2.4 Extended header bit mask
 112  
      */
 113  
     public static final int MASK_V24_TEXT_ENCODING_RESTRICTIONS = FileConstants.BIT5;
 114  
 
 115  
     /**
 116  
      * ID3v2.4 Extended header bit mask
 117  
      */
 118  
     public static final int MASK_V24_TEXT_FIELD_SIZE_RESTRICTIONS = FileConstants.BIT4 | FileConstants.BIT3;
 119  
 
 120  
     /**
 121  
      * ID3v2.4 Extended header bit mask
 122  
      */
 123  
     public static final int MASK_V24_IMAGE_ENCODING = FileConstants.BIT2;
 124  
 
 125  
     /**
 126  
      * ID3v2.4 Extended header bit mask
 127  
      */
 128  
     public static final int MASK_V24_IMAGE_SIZE_RESTRICTIONS = FileConstants.BIT2 | FileConstants.BIT1;
 129  
 
 130  
     /**
 131  
      * ID3v2.4 Header Footer are the same as the header flags. WHY?!?! move the
 132  
      * flags from thier position in 2.3??????????
 133  
      */
 134  
     /**
 135  
      * ID3v2.4 Header Footer bit mask
 136  
      */
 137  
     public static final int MASK_V24_TAG_ALTER_PRESERVATION = FileConstants.BIT6;
 138  
 
 139  
     /**
 140  
      * ID3v2.4 Header Footer bit mask
 141  
      */
 142  
     public static final int MASK_V24_FILE_ALTER_PRESERVATION = FileConstants.BIT5;
 143  
 
 144  
     /**
 145  
      * ID3v2.4 Header Footer bit mask
 146  
      */
 147  
     public static final int MASK_V24_READ_ONLY = FileConstants.BIT4;
 148  
 
 149  
     /**
 150  
      * ID3v2.4 Header Footer bit mask
 151  
      */
 152  
     public static final int MASK_V24_GROUPING_IDENTITY = FileConstants.BIT6;
 153  
 
 154  
     /**
 155  
      * ID3v2.4 Header Footer bit mask
 156  
      */
 157  
     public static final int MASK_V24_COMPRESSION = FileConstants.BIT4;
 158  
 
 159  
     /**
 160  
      * ID3v2.4 Header Footer bit mask
 161  
      */
 162  
     public static final int MASK_V24_ENCRYPTION = FileConstants.BIT3;
 163  
 
 164  
     /**
 165  
      * ID3v2.4 Header Footer bit mask
 166  
      */
 167  
     public static final int MASK_V24_FRAME_UNSYNCHRONIZATION = FileConstants.BIT2;
 168  
 
 169  
     /**
 170  
      * ID3v2.4 Header Footer bit mask
 171  
      */
 172  
     public static final int MASK_V24_DATA_LENGTH_INDICATOR = FileConstants.BIT1;
 173  
 
 174  
     /**
 175  
      * CRC Checksum calculated
 176  
      */
 177  2923
     protected boolean crcDataFlag = false;
 178  
 
 179  
     /**
 180  
      * Experiemntal tag
 181  
      */
 182  2923
     protected boolean experimental = false;
 183  
 
 184  
     /**
 185  
      * Contains extended header
 186  
      */
 187  2923
     protected boolean extended = false;
 188  
 
 189  
     /**
 190  
      * All frames in the tag uses unsynchronisation
 191  
      */
 192  2923
     protected boolean unsynchronization = false;
 193  
 
 194  
     /**
 195  
      * CRC Checksum
 196  
      */
 197  2923
     protected int crcData = 0;
 198  
 
 199  
 
 200  
     /**
 201  
      * Contains a footer
 202  
      */
 203  2923
     protected boolean footer = false;
 204  
 
 205  
     /**
 206  
      * Tag is an update
 207  
      */
 208  2923
     protected boolean updateTag = false;
 209  
 
 210  
     /**
 211  
      * Tag has restrictions
 212  
      */
 213  2923
     protected boolean tagRestriction = false;
 214  
 
 215  
     /**
 216  
      * If Set Image encoding restrictions
 217  
      *
 218  
      *  0   No restrictions
 219  
      *  1   Images are encoded only with PNG [PNG] or JPEG [JFIF].
 220  
      */
 221  2923
     protected byte imageEncodingRestriction = 0;
 222  
 
 223  
     /**
 224  
      * If set Image size restrictions
 225  
      *
 226  
      *  00  No restrictions
 227  
      *  01  All images are 256x256 pixels or smaller.
 228  
      *  10  All images are 64x64 pixels or smaller.
 229  
      *  11  All images are exactly 64x64 pixels, unless required
 230  
      *      otherwise.
 231  
      */
 232  2923
     protected byte imageSizeRestriction = 0;
 233  
 
 234  
     /**
 235  
      * If set then Tag Size Restrictions
 236  
      *
 237  
      *  00   No more than 128 frames and 1 MB total tag size.
 238  
      *  01   No more than 64 frames and 128 KB total tag size.
 239  
      *  10   No more than 32 frames and 40 KB total tag size.
 240  
      *  11   No more than 32 frames and 4 KB total tag size.
 241  
      */
 242  2923
     protected byte tagSizeRestriction = 0;
 243  
 
 244  
     /**
 245  
      *  If set Text encoding restrictions
 246  
      *
 247  
      *  0    No restrictions
 248  
      *  1    Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
 249  
      *       UTF-8 [UTF-8].
 250  
      */
 251  2923
     protected byte textEncodingRestriction = 0;
 252  
 
 253  
     /**
 254  
      * Tag padding
 255  
      */
 256  2923
     protected int paddingSize = 0;
 257  
 
 258  
 
 259  
     /**
 260  
      *  If set Text fields size restrictions
 261  
      *
 262  
      *  00   No restrictions
 263  
      *  01   No string is longer than 1024 characters.
 264  
      *   10   No string is longer than 128 characters.
 265  
      *  11   No string is longer than 30 characters.
 266  
      *
 267  
      *  Note that nothing is said about how many bytes is used to
 268  
      *  represent those characters, since it is encoding dependent. If a
 269  
      *  text frame consists of more than one string, the sum of the
 270  
      *  strungs is restricted as stated.
 271  
      */
 272  2923
     protected byte textFieldSizeRestriction = 0;
 273  
 
 274  
     public static final byte RELEASE = 2;
 275  
     public static final byte MAJOR_VERSION = 4;
 276  
     public static final byte REVISION = 0;
 277  
 
 278  
     /**
 279  
      * Retrieve the Release
 280  
      */
 281  
     public byte getRelease()
 282  
     {
 283  8
         return RELEASE;
 284  
     }
 285  
 
 286  
     /**
 287  
      * Retrieve the Major Version
 288  
      */
 289  
     public byte getMajorVersion()
 290  
     {
 291  1931
         return MAJOR_VERSION;
 292  
     }
 293  
 
 294  
     /**
 295  
      * Retrieve the Revision
 296  
      */
 297  
     public byte getRevision()
 298  
     {
 299  1094
         return REVISION;
 300  
     }
 301  
 
 302  
 
 303  
     /**
 304  
      * Creates a new empty ID3v2_4 datatype.
 305  
      */
 306  
     public ID3v24Tag()
 307  366
     {
 308  366
         frameMap = new LinkedHashMap();
 309  366
     }
 310  
 
 311  
     /**
 312  
      * Copy primitives applicable to v2.4, this is used when cloning a v2.4 datatype
 313  
      * and other objects such as v2.3 so need to check instanceof
 314  
      */
 315  
     protected void copyPrimitives(AbstractID3v2Tag copyObj)
 316  
     {
 317  1120
         logger.info("Copying primitives");
 318  1120
         super.copyPrimitives(copyObj);
 319  
 
 320  1120
         if (copyObj instanceof ID3v24Tag)
 321  
         {
 322  0
             ID3v24Tag copyObject = (ID3v24Tag) copyObj;
 323  0
             this.footer = copyObject.footer;
 324  0
             this.tagRestriction = copyObject.tagRestriction;
 325  0
             this.updateTag = copyObject.updateTag;
 326  0
             this.imageEncodingRestriction = copyObject.imageEncodingRestriction;
 327  0
             this.imageSizeRestriction = copyObject.imageSizeRestriction;
 328  0
             this.tagSizeRestriction = copyObject.tagSizeRestriction;
 329  0
             this.textEncodingRestriction = copyObject.textEncodingRestriction;
 330  0
             this.textFieldSizeRestriction = copyObject.textFieldSizeRestriction;
 331  
         }
 332  1120
     }
 333  
 
 334  
     protected void addFrame(AbstractID3v2Frame frame)
 335  
     {
 336  
         try
 337  
         {
 338  4112
             ID3v24Frame newFrame = new ID3v24Frame(frame);
 339  4112
             copyFrameIntoMap(newFrame.getIdentifier(), newFrame);
 340  
         }
 341  0
         catch (InvalidFrameException ife)
 342  
         {
 343  0
             logger.log(Level.SEVERE, "Unable to convert frame:" + frame.getIdentifier());
 344  4112
         }
 345  4112
     }
 346  
 
 347  
     /*
 348  
        * Copy framne into map, whilst accounting for multiple frame sof same type which can occur even if there were
 349  
        * not frames of the dame type in the original tag
 350  
        *
 351  
        * The frame already exists this shouldnt normally happen because frames
 352  
        * that are allowed to be multiple don't call this method. Frames that
 353  
        * arent allowed to be multiple aren't added to hashmap in first place when
 354  
        * originally added.
 355  
        *
 356  
        * We only want to allow one of the frames going forward but we try and merge
 357  
        * all the information into the one frame. However there is a problem here that
 358  
        * if we then take this, modify it and try to write back the original values
 359  
        * we could lose some information although this info is probably invalid anyway.
 360  
        *
 361  
        * However converting some frames from tag of one version to another may
 362  
        * mean that two different frames both get converted to one frame, this
 363  
        * particulary applies to DateTime fields which were originally two fields
 364  
        * in v2.3 but are one field in v2.4.
 365  
        */
 366  
        @Override
 367  
        protected void copyFrameIntoMap(String id, AbstractID3v2Frame newFrame)
 368  
        {
 369  
 
 370  4112
            if (frameMap.containsKey(newFrame.getIdentifier()))
 371  
            {
 372  548
                Object o = frameMap.get(newFrame.getIdentifier());
 373  548
                if(o instanceof AbstractID3v2Frame)
 374  
                {
 375  
                    //Retrieve the frame with the same id we have already loaded into the map
 376  173
                    AbstractID3v2Frame firstFrame = (AbstractID3v2Frame) frameMap.get(newFrame.getIdentifier());
 377  
 
 378  
 
 379  
                    //Two different frames both converted to TDRCFrames, now if this is the case one of them
 380  
                    //may have actually have been created as a FrameUnsupportedBody because TDRC is only
 381  
                    //supported in ID3v24, but is often created in v23 tags as well together with the valid TYER
 382  
                    //frame
 383  173
                    if (newFrame.getBody() instanceof FrameBodyTDRC)
 384  
                    {
 385  25
                        if (firstFrame.getBody() instanceof FrameBodyTDRC)
 386  
                        {
 387  21
                            logger.finest("Modifying frame in map:" + newFrame.getIdentifier());
 388  21
                            FrameBodyTDRC body = (FrameBodyTDRC) firstFrame.getBody();
 389  21
                            FrameBodyTDRC newBody = (FrameBodyTDRC) newFrame.getBody();
 390  
 
 391  
                            //#304:Check for NullPointer, just ignore this frame
 392  21
                            if(newBody.getOriginalID()==null)
 393  
                            {
 394  0
                                return;
 395  
                            }
 396  
                            //Just add the data to the frame
 397  21
                            if (newBody.getOriginalID().equals(ID3v23Frames.FRAME_ID_V3_TYER))
 398  
                            {
 399  4
                                body.setYear(newBody.getText());
 400  
                            }
 401  17
                            else if (newBody.getOriginalID().equals(ID3v23Frames.FRAME_ID_V3_TDAT))
 402  
                            {
 403  9
                                body.setDate(newBody.getText());
 404  
                            }
 405  8
                            else if (newBody.getOriginalID().equals(ID3v23Frames.FRAME_ID_V3_TIME))
 406  
                            {
 407  8
                                body.setTime(newBody.getText());
 408  
                            }
 409  0
                            else if (newBody.getOriginalID().equals(ID3v23Frames.FRAME_ID_V3_TRDA))
 410  
                            {
 411  0
                                body.setReco(newBody.getText());
 412  
                            }
 413  21
                        }
 414  
                        // The first frame was a TDRC frame that was not really allowed, this new frame was probably a
 415  
                        // valid frame such as TYER which has been converted to TDRC, replace the firstframe with this frame
 416  4
                        else if (firstFrame.getBody() instanceof FrameBodyUnsupported)
 417  
                        {
 418  4
                            frameMap.put(newFrame.getIdentifier(), newFrame);
 419  
                        }
 420  
                        else
 421  
                        {
 422  
                            //we just lose this frame, weve already got one with the correct id.
 423  
                            //TODO may want to store this somewhere
 424  0
                            logger.warning("Found duplicate TDRC frame in invalid situation,discarding:" + newFrame.getIdentifier());
 425  
                        }
 426  
                    }
 427  
                    else
 428  
                    {
 429  148
                        List<AbstractID3v2Frame> list = new ArrayList<AbstractID3v2Frame>();
 430  148
                        list.add(firstFrame);
 431  148
                        list.add(newFrame);
 432  148
                        frameMap.put(newFrame.getIdentifier(), list);
 433  
                    }
 434  173
                }
 435  
                else
 436  
                {
 437  375
                     List<AbstractID3v2Frame> list = (List)o;
 438  375
                     list.add(newFrame);
 439  
                }
 440  548
            }
 441  
            else
 442  
            {
 443  3564
                frameMap.put(newFrame.getIdentifier(), newFrame);
 444  
            }
 445  4112
        }
 446  
 
 447  
     /**
 448  
      * Copy Constructor, creates a new ID3v2_4 Tag based on another ID3v2_4 Tag
 449  
      * @param copyObject
 450  
      */
 451  
     public ID3v24Tag(ID3v24Tag copyObject)
 452  0
     {
 453  0
         logger.info("Creating tag from another tag of same type");
 454  0
         copyPrimitives(copyObject);
 455  0
         copyFrames(copyObject);
 456  0
     }
 457  
 
 458  
     /**
 459  
      * Creates a new ID3v2_4 datatype based on another (non 2.4) tag
 460  
      *
 461  
      * @param mp3tag
 462  
      */
 463  
     public ID3v24Tag(AbstractTag mp3tag)
 464  1153
     {
 465  1153
         logger.info("Creating tag from a tag of a different version");
 466  1153
         frameMap = new LinkedHashMap();
 467  1153
         if (mp3tag != null)
 468  
         {
 469  
             //Should use simpler copy constructor
 470  1136
             if ((mp3tag instanceof ID3v24Tag))
 471  
             {
 472  0
                 throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
 473  
             }
 474  
             /* If we get a tag, we want to convert to id3v2_4
 475  
              * both id3v1 and lyrics3 convert to this type
 476  
              * id3v1 needs to convert to id3v2_4 before converting to lyrics3
 477  
              */
 478  1136
             else if (mp3tag instanceof AbstractID3v2Tag)
 479  
             {
 480  1120
                 copyPrimitives((AbstractID3v2Tag) mp3tag);
 481  1120
                 copyFrames((AbstractID3v2Tag) mp3tag);
 482  
             }
 483  
             //IDv1
 484  16
             else if (mp3tag instanceof ID3v1Tag)
 485  
             {
 486  
                 // convert id3v1 tags.
 487  16
                 ID3v1Tag id3tag = (ID3v1Tag) mp3tag;
 488  
                 ID3v24Frame newFrame;
 489  
                 AbstractID3v2FrameBody newBody;
 490  16
                 if (id3tag.title.length() > 0)
 491  
                 {
 492  16
                     newBody = new FrameBodyTIT2((byte) 0, id3tag.title);
 493  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_TITLE);
 494  16
                     newFrame.setBody(newBody);
 495  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 496  
                 }
 497  16
                 if (id3tag.artist.length() > 0)
 498  
                 {
 499  16
                     newBody = new FrameBodyTPE1((byte) 0, id3tag.artist);
 500  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_ARTIST);
 501  16
                     newFrame.setBody(newBody);
 502  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 503  
                 }
 504  16
                 if (id3tag.album.length() > 0)
 505  
                 {
 506  16
                     newBody = new FrameBodyTALB((byte) 0, id3tag.album);
 507  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_ALBUM);
 508  16
                     newFrame.setBody(newBody);
 509  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 510  
                 }
 511  16
                 if (id3tag.year.length() > 0)
 512  
                 {
 513  16
                     newBody = new FrameBodyTDRC((byte) 0, id3tag.year);
 514  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_YEAR);
 515  16
                     newFrame.setBody(newBody);
 516  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 517  
                 }
 518  16
                 if (id3tag.comment.length() > 0)
 519  
                 {
 520  16
                     newBody = new FrameBodyCOMM((byte) 0, "ENG", "", id3tag.comment);
 521  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_COMMENT);
 522  16
                     newFrame.setBody(newBody);
 523  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 524  
                 }
 525  16
                 if (((id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED) >= 0) && ((id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED) != ID3v1Tag.BYTE_TO_UNSIGNED))
 526  
                 {
 527  16
                     Integer genreId = id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED;
 528  16
                     String genre = "(" + genreId + ") " + GenreTypes.getInstanceOf().getValueForId(genreId);
 529  
 
 530  16
                     newBody = new FrameBodyTCON((byte) 0, genre);
 531  16
                     newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_GENRE);
 532  16
                     newFrame.setBody(newBody);
 533  16
                     frameMap.put(newFrame.getIdentifier(), newFrame);
 534  
                 }
 535  16
                 if (mp3tag instanceof ID3v11Tag)
 536  
                 {
 537  16
                     ID3v11Tag id3tag2 = (ID3v11Tag) mp3tag;
 538  16
                     if (id3tag2.track > 0)
 539  
                     {
 540  16
                         newBody = new FrameBodyTRCK((byte) 0, Byte.toString(id3tag2.track));
 541  16
                         newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_TRACK);
 542  16
                         newFrame.setBody(newBody);
 543  16
                         frameMap.put(newFrame.getIdentifier(), newFrame);
 544  
                     }
 545  
                 }
 546  16
             }
 547  
             //Lyrics 3
 548  0
             else if (mp3tag instanceof AbstractLyrics3)
 549  
             {
 550  
                 //Put the conversion stuff in the individual frame code.
 551  
                 Lyrics3v2 lyric;
 552  0
                 if (mp3tag instanceof Lyrics3v2)
 553  
                 {
 554  0
                     lyric = new Lyrics3v2((Lyrics3v2) mp3tag);
 555  
                 }
 556  
                 else
 557  
                 {
 558  0
                     lyric = new Lyrics3v2(mp3tag);
 559  
                 }
 560  0
                 Iterator<Lyrics3v2Field> iterator = lyric.iterator();
 561  
                 Lyrics3v2Field field;
 562  
                 ID3v24Frame newFrame;
 563  0
                 while (iterator.hasNext())
 564  
                 {
 565  
                     try
 566  
                     {
 567  0
                         field = iterator.next();
 568  0
                         newFrame = new ID3v24Frame(field);
 569  0
                         frameMap.put(newFrame.getIdentifier(), newFrame);
 570  
                     }
 571  0
                     catch (InvalidTagException ex)
 572  
                     {
 573  0
                         logger.warning("Unable to convert Lyrics3 to v24 Frame:Frame Identifier");
 574  0
                     }
 575  
                 }
 576  
             }
 577  
         }
 578  1153
     }
 579  
 
 580  
     /**
 581  
      * Creates a new ID3v2_4 datatype.
 582  
      *
 583  
      * @param buffer
 584  
      * @param loggingFilename
 585  
      * @throws TagException
 586  
      */
 587  
     public ID3v24Tag(ByteBuffer buffer, String loggingFilename) throws TagException
 588  1404
     {
 589  1404
         frameMap = new LinkedHashMap();
 590  1404
         setLoggingFilename(loggingFilename);
 591  1404
         this.read(buffer);
 592  585
     }
 593  
 
 594  
 
 595  
     /**
 596  
      * Creates a new ID3v2_4 datatype.
 597  
      *
 598  
      * @param buffer
 599  
      * @throws TagException
 600  
      * @deprecated use {@link #ID3v24Tag(ByteBuffer,String)} instead
 601  
      */
 602  
     public ID3v24Tag(ByteBuffer buffer) throws TagException
 603  
     {
 604  0
         this(buffer, "");
 605  0
     }
 606  
 
 607  
     /**
 608  
      * @return identifier
 609  
      */
 610  
     public String getIdentifier()
 611  
     {
 612  839
         return "ID3v2.40";
 613  
     }
 614  
 
 615  
     /**
 616  
      * Return tag size based upon the sizes of the frames rather than the physical
 617  
      * no of bytes between start of ID3Tag and start of Audio Data.
 618  
      *
 619  
      * @return size
 620  
      */
 621  
     public int getSize()
 622  
     {
 623  0
         int size = TAG_HEADER_LENGTH;
 624  0
         if (extended)
 625  
         {
 626  0
             size += TAG_EXT_HEADER_LENGTH;
 627  0
             if (updateTag)
 628  
             {
 629  0
                 size += TAG_EXT_HEADER_UPDATE_LENGTH;
 630  
             }
 631  0
             if (crcDataFlag)
 632  
             {
 633  0
                 size += TAG_EXT_HEADER_CRC_LENGTH;
 634  
             }
 635  0
             if (tagRestriction)
 636  
             {
 637  0
                 size += TAG_EXT_HEADER_RESTRICTION_LENGTH;
 638  
             }
 639  
         }
 640  0
         size += super.getSize();
 641  0
         logger.finer("Tag Size is" + size);
 642  0
         return size;
 643  
     }
 644  
 
 645  
     /**
 646  
      * @param obj
 647  
      * @return equality
 648  
      */
 649  
     public boolean equals(Object obj)
 650  
     {
 651  0
         if (!(obj instanceof ID3v24Tag))
 652  
         {
 653  0
             return false;
 654  
         }
 655  0
         ID3v24Tag object = (ID3v24Tag) obj;
 656  0
         if (this.footer != object.footer)
 657  
         {
 658  0
             return false;
 659  
         }
 660  0
         if (this.imageEncodingRestriction != object.imageEncodingRestriction)
 661  
         {
 662  0
             return false;
 663  
         }
 664  0
         if (this.imageSizeRestriction != object.imageSizeRestriction)
 665  
         {
 666  0
             return false;
 667  
         }
 668  0
         if (this.tagRestriction != object.tagRestriction)
 669  
         {
 670  0
             return false;
 671  
         }
 672  0
         if (this.tagSizeRestriction != object.tagSizeRestriction)
 673  
         {
 674  0
             return false;
 675  
         }
 676  0
         if (this.textEncodingRestriction != object.textEncodingRestriction)
 677  
         {
 678  0
             return false;
 679  
         }
 680  0
         if (this.textFieldSizeRestriction != object.textFieldSizeRestriction)
 681  
         {
 682  0
             return false;
 683  
         }
 684  0
         return this.updateTag == object.updateTag && super.equals(obj);
 685  
     }
 686  
 
 687  
     /**
 688  
      * Read the size of a tag, based on  the value written in the tag header
 689  
      *
 690  
      * @param buffer
 691  
      * @return
 692  
      * @throws TagException
 693  
      */
 694  
     public int readSize(ByteBuffer buffer)
 695  
     {
 696  
 
 697  
         //Skip over flags
 698  0
         buffer.get();
 699  
 
 700  
         // Read the size, this is size of tag not including  the tag header
 701  0
         int size = ID3SyncSafeInteger.bufferToValue(buffer);
 702  
 
 703  
         //Return the exact size of tag as set in the tag header
 704  0
         return size + TAG_HEADER_LENGTH;
 705  
     }
 706  
 
 707  
     /**
 708  
      * Read header flags
 709  
      * <p/>
 710  
      * <p>Log info messages for falgs that have been set and log warnings when bits have been set for unknown flags</p>
 711  
      *
 712  
      * @param byteBuffer
 713  
      * @throws TagException
 714  
      */
 715  
     private void readHeaderFlags(ByteBuffer byteBuffer) throws TagException
 716  
     {
 717  
         //Flags
 718  585
         byte flags = byteBuffer.get();
 719  585
         unsynchronization = (flags & MASK_V24_UNSYNCHRONIZATION) != 0;
 720  585
         extended = (flags & MASK_V24_EXTENDED_HEADER) != 0;
 721  585
         experimental = (flags & MASK_V24_EXPERIMENTAL) != 0;
 722  585
         footer = (flags & MASK_V24_FOOTER_PRESENT) != 0;
 723  
 
 724  
         //Not allowable/Unknown Flags
 725  585
         if ((flags & FileConstants.BIT3) != 0)
 726  
         {
 727  0
             logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT3));
 728  
         }
 729  
 
 730  585
         if ((flags & FileConstants.BIT2) != 0)
 731  
         {
 732  0
             logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT2));
 733  
         }
 734  
 
 735  585
         if ((flags & FileConstants.BIT1) != 0)
 736  
         {
 737  0
             logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT1));
 738  
         }
 739  
 
 740  585
         if ((flags & FileConstants.BIT0) != 0)
 741  
         {
 742  0
             logger.warning(ErrorMessage.ID3_INVALID_OR_UNKNOWN_FLAG_SET.getMsg(getLoggingFilename(), FileConstants.BIT0));
 743  
         }
 744  
 
 745  
 
 746  585
         if (isUnsynchronization())
 747  
         {
 748  8
             logger.info(ErrorMessage.ID3_TAG_UNSYNCHRONIZED.getMsg(getLoggingFilename()));
 749  
         }
 750  
 
 751  585
         if (extended)
 752  
         {
 753  0
             logger.info(ErrorMessage.ID3_TAG_EXTENDED.getMsg(getLoggingFilename()));
 754  
         }
 755  
 
 756  585
         if (experimental)
 757  
         {
 758  0
             logger.info(ErrorMessage.ID3_TAG_EXPERIMENTAL.getMsg(getLoggingFilename()));
 759  
         }
 760  
 
 761  585
         if (footer)
 762  
         {
 763  0
             logger.warning(ErrorMessage.ID3_TAG_FOOTER.getMsg(getLoggingFilename()));
 764  
         }
 765  585
     }
 766  
 
 767  
     /**
 768  
      * Read the optional extended header
 769  
      *
 770  
      * @param byteBuffer
 771  
      * @param size
 772  
      * @throws org.jaudiotagger.tag.InvalidTagException
 773  
      */
 774  
     private void readExtendedHeader(ByteBuffer byteBuffer, int size) throws InvalidTagException
 775  
     {
 776  
         byte[] buffer;
 777  
 
 778  
         // int is 4 bytes.
 779  0
         int extendedHeaderSize = byteBuffer.getInt();
 780  
 
 781  
         // the extended header must be at least 6 bytes
 782  0
         if (extendedHeaderSize <= TAG_EXT_HEADER_LENGTH)
 783  
         {
 784  0
             throw new InvalidTagException(ErrorMessage.ID3_EXTENDED_HEADER_SIZE_TOO_SMALL.getMsg(getLoggingFilename(), extendedHeaderSize));
 785  
         }
 786  
 
 787  
         //Number of bytes
 788  0
         byteBuffer.get();
 789  
 
 790  
         // Read the extended flag bytes
 791  0
         byte extFlag = byteBuffer.get();
 792  0
         updateTag       = (extFlag & MASK_V24_TAG_UPDATE)       != 0;
 793  0
         crcDataFlag     = (extFlag & MASK_V24_CRC_DATA_PRESENT) != 0;
 794  0
         tagRestriction  = (extFlag & MASK_V24_TAG_RESTRICTIONS) != 0;
 795  
 
 796  
         // read the length byte if the flag is set
 797  
         // this tag should always be zero but just in case
 798  
         // read this information.
 799  0
         if (updateTag)
 800  
         {
 801  0
             byteBuffer.get();
 802  
         }
 803  
 
 804  
         //CRC-32
 805  0
         if (crcDataFlag)
 806  
         {
 807  
             // the CRC has a variable length
 808  0
             byteBuffer.get();
 809  0
             buffer = new byte[TAG_EXT_HEADER_CRC_DATA_LENGTH];
 810  0
             byteBuffer.get(buffer, 0, TAG_EXT_HEADER_CRC_DATA_LENGTH);
 811  0
             crcData = 0;
 812  0
             for (int i = 0; i < TAG_EXT_HEADER_CRC_DATA_LENGTH; i++)
 813  
             {
 814  0
                 crcData <<= 8;
 815  0
                 crcData += buffer[i];
 816  
             }
 817  
         }
 818  
 
 819  
         //Tag Restriction
 820  0
         if (tagRestriction)
 821  
         {
 822  0
             byteBuffer.get();
 823  0
             buffer = new byte[1];
 824  0
             byteBuffer.get(buffer, 0, 1);
 825  0
             tagSizeRestriction          = (byte) ((buffer[0] & MASK_V24_TAG_SIZE_RESTRICTIONS) >> 6);
 826  0
             textEncodingRestriction     = (byte) ((buffer[0] & MASK_V24_TEXT_ENCODING_RESTRICTIONS) >> 5);
 827  0
             textFieldSizeRestriction    = (byte) ((buffer[0] & MASK_V24_TEXT_FIELD_SIZE_RESTRICTIONS) >> 3);
 828  0
             imageEncodingRestriction    = (byte) ((buffer[0] & MASK_V24_IMAGE_ENCODING) >> 2);
 829  0
             imageSizeRestriction        = (byte) (buffer[0] & MASK_V24_IMAGE_SIZE_RESTRICTIONS);
 830  
         }
 831  0
     }
 832  
 
 833  
     /**
 834  
      * {@inheritDoc}
 835  
      */
 836  
     @Override
 837  
     public void read(ByteBuffer byteBuffer) throws TagException
 838  
     {
 839  
         int size;
 840  
         byte[] buffer;
 841  1404
         if (!seek(byteBuffer))
 842  
         {
 843  819
             throw new TagNotFoundException(getLoggingFilename() + ":" + getIdentifier() + " tag not found");
 844  
         }
 845  585
         logger.info(getLoggingFilename() + ":" + "Reading ID3v24 tag");
 846  585
         readHeaderFlags(byteBuffer);
 847  
 
 848  
         // Read the size, this is size of tag apart from tag header
 849  585
         size = ID3SyncSafeInteger.bufferToValue(byteBuffer);
 850  585
         logger.info(getLoggingFilename() + ":" + "Reading tag from file size set in header is" + size);
 851  
 
 852  585
         if (extended)
 853  
         {
 854  0
             readExtendedHeader(byteBuffer, size);
 855  
         }
 856  
 
 857  
         //Note if there was an extended header the size value has padding taken
 858  
         //off so we dont search it.
 859  585
         readFrames(byteBuffer, size);
 860  585
     }
 861  
 
 862  
     /**
 863  
      * Read frames from tag
 864  
      * @param byteBuffer
 865  
      * @param size
 866  
      */
 867  
     protected void readFrames(ByteBuffer byteBuffer, int size)
 868  
     {
 869  585
         logger.finest(getLoggingFilename() + ":" + "Start of frame body at" + byteBuffer.position());
 870  
         //Now start looking for frames
 871  
         ID3v24Frame next;
 872  585
         frameMap = new LinkedHashMap();
 873  
         //Read the size from the Tag Header
 874  585
         this.fileReadSize = size;
 875  
         // Read the frames until got to upto the size as specified in header
 876  585
         logger.finest(getLoggingFilename() + ":" + "Start of frame body at:" + byteBuffer.position() + ",frames data size is:" + size);
 877  3876
         while (byteBuffer.position() <= size)
 878  
         {
 879  
             String id;
 880  
             try
 881  
             {
 882  
                 //Read Frame
 883  3857
                 logger.finest(getLoggingFilename() + ":" + "looking for next frame at:" + byteBuffer.position());
 884  3857
                 next = new ID3v24Frame(byteBuffer, getLoggingFilename());
 885  3287
                 id = next.getIdentifier();
 886  3287
                 loadFrameIntoMap(id, next);
 887  
             }
 888  
             //Found Empty Frame
 889  4
             catch (EmptyFrameException ex)
 890  
             {
 891  4
                 logger.warning(getLoggingFilename() + ":" + "Empty Frame:" + ex.getMessage());
 892  4
                 this.emptyFrameBytes += TAG_HEADER_LENGTH;
 893  
             }
 894  566
             catch (InvalidFrameIdentifierException ifie)
 895  
             {
 896  566
                 logger.info(getLoggingFilename() + ":" + "Invalid Frame Identifier:" + ifie.getMessage());
 897  566
                 this.invalidFrameBytes++;
 898  
                 //Dont try and find any more frames
 899  566
                 break;
 900  
             }
 901  
             //Problem trying to find frame
 902  0
             catch (InvalidFrameException ife)
 903  
             {
 904  0
                 logger.warning(getLoggingFilename() + ":" + "Invalid Frame:" + ife.getMessage());
 905  0
                 this.invalidFrameBytes++;
 906  
                 //Dont try and find any more frames
 907  0
                 break;
 908  3291
             }
 909  3291
         }
 910  585
     }
 911  
 
 912  
     /**
 913  
      * Write the ID3 header to the ByteBuffer.
 914  
      * <p/>
 915  
      * TODO Calculate the CYC Data Check
 916  
      * TODO Reintroduce Extended Header
 917  
      *
 918  
      * @param padding is the size of the padding
 919  
      * @param size    is the size of the body data
 920  
      * @return ByteBuffer
 921  
      * @throws IOException
 922  
      */
 923  
     private ByteBuffer writeHeaderToBuffer(int padding, int size) throws IOException
 924  
     {
 925  
         //This would only be set if every frame in tag has been unsynchronized, I only unsychronize frames
 926  
         //that need it, in any case I have been advised not to set it even then.
 927  497
         unsynchronization = false;
 928  
 
 929  
         // Flags,currently we never calculate the CRC
 930  
         // and if we dont calculate them cant keep orig values. Tags are not
 931  
         // experimental and we never create extended header to keep things simple.
 932  497
         extended = false;
 933  497
         experimental = false;
 934  497
         footer = false;
 935  
 
 936  
         // Create Header Buffer,allocate maximum possible size for the header
 937  497
         ByteBuffer headerBuffer = ByteBuffer.allocate(TAG_HEADER_LENGTH);
 938  
         //TAGID
 939  497
         headerBuffer.put(TAG_ID);
 940  
 
 941  
         //Major Version
 942  497
         headerBuffer.put(getMajorVersion());
 943  
 
 944  
         //Minor Version
 945  497
         headerBuffer.put(getRevision());
 946  
 
 947  
         //Flags
 948  497
         byte flagsByte = 0;
 949  497
         if (isUnsynchronization())
 950  
         {
 951  0
             flagsByte |= MASK_V24_UNSYNCHRONIZATION;
 952  
         }
 953  497
         if (extended)
 954  
         {
 955  0
             flagsByte |= MASK_V24_EXTENDED_HEADER;
 956  
         }
 957  497
         if (experimental)
 958  
         {
 959  0
             flagsByte |= MASK_V24_EXPERIMENTAL;
 960  
         }
 961  497
         if (footer)
 962  
         {
 963  0
             flagsByte |= MASK_V24_FOOTER_PRESENT;
 964  
         }
 965  497
         headerBuffer.put(flagsByte);
 966  
 
 967  
         //Size As Recorded in Header, don't include the main header length
 968  
         //Additional Header Size,(for completeness we never actually write the extended header, or footer)
 969  497
         int additionalHeaderSize = 0;
 970  497
         if (extended)
 971  
         {
 972  0
             additionalHeaderSize += TAG_EXT_HEADER_LENGTH;
 973  0
             if (updateTag)
 974  
             {
 975  0
                 additionalHeaderSize += TAG_EXT_HEADER_UPDATE_LENGTH;
 976  
             }
 977  0
             if (crcDataFlag)
 978  
             {
 979  0
                 additionalHeaderSize += TAG_EXT_HEADER_CRC_LENGTH;
 980  
             }
 981  0
             if (tagRestriction)
 982  
             {
 983  0
                 additionalHeaderSize += TAG_EXT_HEADER_RESTRICTION_LENGTH;
 984  
             }
 985  
         }
 986  
 
 987  
         //Size As Recorded in Header, don't include the main header length
 988  497
         headerBuffer.put(ID3SyncSafeInteger.valueToBuffer(padding + size + additionalHeaderSize));
 989  
 
 990  
         //Write Extended Header
 991  497
         ByteBuffer extHeaderBuffer = null;
 992  497
         if (extended)
 993  
         {
 994  
             //Write Extended Header Size
 995  0
             int extendedSize = TAG_EXT_HEADER_LENGTH;
 996  0
             if (updateTag)
 997  
             {
 998  0
                 extendedSize += TAG_EXT_HEADER_UPDATE_LENGTH;
 999  
             }
 1000  0
             if (crcDataFlag)
 1001  
             {
 1002  0
                 extendedSize += TAG_EXT_HEADER_CRC_LENGTH;
 1003  
             }
 1004  0
             if (tagRestriction)
 1005  
             {
 1006  0
                 extendedSize += TAG_EXT_HEADER_RESTRICTION_LENGTH;
 1007  
             }
 1008  0
             extHeaderBuffer = ByteBuffer.allocate(extendedSize);
 1009  0
             extHeaderBuffer.putInt(extendedSize);
 1010  
             //Write Number of flags Byte
 1011  0
             extHeaderBuffer.put((byte) TAG_EXT_NUMBER_BYTES_DATA_LENGTH);
 1012  
             //Write Extended Flags
 1013  0
             byte extFlag = 0;
 1014  0
             if (updateTag)
 1015  
             {
 1016  0
                 extFlag |= MASK_V24_TAG_UPDATE;
 1017  
             }
 1018  0
             if (crcDataFlag)
 1019  
             {
 1020  0
                 extFlag |= MASK_V24_CRC_DATA_PRESENT;
 1021  
             }
 1022  0
             if (tagRestriction)
 1023  
             {
 1024  0
                 extFlag |= MASK_V24_TAG_RESTRICTIONS;
 1025  
             }
 1026  0
             extHeaderBuffer.put(extFlag);
 1027  
             //Write Update Data
 1028  0
             if (updateTag)
 1029  
             {
 1030  0
                 extHeaderBuffer.put((byte) 0);
 1031  
             }
 1032  
             //Write CRC Data
 1033  0
             if (crcDataFlag)
 1034  
             {
 1035  0
                 extHeaderBuffer.put((byte) TAG_EXT_HEADER_CRC_DATA_LENGTH);
 1036  0
                 extHeaderBuffer.put((byte) 0);
 1037  0
                 extHeaderBuffer.putInt(crcData);
 1038  
             }
 1039  
             //Write Tag Restriction
 1040  0
             if (tagRestriction)
 1041  
             {
 1042  0
                 extHeaderBuffer.put((byte) TAG_EXT_HEADER_RESTRICTION_DATA_LENGTH);
 1043  
                 //todo not currently setting restrictions
 1044  0
                 extHeaderBuffer.put((byte) 0);
 1045  
             }
 1046  
         }
 1047  
 
 1048  497
         if (extHeaderBuffer != null)
 1049  
         {
 1050  0
             extHeaderBuffer.flip();
 1051  0
             headerBuffer.put(extHeaderBuffer);
 1052  
         }
 1053  
 
 1054  497
         headerBuffer.flip();
 1055  497
         return headerBuffer;
 1056  
     }
 1057  
 
 1058  
     /**
 1059  
      * {@inheritDoc}
 1060  
      */
 1061  
     @Override
 1062  
     public void write(File file, long audioStartLocation) throws IOException
 1063  
     {
 1064  497
         logger.info("Writing tag to file");
 1065  
 
 1066  
         //Write Body Buffer
 1067  497
         byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();
 1068  
 
 1069  
         //Calculate Tag Size including Padding
 1070  497
         int sizeIncPadding = calculateTagSize(bodyByteBuffer.length + TAG_HEADER_LENGTH, (int) audioStartLocation);
 1071  
 
 1072  
         //Calculate padding bytes required
 1073  497
         int padding = sizeIncPadding - (bodyByteBuffer.length + TAG_HEADER_LENGTH);
 1074  
 
 1075  497
         ByteBuffer headerBuffer = writeHeaderToBuffer(padding, bodyByteBuffer.length);
 1076  497
         writeBufferToFile(file, headerBuffer, bodyByteBuffer, padding, sizeIncPadding, audioStartLocation);
 1077  497
     }
 1078  
 
 1079  
     /**
 1080  
      * {@inheritDoc}
 1081  
      */
 1082  
     @Override
 1083  
     public void write(WritableByteChannel channel) throws IOException
 1084  
     {
 1085  0
         logger.info("Writing tag to channel");
 1086  
 
 1087  0
         byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();
 1088  0
         ByteBuffer headerBuffer = writeHeaderToBuffer(0, bodyByteBuffer.length);
 1089  
 
 1090  0
         channel.write(headerBuffer);
 1091  0
         channel.write(ByteBuffer.wrap(bodyByteBuffer));
 1092  0
     }
 1093  
 
 1094  
     /**
 1095  
      * Display the tag in an XMLFormat
 1096  
      */
 1097  
     public void createStructure()
 1098  
     {
 1099  20
         MP3File.getStructureFormatter().openHeadingElement(TYPE_TAG, getIdentifier());
 1100  
 
 1101  20
         super.createStructureHeader();
 1102  
 
 1103  
         //Header
 1104  20
         MP3File.getStructureFormatter().openHeadingElement(TYPE_HEADER, "");
 1105  20
         MP3File.getStructureFormatter().addElement(TYPE_UNSYNCHRONISATION, this.isUnsynchronization());
 1106  20
         MP3File.getStructureFormatter().addElement(TYPE_CRCDATA, this.crcData);
 1107  20
         MP3File.getStructureFormatter().addElement(TYPE_EXPERIMENTAL, this.experimental);
 1108  20
         MP3File.getStructureFormatter().addElement(TYPE_EXTENDED, this.extended);
 1109  20
         MP3File.getStructureFormatter().addElement(TYPE_PADDINGSIZE, this.paddingSize);
 1110  20
         MP3File.getStructureFormatter().addElement(TYPE_FOOTER, this.footer);
 1111  20
         MP3File.getStructureFormatter().addElement(TYPE_IMAGEENCODINGRESTRICTION, this.paddingSize);
 1112  20
         MP3File.getStructureFormatter().addElement(TYPE_IMAGESIZERESTRICTION, this.imageSizeRestriction);
 1113  20
         MP3File.getStructureFormatter().addElement(TYPE_TAGRESTRICTION, this.tagRestriction);
 1114  20
         MP3File.getStructureFormatter().addElement(TYPE_TAGSIZERESTRICTION, this.tagSizeRestriction);
 1115  20
         MP3File.getStructureFormatter().addElement(TYPE_TEXTFIELDSIZERESTRICTION, this.textFieldSizeRestriction);
 1116  20
         MP3File.getStructureFormatter().addElement(TYPE_TEXTENCODINGRESTRICTION, this.textEncodingRestriction);
 1117  20
         MP3File.getStructureFormatter().addElement(TYPE_UPDATETAG, this.updateTag);
 1118  20
         MP3File.getStructureFormatter().closeHeadingElement(TYPE_HEADER);
 1119  
 
 1120  
         //Body
 1121  20
         super.createStructureBody();
 1122  
 
 1123  20
         MP3File.getStructureFormatter().closeHeadingElement(TYPE_TAG);
 1124  20
     }
 1125  
 
 1126  
     /**
 1127  
      * Are all frame swithin this tag unsynchronized
 1128  
      * <p/>
 1129  
      * <p>Because synchronization occurs at the frame level it is not normally desirable to unsynchronize all frames
 1130  
      * and hence this flag is not normally set.
 1131  
      *
 1132  
      * @return are all frames within the tag unsynchronized
 1133  
      */
 1134  
     public boolean isUnsynchronization()
 1135  
     {
 1136  1122
         return unsynchronization;
 1137  
     }
 1138  
 
 1139  
     /**
 1140  
      * Create a new frame with the specified frameid
 1141  
      *
 1142  
      * @param id
 1143  
      * @return
 1144  
      */
 1145  
     public ID3v24Frame createFrame(String id)
 1146  
     {
 1147  221
         return new ID3v24Frame(id);
 1148  
     }
 1149  
 
 1150  
 
 1151  
     /**
 1152  
      * Create Frame for Id3 Key
 1153  
      * <p/>
 1154  
      * Only textual data supported at the moment, should only be used with frames that
 1155  
      * support a simple string argument.
 1156  
      *
 1157  
      * @param id3Key
 1158  
      * @param value
 1159  
      * @return
 1160  
      * @throws KeyNotFoundException
 1161  
      * @throws FieldDataInvalidException
 1162  
      */
 1163  
     public TagField createField(ID3v24FieldKey id3Key, String value) throws KeyNotFoundException, FieldDataInvalidException
 1164  
     {
 1165  0
         if (id3Key == null)
 1166  
         {
 1167  0
             throw new KeyNotFoundException();
 1168  
         }
 1169  0
         return super.doCreateTagField(new FrameAndSubId(id3Key.getFrameId(), id3Key.getSubId()), value);
 1170  
     }
 1171  
 
 1172  
     /**
 1173  
      * Retrieve the first value that exists for this id3v24key
 1174  
      *
 1175  
      * @param id3v24FieldKey
 1176  
      * @return
 1177  
      * @throws org.jaudiotagger.tag.KeyNotFoundException
 1178  
      */
 1179  
     public String getFirst(ID3v24FieldKey id3v24FieldKey) throws KeyNotFoundException
 1180  
     {
 1181  48
         if (id3v24FieldKey == null)
 1182  
         {
 1183  0
             throw new KeyNotFoundException();
 1184  
         }
 1185  
 
 1186  48
         FrameAndSubId frameAndSubId = new FrameAndSubId(id3v24FieldKey.getFrameId(), id3v24FieldKey.getSubId());
 1187  48
         if (id3v24FieldKey == ID3v24FieldKey.TRACK)
 1188  
         {
 1189  4
             AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
 1190  4
             return String.valueOf(((FrameBodyTRCK)frame.getBody()).getTrackNo());
 1191  
         }
 1192  44
         else if (id3v24FieldKey == ID3v24FieldKey.TRACK_TOTAL)
 1193  
         {
 1194  0
             AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
 1195  0
             return String.valueOf(((FrameBodyTRCK)frame.getBody()).getTrackTotal());
 1196  
         }
 1197  44
         else if (id3v24FieldKey == ID3v24FieldKey.DISC_NO)
 1198  
         {
 1199  0
             AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
 1200  0
             return String.valueOf(((FrameBodyTPOS)frame.getBody()).getDiscNo());
 1201  
         }
 1202  44
         else if (id3v24FieldKey == ID3v24FieldKey.DISC_TOTAL)
 1203  
         {
 1204  0
             AbstractID3v2Frame frame = getFirstField(frameAndSubId.getFrameId());
 1205  0
             return String.valueOf(((FrameBodyTPOS)frame.getBody()).getDiscTotal());
 1206  
         }
 1207  
         else
 1208  
         {
 1209  44
             return super.doGetFirst(frameAndSubId);
 1210  
         }
 1211  
     }
 1212  
 
 1213  
 
 1214  
     /**
 1215  
      * Delete fields with this id3v24FieldKey
 1216  
      *
 1217  
      * @param id3v24FieldKey
 1218  
      * @throws org.jaudiotagger.tag.KeyNotFoundException
 1219  
      */
 1220  
     public void deleteField(ID3v24FieldKey id3v24FieldKey) throws KeyNotFoundException
 1221  
     {
 1222  0
         if (id3v24FieldKey == null)
 1223  
         {
 1224  0
             throw new KeyNotFoundException();
 1225  
         }
 1226  0
         super.doDeleteTagField(new FrameAndSubId(id3v24FieldKey.getFrameId(), id3v24FieldKey.getSubId()));
 1227  0
     }
 1228  
 
 1229  
 
 1230  
     protected FrameAndSubId getFrameAndSubIdFromGenericKey(FieldKey genericKey)
 1231  
     {
 1232  723
         ID3v24FieldKey id3v24FieldKey = ID3v24Frames.getInstanceOf().getId3KeyFromGenericKey(genericKey);
 1233  723
         if (id3v24FieldKey == null)
 1234  
         {
 1235  0
             throw new KeyNotFoundException();
 1236  
         }
 1237  723
         return new FrameAndSubId(id3v24FieldKey.getFrameId(), id3v24FieldKey.getSubId());
 1238  
     }
 1239  
 
 1240  
     protected ID3Frames getID3Frames()
 1241  
     {
 1242  129
         return ID3v24Frames.getInstanceOf();
 1243  
     }
 1244  
 
 1245  
     /**
 1246  
      * @return comparator used to order frames in preferred order for writing to file
 1247  
      *         so that most important frames are written first.
 1248  
      */
 1249  
     public Comparator getPreferredFrameOrderComparator()
 1250  
     {
 1251  497
         return ID3v24PreferredFrameOrderComparator.getInstanceof();
 1252  
     }
 1253  
 
 1254  
     public List<Artwork> getArtworkList()
 1255  
     {
 1256  24
         List<TagField> coverartList = getFields(FieldKey.COVER_ART);
 1257  24
         List<Artwork> artworkList = new ArrayList<Artwork>(coverartList.size());
 1258  
 
 1259  24
         for (TagField next : coverartList)
 1260  
         {
 1261  12
             FrameBodyAPIC coverArt = (FrameBodyAPIC) ((AbstractID3v2Frame) next).getBody();
 1262  12
             Artwork artwork = new Artwork();
 1263  12
             artwork.setMimeType(coverArt.getMimeType());
 1264  12
             artwork.setPictureType(coverArt.getPictureType());
 1265  12
             if (coverArt.isImageUrl())
 1266  
             {
 1267  0
                 artwork.setLinked(true);
 1268  0
                 artwork.setImageUrl(coverArt.getImageUrl());
 1269  
             }
 1270  
             else
 1271  
             {
 1272  12
                 artwork.setBinaryData(coverArt.getImageData());
 1273  
             }
 1274  12
             artworkList.add(artwork);
 1275  12
         }
 1276  24
         return artworkList;
 1277  
     }
 1278  
 
 1279  
     public TagField createField(Artwork artwork) throws FieldDataInvalidException
 1280  
     {
 1281  4
         AbstractID3v2Frame frame = createFrame(getFrameAndSubIdFromGenericKey(FieldKey.COVER_ART).getFrameId());
 1282  4
         FrameBodyAPIC body = (FrameBodyAPIC) frame.getBody();
 1283  4
         body.setObjectValue(DataTypes.OBJ_PICTURE_DATA, artwork.getBinaryData());
 1284  4
         body.setObjectValue(DataTypes.OBJ_PICTURE_TYPE, artwork.getPictureType());
 1285  4
         body.setObjectValue(DataTypes.OBJ_MIME_TYPE, artwork.getMimeType());
 1286  4
         body.setObjectValue(DataTypes.OBJ_DESCRIPTION, "");
 1287  4
         return frame;
 1288  
     }
 1289  
 
 1290  
     /**
 1291  
      * Create Artwork
 1292  
      *
 1293  
      * @param data
 1294  
      * @param mimeType of the image
 1295  
      * @see PictureTypes
 1296  
      * @return
 1297  
      */
 1298  
     public TagField createArtworkField(byte[] data, String mimeType)
 1299  
     {
 1300  8
         AbstractID3v2Frame frame = createFrame(getFrameAndSubIdFromGenericKey(FieldKey.COVER_ART).getFrameId());
 1301  8
         FrameBodyAPIC body = (FrameBodyAPIC) frame.getBody();
 1302  8
         body.setObjectValue(DataTypes.OBJ_PICTURE_DATA, data);
 1303  8
         body.setObjectValue(DataTypes.OBJ_PICTURE_TYPE, PictureTypes.DEFAULT_ID);
 1304  8
         body.setObjectValue(DataTypes.OBJ_MIME_TYPE, mimeType);
 1305  8
         body.setObjectValue(DataTypes.OBJ_DESCRIPTION, "");
 1306  8
         return frame;
 1307  
     }
 1308  
 }