EMMA Coverage Report (generated Tue Mar 14 21:50:42 EST 2006)
[all classes][org.farng.mp3.id3]

COVERAGE SUMMARY FOR SOURCE FILE [ID3v1.java]

nameclass, %method, %block, %line, %
ID3v1.java100% (1/1)85%  (40/47)54%  (590/1091)56%  (123.6/222)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ID3v1100% (1/1)85%  (40/47)54%  (590/1091)56%  (123.6/222)
append (AbstractMP3Tag): void 0%   (0/1)0%   (0/108)0%   (0/15)
getIdentifier (): String 0%   (0/1)0%   (0/2)0%   (0/1)
getSize (): int 0%   (0/1)0%   (0/2)0%   (0/1)
iterator (): Iterator 0%   (0/1)0%   (0/5)0%   (0/1)
toString (): String 0%   (0/1)0%   (0/101)0%   (0/8)
write (AbstractMP3Tag): void 0%   (0/1)0%   (0/41)0%   (0/12)
write (RandomAccessFile): void 0%   (0/1)0%   (0/166)0%   (0/36)
delete (RandomAccessFile): void 100% (1/1)45%  (5/11)67%  (2/3)
getID3tag (RandomAccessFile): ID3v1 100% (1/1)54%  (26/48)44%  (8/18)
overwrite (AbstractMP3Tag): void 100% (1/1)74%  (63/85)74%  (11.1/15)
equals (Object): boolean 100% (1/1)76%  (45/59)56%  (9/16)
ID3v1 (AbstractMP3Tag): void 100% (1/1)87%  (78/90)88%  (17.5/20)
ID3v1 (): void 100% (1/1)100% (21/21)100% (8/8)
ID3v1 (ID3v1): void 100% (1/1)100% (61/61)100% (14/14)
ID3v1 (RandomAccessFile): void 100% (1/1)100% (24/24)100% (9/9)
getAlbum (): String 100% (1/1)100% (3/3)100% (1/1)
getAlbumTitle (): String 100% (1/1)100% (4/4)100% (1/1)
getArtist (): String 100% (1/1)100% (3/3)100% (1/1)
getAuthorComposer (): String 100% (1/1)100% (5/5)100% (1/1)
getComment (): String 100% (1/1)100% (3/3)100% (1/1)
getGenre (): byte 100% (1/1)100% (3/3)100% (1/1)
getLeadArtist (): String 100% (1/1)100% (4/4)100% (1/1)
getSongComment (): String 100% (1/1)100% (4/4)100% (1/1)
getSongGenre (): String 100% (1/1)100% (4/4)100% (1/1)
getSongLyric (): String 100% (1/1)100% (5/5)100% (1/1)
getSongTitle (): String 100% (1/1)100% (4/4)100% (1/1)
getTitle (): String 100% (1/1)100% (3/3)100% (1/1)
getTrackNumberOnAlbum (): String 100% (1/1)100% (5/5)100% (1/1)
getYear (): String 100% (1/1)100% (3/3)100% (1/1)
getYearReleased (): String 100% (1/1)100% (4/4)100% (1/1)
read (RandomAccessFile): void 100% (1/1)100% (104/104)100% (16/16)
seek (RandomAccessFile): boolean 100% (1/1)100% (26/26)100% (5/5)
setAlbum (String): void 100% (1/1)100% (6/6)100% (2/2)
setAlbumTitle (String): void 100% (1/1)100% (5/5)100% (2/2)
setArtist (String): void 100% (1/1)100% (6/6)100% (2/2)
setAuthorComposer (String): void 100% (1/1)100% (5/5)100% (1/1)
setComment (String): void 100% (1/1)100% (6/6)100% (2/2)
setGenre (byte): void 100% (1/1)100% (4/4)100% (2/2)
setLeadArtist (String): void 100% (1/1)100% (5/5)100% (2/2)
setSongComment (String): void 100% (1/1)100% (5/5)100% (2/2)
setSongGenre (String): void 100% (1/1)100% (6/6)100% (2/2)
setSongLyric (String): void 100% (1/1)100% (5/5)100% (1/1)
setSongTitle (String): void 100% (1/1)100% (5/5)100% (2/2)
setTitle (String): void 100% (1/1)100% (6/6)100% (2/2)
setTrackNumberOnAlbum (String): void 100% (1/1)100% (5/5)100% (1/1)
setYear (String): void 100% (1/1)100% (6/6)100% (2/2)
setYearReleased (String): void 100% (1/1)100% (5/5)100% (2/2)

1package org.farng.mp3.id3;
2 
3import org.farng.mp3.AbstractMP3Tag;
4import org.farng.mp3.TagNotFoundException;
5import org.farng.mp3.TagOptionSingleton;
6import org.farng.mp3.TagUtility;
7 
8import java.io.IOException;
9import java.io.RandomAccessFile;
10import java.util.Iterator;
11 
12/**
13 * <TABLE border=0> <TBODY> <TR> <TD class=h2>What is ID3 (v1)?</TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> <TR
14 * vAlign=top> <TD> <P>The audio format MPEG layer I, layer II and layer III (MP3) has no native way of saving
15 * information about the contents, except for some simple yes/no parameters like "private", "copyrighted" and "original
16 * home" (meaning this is the original file and not a copy). A solution to this problem was introduced with the program
17 * "Studio3" by Eric Kemp alias NamkraD in 1996. By adding a small chunk of extra data in the end of the file one could
18 * get the MP3 file to carry information about the audio and not just the audio itself.</P>
19 * <p/>
20 * <P>The placement of the tag, as the data was called, was probably chosen as there were little chance that it should
21 * disturb decoders. In order to make it easy to detect a fixed size of 128 bytes was chosen. The tag has the following
22 * layout (as hinted by the scheme to the right):</P> <CENTER>
23 * <p/>
24 * <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> <TD>Song title</TD> <TD>30 characters</TD></TR> <TR>
25 * <TD>Artist</TD> <TD>30 characters</TD></TR> <TR> <TD>Album</TD> <TD>30 characters</TD></TR> <TR> <TD>Year</TD> <TD>4
26 * characters</TD></TR> <TR> <TD>Comment</TD> <TD>30 characters</TD></TR> <TR> <TD>Genre</TD> <TD>1
27 * byte</TD></TR></TBODY></TABLE> </P></CENTER>
28 * <p/>
29 * <P class=t>If you one sum the the size of all these fields we see that 30+30+30+4+30+1 equals 125 bytes and not 128
30 * bytes. The missing three bytes can be found at the very end of the tag, before the song title. These three bytes are
31 * always "TAG" and is the identification that this is indeed a ID3 tag. The easiest way to find a ID3v1/1.1 tag is to
32 * look for the word "TAG" 128 bytes from the end of a file.</P>
33 * <p/>
34 * <P class=t>As all artists doesn't have a 30 character name it is said that if there is some bytes left after the
35 * information is entered in the field, those bytes should be fille with the binary value 0. You might also think that
36 * you cannot write that much in the genre field, being one byte big, but it is more clever than that. The byte value
37 * you enter in the genre field corresponds to a value in a predefined list. The list that Eric Kemp created had 80
38 * entries, ranging from 0 to 79.</P></TD> </TR></TBODY></TABLE>
39 *
40 * @author Eric Farng
41 * @version $Revision: 1.6 $
42 */
43public class ID3v1 extends AbstractID3v1 {
44 
45    protected String album = "";
46    protected String artist = "";
47    protected String comment = "";
48    protected String title = "";
49    protected String year = "";
50    protected byte genre = -1;
51 
52    /**
53     * Creates a new ID3v1 object.
54     */
55    public ID3v1() {
56        // base empty constructor
57    }
58 
59    /**
60     * Creates a new ID3v1 object.
61     */
62    public ID3v1(final ID3v1 copyObject) {
63        super(copyObject);
64        this.album = new String(copyObject.album);
65        this.artist = new String(copyObject.artist);
66        this.comment = new String(copyObject.comment);
67        this.title = new String(copyObject.title);
68        this.year = new String(copyObject.year);
69        this.genre = copyObject.genre;
70    }
71 
72    /**
73     * Creates a new ID3v1 object.
74     */
75    public ID3v1(final AbstractMP3Tag mp3tag) {
76        if (mp3tag != null) {
77            final ID3v1_1 convertedTag;
78            if (mp3tag instanceof ID3v1 && !(mp3tag instanceof ID3v1_1)) {
79                throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
80            }
81            if (mp3tag instanceof ID3v1_1) {
82                convertedTag = (ID3v1_1) mp3tag;
83            } else {
84                convertedTag = new ID3v1_1(mp3tag);
85            }
86            this.album = new String(convertedTag.album.trim());
87            this.artist = new String(convertedTag.artist.trim());
88            this.comment = new String(convertedTag.comment.trim());
89            this.title = new String(convertedTag.title.trim());
90            this.year = new String(convertedTag.year.trim());
91            this.genre = convertedTag.genre;
92        }
93    }
94 
95    /**
96     * Creates a new ID3v1 object.
97     */
98    public ID3v1(final RandomAccessFile file) throws TagNotFoundException, IOException {
99        this.read(file);
100    }
101 
102    public void setAlbum(final String album) {
103        this.album = TagUtility.truncate(album, 30);
104    }
105 
106    public String getAlbum() {
107        return this.album;
108    }
109 
110    public void setArtist(final String artist) {
111        this.artist = TagUtility.truncate(artist, 30);
112    }
113 
114    public String getArtist() {
115        return this.artist;
116    }
117 
118    public void setComment(final String comment) {
119        this.comment = TagUtility.truncate(comment, 30);
120    }
121 
122    public String getComment() {
123        return this.comment;
124    }
125 
126    public void setGenre(final byte genre) {
127        this.genre = genre;
128    }
129 
130    public byte getGenre() {
131        return this.genre;
132    }
133 
134    public ID3v1 getID3tag(final RandomAccessFile file) throws IOException {
135        ID3v1 id3v1tag = new ID3v1_1();
136 
137        // look for id3v1_1 tag
138        if (id3v1tag.seek(file) == true) {
139            try {
140                id3v1tag.read(file);
141                id3v1tag.delete(file);
142            } catch (TagNotFoundException ex) {
143                id3v1tag = null;
144            }
145        } else {
146            id3v1tag = null;
147        }
148        if (id3v1tag == null) {
149            // look for id3v1 tag
150            id3v1tag = new ID3v1();
151            if (id3v1tag.seek(file) == true) {
152                try {
153                    id3v1tag.read(file);
154                    id3v1tag.delete(file);
155                } catch (TagNotFoundException ex) {
156                    id3v1tag = null;
157                }
158            } else {
159                id3v1tag = null;
160            }
161        }
162        return id3v1tag;
163    }
164 
165    public String getIdentifier() {
166        return "ID3v1.00";
167    }
168 
169    public int getSize() {
170        return 128;
171    }
172 
173    public void setTitle(final String title) {
174        this.title = TagUtility.truncate(title, 30);
175    }
176 
177    public String getTitle() {
178        return this.title;
179    }
180 
181    public void setYear(final String year) {
182        this.year = TagUtility.truncate(year, 4);
183    }
184 
185    public String getYear() {
186        return this.year;
187    }
188 
189    public void append(final AbstractMP3Tag tag) {
190        final ID3v1 oldTag = this;
191        final ID3v1 newTag;
192        if (tag != null) {
193            if (tag instanceof ID3v1) {
194                newTag = (ID3v1) tag;
195            } else {
196                newTag = new ID3v1();
197            }
198            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
199                TagOptionSingleton.getInstance().setId3v1SaveYear(false);
200                TagOptionSingleton.getInstance().setId3v1SaveComment(false);
201            }
202            oldTag.title = (TagOptionSingleton.getInstance().isId3v1SaveTitle() && (oldTag.title.length() == 0)) ?
203                           newTag.title :
204                           oldTag.title;
205            oldTag.artist = (TagOptionSingleton.getInstance().isId3v1SaveArtist() && (oldTag.artist.length() == 0)) ?
206                            newTag.artist :
207                            oldTag.artist;
208            oldTag.album = (TagOptionSingleton.getInstance().isId3v1SaveAlbum() && (oldTag.album.length() == 0)) ?
209                           newTag.album :
210                           oldTag.album;
211            oldTag.year = (TagOptionSingleton.getInstance().isId3v1SaveYear() && (oldTag.year.length() == 0)) ?
212                          newTag.year :
213                          oldTag.year;
214            oldTag.comment = (TagOptionSingleton.getInstance().isId3v1SaveComment() && (oldTag.comment.length() == 0)) ?
215                             newTag.comment :
216                             oldTag.comment;
217            oldTag.genre = (TagOptionSingleton.getInstance().isId3v1SaveGenre() && (oldTag.genre < 0)) ?
218                           newTag.genre :
219                           oldTag.genre;
220 
221            // we don't need to reset the tag options because
222            // we want to save all fields (default)
223        }
224    }
225 
226    public void delete(final RandomAccessFile file) throws IOException {
227        if (seek(file)) {
228            file.setLength(file.length() - 128);
229        }
230    }
231 
232    public boolean equals(final Object obj) {
233        if ((obj instanceof ID3v1) == false) {
234            return false;
235        }
236        final ID3v1 id3v1 = (ID3v1) obj;
237        if (this.album.equals(id3v1.album) == false) {
238            return false;
239        }
240        if (this.artist.equals(id3v1.artist) == false) {
241            return false;
242        }
243        if (this.comment.equals(id3v1.comment) == false) {
244            return false;
245        }
246        if (this.genre != id3v1.genre) {
247            return false;
248        }
249        if (this.title.equals(id3v1.title) == false) {
250            return false;
251        }
252        if (this.year.equals(id3v1.year) == false) {
253            return false;
254        }
255        return super.equals(obj);
256    }
257 
258    public Iterator iterator() {
259        return new ID3v1Iterator(this);
260    }
261 
262    public void overwrite(final AbstractMP3Tag tag) {
263        final ID3v1 oldTag = this;
264        final ID3v1 newTag;
265        if (tag != null) {
266            if (tag instanceof ID3v1) {
267                newTag = (ID3v1) tag;
268            } else {
269                newTag = new ID3v1();
270            }
271            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
272                TagOptionSingleton.getInstance().setId3v1SaveYear(false);
273                TagOptionSingleton.getInstance().setId3v1SaveComment(false);
274            }
275            oldTag.title = TagOptionSingleton.getInstance().isId3v1SaveTitle() ? newTag.title : oldTag.artist;
276            oldTag.artist = TagOptionSingleton.getInstance().isId3v1SaveArtist() ? newTag.artist : oldTag.artist;
277            oldTag.album = TagOptionSingleton.getInstance().isId3v1SaveAlbum() ? newTag.album : oldTag.album;
278            oldTag.year = TagOptionSingleton.getInstance().isId3v1SaveYear() ? newTag.year : oldTag.year;
279            oldTag.comment = TagOptionSingleton.getInstance().isId3v1SaveComment() ? newTag.comment : oldTag.comment;
280            oldTag.genre = TagOptionSingleton.getInstance().isId3v1SaveGenre() ? newTag.genre : oldTag.genre;
281 
282            // we don't need to reset the tag options because
283            // we want to save all fields (default)
284        }
285    }
286 
287    public void read(final RandomAccessFile file) throws TagNotFoundException, IOException {
288        final byte[] buffer = new byte[30];
289        if (seek(file) == false) {
290            throw new TagNotFoundException("ID3v1 tag not found");
291        }
292        file.read(buffer, 0, 30);
293        this.title = new String(buffer, 0, 30, "ISO-8859-1").trim();
294        file.read(buffer, 0, 30);
295        this.artist = new String(buffer, 0, 30, "ISO-8859-1").trim();
296        file.read(buffer, 0, 30);
297        this.album = new String(buffer, 0, 30, "ISO-8859-1").trim();
298        file.read(buffer, 0, 4);
299        this.year = new String(buffer, 0, 4, "ISO-8859-1").trim();
300        file.read(buffer, 0, 30);
301        this.comment = new String(buffer, 0, 30, "ISO-8859-1").trim();
302        file.read(buffer, 0, 1);
303        this.genre = buffer[0];
304    }
305 
306    public boolean seek(final RandomAccessFile file) throws IOException {
307        final byte[] buffer = new byte[3];
308 
309        // If there's a tag, it's 127 bytes long and we'll find the tag
310        file.seek(file.length() - 128);
311 
312        // read the TAG value
313        file.read(buffer, 0, 3);
314        final String tag = new String(buffer, 0, 3);
315        return tag.equals("TAG");
316    }
317 
318    public String toString() {
319        String str = getIdentifier() + " " + this.getSize() + "\n";
320        str += ("Title = " + this.title + "\n");
321        str += ("Artist = " + this.artist + "\n");
322        str += ("Album = " + this.album + "\n");
323        str += ("Comment = " + this.comment + "\n");
324        str += ("Year = " + this.year + "\n");
325        str += ("Genre = " + this.genre + "\n");
326        return str;
327    }
328 
329    public void write(final AbstractMP3Tag tag) {
330        final ID3v1 oldTag = this;
331        final ID3v1 newTag;
332        if (tag != null) {
333            if (tag instanceof ID3v1) {
334                newTag = (ID3v1) tag;
335            } else {
336                newTag = new ID3v1_1(tag);
337            }
338            oldTag.title = newTag.title;
339            oldTag.artist = newTag.artist;
340            oldTag.album = newTag.album;
341            oldTag.year = newTag.year;
342            oldTag.comment = newTag.comment;
343            oldTag.genre = newTag.genre;
344        }
345    }
346 
347    public void write(final RandomAccessFile file) throws IOException {
348        final byte[] buffer = new byte[128];
349        int i;
350        int offset = 3;
351        String str;
352        delete(file);
353        file.seek(file.length());
354        buffer[0] = (byte) 'T';
355        buffer[1] = (byte) 'A';
356        buffer[2] = (byte) 'G';
357        if (TagOptionSingleton.getInstance().isId3v1SaveTitle()) {
358            str = TagUtility.truncate(this.title, 30);
359            for (i = 0; i < str.length(); i++) {
360                buffer[i + offset] = (byte) str.charAt(i);
361            }
362        }
363        offset += 30;
364        if (TagOptionSingleton.getInstance().isId3v1SaveArtist()) {
365            str = TagUtility.truncate(this.artist, 30);
366            for (i = 0; i < str.length(); i++) {
367                buffer[i + offset] = (byte) str.charAt(i);
368            }
369        }
370        offset += 30;
371        if (TagOptionSingleton.getInstance().isId3v1SaveAlbum()) {
372            str = TagUtility.truncate(this.album, 30);
373            for (i = 0; i < str.length(); i++) {
374                buffer[i + offset] = (byte) str.charAt(i);
375            }
376        }
377        offset += 30;
378        if (TagOptionSingleton.getInstance().isId3v1SaveYear()) {
379            str = TagUtility.truncate(this.year, 4);
380            for (i = 0; i < str.length(); i++) {
381                buffer[i + offset] = (byte) str.charAt(i);
382            }
383        }
384        offset += 4;
385        if (TagOptionSingleton.getInstance().isId3v1SaveComment()) {
386            str = TagUtility.truncate(this.comment, 30);
387            for (i = 0; i < str.length(); i++) {
388                buffer[i + offset] = (byte) str.charAt(i);
389            }
390        }
391        offset += 30;
392        if (TagOptionSingleton.getInstance().isId3v1SaveGenre()) {
393            buffer[offset] = this.genre;
394        }
395        file.write(buffer);
396    }
397 
398    public String getSongTitle() {
399        return getTitle().trim();
400    }
401 
402    public String getLeadArtist() {
403        return getArtist().trim();
404    }
405 
406    public String getAlbumTitle() {
407        return getAlbum().trim();
408    }
409 
410    public String getYearReleased() {
411        return getYear().trim();
412    }
413 
414    public String getSongComment() {
415        return getComment().trim();
416    }
417 
418    public String getSongGenre() {
419        return Integer.toString(getGenre());
420    }
421 
422    public String getTrackNumberOnAlbum() {
423        throw new UnsupportedOperationException("This tag does not contain that information");
424    }
425 
426    public String getSongLyric() {
427        throw new UnsupportedOperationException("This tag does not contain that information");
428    }
429 
430    public String getAuthorComposer() {
431        throw new UnsupportedOperationException("This tag does not contain that information");
432    }
433 
434    public void setSongTitle(String songTitle) {
435        setTitle(songTitle.trim());
436    }
437 
438    public void setLeadArtist(String leadArtist) {
439        setArtist(leadArtist.trim());
440    }
441 
442    public void setAlbumTitle(String albumTitle) {
443        setAlbum(albumTitle.trim());
444    }
445 
446    public void setYearReleased(String yearReleased) {
447        setYear(yearReleased.trim());
448    }
449 
450    public void setSongComment(String songComment) {
451        setComment(songComment.trim());
452    }
453 
454    public void setSongGenre(String songGenre) {
455        setGenre(Byte.parseByte(songGenre.trim()));
456    }
457 
458    public void setTrackNumberOnAlbum(String trackNumberOnAlbum) {
459        throw new UnsupportedOperationException("This tag does not contain that information");
460    }
461 
462    public void setSongLyric(String songLyrics) {
463        throw new UnsupportedOperationException("This tag does not contain that information");
464    }
465 
466    public void setAuthorComposer(String authorComposer) {
467        throw new UnsupportedOperationException("This tag does not contain that information");
468    }
469}

[all classes][org.farng.mp3.id3]
EMMA 2.0.5312 (C) Vladimir Roubtsov