View Javadoc

1   /*
2    * Entagged Audio Tag library
3    * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
4    * 
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *  
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   * 
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package entagged.audioformats.generic;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import entagged.audioformats.Tag;
28  
29  /***
30   * This class is the default implementation for
31   * {@link entagged.audioformats.Tag} and introduces some more useful
32   * functionality to be implemented.<br>
33   * 
34   * @author Raphaël Slinckx
35   */
36  public abstract class AbstractTag implements Tag {
37  
38  	/***
39  	 * Stores the amount of {@link TagField} with {@link TagField#isCommon()}
40  	 * <code>true</code>.
41  	 */
42  	protected int commonNumber = 0;
43  
44  	/***
45  	 * This map stores the {@linkplain TagField#getId() ids} of the stored
46  	 * fields to the {@linkplain TagField fields} themselves.<br>
47  	 */
48  	protected HashMap fields = new HashMap();
49  
50  	/***
51  	 * (overridden) The use of this method is not recommended. One must know the
52  	 * underlying audio files implementation.
53  	 * 
54  	 * @see entagged.audioformats.Tag#add(entagged.audioformats.generic.TagField)
55  	 */
56  	/***
57  	 * (overridden)
58  	 * 
59  	 * @see entagged.audioformats.Tag#add(entagged.audioformats.generic.TagField)
60  	 */
61  	public void add(TagField field) {
62  		if (field == null || field.isEmpty())
63  			return;
64  
65  		List list = (List) fields.get(field.getId());
66  
67  		// There was no previous item
68  		if (list == null) {
69  			list = new ArrayList();
70  			list.add(field);
71  			fields.put(field.getId(), list);
72  			if (field.isCommon())
73  				commonNumber++;
74  		} else {
75  			// We append to existing list
76  			list.add(field);
77  		}
78  	}
79  
80  	/***
81  	 * (overridden)
82  	 * 
83  	 * @see entagged.audioformats.Tag#addAlbum(java.lang.String)
84  	 */
85  	public void addAlbum(String s) {
86  		add(createAlbumField(s));
87  	}
88  
89  	/***
90  	 * (overridden)
91  	 * 
92  	 * @see entagged.audioformats.Tag#addArtist(java.lang.String)
93  	 */
94  	public void addArtist(String s) {
95  		add(createArtistField(s));
96  	}
97  
98  	/***
99  	 * (overridden)
100 	 * 
101 	 * @see entagged.audioformats.Tag#addComment(java.lang.String)
102 	 */
103 	public void addComment(String s) {
104 		add(createCommentField(s));
105 	}
106 
107 	/***
108 	 * (overridden)
109 	 * 
110 	 * @see entagged.audioformats.Tag#addGenre(java.lang.String)
111 	 */
112 	public void addGenre(String s) {
113 		add(createGenreField(s));
114 	}
115 
116 	/***
117 	 * (overridden)
118 	 * 
119 	 * @see entagged.audioformats.Tag#addTitle(java.lang.String)
120 	 */
121 	public void addTitle(String s) {
122 		add(createTitleField(s));
123 	}
124 
125 	/***
126 	 * (overridden)
127 	 * 
128 	 * @see entagged.audioformats.Tag#addTrack(java.lang.String)
129 	 */
130 	public void addTrack(String s) {
131 		add(createTrackField(s));
132 	}
133 
134 	/***
135 	 * (overridden)
136 	 * 
137 	 * @see entagged.audioformats.Tag#addYear(java.lang.String)
138 	 */
139 	public void addYear(String s) {
140 		add(createYearField(s));
141 	}
142 
143 	/***
144 	 * Creates a field which represents the &quot;album&quot;.<br>
145 	 * The field will already contain the given content.
146 	 * 
147 	 * @param content
148 	 *            The content of the created field.
149 	 * @return tagfield representing the &quot;album&quot;
150 	 */
151 	protected abstract TagField createAlbumField(String content);
152 
153 	/***
154 	 * Creates a field which represents the &quot;artist&quot;.<br>
155 	 * The field will already contain the given content.
156 	 * 
157 	 * @param content
158 	 *            The content of the created field.
159 	 * @return tagfield representing the &quot;artist&quot;
160 	 */
161 	protected abstract TagField createArtistField(String content);
162 
163 	/***
164 	 * Creates a field which represents the &quot;comment&quot;.<br>
165 	 * The field will already contain the given content.
166 	 * 
167 	 * @param content
168 	 *            The content of the created field.
169 	 * @return tagfield representing the &quot;comment&quot;
170 	 */
171 	protected abstract TagField createCommentField(String content);
172 
173 	/***
174 	 * Creates a field which represents the &quot;genre&quot;.<br>
175 	 * The field will already contain the given content.
176 	 * 
177 	 * @param content
178 	 *            The content of the created field.
179 	 * @return tagfield representing the &quot;genre&quot;
180 	 */
181 	protected abstract TagField createGenreField(String content);
182 
183 	/***
184 	 * Creates a field which represents the &quot;title&quot;.<br>
185 	 * The field will already contain the given content.
186 	 * 
187 	 * @param content
188 	 *            The content of the created field.
189 	 * @return tagfield representing the &quot;title&quot;
190 	 */
191 	protected abstract TagField createTitleField(String content);
192 
193 	/***
194 	 * Creates a field which represents the &quot;track&quot;.<br>
195 	 * The field will already contain the given content.
196 	 * 
197 	 * @param content
198 	 *            The content of the created field.
199 	 * @return tagfield representing the &quot;track&quot;
200 	 */
201 	protected abstract TagField createTrackField(String content);
202 
203 	/***
204 	 * Creates a field which represents the &quot;year&quot;.<br>
205 	 * The field will already contain the given content.
206 	 * 
207 	 * @param content
208 	 *            The content of the created field.
209 	 * @return tagfield representing the &quot;year&quot;
210 	 */
211 	protected abstract TagField createYearField(String content);
212 
213 	/***
214 	 * (overridden)
215 	 * 
216 	 * @see entagged.audioformats.Tag#get(java.lang.String)
217 	 */
218 	public List get(String id) {
219 		List list = (List) fields.get(id);
220 
221 		if (list == null)
222 			return new ArrayList();
223 
224 		return list;
225 	}
226 
227 	/***
228 	 * (overridden)
229 	 * 
230 	 * @see entagged.audioformats.Tag#getAlbum()
231 	 */
232 	public List getAlbum() {
233 		return get(getAlbumId());
234 	}
235 
236 	/***
237 	 * Returns the identifier for a field representing the &quot;album&quot;<br>
238 	 * 
239 	 * @see TagField#getId()
240 	 * @return identifier for the &quot;album&quot; field.
241 	 */
242 	protected abstract String getAlbumId();
243 
244 	/***
245 	 * (overridden)
246 	 * 
247 	 * @see entagged.audioformats.Tag#getArtist()
248 	 */
249 	public List getArtist() {
250 		return get(getArtistId());
251 	}
252 
253 	/***
254 	 * Returns the identifier for a field representing the &quot;artist&quot;<br>
255 	 * 
256 	 * @see TagField#getId()
257 	 * @return identifier for the &quot;artist&quot; field.
258 	 */
259 	protected abstract String getArtistId();
260 
261 	/***
262 	 * (overridden)
263 	 * 
264 	 * @see entagged.audioformats.Tag#getComment()
265 	 */
266 	public List getComment() {
267 		return get(getCommentId());
268 	}
269 
270 	/***
271 	 * Returns the identifier for a field representing the &quot;comment&quot;<br>
272 	 * 
273 	 * @see TagField#getId()
274 	 * @return identifier for the &quot;comment&quot; field.
275 	 */
276 	protected abstract String getCommentId();
277 
278 	/***
279 	 * (overridden)
280 	 * 
281 	 * @see entagged.audioformats.Tag#getFields()
282 	 */
283 	public Iterator getFields() {
284 		final Iterator it = this.fields.entrySet().iterator();
285 		return new Iterator() {
286 			private Iterator fieldsIt;
287 
288 			private void changeIt() {
289 				if (!it.hasNext())
290 					return;
291 
292 				List l = (List) ((Map.Entry) it.next()).getValue();
293 				fieldsIt = l.iterator();
294 			}
295 
296 			public boolean hasNext() {
297 				if (fieldsIt == null) {
298 					changeIt();
299 				}
300 				return it.hasNext() || (fieldsIt != null && fieldsIt.hasNext());
301 			}
302 
303 			public Object next() {
304 				if (!fieldsIt.hasNext())
305 					changeIt();
306 
307 				return fieldsIt.next();
308 			}
309 
310 			public void remove() {
311 				fieldsIt.remove();
312 			}
313 		};
314 	}
315 
316 	/***
317 	 * (overridden)
318 	 * 
319 	 * @see entagged.audioformats.Tag#getFirstAlbum()
320 	 */
321 	public String getFirstAlbum() {
322 		List l = get(getAlbumId());
323 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
324 	}
325 
326 	/***
327 	 * (overridden)
328 	 * 
329 	 * @see entagged.audioformats.Tag#getFirstArtist()
330 	 */
331 	public String getFirstArtist() {
332 		List l = get(getArtistId());
333 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
334 	}
335 
336 	/***
337 	 * (overridden)
338 	 * 
339 	 * @see entagged.audioformats.Tag#getFirstComment()
340 	 */
341 	public String getFirstComment() {
342 		List l = get(getCommentId());
343 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
344 	}
345 
346 	/***
347 	 * (overridden)
348 	 * 
349 	 * @see entagged.audioformats.Tag#getFirstGenre()
350 	 */
351 	public String getFirstGenre() {
352 		List l = get(getGenreId());
353 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
354 	}
355 
356 	/***
357 	 * (overridden)
358 	 * 
359 	 * @see entagged.audioformats.Tag#getFirstTitle()
360 	 */
361 	public String getFirstTitle() {
362 		List l = get(getTitleId());
363 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
364 	}
365 
366 	/***
367 	 * (overridden)
368 	 * 
369 	 * @see entagged.audioformats.Tag#getFirstTrack()
370 	 */
371 	public String getFirstTrack() {
372 		List l = get(getTrackId());
373 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
374 	}
375 
376 	/***
377 	 * (overridden)
378 	 * 
379 	 * @see entagged.audioformats.Tag#getFirstYear()
380 	 */
381 	public String getFirstYear() {
382 		List l = get(getYearId());
383 		return (l.size() != 0) ? ((TagTextField) l.get(0)).getContent() : "";
384 	}
385 
386 	/***
387 	 * (overridden)
388 	 * 
389 	 * @see entagged.audioformats.Tag#getGenre()
390 	 */
391 	public List getGenre() {
392 		return get(getGenreId());
393 	}
394 
395 	/***
396 	 * Returns the identifier for a field representing the &quot;genre&quot;<br>
397 	 * 
398 	 * @see TagField#getId()
399 	 * @return identifier for the &quot;genre&quot; field.
400 	 */
401 	protected abstract String getGenreId();
402 
403 	/***
404 	 * (overridden)
405 	 * 
406 	 * @see entagged.audioformats.Tag#getTitle()
407 	 */
408 	public List getTitle() {
409 		return get(getTitleId());
410 	}
411 
412 	/***
413 	 * Returns the identifier for a field representing the &quot;title&quot;<br>
414 	 * 
415 	 * @see TagField#getId()
416 	 * @return identifier for the &quot;title&quot; field.
417 	 */
418 	protected abstract String getTitleId();
419 
420 	/***
421 	 * (overridden)
422 	 * 
423 	 * @see entagged.audioformats.Tag#getTrack()
424 	 */
425 	public List getTrack() {
426 		return get(getTrackId());
427 	}
428 
429 	/***
430 	 * Returns the identifier for a field representing the &quot;track&quot;<br>
431 	 * 
432 	 * @see TagField#getId()
433 	 * @return identifier for the &quot;track&quot; field.
434 	 */
435 	protected abstract String getTrackId();
436 
437 	/***
438 	 * (overridden)
439 	 * 
440 	 * @see entagged.audioformats.Tag#getYear()
441 	 */
442 	public List getYear() {
443 		return get(getYearId());
444 	}
445 
446 	/***
447 	 * Returns the identifier for a field representing the &quot;year&quot;<br>
448 	 * 
449 	 * @see TagField#getId()
450 	 * @return identifier for the &quot;year&quot; field.
451 	 */
452 	protected abstract String getYearId();
453 
454 	/***
455 	 * (overridden)
456 	 * 
457 	 * @see entagged.audioformats.Tag#hasCommonFields()
458 	 */
459 	public boolean hasCommonFields() {
460 		return commonNumber != 0;
461 	}
462 
463 	/***
464 	 * (overridden)
465 	 * 
466 	 * @see entagged.audioformats.Tag#hasField(java.lang.String)
467 	 */
468 	public boolean hasField(String id) {
469 		return get(id).size() != 0;
470 	}
471 
472 	/***
473 	 * Determines whether the given charset encoding may be used for the
474 	 * represented tagging system.
475 	 * 
476 	 * @param enc
477 	 *            charset encoding.
478 	 * @return <code>true</code> if the given encoding can be used.
479 	 */
480 	protected abstract boolean isAllowedEncoding(String enc);
481 
482 	/***
483 	 * (overridden)
484 	 * 
485 	 * @see entagged.audioformats.Tag#isEmpty()
486 	 */
487 	public boolean isEmpty() {
488 		return fields.size() == 0;
489 	}
490 
491 	/***
492 	 * (overridden)
493 	 * 
494 	 * @see entagged.audioformats.Tag#merge(entagged.audioformats.Tag)
495 	 */
496 	public void merge(Tag tag) {
497 		// FIXME: Improve me, for the moment,
498 		// it overwrites this tag with other values
499 		// FIXME: TODO: an abstract method that merges particular things for
500 		// each
501 		// format
502 		if (getTitle().size() == 0)
503 			setTitle(tag.getFirstTitle());
504 		if (getArtist().size() == 0)
505 			setArtist(tag.getFirstArtist());
506 		if (getAlbum().size() == 0)
507 			setAlbum(tag.getFirstAlbum());
508 		if (getYear().size() == 0)
509 			setYear(tag.getFirstYear());
510 		if (getComment().size() == 0)
511 			setComment(tag.getFirstComment());
512 		if (getTrack().size() == 0)
513 			setTrack(tag.getFirstTrack());
514 		if (getGenre().size() == 0)
515 			setGenre(tag.getFirstGenre());
516 	}
517 
518 	/***
519 	 * (overridden)
520 	 * 
521 	 * @see entagged.audioformats.Tag#set(entagged.audioformats.generic.TagField)
522 	 */
523 	public void set(TagField field) {
524 		if (field == null)
525 			return;
526 
527 		// If an empty field is passed, we delete all the previous ones
528 		if (field.isEmpty()) {
529 			Object removed = fields.remove(field.getId());
530 			if (removed != null && field.isCommon())
531 				commonNumber--;
532 			return;
533 		}
534 
535 		// If there is already an existing field with same id
536 		// and both are TextFields, we update the first element
537 		List l = (List) fields.get(field.getId());
538 		if (l != null) {
539 			TagField f = (TagField) l.get(0);
540 			f.copyContent(field);
541 			return;
542 		}
543 
544 		// Else we put the new field in the fields.
545 		l = new ArrayList();
546 		l.add(field);
547 		fields.put(field.getId(), l);
548 		if (field.isCommon())
549 			commonNumber++;
550 	}
551 
552 	/***
553 	 * (overridden)
554 	 * 
555 	 * @see entagged.audioformats.Tag#setAlbum(java.lang.String)
556 	 */
557 	public void setAlbum(String s) {
558 		set(createAlbumField(s));
559 	}
560 
561 	/***
562 	 * (overridden)
563 	 * 
564 	 * @see entagged.audioformats.Tag#setArtist(java.lang.String)
565 	 */
566 	public void setArtist(String s) {
567 		set(createArtistField(s));
568 	}
569 
570 	/***
571 	 * (overridden)
572 	 * 
573 	 * @see entagged.audioformats.Tag#setComment(java.lang.String)
574 	 */
575 	public void setComment(String s) {
576 		set(createCommentField(s));
577 	}
578 
579 	/***
580 	 * (overridden)
581 	 * 
582 	 * @see entagged.audioformats.Tag#setEncoding(java.lang.String)
583 	 */
584 	public boolean setEncoding(String enc) {
585 		if (!isAllowedEncoding(enc)) {
586 			return false;
587 		}
588 
589 		Iterator it = getFields();
590 		while (it.hasNext()) {
591 			TagField field = (TagField) it.next();
592 			if (field instanceof TagTextField) {
593 				((TagTextField) field).setEncoding(enc);
594 			}
595 		}
596 
597 		return true;
598 	}
599 
600 	/***
601 	 * (overridden)
602 	 * 
603 	 * @see entagged.audioformats.Tag#setGenre(java.lang.String)
604 	 */
605 	public void setGenre(String s) {
606 		set(createGenreField(s));
607 	}
608 
609 	/***
610 	 * (overridden)
611 	 * 
612 	 * @see entagged.audioformats.Tag#setTitle(java.lang.String)
613 	 */
614 	public void setTitle(String s) {
615 		set(createTitleField(s));
616 	}
617 
618 	/***
619 	 * (overridden)
620 	 * 
621 	 * @see entagged.audioformats.Tag#setTrack(java.lang.String)
622 	 */
623 	public void setTrack(String s) {
624 		set(createTrackField(s));
625 	}
626 
627 	/***
628 	 * (overridden)
629 	 * 
630 	 * @see entagged.audioformats.Tag#setYear(java.lang.String)
631 	 */
632 	public void setYear(String s) {
633 		set(createYearField(s));
634 	}
635 
636 	/***
637 	 * (overridden)
638 	 * 
639 	 * @see java.lang.Object#toString()
640 	 */
641 	public String toString() {
642 		StringBuffer out = new StringBuffer();
643 		out.append("Tag content:\n");
644 		Iterator it = getFields();
645 		while (it.hasNext()) {
646 			TagField field = (TagField) it.next();
647 			out.append("\t");
648 			out.append(field.getId());
649 			out.append(" : ");
650 			out.append(field.toString());
651 			out.append("\n");
652 		}
653 		return out.toString().substring(0, out.length() - 1);
654 	}
655 }