Coverage Report - org.jaudiotagger.tag.id3.AbstractID3v2Tag
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractID3v2Tag
68%
493/724
57%
207/362
0
AbstractID3v2Tag$1
89%
32/36
92%
22/24
0
AbstractID3v2Tag$FrameAndSubId
100%
6/6
N/A
0
 
 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.audio.generic.Utils;
 19  
 import org.jaudiotagger.audio.mp3.MP3File;
 20  
 import org.jaudiotagger.audio.AudioFile;
 21  
 import org.jaudiotagger.audio.exceptions.UnableToCreateFileException;
 22  
 import org.jaudiotagger.audio.exceptions.UnableToModifyFileException;
 23  
 import org.jaudiotagger.audio.exceptions.UnableToRenameFileException;
 24  
 import org.jaudiotagger.logging.ErrorMessage;
 25  
 import org.jaudiotagger.logging.FileSystemMessage;
 26  
 import org.jaudiotagger.tag.*;
 27  
 import org.jaudiotagger.tag.mp4.Mp4TagField;
 28  
 import org.jaudiotagger.tag.mp4.Mp4FieldKey;
 29  
 import org.jaudiotagger.tag.mp4.field.Mp4TagCoverField;
 30  
 import org.jaudiotagger.tag.datatype.DataTypes;
 31  
 import org.jaudiotagger.tag.datatype.Artwork;
 32  
 import org.jaudiotagger.tag.id3.framebody.*;
 33  
 import org.jaudiotagger.tag.id3.valuepair.ImageFormats;
 34  
 import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
 35  
 import org.jaudiotagger.tag.reference.PictureTypes;
 36  
 
 37  
 import java.io.*;
 38  
 import java.nio.ByteBuffer;
 39  
 import java.nio.channels.FileChannel;
 40  
 import java.nio.channels.FileLock;
 41  
 import java.nio.channels.WritableByteChannel;
 42  
 import java.util.*;
 43  
 import java.util.logging.Level;
 44  
 
 45  
 /**
 46  
  * This is the abstract base class for all ID3v2 tags.
 47  
  *
 48  
  * @author : Paul Taylor
 49  
  * @author : Eric Farng
 50  
  * @version $Id: AbstractID3v2Tag.java,v 1.56 2008/11/24 09:53:12 paultaylor Exp $
 51  
  */
 52  8
 public abstract class AbstractID3v2Tag extends AbstractID3Tag implements Tag
 53  
 {
 54  
     protected static final String TYPE_HEADER = "header";
 55  
     protected static final String TYPE_BODY = "body";
 56  
 
 57  
     //Tag ID as held in file
 58  52
     protected static final byte[] TAG_ID = {'I', 'D', '3'};
 59  
 
 60  
     //The tag header is the same for ID3v2 versions
 61  
     public static final int TAG_HEADER_LENGTH = 10;
 62  
     protected static final int FIELD_TAGID_LENGTH = 3;
 63  
     protected static final int FIELD_TAG_MAJOR_VERSION_LENGTH = 1;
 64  
     protected static final int FIELD_TAG_MINOR_VERSION_LENGTH = 1;
 65  
     protected static final int FIELD_TAG_FLAG_LENGTH = 1;
 66  
     protected static final int FIELD_TAG_SIZE_LENGTH = 4;
 67  
 
 68  
     protected static final int FIELD_TAGID_POS = 0;
 69  
     protected static final int FIELD_TAG_MAJOR_VERSION_POS = 3;
 70  
     protected static final int FIELD_TAG_MINOR_VERSION_POS = 4;
 71  
     protected static final int FIELD_TAG_FLAG_POS = 5;
 72  
     protected static final int FIELD_TAG_SIZE_POS = 6;
 73  
 
 74  
     protected static final int TAG_SIZE_INCREMENT = 100;
 75  
 
 76  
     //The max size we try to write in one go to avoid out of memory errors (10mb)
 77  
     private static final long MAXIMUM_WRITABLE_CHUNK_SIZE = 10000000;
 78  
 
 79  
     /**
 80  
      * Map of all frames for this tag
 81  
      */
 82  998
     public HashMap frameMap = null;
 83  
 
 84  
     /**
 85  
      * Holds the ids of invalid duplicate frames
 86  
      */
 87  
     protected static final String TYPE_DUPLICATEFRAMEID = "duplicateFrameId";
 88  998
     protected String duplicateFrameId = "";
 89  
 
 90  
     /**
 91  
      * Holds byte count of invalid duplicate frames
 92  
      */
 93  
     protected static final String TYPE_DUPLICATEBYTES = "duplicateBytes";
 94  998
     protected int duplicateBytes = 0;
 95  
 
 96  
     /**
 97  
      * Holds byte count of empty frames
 98  
      */
 99  
     protected static final String TYPE_EMPTYFRAMEBYTES = "emptyFrameBytes";
 100  998
     protected int emptyFrameBytes = 0;
 101  
 
 102  
     /**
 103  
      * Holds the size of the tag as reported by the tag header
 104  
      */
 105  
     protected static final String TYPE_FILEREADSIZE = "fileReadSize";
 106  998
     protected int fileReadSize = 0;
 107  
 
 108  
     /**
 109  
      * Holds byte count of invalid frames
 110  
      */
 111  
     protected static final String TYPE_INVALIDFRAMEBYTES = "invalidFrameBytes";
 112  998
     protected int invalidFrameBytes = 0;
 113  
 
 114  
     /**
 115  
      * Empty Constructor
 116  
      */
 117  
     public AbstractID3v2Tag()
 118  998
     {
 119  998
     }
 120  
 
 121  
     /**
 122  
      * This constructor is used when a tag is created as a duplicate of another
 123  
      * tag of the same type and version.
 124  
      */
 125  
     protected AbstractID3v2Tag(AbstractID3v2Tag copyObject)
 126  0
     {
 127  0
     }
 128  
 
 129  
     /**
 130  
      * Copy primitives apply to all tags
 131  
      */
 132  
     protected void copyPrimitives(AbstractID3v2Tag copyObject)
 133  
     {
 134  265
         logger.info("Copying Primitives");
 135  
         //Primitives type variables common to all IDv2 Tags
 136  265
         this.duplicateFrameId = new String(copyObject.duplicateFrameId);
 137  265
         this.duplicateBytes = copyObject.duplicateBytes;
 138  265
         this.emptyFrameBytes = copyObject.emptyFrameBytes;
 139  265
         this.fileReadSize = copyObject.fileReadSize;
 140  265
         this.invalidFrameBytes = copyObject.invalidFrameBytes;
 141  265
     }
 142  
 
 143  
     /**
 144  
      * Copy frames from another tag, needs implemanting by subclasses
 145  
      */
 146  
     protected abstract void copyFrames(AbstractID3v2Tag copyObject);
 147  
 
 148  
 
 149  
     /**
 150  
      * Returns the number of bytes which come from duplicate frames
 151  
      *
 152  
      * @return the number of bytes which come from duplicate frames
 153  
      */
 154  
     public int getDuplicateBytes()
 155  
     {
 156  0
         return duplicateBytes;
 157  
     }
 158  
 
 159  
     /**
 160  
      * Return the string which holds the ids of all
 161  
      * duplicate frames.
 162  
      *
 163  
      * @return the string which holds the ids of all duplicate frames.
 164  
      */
 165  
     public String getDuplicateFrameId()
 166  
     {
 167  0
         return duplicateFrameId;
 168  
     }
 169  
 
 170  
     /**
 171  
      * Returns the number of bytes which come from empty frames
 172  
      *
 173  
      * @return the number of bytes which come from empty frames
 174  
      */
 175  
     public int getEmptyFrameBytes()
 176  
     {
 177  0
         return emptyFrameBytes;
 178  
     }
 179  
 
 180  
     /**
 181  
      * Return  byte count of invalid frames
 182  
      *
 183  
      * @return byte count of invalid frames
 184  
      */
 185  
     public int getInvalidFrameBytes()
 186  
     {
 187  0
         return invalidFrameBytes;
 188  
     }
 189  
 
 190  
     /**
 191  
      * Returns the tag size as reported by the tag header
 192  
      *
 193  
      * @return the tag size as reported by the tag header
 194  
      */
 195  
     public int getFileReadBytes()
 196  
     {
 197  0
         return fileReadSize;
 198  
     }
 199  
 
 200  
     /**
 201  
      * Return whether tag has frame with this identifier
 202  
      * <p/>
 203  
      * Warning the match is only done against the identifier so if a tag contains a frame with an unsuported body
 204  
      * but happens to have an identifier that is valid for another version of the tag it will return true
 205  
      *
 206  
      * @param identifier frameId to lookup
 207  
      * @return true if tag has frame with this identifier
 208  
      */
 209  
     public boolean hasFrame(String identifier)
 210  
     {
 211  48
         return frameMap.containsKey(identifier);
 212  
     }
 213  
 
 214  
 
 215  
     /**
 216  
      * Return whether tag has frame with this identifier and a related body. This is required to protect
 217  
      * against circumstances whereby a tag contains a frame with an unsupported body
 218  
      * but happens to have an identifier that is valid for another version of the tag which it has been converted to
 219  
      * <p/>
 220  
      * e.g TDRC is an invalid frame in a v23 tag but if somehow a v23tag has been created by another application
 221  
      * with a TDRC frame we construct an UnsupportedFrameBody to hold it, then this library constructs a
 222  
      * v24 tag, it will contain a frame with id TDRC but it will not have the expected frame body it is not really a
 223  
      * TDRC frame.
 224  
      *
 225  
      * @param identifier frameId to lookup
 226  
      * @return true if tag has frame with this identifier
 227  
      */
 228  
     public boolean hasFrameAndBody(String identifier)
 229  
     {
 230  0
         if (hasFrame(identifier))
 231  
         {
 232  0
             Object o = getFrame(identifier);
 233  0
             if (o instanceof AbstractID3v2Frame)
 234  
             {
 235  0
                 if (((AbstractID3v2Frame) o).getBody() instanceof FrameBodyUnsupported)
 236  
                 {
 237  0
                     return false;
 238  
                 }
 239  0
                 return true;
 240  
             }
 241  0
             return true;
 242  
         }
 243  0
         return false;
 244  
     }
 245  
 
 246  
     /**
 247  
      * Return whether tag has frame starting with this identifier
 248  
      * <p/>
 249  
      * Warning the match is only done against the identifier so if a tag contains a frame with an unsupported body
 250  
      * but happens to have an identifier that is valid for another version of the tag it will return true
 251  
      *
 252  
      * @param identifier start of frameId to lookup
 253  
      * @return tag has frame starting with this identifier
 254  
      */
 255  
     public boolean hasFrameOfType(String identifier)
 256  
     {
 257  0
         Iterator<String> iterator = frameMap.keySet().iterator();
 258  
         String key;
 259  0
         boolean found = false;
 260  0
         while (iterator.hasNext() && !found)
 261  
         {
 262  0
             key = iterator.next();
 263  0
             if (key.startsWith(identifier))
 264  
             {
 265  0
                 found = true;
 266  
             }
 267  
         }
 268  0
         return found;
 269  
     }
 270  
 
 271  
 
 272  
     /**
 273  
      * For single frames return the frame in this tag with given identifier if it exists, if multiple frames
 274  
      * exist with the same identifier it will return a list containing all the frames with this identifier
 275  
      * <p/>
 276  
      * Warning the match is only done against the identifier so if a tag contains a frame with an unsupported body
 277  
      * but happens to have an identifier that is valid for another version of the tag it will be returned.
 278  
      * <p/>
 279  
      *
 280  
      * @param identifier is an ID3Frame identifier
 281  
      * @return matching frame, or list of matching frames
 282  
      */
 283  
     //TODO:This method is problematic because sometimes it returns a list and sometimes a frame, we need to
 284  
     //replace with two seperate methods as in the tag interface.
 285  
     public Object getFrame(String identifier)
 286  
     {
 287  571
         return frameMap.get(identifier);
 288  
     }
 289  
 
 290  
     /**
 291  
      * Retrieve the first value that exists for this identifier
 292  
      * <p/>
 293  
      * If the value is a String it returns that, otherwise returns a summary of the fields information
 294  
      * <p/>
 295  
      *
 296  
      * @param identifier
 297  
      * @return
 298  
      */
 299  
     //TODO:we should be just be using the bodies toString() method so we dont have if statement in this method
 300  
     //but this is being used by something else at the moment
 301  
     public String getFirst(String identifier)
 302  
     {
 303  165
         AbstractID3v2Frame frame = getFirstField(identifier);
 304  165
         if (frame == null)
 305  
         {
 306  11
             return "";
 307  
         }
 308  154
         if (frame.getBody() instanceof FrameBodyCOMM)
 309  
         {
 310  11
             return ((FrameBodyCOMM) frame.getBody()).getText();
 311  
         }
 312  143
         else if (frame.getBody() instanceof FrameBodyUSLT)
 313  
         {
 314  1
             return ((FrameBodyUSLT) frame.getBody()).getFirstTextValue();
 315  
         }
 316  142
         else if (frame.getBody() instanceof AbstractFrameBodyTextInfo)
 317  
         {
 318  142
             return ((AbstractFrameBodyTextInfo) frame.getBody()).getFirstTextValue();
 319  
         }
 320  0
         else if (frame.getBody() instanceof AbstractFrameBodyUrlLink)
 321  
         {
 322  0
             return ((AbstractFrameBodyUrlLink) frame.getBody()).getUrlLink();
 323  
         }
 324  
         else
 325  
         {
 326  0
             return frame.getBody().toString();
 327  
         }
 328  
     }
 329  
 
 330  
     public TagField getFirstField(TagFieldKey genericKey) throws KeyNotFoundException
 331  
     {
 332  0
         List<TagField> fields = get(genericKey);
 333  0
         if(fields.size()>0)
 334  
         {
 335  0
             return fields.get(0);
 336  
         }
 337  0
         return null;
 338  
     }
 339  
 
 340  
 
 341  
 
 342  
     /**
 343  
      * Retrieve the first tagfield that exists for this identifier
 344  
      *
 345  
      * @param identifier
 346  
      * @return tag field or null if doesnt exist
 347  
      */
 348  
     public AbstractID3v2Frame getFirstField(String identifier)
 349  
     {
 350  175
         Object object = getFrame(identifier);
 351  175
         if (object == null)
 352  
         {
 353  11
             return null;
 354  
         }
 355  164
         if (object instanceof List)
 356  
         {
 357  0
             return ((List<AbstractID3v2Frame>) object).get(0);
 358  
         }
 359  
         else
 360  
         {
 361  164
             return (AbstractID3v2Frame) object;
 362  
         }
 363  
     }
 364  
 
 365  
     /**
 366  
      * Add a frame to this tag
 367  
      *
 368  
      * @param frame the frame to add
 369  
      *              <p/>
 370  
      *              <p/>
 371  
      *              Warning if frame(s) already exists for this identifier thay are overwritten
 372  
      *              <p/>
 373  
      */
 374  
     //TODO needs to ensure do not add an invalid frame for this tag
 375  
     //TODO what happens if already contains a list with this ID
 376  
     public void setFrame(AbstractID3v2Frame frame)
 377  
     {
 378  106
         frameMap.put(frame.getIdentifier(), frame);
 379  106
     }
 380  
 
 381  
     protected abstract ID3Frames getID3Frames();
 382  
 
 383  
     /**
 384  
      * @param field
 385  
      * @throws FieldDataInvalidException
 386  
      */
 387  
     public void set(TagField field) throws FieldDataInvalidException
 388  
     {
 389  98
         if (!(field instanceof AbstractID3v2Frame))
 390  
         {
 391  0
             throw new FieldDataInvalidException("Field " + field + " is not of type AbstractID3v2Frame");
 392  
         }
 393  
 
 394  98
         AbstractID3v2Frame newFrame = (AbstractID3v2Frame) field;
 395  
 
 396  98
         Object o = frameMap.get(field.getId());
 397  98
         if (o == null || (!getID3Frames().isMultipleAllowed(newFrame.getId())))
 398  
         {
 399  67
             frameMap.put(field.getId(), field);
 400  
         }
 401  31
         else if (o instanceof AbstractID3v2Frame)
 402  
         {
 403  11
             AbstractID3v2Frame oldFrame = (AbstractID3v2Frame) o;
 404  11
             if (newFrame.getBody() instanceof FrameBodyTXXX)
 405  
             {
 406  
                 //Different key so convert to list and add as new frame
 407  4
                 if (!((FrameBodyTXXX) newFrame.getBody()).getDescription()
 408  
                         .equals(((FrameBodyTXXX) oldFrame.getBody()).getDescription()))
 409  
                 {
 410  4
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 411  4
                     frames.add(oldFrame);
 412  4
                     frames.add(newFrame);
 413  4
                     frameMap.put(newFrame.getId(), frames);
 414  4
                 }
 415  
                 //Same key so replace
 416  
                 else
 417  
                 {
 418  0
                     frameMap.put(newFrame.getId(), newFrame);
 419  
                 }
 420  
             }
 421  7
             else if (newFrame.getBody() instanceof FrameBodyWXXX)
 422  
             {
 423  
                 //Different key so convert to list and add as new frame
 424  3
                 if (!((FrameBodyWXXX) newFrame.getBody()).getDescription()
 425  
                         .equals(((FrameBodyWXXX) oldFrame.getBody()).getDescription()))
 426  
                 {
 427  3
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 428  3
                     frames.add(oldFrame);
 429  3
                     frames.add(newFrame);
 430  3
                     frameMap.put(newFrame.getId(), frames);
 431  3
                 }
 432  
                 //Same key so replace
 433  
                 else
 434  
                 {
 435  0
                     frameMap.put(newFrame.getId(), newFrame);
 436  
                 }
 437  
             }
 438  4
             else if (newFrame.getBody() instanceof FrameBodyCOMM)
 439  
             {
 440  2
                 if (!((FrameBodyCOMM) newFrame.getBody()).getDescription()
 441  
                         .equals(((FrameBodyCOMM) oldFrame.getBody()).getDescription()))
 442  
                 {
 443  2
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 444  2
                     frames.add(oldFrame);
 445  2
                     frames.add(newFrame);
 446  2
                     frameMap.put(newFrame.getId(), frames);
 447  2
                 }
 448  
                 //Same key so replace
 449  
                 else
 450  
                 {
 451  0
                     frameMap.put(newFrame.getId(), newFrame);
 452  
                 }
 453  
             }
 454  2
             else if (newFrame.getBody() instanceof FrameBodyUFID)
 455  
             {
 456  1
                 if (!((FrameBodyUFID) newFrame.getBody()).getOwner()
 457  
                         .equals(((FrameBodyUFID) oldFrame.getBody()).getOwner()))
 458  
                 {
 459  1
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 460  1
                     frames.add(oldFrame);
 461  1
                     frames.add(newFrame);
 462  1
                     frameMap.put(newFrame.getId(), frames);
 463  1
                 }
 464  
                 //Same key so replace
 465  
                 else
 466  
                 {
 467  0
                     frameMap.put(newFrame.getId(), newFrame);
 468  
                 }
 469  
             }
 470  1
             else if (newFrame.getBody() instanceof FrameBodyUSLT)
 471  
             {
 472  1
                 if (!((FrameBodyUSLT) newFrame.getBody()).getDescription()
 473  
                         .equals(((FrameBodyUSLT) oldFrame.getBody()).getDescription()))
 474  
                 {
 475  1
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 476  1
                     frames.add(oldFrame);
 477  1
                     frames.add(newFrame);
 478  1
                     frameMap.put(newFrame.getId(), frames);
 479  1
                 }
 480  
                 //Same key so replace
 481  
                 else
 482  
                 {
 483  0
                     frameMap.put(newFrame.getId(), newFrame);
 484  
                 }
 485  
             }
 486  0
             else if (newFrame.getBody() instanceof FrameBodyPOPM)
 487  
             {
 488  0
                 if (!((FrameBodyPOPM) newFrame.getBody()).getEmailToUser()
 489  
                         .equals(((FrameBodyPOPM) oldFrame.getBody()).getEmailToUser()))
 490  
                 {
 491  0
                     List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 492  0
                     frames.add(oldFrame);
 493  0
                     frames.add(newFrame);
 494  0
                     frameMap.put(newFrame.getId(), frames);
 495  0
                 }
 496  
                 //Same key so replace
 497  
                 else
 498  
                 {
 499  0
                     frameMap.put(newFrame.getId(), newFrame);
 500  
                 }
 501  
             }
 502  
             //Just add new one
 503  
             else
 504  
             {
 505  0
                 List<AbstractID3v2Frame> frames = new ArrayList<AbstractID3v2Frame>();
 506  0
                 frames.add(oldFrame);
 507  0
                 frames.add(newFrame);
 508  0
                 frameMap.put(newFrame.getId(), frames);
 509  
             }
 510  11
         }
 511  20
         else if (o instanceof List)
 512  
         {
 513  20
             for (ListIterator<AbstractID3v2Frame> li = ((List<AbstractID3v2Frame>) o).listIterator(); li.hasNext();)
 514  
             {
 515  52
                 AbstractID3v2Frame nextFrame = li.next();
 516  
 
 517  52
                 if (newFrame.getBody() instanceof FrameBodyTXXX)
 518  
                 {
 519  
                     //Value with matching key exists so replace
 520  8
                     if (((FrameBodyTXXX) newFrame.getBody()).getDescription()
 521  
                             .equals(((FrameBodyTXXX) nextFrame.getBody()).getDescription()))
 522  
                     {
 523  1
                         li.set(newFrame);
 524  1
                         frameMap.put(newFrame.getId(), o);
 525  1
                         return;
 526  
                     }
 527  
                 }
 528  44
                 else if (newFrame.getBody() instanceof FrameBodyWXXX)
 529  
                 {
 530  
                     //Value with matching key exists so replace
 531  27
                     if (((FrameBodyWXXX) newFrame.getBody()).getDescription()
 532  
                             .equals(((FrameBodyWXXX) nextFrame.getBody()).getDescription()))
 533  
                     {
 534  0
                         li.set(newFrame);
 535  0
                         frameMap.put(newFrame.getId(), o);
 536  0
                         return;
 537  
                     }
 538  
                 }
 539  17
                 else if (newFrame.getBody() instanceof FrameBodyCOMM)
 540  
                 {
 541  13
                     if (((FrameBodyCOMM) newFrame.getBody()).getDescription()
 542  
                             .equals(((FrameBodyCOMM) nextFrame.getBody()).getDescription()))
 543  
                     {
 544  3
                         li.set(newFrame);
 545  3
                         frameMap.put(newFrame.getId(), o);
 546  3
                         return;
 547  
                     }
 548  
                 }
 549  4
                 else if (newFrame.getBody() instanceof FrameBodyUFID)
 550  
                 {
 551  2
                     if (((FrameBodyUFID) newFrame.getBody()).getOwner()
 552  
                             .equals(((FrameBodyUFID) nextFrame.getBody()).getOwner()))
 553  
                     {
 554  1
                         li.set(newFrame);
 555  1
                         frameMap.put(newFrame.getId(), o);
 556  1
                         return;
 557  
                     }
 558  
                 }
 559  2
                 else if (newFrame.getBody() instanceof FrameBodyUSLT)
 560  
                 {
 561  2
                     if (((FrameBodyUSLT) newFrame.getBody()).getDescription()
 562  
                             .equals(((FrameBodyUSLT) nextFrame.getBody()).getDescription()))
 563  
                     {
 564  1
                         li.set(newFrame);
 565  1
                         frameMap.put(newFrame.getId(), o);
 566  1
                         return;
 567  
                     }
 568  
                 }
 569  0
                 else if (newFrame.getBody() instanceof FrameBodyPOPM)
 570  
                 {
 571  0
                     if (((FrameBodyPOPM) newFrame.getBody()).getEmailToUser()
 572  
                             .equals(((FrameBodyPOPM) nextFrame.getBody()).getEmailToUser()))
 573  
                     {
 574  0
                         li.set(newFrame);
 575  0
                         frameMap.put(newFrame.getId(), o);
 576  0
                         return;
 577  
                     }
 578  
                 }
 579  46
             }
 580  
             //No match found so add new one
 581  14
             ((List<AbstractID3v2Frame>) o).add(newFrame);
 582  
         }
 583  92
     }
 584  
 
 585  
     public void setAlbum(String s) throws FieldDataInvalidException
 586  
     {
 587  9
         if (s == null)
 588  
         {
 589  1
             throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg());
 590  
         }
 591  8
         set(createAlbumField(s));
 592  8
     }
 593  
 
 594  
     public void setArtist(String s) throws FieldDataInvalidException
 595  
     {
 596  4
         if (s == null)
 597  
         {
 598  1
             throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg());
 599  
         }
 600  3
         set(createArtistField(s));
 601  3
     }
 602  
 
 603  
     public void setComment(String s) throws FieldDataInvalidException
 604  
     {
 605  13
         if (s == null)
 606  
         {
 607  1
             throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg());
 608  
         }
 609  12
         set(createCommentField(s));
 610  12
     }
 611  
 
 612  
     public void setGenre(String s) throws FieldDataInvalidException
 613  
     {
 614  7
         if (s == null)
 615  
         {
 616  1
             throw new IllegalArgumentException(ErrorMessage.GENERAL_INVALID_NULL_ARGUMENT.getMsg());
 617