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

COVERAGE SUMMARY FOR SOURCE FILE [ID3v2_4.java]

nameclass, %method, %block, %line, %
ID3v2_4.java100% (1/1)82%  (14/17)56%  (957/1706)61%  (206.6/336)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ID3v2_4100% (1/1)82%  (14/17)56%  (957/1706)61%  (206.6/336)
overwrite (AbstractMP3Tag): void 0%   (0/1)0%   (0/47)0%   (0/11)
toString (): String 0%   (0/1)0%   (0/257)0%   (0/21)
write (AbstractMP3Tag): void 0%   (0/1)0%   (0/47)0%   (0/11)
write (RandomAccessFile): void 100% (1/1)34%  (82/238)37%  (22/59)
read (RandomAccessFile): void 100% (1/1)40%  (142/353)47%  (29.6/63)
getSize (): int 100% (1/1)64%  (23/36)50%  (7/14)
equals (Object): boolean 100% (1/1)74%  (50/68)55%  (11/20)
ID3v2_4 (): void 100% (1/1)100% (33/33)100% (12/12)
ID3v2_4 (AbstractMP3Tag): void 100% (1/1)100% (295/295)100% (59/59)
ID3v2_4 (ID3v2_4): void 100% (1/1)100% (60/60)100% (18/18)
ID3v2_4 (RandomAccessFile): void 100% (1/1)100% (30/30)100% (11/11)
append (AbstractMP3Tag): void 100% (1/1)100% (47/47)100% (11/11)
copyFromID3v2Tag (AbstractID3v2): void 100% (1/1)100% (100/100)100% (28/28)
getIdentifier (): String 100% (1/1)100% (2/2)100% (1/1)
getYearReleased (): String 100% (1/1)100% (18/18)100% (6/6)
seek (RandomAccessFile): boolean 100% (1/1)100% (50/50)100% (9/9)
setYearReleased (String): void 100% (1/1)100% (25/25)100% (6/6)

1package org.farng.mp3.id3;
2 
3import org.farng.mp3.AbstractMP3Tag;
4import org.farng.mp3.InvalidTagException;
5import org.farng.mp3.MP3File;
6import org.farng.mp3.TagConstant;
7import org.farng.mp3.TagException;
8import org.farng.mp3.TagNotFoundException;
9import org.farng.mp3.filename.FilenameTag;
10import org.farng.mp3.lyrics3.AbstractLyrics3;
11import org.farng.mp3.lyrics3.Lyrics3v2;
12import org.farng.mp3.lyrics3.Lyrics3v2Field;
13 
14import java.io.IOException;
15import java.io.RandomAccessFile;
16import java.util.Iterator;
17 
18/**
19 * <p>&nbsp;&nbsp; ID3v2 is a general tagging format for audio, which makes it possible<br> &nbsp;&nbsp; to store meta
20 * data about the audio inside the audio file itself. The<br> &nbsp;&nbsp; ID3 tag described in this document is mainly
21 * targeted at files<br> &nbsp;&nbsp; encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III<br>
22 * &nbsp;&nbsp; and MPEG-2.5, but may work with other types of encoded audio or as a<br> &nbsp;&nbsp; stand alone format
23 * for audio meta data.</p>
24 * <p/>
25 * <p>&nbsp;&nbsp; ID3v2 is designed to be as flexible and expandable as possible to<br> &nbsp;&nbsp; meet new meta
26 * information needs that might arise. To achieve that<br> &nbsp;&nbsp; ID3v2 is constructed as a container for several
27 * information blocks,<br> &nbsp;&nbsp; called frames, whose format need not be known to the software that<br>
28 * &nbsp;&nbsp; encounters them. At the start of every frame is an unique and<br> &nbsp;&nbsp; predefined identifier, a
29 * size descriptor that allows software to skip<br> &nbsp;&nbsp; unknown frames and a flags field. The flags describes
30 * encoding<br> &nbsp;&nbsp; details and if the frame should remain in the tag, should it be<br> &nbsp;&nbsp; unknown to
31 * the software, if the file is altered.</p>
32 * <p/>
33 * <p>&nbsp;&nbsp; The bitorder in ID3v2 is most significant bit first (MSB). The<br> &nbsp;&nbsp; byteorder in
34 * multibyte numbers is most significant byte first (e.g.<br> &nbsp;&nbsp; $12345678 would be encoded $12 34 56 78),
35 * also known as big endian<br> &nbsp;&nbsp; and network byte order.</p>
36 * <p/>
37 * <p>&nbsp;&nbsp; Overall tag structure:</p> <table border="1"> <tr> <td width="100%" align="center"> <p
38 * align="center">Header (10 bytes)</p> </td> </tr> <tr> <td width="100%" align="center">Extended Header (variable
39 * length, OPTIONAL)</td> </tr> <tr> <td width="100%" align="center">Frames (variable length)</td> </tr> <tr> <td
40 * width="100%" align="center">Padding (variable length, OPTIONAL)</td> </tr> <tr> <td width="100%"
41 * align="center">Footer (10 bytes, OPTIONAL)</td> </tr> </table> <p>&nbsp;&nbsp; In general, padding and footer are
42 * mutually exclusive. See details in<br> &nbsp;&nbsp; sections 3.3, 3.4 and 5.<br> </p> <a name="sec3.1"></a>
43 * <p/>
44 * <h3>3.1.&nbsp;&nbsp; ID3v2 header</h3>
45 * <p/>
46 * <p>&nbsp;&nbsp; The first part of the ID3v2 tag is the 10 byte tag header, laid out<br> &nbsp;&nbsp; as follows:</p>
47 * <p/>
48 * <p>&nbsp;&nbsp;&nbsp;&nbsp; ID3v2/file identifier&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ID3&quot;<br>
49 * &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 version&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
50 * $04 00<br> &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
51 * %abcd0000<br> &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
52 * 4 * %0xxxxxxx</p>
53 * <p/>
54 * <p>&nbsp;&nbsp; The first three bytes of the tag are always &quot;ID3&quot;, to indicate that<br> &nbsp;&nbsp; this
55 * is an ID3v2 tag, directly followed by the two version bytes. The<br> &nbsp;&nbsp; first byte of ID3v2 version is its
56 * major version, while the second<br> &nbsp;&nbsp; byte is its revision number. In this case this is ID3v2.4.0. All<br>
57 * &nbsp;&nbsp; revisions are backwards compatible while major versions are not. If<br> &nbsp;&nbsp; software with
58 * ID3v2.4.0 and below support should encounter version<br> &nbsp;&nbsp; five or higher it should simply ignore the
59 * whole tag. Version or<br> &nbsp;&nbsp; revision will never be $FF.</p>
60 * <p/>
61 * <p>&nbsp;&nbsp; The version is followed by the ID3v2 flags field, of which currently<br> &nbsp;&nbsp; four flags are
62 * used.<br> </p>
63 * <p/>
64 * <p>&nbsp;&nbsp; a - Unsynchronisation</p>
65 * <p/>
66 * <p>&nbsp;&nbsp;&nbsp;&nbsp; Bit 7 in the 'ID3v2 flags' indicates whether or not<br> &nbsp;&nbsp;&nbsp;&nbsp;
67 * unsynchronisation is applied on all frames (see section 6.1 for<br> &nbsp;&nbsp;&nbsp;&nbsp; details); a set bit
68 * indicates usage.<br> </p>
69 * <p/>
70 * <p>&nbsp;&nbsp; b - Extended header</p>
71 * <p/>
72 * <p>&nbsp;&nbsp;&nbsp;&nbsp; The second bit (bit 6) indicates whether or not the header is<br>
73 * &nbsp;&nbsp;&nbsp;&nbsp; followed by an extended header. The extended header is described in<br>
74 * &nbsp;&nbsp;&nbsp;&nbsp; section 3.2. A set bit indicates the presence of an extended<br> &nbsp;&nbsp;&nbsp;&nbsp;
75 * header.<br> </p>
76 * <p/>
77 * <p>&nbsp;&nbsp; c - Experimental indicator</p>
78 * <p/>
79 * <p>&nbsp;&nbsp;&nbsp;&nbsp; The third bit (bit 5) is used as an 'experimental indicator'. This<br>
80 * &nbsp;&nbsp;&nbsp;&nbsp; flag SHALL always be set when the tag is in an experimental stage.<br> </p>
81 * <p/>
82 * <p>&nbsp;&nbsp; d - Footer present</p>
83 * <p/>
84 * <p>&nbsp;&nbsp;&nbsp;&nbsp; Bit 4 indicates that a footer (section 3.4) is present at the very<br>
85 * &nbsp;&nbsp;&nbsp;&nbsp; end of the tag. A set bit indicates the presence of a footer.<br> </p>
86 * <p/>
87 * <p>&nbsp;&nbsp; All the other flags MUST be cleared. If one of these undefined flags<br> &nbsp;&nbsp; are set, the
88 * tag might not be readable for a parser that does not<br> &nbsp;&nbsp; know the flags function.</p>
89 * <p/>
90 * <p>&nbsp;&nbsp; The ID3v2 tag size is stored as a 32 bit synchsafe integer (section<br> &nbsp;&nbsp; 6.2), making a
91 * total of 28 effective bits (representing up to 256MB).</p>
92 * <p/>
93 * <p>&nbsp;&nbsp; The ID3v2 tag size is the sum of the byte length of the extended<br> &nbsp;&nbsp; header, the padding
94 * and the frames after unsynchronisation. If a<br> &nbsp;&nbsp; footer is present this equals to ('total size' - 20)
95 * bytes, otherwise<br> &nbsp;&nbsp; ('total size' - 10) bytes.</p>
96 * <p/>
97 * <p>&nbsp;&nbsp; An ID3v2 tag can be detected with the following pattern:<br> &nbsp;&nbsp;&nbsp;&nbsp; $49 44 33 yy yy
98 * xx zz zz zz zz<br> &nbsp;&nbsp; Where yy is less than $FF, xx is the 'flags' byte and zz is less than<br>
99 * &nbsp;&nbsp; $80.<br> </p> <a name="sec3.2"></a>
100 * <p/>
101 * <h3>3.2. Extended header</h3>
102 * <p/>
103 * <p>&nbsp;&nbsp; The extended header contains information that can provide further<br> &nbsp;&nbsp; insight in the
104 * structure of the tag, but is not vital to the correct<br> &nbsp;&nbsp; parsing of the tag information; hence the
105 * extended header is<br> &nbsp;&nbsp; optional.</p>
106 * <p/>
107 * <p>&nbsp;&nbsp;&nbsp;&nbsp; Extended header size&nbsp;&nbsp; 4 * %0xxxxxxx<br> &nbsp;&nbsp;&nbsp;&nbsp; Number of
108 * flag bytes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $01<br> &nbsp;&nbsp;&nbsp;&nbsp; Extended
109 * Flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $xx</p>
110 * <p/>
111 * <p>&nbsp;&nbsp; Where the 'Extended header size' is the size of the whole extended<br> &nbsp;&nbsp; header, stored as
112 * a 32 bit synchsafe integer. An extended header can<br> &nbsp;&nbsp; thus never have a size of fewer than six
113 * bytes.</p>
114 * <p/>
115 * <p>&nbsp;&nbsp; The extended flags field, with its size described by 'number of flag<br> &nbsp;&nbsp; bytes', is
116 * defined as:</p>
117 * <p/>
118 * <p>&nbsp;&nbsp;&nbsp;&nbsp; %0bcd0000</p>
119 * <p/>
120 * <p>&nbsp;&nbsp; Each flag that is set in the extended header has data attached, which<br> &nbsp;&nbsp; comes in the
121 * order in which the flags are encountered (i.e. the data<br> &nbsp;&nbsp; for flag 'b' comes before the data for flag
122 * 'c'). Unset flags cannot<br> &nbsp;&nbsp; have any attached data. All unknown flags MUST be unset and their<br>
123 * &nbsp;&nbsp; corresponding data removed when a tag is modified.</p>
124 * <p/>
125 * <p>&nbsp;&nbsp; Every set flag's data starts with a length byte, which contains a<br> &nbsp;&nbsp; value between 0
126 * and 127 ($00 - $7f), followed by data that has the<br> &nbsp;&nbsp; field length indicated by the length byte. If a
127 * flag has no attached<br> &nbsp;&nbsp; data, the value $00 is used as length byte.<br> </p>
128 * <p/>
129 * <p>&nbsp;&nbsp; b - Tag is an update</p>
130 * <p/>
131 * <p>&nbsp;&nbsp;&nbsp;&nbsp; If this flag is set, the present tag is an update of a tag found<br>
132 * &nbsp;&nbsp;&nbsp;&nbsp; earlier in the present file or stream. If frames defined as unique<br>
133 * &nbsp;&nbsp;&nbsp;&nbsp; are found in the present tag, they are to override any<br> &nbsp;&nbsp;&nbsp;&nbsp;
134 * corresponding ones found in the earlier tag. This flag has no<br> &nbsp;&nbsp;&nbsp;&nbsp; corresponding data.</p>
135 * <p/>
136 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Flag data length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $00</p>
137 * <p/>
138 * <p>&nbsp;&nbsp; c - CRC data present</p>
139 * <p/>
140 * <p>&nbsp;&nbsp;&nbsp;&nbsp; If this flag is set, a CRC-32 [ISO-3309] data is included in the<br>
141 * &nbsp;&nbsp;&nbsp;&nbsp; extended header. The CRC is calculated on all the data between the<br>
142 * &nbsp;&nbsp;&nbsp;&nbsp; header and footer as indicated by the header's tag length field,<br>
143 * &nbsp;&nbsp;&nbsp;&nbsp; minus the extended header. Note that this includes the padding (if<br>
144 * &nbsp;&nbsp;&nbsp;&nbsp; there is any), but excludes the footer. The CRC-32 is stored as an<br>
145 * &nbsp;&nbsp;&nbsp;&nbsp; 35 bit synchsafe integer, leaving the upper four bits always<br> &nbsp;&nbsp;&nbsp;&nbsp;
146 * zeroed.</p>
147 * <p/>
148 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Flag data length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $05<br>
149 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Total frame CRC&nbsp;&nbsp;&nbsp; 5 * %0xxxxxxx</p>
150 * <p/>
151 * <p>&nbsp;&nbsp; d - Tag restrictions</p>
152 * <p/>
153 * <p>&nbsp;&nbsp;&nbsp;&nbsp; For some applications it might be desired to restrict a tag in more<br>
154 * &nbsp;&nbsp;&nbsp;&nbsp; ways than imposed by the ID3v2 specification. Note that the<br> &nbsp;&nbsp;&nbsp;&nbsp;
155 * presence of these restrictions does not affect how the tag is<br> &nbsp;&nbsp;&nbsp;&nbsp; decoded, merely how it was
156 * restricted before encoding. If this flag<br> &nbsp;&nbsp;&nbsp;&nbsp; is set the tag is restricted as follows:</p>
157 * <p/>
158 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Flag data length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $01<br>
159 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Restrictions&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
160 * %ppqrrstt</p>
161 * <p/>
162 * <p>&nbsp;&nbsp;&nbsp;&nbsp; p - Tag size restrictions</p>
163 * <p/>
164 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp; No more than 128 frames and 1 MB total tag size.<br>
165 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 01&nbsp;&nbsp; No more than 64 frames and 128 KB total tag size.<br>
166 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp; No more than 32 frames and 40 KB total tag size.<br>
167 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11&nbsp;&nbsp; No more than 32 frames and 4 KB total tag size.</p>
168 * <p/>
169 * <p>&nbsp;&nbsp;&nbsp;&nbsp; q - Text encoding restrictions</p>
170 * <p/>
171 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; No restrictions<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
172 * 1&nbsp;&nbsp;&nbsp; Strings are only encoded with ISO-8859-1 [ISO-8859-1] or<br>
173 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UTF-8 [UTF-8].</p>
174 * <p/>
175 * <p>&nbsp;&nbsp;&nbsp;&nbsp; r - Text fields size restrictions</p>
176 * <p/>
177 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp; No restrictions<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
178 * 01&nbsp;&nbsp; No string is longer than 1024 characters.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp; No
179 * string is longer than 128 characters.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11&nbsp;&nbsp; No string is longer
180 * than 30 characters.</p>
181 * <p/>
182 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Note that nothing is said about how many bytes is used to<br>
183 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; represent those characters, since it is encoding dependent. If a<br>
184 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text frame consists of more than one string, the sum of the<br>
185 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strungs is restricted as stated.</p>
186 * <p/>
187 * <p>&nbsp;&nbsp;&nbsp;&nbsp; s - Image encoding restrictions</p>
188 * <p/>
189 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp; No restrictions<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
190 * 1&nbsp;&nbsp; Images are encoded only with PNG [PNG] or JPEG [JFIF].</p>
191 * <p/>
192 * <p>&nbsp;&nbsp;&nbsp;&nbsp; t - Image size restrictions</p>
193 * <p/>
194 * <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00&nbsp; No restrictions<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 01&nbsp;
195 * All images are 256x256 pixels or smaller.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp; All images are 64x64
196 * pixels or smaller.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11&nbsp; All images are exactly 64x64 pixels, unless
197 * required<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; otherwise.<br> </p> <a name="sec3.3"></a>
198 * <p/>
199 * <h3>3.3.&nbsp;&nbsp; Padding</h3>
200 * <p/>
201 * <p>&nbsp;&nbsp; It is OPTIONAL to include padding after the final frame (at the end<br> &nbsp;&nbsp; of the ID3 tag),
202 * making the size of all the frames together smaller<br> &nbsp;&nbsp; than the size given in the tag header. A possible
203 * purpose of this<br> &nbsp;&nbsp; padding is to allow for adding a few additional frames or enlarge<br> &nbsp;&nbsp;
204 * existing frames within the tag without having to rewrite the entire<br> &nbsp;&nbsp; file. The value of the padding
205 * bytes must be $00. A tag MUST NOT have<br> &nbsp;&nbsp; any padding between the frames or between the tag header and
206 * the<br> &nbsp;&nbsp; frames. Furthermore it MUST NOT have any padding when a tag footer is<br> &nbsp;&nbsp; added to
207 * the tag.<br> </p> <a name="sec3.4"></a>
208 * <p/>
209 * <h3>3.4.&nbsp;&nbsp; ID3v2 footer</h3>
210 * <p/>
211 * <p>&nbsp;&nbsp; To speed up the process of locating an ID3v2 tag when searching from<br> &nbsp;&nbsp; the end of a
212 * file, a footer can be added to the tag. It is REQUIRED<br> &nbsp;&nbsp; to add a footer to an appended tag, i.e. a
213 * tag located after all<br> &nbsp;&nbsp; audio data. The footer is a copy of the header, but with a different<br>
214 * &nbsp;&nbsp; identifier.</p>
215 * <p/>
216 * <p>&nbsp;&nbsp;&nbsp;&nbsp; ID3v2 identifier&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
217 * &quot;3DI&quot;<br> &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 version&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
218 * $04 00<br> &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
219 * %abcd0000<br> &nbsp;&nbsp;&nbsp;&nbsp; ID3v2 size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
220 * 4 * %0xxxxxxx<br> </p>
221 * <p/>
222 * <p>&nbsp;&nbsp; The default location of an ID3v2 tag is prepended to the audio so<br> &nbsp;&nbsp; that players can
223 * benefit from the information when the data is<br> &nbsp;&nbsp; streamed. It is however possible to append the tag, or
224 * make a<br> &nbsp;&nbsp; prepend/append combination. When deciding upon where an unembedded<br> &nbsp;&nbsp; tag
225 * should be located, the following order of preference SHOULD be<br> &nbsp;&nbsp; considered.<br> &nbsp;&nbsp; </p>
226 * <p/>
227 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 1. Prepend the tag.</p>
228 * <p/>
229 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 2. Prepend a tag with all vital information and add a second tag at <br>
230 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the end of the file, before tags from other tagging systems. The<br>
231 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; first tag is required to have a SEEK frame.<br>
232 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
233 * <p/>
234 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 3. Add a tag at the end of the file, before tags from other tagging<br>
235 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; systems.<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
236 * <p/>
237 * <p>&nbsp;&nbsp; In case 2 and 3 the tag can simply be appended if no other known tags<br> &nbsp;&nbsp; are present.
238 * The suggested method to find ID3v2 tags are:<br> &nbsp;&nbsp; </p>
239 * <p/>
240 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 1. Look for a prepended tag using the pattern found in section 3.1.</p>
241 * <p/>
242 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 2. If a SEEK frame was found, use its values to guide further<br>
243 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; searching.</p>
244 * <p/>
245 * <p>&nbsp;&nbsp;&nbsp;&nbsp; 3. Look for a tag footer, scanning from the back of the file.</p>
246 * <p/>
247 * <p>&nbsp;&nbsp; For every new tag that is found, the old tag should be discarded<br> &nbsp;&nbsp; unless the update
248 * flag in the extended header (section 3.2) is set.<br> &nbsp;&nbsp; <br> </p> <a name="sec6"></a>
249 * <p/>
250 * <h3>6.&nbsp;&nbsp; Unsynchronisation</h3>
251 * <p/>
252 * <p>&nbsp;&nbsp; The only purpose of unsynchronisation is to make the ID3v2 tag as<br> &nbsp;&nbsp; compatible as
253 * possible with existing software and hardware. There is<br> &nbsp;&nbsp; no use in 'unsynchronising' tags if the file
254 * is only to be processed<br> &nbsp;&nbsp; only by ID3v2 aware software and hardware. Unsynchronisation is only<br>
255 * &nbsp;&nbsp; useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC<br> &nbsp;&nbsp; files.<br> </p>
256 *
257 * @author Eric Farng
258 * @version $Revision: 1.6 $
259 */
260public class ID3v2_4 extends ID3v2_3 {
261 
262    protected boolean footer = false;
263    protected boolean tagRestriction = false;
264    protected boolean updateTag = false;
265    protected byte imageEncodingRestriction = 0;
266    protected byte imageSizeRestriction = 0;
267    protected byte tagSizeRestriction = 0;
268    protected byte textEncodingRestriction = 0;
269    protected byte textFieldSizeRestriction = 0;
270 
271    /**
272     * Creates a new ID3v2_4 object.
273     */
274    public ID3v2_4() {
275        setMajorVersion((byte) 2);
276        setRevision((byte) 4);
277    }
278 
279    /**
280     * Creates a new ID3v2_4 object.
281     */
282    public ID3v2_4(final ID3v2_4 copyObject) {
283        super(copyObject);
284        this.footer = copyObject.footer;
285        this.tagRestriction = copyObject.tagRestriction;
286        this.updateTag = copyObject.updateTag;
287        this.imageEncodingRestriction = copyObject.imageEncodingRestriction;
288        this.imageSizeRestriction = copyObject.imageSizeRestriction;
289        this.tagSizeRestriction = copyObject.tagSizeRestriction;
290        this.textEncodingRestriction = copyObject.textEncodingRestriction;
291        this.textFieldSizeRestriction = copyObject.textFieldSizeRestriction;
292    }
293 
294    /**
295     * Creates a new ID3v2_4 object.
296     */
297    public ID3v2_4(final AbstractMP3Tag mp3tag) {
298        if (mp3tag != null) {
299            // if we get a tag, we want to convert to id3v2_4
300            // both id3v1 and lyrics3 convert to this type
301            // id3v1 needs to convert to id3v2_4 before converting to lyrics3
302            if (mp3tag instanceof AbstractID3v2) {
303                copyFromID3v2Tag((AbstractID3v2) mp3tag);
304            } else if (mp3tag instanceof ID3v1) {
305                // convert id3v1 tags.
306                final ID3v1 id3tag = (ID3v1) mp3tag;
307                ID3v2_4Frame newFrame;
308                AbstractID3v2FrameBody newBody;
309                if (id3tag.title.length() > 0) {
310                    newBody = new FrameBodyTIT2((byte) 0, id3tag.title);
311                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
312                    this.setFrame(newFrame);
313                }
314                if (id3tag.artist.length() > 0) {
315                    newBody = new FrameBodyTPE1((byte) 0, id3tag.artist);
316                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
317                    this.setFrame(newFrame);
318                }
319                if (id3tag.album.length() > 0) {
320                    newBody = new FrameBodyTALB((byte) 0, id3tag.album);
321                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
322                    this.setFrame(newFrame);
323                }
324                if (id3tag.year.length() > 0) {
325                    newBody = new FrameBodyTDRC((byte) 0, id3tag.year);
326                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
327                    this.setFrame(newFrame);
328                }
329                if (id3tag.comment.length() > 0) {
330                    newBody = new FrameBodyCOMM((byte) 0, "ENG", "", id3tag.comment);
331                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
332                    this.setFrame(newFrame);
333                }
334                if (id3tag.genre >= 0) {
335                    final String genre = "(" +
336                                         Byte.toString(id3tag.genre) +
337                                         ") " +
338                                         TagConstant.genreIdToString.get(new Long(id3tag.genre));
339                    newBody = new FrameBodyTCON((byte) 0, genre);
340                    newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
341                    this.setFrame(newFrame);
342                }
343                if (mp3tag instanceof ID3v1_1) {
344                    final ID3v1_1 id3tag2 = (ID3v1_1) mp3tag;
345                    if (id3tag2.track > 0) {
346                        newBody = new FrameBodyTRCK((byte) 0, Byte.toString(id3tag2.track));
347                        newFrame = new ID3v2_4Frame(false, false, false, false, false, false, newBody);
348                        this.setFrame(newFrame);
349                    }
350                }
351            } else if (mp3tag instanceof AbstractLyrics3) {
352                // put the conversion stuff in the individual frame code.
353                final Lyrics3v2 lyric;
354                if (mp3tag instanceof Lyrics3v2) {
355                    lyric = new Lyrics3v2((Lyrics3v2) mp3tag);
356                } else {
357                    lyric = new Lyrics3v2(mp3tag);
358                }
359                final Iterator iterator = lyric.iterator();
360                Lyrics3v2Field field;
361                ID3v2_4Frame newFrame;
362                while (iterator.hasNext()) {
363                    try {
364                        field = (Lyrics3v2Field) iterator.next();
365                        newFrame = new ID3v2_4Frame(field);
366                        this.setFrame(newFrame);
367                    } catch (InvalidTagException ex) {
368                    }
369                }
370            } else if (mp3tag instanceof FilenameTag) {
371                copyFromID3v2Tag(((FilenameTag) mp3tag).getId3tag());
372            }
373        }
374    }
375 
376    /**
377     * Creates a new ID3v2_4 object.
378     */
379    public ID3v2_4(final RandomAccessFile file) throws TagException, IOException {
380        this.read(file);
381    }
382 
383    public String getIdentifier() {
384        return "ID3v2.40";
385    }
386 
387    public int getSize() {
388        int size = 3 + 2 + 1 + 4;
389        if (this.extended) {
390            size += (4 + 1 + 1);
391            if (this.updateTag) {
392                size++;
393            }
394            if (this.crcDataFlag) {
395                size += 5;
396            }
397            if (this.tagRestriction) {
398                size += 2;
399            }
400        }
401        final Iterator iterator = this.getFrameIterator();
402        AbstractID3v2Frame frame;
403        while (iterator.hasNext()) {
404            frame = (AbstractID3v2Frame) iterator.next();
405            size += frame.getSize();
406        }
407        return size;
408    }
409 
410    public void append(final AbstractMP3Tag tag) {
411        if (tag instanceof ID3v2_4) {
412            this.updateTag = ((ID3v2_4) tag).updateTag;
413            this.footer = ((ID3v2_4) tag).footer;
414            this.tagRestriction = ((ID3v2_4) tag).tagRestriction;
415            this.tagSizeRestriction = ((ID3v2_4) tag).tagSizeRestriction;
416            this.textEncodingRestriction = ((ID3v2_4) tag).textEncodingRestriction;
417            this.textFieldSizeRestriction = ((ID3v2_4) tag).textFieldSizeRestriction;
418            this.imageEncodingRestriction = ((ID3v2_4) tag).imageEncodingRestriction;
419            this.imageSizeRestriction = ((ID3v2_4) tag).imageSizeRestriction;
420        }
421        super.append(tag);
422    }
423 
424    public boolean equals(final Object obj) {
425        if ((obj instanceof ID3v2_4) == false) {
426            return false;
427        }
428        final ID3v2_4 id3v2_4 = (ID3v2_4) obj;
429        if (this.footer != id3v2_4.footer) {
430            return false;
431        }
432        if (this.imageEncodingRestriction != id3v2_4.imageEncodingRestriction) {
433            return false;
434        }
435        if (this.imageSizeRestriction != id3v2_4.imageSizeRestriction) {
436            return false;
437        }
438        if (this.tagRestriction != id3v2_4.tagRestriction) {
439            return false;
440        }
441        if (this.tagSizeRestriction != id3v2_4.tagSizeRestriction) {
442            return false;
443        }
444        if (this.textEncodingRestriction != id3v2_4.textEncodingRestriction) {
445            return false;
446        }
447        if (this.textFieldSizeRestriction != id3v2_4.textFieldSizeRestriction) {
448            return false;
449        }
450        if (this.updateTag != id3v2_4.updateTag) {
451            return false;
452        }
453        return super.equals(obj);
454    }
455 
456    public void overwrite(final AbstractMP3Tag tag) {
457        if (tag instanceof ID3v2_4) {
458            this.updateTag = ((ID3v2_4) tag).updateTag;
459            this.footer = ((ID3v2_4) tag).footer;
460            this.tagRestriction = ((ID3v2_4) tag).tagRestriction;
461            this.tagSizeRestriction = ((ID3v2_4) tag).tagSizeRestriction;
462            this.textEncodingRestriction = ((ID3v2_4) tag).textEncodingRestriction;
463            this.textFieldSizeRestriction = ((ID3v2_4) tag).textFieldSizeRestriction;
464            this.imageEncodingRestriction = ((ID3v2_4) tag).imageEncodingRestriction;
465            this.imageSizeRestriction = ((ID3v2_4) tag).imageSizeRestriction;
466        }
467        super.overwrite(tag);
468    }
469 
470    public void read(final RandomAccessFile file) throws TagException, IOException {
471        final int size;
472        byte[] buffer = new byte[4];
473        file.seek(0);
474        if (seek(file) == false) {
475            throw new TagNotFoundException(getIdentifier() + " tag not found");
476        }
477 
478        // read the major and minor @version bytes & flag bytes
479        file.read(buffer, 0, 3);
480        if ((buffer[0] != 4) || (buffer[1] != 0)) {
481            throw new TagNotFoundException(getIdentifier() + " tag not found");
482        }
483        setMajorVersion(buffer[0]);
484        setRevision(buffer[1]);
485        this.unsynchronization = (buffer[2] & TagConstant.MASK_V24_UNSYNCHRONIZATION) != 0;
486        this.extended = (buffer[2] & TagConstant.MASK_V24_EXTENDED_HEADER) != 0;
487        this.experimental = (buffer[2] & TagConstant.MASK_V24_EXPERIMENTAL) != 0;
488        this.footer = (buffer[2] & TagConstant.MASK_V24_FOOTER_PRESENT) != 0;
489 
490        // read the size
491        file.read(buffer, 0, 4);
492        size = byteArrayToSize(buffer);
493        final long filePointer = file.getFilePointer();
494        if (this.extended) {
495            // int is 4 bytes.
496            final int extendedHeaderSize = file.readInt();
497 
498            // the extended header must be atleast 6 bytes
499            if (extendedHeaderSize <= 6) {
500                throw new InvalidTagException("Invalid Extended Header Size.");
501            }
502            final byte numberOfFlagBytes = file.readByte();
503 
504            // read the flag bytes
505            file.read(buffer, 0, numberOfFlagBytes);
506            this.updateTag = (buffer[0] & TagConstant.MASK_V24_TAG_UPDATE) != 0;
507            this.crcDataFlag = (buffer[0] & TagConstant.MASK_V24_CRC_DATA_PRESENT) != 0;
508            this.tagRestriction = (buffer[0] & TagConstant.MASK_V24_TAG_RESTRICTIONS) != 0;
509 
510            // read the length byte if the flag is set
511            // this tag should always be zero but just in case
512            // read this information.
513            if (this.updateTag) {
514                final int len = file.readByte();
515                buffer = new byte[len];
516                file.read(buffer, 0, len);
517            }
518            if (this.crcDataFlag) {
519                // the CRC has a variable length
520                final int len = file.readByte();
521                buffer = new byte[len];
522                file.read(buffer, 0, len);
523                this.crcData = 0;
524                for (int i = 0; i < len; i++) {
525                    this.crcData <<= 8;
526                    this.crcData += buffer[i];
527                }
528            }
529            if (this.tagRestriction) {
530                final int len = file.readByte();
531                buffer = new byte[len];
532                file.read(buffer, 0, len);
533                this.tagSizeRestriction = (byte) ((buffer[0] & TagConstant.MASK_V24_TAG_SIZE_RESTRICTIONS) >> 6);
534                this.textEncodingRestriction = (byte) ((buffer[0] & TagConstant.MASK_V24_TEXT_ENCODING_RESTRICTIONS) >>
535                                                       5);
536                this.textFieldSizeRestriction = (byte) ((buffer[0] & TagConstant
537                        .MASK_V24_TEXT_FIELD_SIZE_RESTRICTIONS) >> 3);
538                this.imageEncodingRestriction = (byte) ((buffer[0] & TagConstant.MASK_V24_IMAGE_ENCODING) >> 2);
539                this.imageSizeRestriction = (byte) (buffer[0] & TagConstant.MASK_V24_IMAGE_SIZE_RESTRICTIONS);
540            }
541        }
542        ID3v2_4Frame next;
543        this.clearFrameMap();
544 
545        // read the frames
546        this.setFileReadBytes(size);
547        resetPaddingCounter();
548        while ((file.getFilePointer() - filePointer) <= size) {
549            try {
550                next = new ID3v2_4Frame(file);
551                final String id = next.getIdentifier();
552                if (this.hasFrame(id)) {
553                    this.appendDuplicateFrameId(id + "; ");
554                    this.incrementDuplicateBytes(this.getFrame(id).getSize());
555                }
556                this.setFrame(next);
557            } catch (InvalidTagException ex) {
558                if (ex.getMessage().equals("Found empty frame")) {
559                    this.incrementEmptyFrameBytes(10);
560                } else {
561                    this.incrementInvalidFrameBytes();
562                }
563            }
564        }
565        this.setPaddingSize(getPaddingCounter());
566 
567        /**
568         * int newSize = this.getSize(); if ((this.padding + newSize - 10) !=
569         * size) { System.out.println("WARNING: Tag sizes don't add up");
570         * System.out.println("ID3v2.40 tag size : " + newSize);
571         * System.out.println("ID3v2.40 padding : " + this.padding);
572         * System.out.println("ID3v2.40 total : " + (this.padding + newSize));
573         * System.out.println("ID3v2.40 file size: " + size); }
574         */
575    }
576 
577    public boolean seek(final RandomAccessFile file) throws IOException {
578        final byte[] buffer = new byte[3];
579        file.seek(0);
580 
581        // read the tag if it exists
582        file.read(buffer, 0, 3);
583        final String tag = new String(buffer, 0, 3);
584        if (tag.equals("ID3") == false) {
585            return false;
586        }
587 
588        // read the major and minor @version number
589        file.read(buffer, 0, 2);
590 
591        // read back the @version bytes so we can read and save them later
592        file.seek(file.getFilePointer() - 2);
593        return ((buffer[0] == 4) && (buffer[1] == 0));
594    }
595 
596    public String toString() {
597        final Iterator iterator = this.getFrameIterator();
598        AbstractID3v2Frame frame;
599        String str = getIdentifier() + " " + this.getSize() + "\n";
600        str += ("compression              = " + this.compression + "\n");
601        str += ("unsynchronization        = " + this.unsynchronization + "\n");
602        str += ("crcData                  = " + this.crcData + "\n");
603        str += ("crcDataFlag              = " + this.crcDataFlag + "\n");
604        str += ("experimental             = " + this.experimental + "\n");
605        str += ("extended                 = " + this.extended + "\n");
606        str += ("paddingSize              = " + this.paddingSize + "\n");
607        str += ("footer                   = " + this.footer + "\n");
608        str += ("imageEncodingRestriction = " + this.imageEncodingRestriction + "\n");
609        str += ("imageSizeRestriction     = " + this.imageSizeRestriction + "\n");
610        str += ("tagRestriction           = " + this.tagRestriction + "\n");
611        str += ("tagSizeRestriction       = " + this.tagSizeRestriction + "\n");
612        str += ("textEncodingRestriction  = " + this.textEncodingRestriction + "\n");
613        str += ("textFieldSizeRestriction = " + this.textFieldSizeRestriction + "\n");
614        str += ("updateTag                = " + this.updateTag + "\n");
615        while (iterator.hasNext()) {
616            frame = (ID3v2_4Frame) iterator.next();
617            str += (frame.toString() + "\n");
618        }
619        return str + "\n";
620    }
621 
622    public void write(final AbstractMP3Tag tag) {
623        if (tag instanceof ID3v2_4) {
624            this.updateTag = ((ID3v2_4) tag).updateTag;
625            this.footer = ((ID3v2_4) tag).footer;
626            this.tagRestriction = ((ID3v2_4) tag).tagRestriction;
627            this.tagSizeRestriction = ((ID3v2_4) tag).tagSizeRestriction;
628            this.textEncodingRestriction = ((ID3v2_4) tag).textEncodingRestriction;
629            this.textFieldSizeRestriction = ((ID3v2_4) tag).textFieldSizeRestriction;
630            this.imageEncodingRestriction = ((ID3v2_4) tag).imageEncodingRestriction;
631            this.imageSizeRestriction = ((ID3v2_4) tag).imageSizeRestriction;
632        }
633        super.write(tag);
634    }
635 
636    public void write(final RandomAccessFile file) throws IOException {
637        int size;
638        final String str;
639        final Iterator iterator;
640        ID3v2_4Frame frame;
641        final byte[] buffer = new byte[6];
642        final MP3File mp3 = new MP3File();
643        mp3.seekMP3Frame(file);
644        final long mp3start = file.getFilePointer();
645        file.seek(0);
646        str = "ID3";
647        for (int i = 0; i < str.length(); i++) {
648            buffer[i] = (byte) str.charAt(i);
649        }
650        buffer[3] = 4;
651        buffer[4] = 0;
652        if (this.unsynchronization) {
653            buffer[5] |= TagConstant.MASK_V24_UNSYNCHRONIZATION;
654        }
655        if (this.extended) {
656            buffer[5] |= TagConstant.MASK_V24_EXTENDED_HEADER;
657        }
658        if (this.experimental) {
659            buffer[5] |= TagConstant.MASK_V24_EXPERIMENTAL;
660        }
661        if (this.footer) {
662            buffer[5] |= TagConstant.MASK_V24_FOOTER_PRESENT;
663        }
664        file.write(buffer);
665 
666        // write size
667        file.write(sizeToByteArray((int) mp3start - 10));
668        if (this.extended) {
669            size = 6;
670            if (this.updateTag) {
671                size++;
672            }
673            if (this.crcDataFlag) {
674                size += 5;
675            }
676            if (this.tagRestriction) {
677                size += 2;
678            }
679            file.writeInt(size);
680            file.writeByte(1); // always 1 byte of flags in this tag
681            buffer[0] = 0;
682            if (this.updateTag) {
683                buffer[0] |= TagConstant.MASK_V24_TAG_UPDATE;
684            }
685            if (this.crcDataFlag) {
686                buffer[0] |= TagConstant.MASK_V24_CRC_DATA_PRESENT;
687            }
688            if (this.tagRestriction) {
689                buffer[0] |= TagConstant.MASK_V24_TAG_RESTRICTIONS;
690            }
691            file.writeByte(buffer[0]);
692            if (this.updateTag) {
693                file.writeByte(0);
694            }
695 
696            // this can be variable length, but this is easier
697            if (this.crcDataFlag) {
698                file.writeByte(4);
699                file.writeInt(this.crcData);
700            }
701            if (this.tagRestriction) {
702                // todo we need to finish this
703                file.writeByte(1);
704                buffer[0] = (byte) 0;
705                if (this.tagRestriction) {
706                    buffer[0] |= TagConstant.MASK_V24_TAG_SIZE_RESTRICTIONS;
707                }
708                file.writeByte(this.tagSizeRestriction);
709                file.writeByte(this.textEncodingRestriction);
710                file.writeByte(this.textFieldSizeRestriction);
711                file.writeByte(this.imageEncodingRestriction);
712                file.writeByte(this.imageSizeRestriction);
713                file.writeByte(buffer[0]);
714            }
715        }
716 
717        // write all frames
718        iterator = this.getFrameIterator();
719        while (iterator.hasNext()) {
720            frame = (ID3v2_4Frame) iterator.next();
721            frame.write(file);
722        }
723    }
724 
725    private void copyFromID3v2Tag(final AbstractID3v2 mp3tag) {
726        // if the tag is id3v2_4
727        if (mp3tag instanceof ID3v2_4) {
728            final ID3v2_4 tag = (ID3v2_4) mp3tag;
729            this.footer = tag.footer;
730            this.tagRestriction = tag.tagRestriction;
731            this.updateTag = tag.updateTag;
732            this.imageEncodingRestriction = tag.imageEncodingRestriction;
733            this.imageSizeRestriction = tag.imageSizeRestriction;
734            this.tagSizeRestriction = tag.tagSizeRestriction;
735            this.textEncodingRestriction = tag.textEncodingRestriction;
736            this.textFieldSizeRestriction = tag.textFieldSizeRestriction;
737        }
738        if (mp3tag instanceof ID3v2_3) {
739            // and id3v2_4 tag is an instance of id3v2_3 also ...
740            final ID3v2_3 id3tag = (ID3v2_3) mp3tag;
741            this.extended = id3tag.extended;
742            this.experimental = id3tag.experimental;
743            this.crcDataFlag = id3tag.crcDataFlag;
744            this.crcData = id3tag.crcData;
745            this.paddingSize = id3tag.paddingSize;
746        }
747        if (mp3tag instanceof ID3v2_2) {
748            final ID3v2_2 id3tag = (ID3v2_2) mp3tag;
749            this.compression = id3tag.compression;
750            this.unsynchronization = id3tag.unsynchronization;
751        }
752        final AbstractID3v2 id3tag = mp3tag;
753        final Iterator iterator = id3tag.getFrameIterator();
754        AbstractID3v2Frame frame;
755        ID3v2_4Frame newFrame;
756        while (iterator.hasNext()) {
757            frame = (AbstractID3v2Frame) iterator.next();
758            newFrame = new ID3v2_4Frame(frame);
759            this.setFrame(newFrame);
760        }
761    }
762 
763    public String getYearReleased() {
764        String text = "";
765        AbstractID3v2Frame frame = getFrame("TDRC");
766        if (frame != null) {
767            FrameBodyTDRC body = (FrameBodyTDRC) frame.getBody();
768            text = body.getText();
769        }
770        return text.trim();
771    }
772 
773    public void setYearReleased(String yearReleased) {
774        AbstractID3v2Frame field = getFrame("TDRC");
775        if (field == null) {
776            field = new ID3v2_3Frame(new FrameBodyTDRC((byte) 0, yearReleased));
777            setFrame(field);
778        } else {
779            ((FrameBodyTDRC) field.getBody()).setText(yearReleased);
780        }
781    }
782}

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