View Javadoc

1   package com.melloware.jukes.file.tag;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.Map;
7   
8   import org.apache.commons.lang.StringUtils;
9   import org.apache.commons.logging.Log;
10  import org.apache.commons.logging.LogFactory;
11  import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
12  import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
13  import org.jaudiotagger.audio.mp3.MP3File;
14  import org.jaudiotagger.tag.FieldKey;
15  import org.jaudiotagger.tag.TagException;
16  import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
17  import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
18  import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
19  import org.jaudiotagger.tag.id3.ID3v11Tag;
20  import org.jaudiotagger.tag.id3.ID3v24Frame;
21  import org.jaudiotagger.tag.id3.ID3v24Tag;
22  import org.jaudiotagger.tag.id3.framebody.AbstractID3v2FrameBody;
23  import org.jaudiotagger.tag.id3.framebody.FrameBodyCOMM;
24  import org.jaudiotagger.tag.id3.framebody.FrameBodyTALB;
25  import org.jaudiotagger.tag.id3.framebody.FrameBodyTCON;
26  import org.jaudiotagger.tag.id3.framebody.FrameBodyTDRC;
27  import org.jaudiotagger.tag.id3.framebody.FrameBodyTENC;
28  import org.jaudiotagger.tag.id3.framebody.FrameBodyTIT2;
29  import org.jaudiotagger.tag.id3.framebody.FrameBodyTLEN;
30  import org.jaudiotagger.tag.id3.framebody.FrameBodyTPE1;
31  import org.jaudiotagger.tag.id3.framebody.FrameBodyTPE2;
32  import org.jaudiotagger.tag.id3.framebody.FrameBodyTPOS;
33  import org.jaudiotagger.tag.id3.framebody.FrameBodyTRCK;
34  import org.jaudiotagger.tag.id3.framebody.FrameBodyTXXX;
35  
36  import com.jgoodies.uif.application.Application;
37  import com.jgoodies.uif.util.ResourceUtils;
38  import com.melloware.jukes.exception.MusicTagException;
39  import com.melloware.jukes.gui.view.MainFrame;
40  import com.melloware.jukes.util.MessageUtil;
41  
42  /**
43   * MusicTag class used for editing MP3 file tags.  Both version ID3v1.1 and
44   * IDv2.4 are supported.
45   * <p>
46   * The JAudioTagger (https://jaudiotagger.dev.java.net/) library is used to read
47   * these types of Tags.
48   * <p>
49   * Copyright (c) 2006
50   * Melloware, Inc. <http://www.melloware.com>
51   * @author Emil A. Lefkof III <info@melloware.com>
52   * @version 4.0
53   * AZ (C) 2009,2010
54   */
55  public final class Mp3Tag
56      extends MusicTag {
57  
58      private static final Log LOG = LogFactory.getLog(Mp3Tag.class);
59      public static final String V2_CODE_ALBUM = "TALB";
60      public static final String V2_CODE_TITLE = "TIT2";
61      public static final String V2_CODE_ARTIST = "TPE1";
62      public static final String V2_CODE_ALBUM_ARTIST = "TPE2";
63      public static final String V2_CODE_TXXX = "TXXX";
64      public static final String V2_CODE_YEAR = "TDRC";
65      public static final String V2_CODE_COMMENT = "COMM";
66      public static final String V2_CODE_GENRE = "TCON";
67      public static final String V2_CODE_TRACK = "TRCK";
68      public static final String V2_CODE_ENCODE = "TENC";
69      public static final String V2_CODE_LENGTH = "TLEN";
70      private static final String LINE_BREAK = "\n\n";
71      private String CODE_ALBUM_ARTIST = "ALBUM ARTIST"; //AZ
72      private String V2_CODE_DISC_NUMBER = "TPOS";  //AZ
73      private MP3File audioFile;
74      
75      
76      /**
77       * Constructor that takes a file.
78       * <p>
79       * @param aFile the music file
80       * @throws MusicTagException if any error occurs reading file
81       */
82      public Mp3Tag(File aFile)
83             throws MusicTagException {
84          super(aFile);
85  
86          try {
87              // create the audio file object
88              this.audioFile = new MP3File(aFile, MP3File.LOAD_ALL, true);
89              // initialize all tags
90              initializeTags();
91          } 
92            catch (IOException ex) {
93              LOG.error("IOException "+ex.getMessage(), ex);
94              throw new MusicTagException("IOException opening Music File Tag. " + aFile.getAbsolutePath() + LINE_BREAK
95                                         + ex.getMessage());
96          } catch (TagException ex) {
97              LOG.error("TagException "+ex.getMessage(), ex);
98              throw new MusicTagException("MusicTagException opening Music File Tag. " + aFile.getAbsolutePath() + LINE_BREAK
99                                          + ex.getMessage());
100         } /** AZ **/
101           catch (InvalidAudioFrameException ex) {
102             LOG.error("InvalidAudioFrameException "+ex.getMessage(), ex);
103             throw new MusicTagException("InvalidAudioFrameException opening Music File Tag. " + aFile.getAbsolutePath() + LINE_BREAK
104                                         + ex.getMessage());
105         }
106           catch (RuntimeException ex) {
107             LOG.error("RuntimeException "+ex.getMessage(), ex);
108             throw new MusicTagException("RuntimeException opening Music File Tag. " + aFile.getAbsolutePath() + LINE_BREAK
109                                         + ex.getMessage());
110         } catch (Exception ex) {
111             LOG.error("Exception "+ex.getMessage(), ex);
112             throw new MusicTagException("Unexpected exception opening Music File Tag. " + aFile.getAbsolutePath()
113                                         + LINE_BREAK + ex.getMessage());
114         }
115     }
116 
117     /**
118      * Gets the artist.
119      * <p>
120      * @return Returns the artist.
121      */
122     public String getArtist() {
123 	   	/** AZ - get settings for MainModule for Application **/
124         CODE_ALBUM_ARTIST = settings.getAlbumArtistTag();
125          	    
126         if (StringUtils.isBlank(this.artist)) {
127             String v1 = null;
128             String v2 = null;
129             String d1 = null;
130 
131             if (audioFile.hasID3v1Tag()) {
132                 v1 = StringUtils.defaultIfEmpty(audioFile.getID3v1Tag().getFirst(FieldKey.ARTIST), NO_TAG);
133             }
134             /** AZ **/
135             final AbstractID3v2Frame frame2 = loadFrame(V2_CODE_TXXX);
136             if (frame2 != null) {
137                 final AbstractID3v2FrameBody frameBody = ((FrameBodyTXXX)frame2.getBody());
138                 d1 = ((FrameBodyTXXX)frameBody).getDescription().toUpperCase().trim();
139                 if (d1.equals(CODE_ALBUM_ARTIST)) {
140                     v2 = ((FrameBodyTXXX)frameBody).getText().trim();
141                 }    
142             }
143             else {
144             final AbstractID3v2Frame frame1 = loadFrame(V2_CODE_ALBUM_ARTIST);
145             if (frame1 != null) {
146             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTPE2)frame1.getBody());
147                 v2 = ((FrameBodyTPE2)frameBody).getText().trim();
148             }
149             }
150             if (v2 == null ) {
151             final AbstractID3v2Frame frame = loadFrame(V2_CODE_ARTIST);
152             if (frame != null) {
153             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTPE1)frame.getBody());
154                 v2 = ((FrameBodyTPE1)frameBody).getText();
155             }
156             }
157             // return v2 tag else if empty return the v1 tag
158             this.artist = StringUtils.defaultIfEmpty(v2, v1).trim();
159         }
160         return this.artist;
161     }
162 
163     /**
164      * Gets the maximum bitrate for this file.
165      * <p>
166      * @return Returns the maximum bitrate for this file
167      */
168     public Long getBitRate() {
169         if (this.bitRate == null) {
170             final Integer bitrate = (header == null) ? Integer.valueOf(1000)
171                                                      : ((Integer)header.get("mp3.bitrate.nominal.bps"));
172             this.bitRate = Long.valueOf((bitrate.longValue() / 1000));
173         }
174 
175         return this.bitRate;
176     }
177 
178     /**
179      * Gets the comment.
180      * <p>
181      * @return Returns the comment.
182      */
183     public String getComment() {
184         if (StringUtils.isBlank(this.comment)) {
185             String v1 = null;
186             String v2 = null;
187             if (audioFile.hasID3v1Tag()) {
188                 v1 = StringUtils.defaultIfEmpty(audioFile.getID3v1Tag().getFirst(FieldKey.COMMENT), " ");
189             }
190             final AbstractID3v2Frame frame = loadFrame(V2_CODE_COMMENT);
191             if (frame != null) {
192             	final AbstractID3v2FrameBody frameBody = ((FrameBodyCOMM)frame.getBody());
193                 v2 = ((FrameBodyCOMM)frameBody).getText();
194             }
195 
196             // return v2 tag else if empty return the v1 tag
197             this.comment = StringUtils.defaultIfEmpty(v2, v1);
198         }
199         return this.comment;
200     }
201 
202     /* (non-Javadoc)
203      * @see com.melloware.jukes.file.tag.MusicTag#getCopyrighted()
204      */
205     public String getCopyrighted() {
206         return Boolean.toString(audioFile.getMP3AudioHeader().isCopyrighted());
207     }
208 
209     /**
210      * Gets the disc.
211      * <p>
212      * @return Returns the disc.
213      */
214     public String getDisc() {
215        
216         if (StringUtils.isBlank(this.disc)) {
217             String v1 = null;
218             String v2 = null;
219             String v3 = null;
220             if (audioFile.hasID3v1Tag()) {
221                 v1 = StringUtils.defaultIfEmpty(audioFile.getID3v1Tag().getFirst(FieldKey.ALBUM), NO_TAG);
222             }
223             /** AZ 
224             Add Number of disc (TPOS) to the disc name **/
225             if (settings.isUseCDNumber()) {
226             final AbstractID3v2Frame frame2 = loadFrame(V2_CODE_DISC_NUMBER);
227             if (frame2 != null) {
228                 final AbstractID3v2FrameBody frameBody = ((FrameBodyTPOS)frame2.getBody());
229                 v3 = ((FrameBodyTPOS)frameBody).getText().trim();
230             }
231             }
232             final AbstractID3v2Frame frame = loadFrame(V2_CODE_ALBUM);
233             if (frame != null) {
234             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTALB)frame.getBody());
235                 v2 = ((FrameBodyTALB)frameBody).getText();
236                 v2 = v2.trim();
237                 if ((v3 != null) & (v3.length()!= 0)) {
238                  v3 = " - CD " + v3;
239                  v2 = v2.concat(v3); 
240                 }
241             }
242             // return v2 tag else if empty return the v1 tag
243             this.disc = StringUtils.defaultIfEmpty(v2, v1).trim();
244         }
245         return this.disc;
246     }
247 
248     /* (non-Javadoc)
249      * @see com.melloware.jukes.file.tag.MusicTag#getEmphasis()
250      */
251     public String getEmphasis() {
252         return audioFile.getMP3AudioHeader().getEmphasis();
253     }
254 
255     /**
256      * Gets the encodedBy.
257      * <p>
258      * @return Returns the encodedBy.
259      */
260     public String getEncodedBy() {
261         if (StringUtils.isBlank(this.encodedBy)) {
262 
263         	final AbstractID3v2Frame frame = loadFrame(V2_CODE_ENCODE);
264             if (frame != null) {
265             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTENC)frame.getBody());
266                 this.encodedBy = ((FrameBodyTENC)frameBody).getText().trim();
267             }
268         }
269         return this.encodedBy;
270     }
271 
272     /* (non-Javadoc)
273      * @see com.melloware.jukes.file.tag.MusicTag#getFrequency()
274      */
275     public String getFrequency() {
276         return audioFile.getMP3AudioHeader().getSampleRate();
277     }
278 
279     /**
280      * Gets the genre.
281      * <p>
282      * @return Returns the genre.
283      */
284     public String getGenre() {
285         if (StringUtils.isBlank(this.genre)) {
286             String v1 = null;
287             String v2 = null;
288             if (audioFile.hasID3v1Tag()) {
289                 v1 = audioFile.getID3v1Tag().getFirst(FieldKey.GENRE);
290                 if (v1.trim().length() == 0) {
291                 	v1 = NO_TAG;
292                 }
293             }
294             final AbstractID3v2Frame frame = loadFrame(V2_CODE_GENRE);
295             if (frame != null) {
296             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTCON)frame.getBody());
297                 v2 = StringUtils.defaultIfEmpty(((FrameBodyTCON)frameBody).getText(), NO_TAG);
298             }
299             // return v2 tag else if empty return the v1 tag
300             this.genre = StringUtils.defaultIfEmpty(v2, v1);
301             //AZ process Genres string of iTunes type "(NNN)"
302             if (this.genre.startsWith("(")) {
303             final String genreString = this.genre.toString();
304               if (genreString.indexOf(")") != -1) {
305             	try {
306                     int i = Integer.parseInt(genreString.substring(1,genreString.indexOf(")")));
307                     this.genre = getStandardGenreType(i);
308                   } catch (NumberFormatException nfe) {
309                 	  LOG.error("NumberFormatException: " + nfe.getMessage());
310                 	  final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
311                 	  MessageUtil.showError(mainFrame, "NumberFormatException: " + nfe.getMessage()); //AZ 
312                  }         	
313               }
314             }//end of processing Genres string of iTunes type "(NNN)"
315             
316             //AZ remove last char if char(00) which is added by foobar player
317             final String s = Character.toString((char)00);
318             final String tempString = this.genre;
319             if (tempString.endsWith(s)) {
320             	this.genre = tempString.substring(0, tempString.length()-1);
321             }
322         }
323         return this.genre;
324     }
325 
326     /* (non-Javadoc)
327      * @see com.melloware.jukes.file.tag.MusicTag#getHeader()
328      */
329     public Map getHeader() {
330         return header;
331     }
332 
333     /* (non-Javadoc)
334      * @see com.melloware.jukes.file.tag.MusicTag#getLayer()
335      */
336     public String getLayer() {
337         return audioFile.getMP3AudioHeader().getMpegLayer();
338     }
339 
340     /* (non-Javadoc)
341      * @see com.melloware.jukes.file.tag.MusicTag#getMode()
342      */
343     public String getMode() {
344         return audioFile.getMP3AudioHeader().getChannels();
345     }
346 
347     /**
348      * Gets the title.
349      * <p>
350      * @return Returns the title.
351      */
352     public String getTitle() {
353 
354     	// if not blank then just return the title
355         if (StringUtils.isNotBlank(this.title)) {
356             return this.title;
357         }
358 
359         String v1 = null;
360         String v2 = null;
361         if (audioFile.hasID3v1Tag()) {
362             v1 = StringUtils.defaultIfEmpty(audioFile.getID3v1Tag().getFirstTitle(), NO_TAG);
363 
364             // if this is the max length for a v1 tag, or it begins with "Track"
365             // try and grab by filename it may be longer
366             if ((v1.length() == 30) || (v1.equals(NO_TAG)) || (v1.startsWith("Track"))) {
367                 v1 = extractTitleFromFilename();
368                 LOG.debug("Filename extracted");
369             }
370         }
371         final AbstractID3v2Frame frame = loadFrame(V2_CODE_TITLE);
372         if (frame != null) {
373         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTIT2)frame.getBody());
374             v2 = ((FrameBodyTIT2)frameBody).getText();
375         }
376 
377         // return v2 tag else if empty return the v1 tag
378         this.title = StringUtils.defaultIfEmpty(v2, v1).trim();
379 
380         return this.title;
381     }
382 
383     /**
384      * Gets the track.
385      * <p>
386      * @return Returns the track.
387      */
388     public String getTrack() {
389         if (StringUtils.isBlank(this.track)) {
390             String v1 = null;
391             String v2 = null;
392             if ((audioFile.hasID3v1Tag()) && (audioFile.getID3v1Tag() instanceof ID3v11Tag)) {
393                 v1 = ((ID3v11Tag)audioFile.getID3v1Tag()).getFirstTrack();
394 
395                 // default it to  non number if empty so isNumeric() test fails
396                 v1 = StringUtils.defaultIfEmpty(v1, "X");
397             }
398             final AbstractID3v2Frame frame = loadFrame(V2_CODE_TRACK);
399             if (frame != null) {
400             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTRCK)frame.getBody());
401                 v2 = ((FrameBodyTRCK)frameBody).getText();
402                 // default it to  non number if empty so isNumeric() test fails
403                 v2 = StringUtils.defaultIfEmpty(v2, "X");
404             }
405 
406             // if V1 is a number grab it as a number
407             int track1 = 0;
408             int track2 = 0;
409             if (StringUtils.isNumeric(v1)) {
410                 track1 = Integer.parseInt(v1);
411             }
412 
413             // v2 can contain format like 7 of 13, 12/15, etc.
414             if (StringUtils.isNumeric(v2)) {
415                 track2 = Integer.valueOf(v2).intValue();
416             }
417             // find the best match which is the larger of these two values
418             final int bestMatch = Math.max(track1, track2);
419 
420             // return v2 tag else if empty return the v1 tag
421             this.track = StringUtils.leftPad(String.valueOf(bestMatch), 2, "0").trim();
422         }
423         return this.track;
424     }
425 
426     /**
427      * Gets the track length in seconds.
428      * <p>
429      * @return Returns the track length in seconds.
430      */
431     public long getTrackLength() {
432         if (this.trackLength > 1) {
433             return this.trackLength;
434         }
435         final Long duration = (header == null) ? Long.valueOf(100000) : ((Long)header.get("duration"));
436         this.trackLength = ((duration.longValue() / 1000) / 1000);
437         return this.trackLength;
438     }
439 
440     /* (non-Javadoc)
441      * @see com.melloware.jukes.file.tag.MusicTag#getVersion()
442      */
443     public String getVersion() {
444         return audioFile.getMP3AudioHeader().getMpegVersion();
445     }
446 
447     /**
448      * Gets the year.
449      * <p>
450      * @return Returns the year.
451      */
452     public String getYear() {
453         if (StringUtils.isBlank(this.year)) {
454             String v1 = null;
455             String v2 = null;
456             if (audioFile.hasID3v1Tag()) {
457                 v1 = audioFile.getID3v1Tag().getFirstYear();
458                 v1 = StringUtils.defaultIfEmpty(v1, CURRENT_YEAR);
459             }
460             final AbstractID3v2Frame frame = loadFrame(V2_CODE_YEAR);
461             if (frame != null) {
462             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTDRC)frame.getBody());
463                 v2 = ((FrameBodyTDRC)frameBody).getText();
464             }
465 
466             // return v2 tag else if empty return the v1 tag
467             this.year = StringUtils.defaultIfEmpty(v2, v1).trim();
468         }
469         return this.year;
470     }
471 
472     /**
473      * Sets the artist.
474      * <p>
475      * @param aArtist The artist to set.
476      */
477     public void setArtist(final String aArtist) {
478         this.artist = StringUtils.defaultIfEmpty(aArtist, NO_TAG).trim();
479 
480         if (audioFile.hasID3v1Tag()) {
481             audioFile.getID3v1Tag().setArtist(this.artist);
482         }
483         final AbstractID3v2Frame frame = loadFrame(V2_CODE_ARTIST);
484         if (frame != null) {
485         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTPE1)frame.getBody());
486             ((FrameBodyTPE1)frameBody).setText(this.artist);
487         }
488     }
489 
490     /**
491      * Sets the comment.
492      * <p>
493      * @param aComment The comment to set.
494      */
495     public void setComment(final String aComment) {
496         this.comment = StringUtils.defaultIfEmpty(aComment, "").trim();
497 
498         if (audioFile.hasID3v1Tag()) {
499             audioFile.getID3v1Tag().setComment(this.comment);
500         }
501         final AbstractID3v2Frame frame = loadFrame(V2_CODE_COMMENT);
502         if (frame != null) {
503         	final AbstractID3v2FrameBody frameBody = ((FrameBodyCOMM)frame.getBody());
504             ((FrameBodyCOMM)frameBody).setText(this.comment);
505         }
506     }
507 
508     /**
509      * Sets the disc.
510      * <p>
511      * @param aDisc The disc to set.
512      */
513     public void setDisc(final String aDisc) {
514         this.disc = StringUtils.defaultIfEmpty(aDisc, NO_TAG).trim();
515 
516         if (audioFile.hasID3v1Tag()) {
517             audioFile.getID3v1Tag().setAlbum(this.disc);
518         }
519         final AbstractID3v2Frame frame = loadFrame(V2_CODE_ALBUM);
520         if (frame != null) {
521         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTALB)frame.getBody());
522             ((FrameBodyTALB)frameBody).setText(this.disc);
523         }
524     }
525 
526     /**
527      * Sets the Encoded By.
528      * <p>
529      * @param aEncodedBy The encoded by to set.
530      */
531     public void setEncodedBy(final String aEncodedBy) {
532     	//AZ - Do not fill EncodedBy with "Jukes" 
533         //this.encodedBy = StringUtils.defaultIfEmpty(aEncodedBy, System.getProperty("application.name")).trim();
534         this.encodedBy = aEncodedBy;
535 
536         final AbstractID3v2Frame frame = loadFrame(V2_CODE_ENCODE);
537         if (frame != null) {
538         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTENC)frame.getBody());
539             ((FrameBodyTENC)frameBody).setText(encodedBy);
540         }
541     }
542 
543     /**
544      * Sets the genre.
545      * <p>
546      * @param aGenre The genre to set.
547      */
548     public void setGenre(final String aGenre) {
549         this.genre = StringUtils.defaultIfEmpty(aGenre, NO_TAG).trim();
550 
551         if (audioFile.hasID3v1Tag()) {
552             audioFile.getID3v1Tag().setGenre(this.genre);
553         }
554         final AbstractID3v2Frame frame = loadFrame(V2_CODE_GENRE);
555         if (frame != null) {
556         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTCON)frame.getBody());
557             ((FrameBodyTCON)frameBody).setText(this.genre);
558         }
559     }
560 
561     /**
562      * Sets the title.
563      * <p>
564      * @param aTitle The title to set.
565      */
566     public void setTitle(final String aTitle) {
567         this.title = StringUtils.defaultIfEmpty(aTitle, NO_TAG).trim();
568 
569         if (audioFile.hasID3v1Tag()) {
570             audioFile.getID3v1Tag().setTitle(this.title);
571         }
572         final AbstractID3v2Frame frame = loadFrame(V2_CODE_TITLE);
573         if (frame != null) {
574         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTIT2)frame.getBody());
575             ((FrameBodyTIT2)frameBody).setText(this.title);
576         }
577     }
578 
579     /**
580      * Sets the track.
581      * <p>
582      * @param aTrack The track to set.
583      */
584     public void setTrack(final String aTrack) {
585         setTrack(aTrack, 2);
586     }
587     
588     
589     /**
590      * Sets the track.
591      * <p>
592      * @param aTrack The track to set.
593      * @param aPadding the number of 0's to pad this track with
594      */
595     public void setTrack(final String aTrack, final int aPadding) {
596        final String current = StringUtils.defaultIfEmpty(aTrack, "0").trim();
597        this.track = StringUtils.leftPad(current, aPadding, "0").trim();
598 
599        if ((audioFile.hasID3v1Tag()) && (audioFile.getID3v1Tag() instanceof ID3v11Tag)) {
600            ((ID3v11Tag)audioFile.getID3v1Tag()).setTrack(this.track);
601        }
602        final AbstractID3v2Frame frame = loadFrame(V2_CODE_TRACK);
603        if (frame != null) {
604         final AbstractID3v2FrameBody frameBody = ((FrameBodyTRCK)frame.getBody());
605            ((FrameBodyTRCK)frameBody).setText(this.track);
606        }
607     }
608 
609     /**
610      * Sets the trackLength.
611      * <p>
612      * @param aTrackLength The trackLength to set.
613      */
614     public void setTrackLength(final long aTrackLength) {
615         this.trackLength = aTrackLength;
616 
617         if (audioFile.hasID3v2Tag()) {
618         	final AbstractID3v2Frame frame = loadFrame(V2_CODE_LENGTH);
619             if (frame != null) {
620             	final AbstractID3v2FrameBody frameBody = ((FrameBodyTLEN)frame.getBody());
621                 ((FrameBodyTLEN)frameBody).setText(String.valueOf(this.trackLength * 1000));
622             }
623         }
624     }
625 
626     /**
627      * Sets the year.
628      * <p>
629      * @param aYear The year to set.
630      */
631     public void setYear(final String aYear) {
632         this.year = StringUtils.defaultIfEmpty(aYear, CURRENT_YEAR).trim();
633 
634         if (audioFile.hasID3v1Tag()) {
635             audioFile.getID3v1Tag().setYear(this.year);
636         }
637         final AbstractID3v2Frame frame = loadFrame(V2_CODE_YEAR);
638         if (frame != null) {
639         	final AbstractID3v2FrameBody frameBody = ((FrameBodyTDRC)frame.getBody());
640             ((FrameBodyTDRC)frameBody).setText(this.year);
641         }
642     }
643 
644     /**
645      * Is this file a variable bit rate.
646      * <p>
647      * @return true if variable false if constant bit rate
648      */
649     public boolean isVBR() {
650         return ((header == null) ? Boolean.FALSE : ((Boolean)header.get("mp3.vbr"))).booleanValue();
651     }
652 
653     /**
654      * Removes tags from the audio file.
655      * <p>
656      * @throws MusicTagException if any error occurs removing the tag
657      */
658     public void removeTags()
659                     throws MusicTagException {
660         try {
661             if (audioFile != null) {
662                 if (audioFile.hasID3v1Tag()) {
663                     audioFile.setID3v1Tag(null);
664                 }
665                 if (audioFile.hasID3v2Tag()) {
666                     audioFile.setID3v2Tag(null);
667                 }
668                 audioFile.save();
669                 initializeTags();
670             }
671         } catch (IOException ex) {
672             LOG.error(ex.getMessage(), ex);
673             throw new MusicTagException("IOException removing Music File Tag.", ex);
674         } catch (TagException ex) {
675             LOG.error(ex.getMessage(), ex);
676             throw new MusicTagException("TagException removing Music File Tag.", ex);
677         } catch (Exception ex) {
678             LOG.error(ex.getMessage(), ex);
679             throw new MusicTagException("Exception removing Music File Tag.", ex);
680         }
681     }
682 
683     /**
684      * Renames this Music file based on a format from prefs.  The format is in
685      * aFormat and can have values %n for track number, %t for title,
686      * %a for artist, and %d for disc.    Replaces any invalid characters
687      * (\\, /, :, , *, ?, ", <, >, or |) with underscores _ to prevent any
688      * errors on file systems.
689      *
690      * Examples:
691      * %n -%t = 01 - Track.mp3
692      * %a - %d - %n - %t = Artist - Album - 01 - Track.mp3
693      * <p>
694      * @param aFormat the string format like %n -%t to rename 01 - Track.mp3
695      * @return true if renamed, false if failure
696      */
697     public boolean renameFile(final String aFormat) {
698         boolean result = false;
699         try {
700             final String newFileName = createFilenameFromFormat(aFormat);
701             final File newFile = new File(newFileName);
702 
703             // close the audioFile
704             audioFile = null;
705 
706             result = this.file.renameTo(newFile);
707             if (result) {
708                 this.file = newFile;
709                 audioFile = new MP3File(newFile);
710                 initializeTags();
711             }
712         } catch (IOException ex) {
713         	final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
714       	    final String errorMessage = ResourceUtils.getString("messages.ErrorRenamingFile"); 
715             MessageUtil.showError(mainFrame, errorMessage); //AZ 
716             LOG.error(errorMessage, ex);
717         } catch (TagException ex) {
718         	final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
719         	MessageUtil.showError(mainFrame, "TagException"); //AZ 
720             LOG.error("TagException", ex);
721         } catch (ReadOnlyFileException ex) {
722         	final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
723         	MessageUtil.showError(mainFrame, "ReadOnlyFileException"); //AZ 
724             LOG.error("ReadOnlyFileException", ex);
725       } catch (InvalidAudioFrameException ex) {
726     	  final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
727     	  MessageUtil.showError(mainFrame, "InvalidAudioFrameException"); //AZ 
728           LOG.error("InvalidAudioFrameException", ex);
729       } 
730         return result;
731     }
732 
733     /**
734      * Saves the tag back to the file.
735      * <p>
736      * @throws MusicTagException if any error occurs saving the file
737      */
738     public void save()
739               throws MusicTagException {
740         try {
741             if (audioFile != null) {
742                 this.synchronize();
743                 audioFile.save();
744             }
745         } catch (IOException ex) {
746             LOG.error(ex.getMessage(), ex);
747             throw new MusicTagException("IOException saving Music File Tag " + this.file.getName(), ex);
748         } catch (TagException ex) {
749             LOG.error(ex.getMessage(), ex);
750             throw new MusicTagException("MusicTagException saving Music File Tag " + this.file.getName(), ex);
751         } catch (Exception ex) {
752             LOG.error(ex.getMessage(), ex);
753             throw new MusicTagException("Error saving Music File Tag " + this.file.getName(), ex);
754         }
755     }
756 
757     /**
758      * Initialize the V1 and V2 tags for this audio file.
759      */
760     private void initializeTags() {
761         // check for V1 tag, if not there then create one, if V0 then convert it to V11
762         if (audioFile.hasID3v1Tag()) {
763             if (audioFile.getID3v1Tag().getMajorVersion() == 0) {
764                 LOG.debug("Updating to v11 tag from old V10 tag.");
765                 final ID3v11Tag newTagVersion = new ID3v11Tag(audioFile.getID3v1Tag());
766                 // Set to the correct tag need to cast
767                 audioFile.setID3v1Tag((ID3v11Tag)newTagVersion);
768             }
769         } else {
770             LOG.debug("Create v11 tag.");
771             audioFile.setID3v1Tag(new ID3v11Tag());
772         }
773 
774         // check for V2 tag and set it as the default V2 tag in this file
775         if (audioFile.hasID3v2Tag()) {
776             if (LOG.isDebugEnabled()) {
777                 LOG.debug("V2 Version = " + audioFile.getID3v2Tag().getMajorVersion());
778             }
779             final AbstractID3v2Tag newTagVersion = audioFile.getID3v2TagAsv24();
780             audioFile.setID3v2TagOnly(newTagVersion);
781         }
782         
783         // initialize private variables from tags
784         this.getDisc();
785         this.getArtist();
786         this.getComment();
787         this.getGenre();
788         this.getTitle();
789         this.getTrack();
790         this.getYear();
791         this.getTrackLength();
792         this.getEncodedBy();
793         // now create the new V2 tag to fill and delete the old V2 tag
794         LOG.debug("Create v24 tag.");
795         audioFile.setID3v2Tag(new ID3v24Tag());
796         audioFile.setID3v2TagOnly(audioFile.getID3v2TagAsv24());
797     }
798 
799     /**
800     * Gets the V2 tag frame requested by the aFrameCode value.
801     * <p>
802     * @param aFrameCode the code such as TALB, COMM, TIT2
803     * @return the AbstractID3v2Frame or null if not found
804     */
805     private AbstractID3v2Frame loadFrame(final String aFrameCode) {
806         AbstractID3v2Frame frame = null;
807         AbstractTagFrameBody frameBody = null;
808 
809         if (StringUtils.isBlank(aFrameCode)) {
810             throw new IllegalArgumentException("aFrameCode must have a value.");
811         }
812 
813         // look for the correct frame in the tag
814         if (audioFile.hasID3v2Tag()) {
815             final Object frames = audioFile.getID3v2Tag().getFrame(aFrameCode);
816             if (frames instanceof AbstractID3v2Frame) {
817                 frame = (AbstractID3v2Frame)frames;
818             } else if (frames instanceof ArrayList) {
819                 // more than one frame of this type so just return the first one
820                 final ArrayList frameList = (ArrayList)frames;
821                 frame = (AbstractID3v2Frame)frameList.get(0);
822             } else {
823                 frame = null;
824             }
825         } else {
826             return null;
827         }
828 
829         // if no frame was found create a new one then
830         if (frame == null) {
831             if (V2_CODE_ALBUM.equals(aFrameCode)) {
832                 frame = new ID3v24Frame(V2_CODE_ALBUM);
833                 frameBody = frame.getBody();
834                 final FrameBodyTALB body = (FrameBodyTALB)frameBody;
835                 body.setText("");
836             } else if (V2_CODE_ARTIST.equals(aFrameCode)) {
837                 frame = new ID3v24Frame(V2_CODE_ARTIST);
838                 frameBody = frame.getBody();
839                 final FrameBodyTPE1 body = (FrameBodyTPE1)frameBody;
840                 body.setText("");
841             } 
842             /** AZ **/ 
843             else if (V2_CODE_ALBUM_ARTIST.equals(aFrameCode)) {
844                 frame = new ID3v24Frame(V2_CODE_ALBUM_ARTIST);
845                 frameBody = frame.getBody();
846                 final FrameBodyTPE2 body = (FrameBodyTPE2)frameBody;
847                 body.setText("");
848             } 
849             else if (V2_CODE_TXXX.equals(aFrameCode)) {
850                 frame = new ID3v24Frame(V2_CODE_TXXX);
851                 frameBody = frame.getBody();
852                 final FrameBodyTXXX body = (FrameBodyTXXX)frameBody;
853                 body.setText("");
854             } 
855             else if (V2_CODE_DISC_NUMBER.equals(aFrameCode)) {
856                 frame = new ID3v24Frame(V2_CODE_DISC_NUMBER);
857                 frameBody = frame.getBody();
858                 final FrameBodyTPOS body = (FrameBodyTPOS)frameBody;
859                 body.setText("");
860             } 
861             
862             else if (V2_CODE_TITLE.equals(aFrameCode)) {
863                 frame = new ID3v24Frame(V2_CODE_TITLE);
864                 frameBody = frame.getBody();
865                 final FrameBodyTIT2 body = (FrameBodyTIT2)frameBody;
866                 body.setText("");
867             } else if (V2_CODE_YEAR.equals(aFrameCode)) {
868                 frame = new ID3v24Frame(V2_CODE_YEAR);
869                 frameBody = frame.getBody();
870                 final FrameBodyTDRC body = (FrameBodyTDRC)frameBody;
871                 body.setText("");
872             } else if (V2_CODE_COMMENT.equals(aFrameCode)) {
873                 frame = new ID3v24Frame(V2_CODE_COMMENT);
874                 frameBody = frame.getBody();
875                 final FrameBodyCOMM body = (FrameBodyCOMM)frameBody;
876                 body.setText("");
877             } else if (V2_CODE_GENRE.equals(aFrameCode)) {
878                 frame = new ID3v24Frame(V2_CODE_GENRE);
879                 frameBody = frame.getBody();
880                 final FrameBodyTCON body = (FrameBodyTCON)frameBody;
881                 body.setText("");
882             } else if (V2_CODE_TRACK.equals(aFrameCode)) {
883                 frame = new ID3v24Frame(V2_CODE_TRACK);
884                 frameBody = frame.getBody();
885                 final FrameBodyTRCK body = (FrameBodyTRCK)frameBody;
886                 body.setText("");
887             } else if (V2_CODE_ENCODE.equals(aFrameCode)) {
888                 frame = new ID3v24Frame(V2_CODE_ENCODE);
889                 frameBody = frame.getBody();
890                 final FrameBodyTENC body = (FrameBodyTENC)frameBody;
891                 body.setText("");
892             } else if (V2_CODE_LENGTH.equals(aFrameCode)) {
893                 frame = new ID3v24Frame(V2_CODE_LENGTH);
894                 frameBody = frame.getBody();
895                 final FrameBodyTLEN body = (FrameBodyTLEN)frameBody;
896                 body.setText("1");
897             } else {
898                 throw new IllegalArgumentException(aFrameCode + " is not a valid Frame Type.");
899             }
900 
901             if (frame != null) {
902                 audioFile.getID3v2Tag().setFrame(frame);
903             }
904         }
905 
906         return frame;
907     }
908 
909 }