/*
* jaudiotagger library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.generic;
import org.jaudiotagger.tag.*;
import org.jaudiotagger.tag.datatype.Artwork;
import java.util.*;
/**
* This class is the default implementation for
* {@link org.jaudiotagger.tag.Tag} and introduces some more useful
* functionality to be implemented.<br>
*
* @author Raphaƫl Slinckx
*/
public abstract class AbstractTag implements Tag {
/**
* Stores the amount of {@link TagField} with {@link TagField#isCommon()}
* <code>true</code>.
*/
protected int commonNumber = 0;
/**
* This map stores the {@linkplain TagField#getId() ids} of the stored
* fields to the {@linkplain TagField fields} themselves. Because a linked hashMap is used the order
* that they are added in is preserved, the only exception to this rule is when two fields of the same id
* exist, both will be returned according to when the first item was added to the file. <br>
*/
protected Map<String, List<TagField>> fields = new LinkedHashMap<String, List<TagField>>();
/**
* Add field
*
* @see org.jaudiotagger.tag.Tag#addField(org.jaudiotagger.tag.TagField)
* <p/>
* Changed so add empty fields
*/
public void addField(TagField field) {
if (field == null) {
return;
}
List<TagField> list = fields.get(field.getId());
// There was no previous item
if (list == null) {
list = new ArrayList<TagField>();
list.add(field);
fields.put(field.getId(), list);
if (field.isCommon()) {
commonNumber++;
}
} else {
// We append to existing list
list.add(field);
}
}
/**
* Get list of fields within this tag with the specified id
*
* @see org.jaudiotagger.tag.Tag#getFields(java.lang.String)
*/
public List<TagField> getFields(String id) {
List<TagField> list = fields.get(id);
if (list == null) {
return new ArrayList<TagField>();
}
return list;
}
/**
* @param id
* @return
*/
//Needs to be overridden
//TODO remove
public List<TagField> getFields(FieldKey id) throws KeyNotFoundException {
List<TagField> list = fields.get(id.name());
if (list == null) {
return new ArrayList<TagField>();
}
return list;
}
/**
* @param id
* @param index
* @return
*/
public String getItem(String id, int index) {
List<TagField> l = getFields(id);
return (l.size() > index) ? l.get(index).toString() : "";
}
/**
* Retrieve the first value that exists for this generic key
*
* @param genericKey
* @return
*/
public String getFirst(FieldKey genericKey) throws KeyNotFoundException {
return getValue(genericKey, 0);
}
/**
* @param id
* @return
*/
public String getFirst(String id) {
List<TagField> l = getFields(id);
return (l.size() != 0) ? l.get(0).toString() : "";
}
/**
* @param id audio specific key
* @return
*/
public TagField getFirstField(String id) {
List<TagField> l = getFields(id);
return (l.size() != 0) ? l.get(0) : null;
}
/**
* @see org.jaudiotagger.tag.Tag#getFields()
*/
public Iterator<TagField> getFields() {
final Iterator<Map.Entry<String, List<TagField>>> it = this.fields.entrySet().iterator();
return new Iterator<TagField>() {
private Iterator<TagField> fieldsIt;
private void changeIt() {
if (!it.hasNext()) {
return;
}
Map.Entry<String, List<TagField>> e = it.next();
List<TagField> l = e.getValue();
fieldsIt = l.iterator();
}
public boolean hasNext() {
if (fieldsIt == null) {
changeIt();
}
return it.hasNext() || (fieldsIt != null && fieldsIt.hasNext());
}
public TagField next() {
if (!fieldsIt.hasNext()) {
changeIt();
}
return fieldsIt.next();
}
public void remove() {
fieldsIt.remove();
}
};
}
/**
* Return field count
* <p/>
* TODO:There must be a more efficient way to do this.
*
* @return field count
*/
public int getFieldCount() {
Iterator it = getFields();
int count = 0;
while (it.hasNext()) {
count++;
it.next();
}
return count;
}
public int getFieldCountIncludingSubValues() {
return getFieldCount();
}
/**
* Does this tag contain any comon fields
*
* @see org.jaudiotagger.tag.Tag#hasCommonFields()
*/
public boolean hasCommonFields() {
return commonNumber != 0;
}
/**
* Does this tag contain a field with the specified id
*
* @see org.jaudiotagger.tag.Tag#hasField(java.lang.String)
*/
public boolean hasField(String id) {
return getFields(id).size() != 0;
}
/**
* Determines whether the given charset encoding may be used for the
* represented tagging system.
*
* @param enc charset encoding.
* @return <code>true</code> if the given encoding can be used.
*/
protected abstract boolean isAllowedEncoding(String enc);
/**
* Is this tag empty
*
* @see org.jaudiotagger.tag.Tag#isEmpty()
*/
public boolean isEmpty() {
return fields.size() == 0;
}
/**
* Create new field and set it in the tag
*
* @param genericKey
* @param value
* @throws KeyNotFoundException
* @throws FieldDataInvalidException
*/
public void setField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException {
TagField tagfield = createField(genericKey, value);
setField(tagfield);
}
/**
* Create new field and add it to the tag
*
* @param genericKey
* @param value
* @throws KeyNotFoundException
* @throws FieldDataInvalidException
*/
public void addField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException {
TagField tagfield = createField(genericKey, value);
addField(tagfield);
}
/**
* Set field
* <p/>
* Changed:Just because field is empty it doesn't mean it should be deleted. That should be the choice
* of the developer. (Or does this break things)
*
* @see org.jaudiotagger.tag.Tag#setField(org.jaudiotagger.tag.TagField)
*/
public void setField(TagField field) {
if (field == null) {
return;
}
// If there is already an existing field with same id
// and both are TextFields, we replace the first element
List<TagField> list = fields.get(field.getId());
if (list != null) {
list.set(0, field);
return;
}
// Else we put the new field in the fields.
list = new ArrayList<TagField>();
list.add(field);
fields.put(field.getId(), list);
if (field.isCommon()) {
commonNumber++;
}
}
/**
* The m parameter is effectively ignored
*
* @param id
* @param n
* @param m
* @return
*/
public String getSubValue(FieldKey id, int n, int m) {
return getValue(id, n);
}
/**
* Set or add encoding
*
* @see org.jaudiotagger.tag.Tag#setEncoding(java.lang.String)
*/
public boolean setEncoding(String enc) {
if (!isAllowedEncoding(enc)) {
return false;
}
Iterator it = getFields();
while (it.hasNext()) {
TagField field = (TagField) it.next();
if (field instanceof TagTextField) {
((TagTextField) field).setEncoding(enc);
}
}
return true;
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
public String toString() {
StringBuffer out = new StringBuffer();
out.append("Tag content:\n");
Iterator it = getFields();
while (it.hasNext()) {
TagField field = (TagField) it.next();
out.append("\t");
out.append(field.getId());
out.append(":");
out.append(field.toString());
out.append("\n");
}
return out.toString().substring(0, out.length() - 1);
}
/**
* @param genericKey
* @param value
* @return
* @throws KeyNotFoundException
* @throws FieldDataInvalidException
*/
public abstract TagField createField(FieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException;
/**
* @param genericKey
* @return
* @throws KeyNotFoundException
*/
public abstract TagField getFirstField(FieldKey genericKey) throws KeyNotFoundException;
/**
* @param fieldKey
* @throws KeyNotFoundException
*/
public abstract void deleteField(FieldKey fieldKey) throws KeyNotFoundException;
/**
* Delete all occurrences of field with this id.
*
* @param key
*/
public void deleteField(String key) {
fields.remove(key);
}
public Artwork getFirstArtwork() {
List<Artwork> artwork = getArtworkList();
if (artwork.size() > 0) {
return artwork.get(0);
}
return null;
}
/**
* Create field and then set within tag itself
*
* @param artwork
* @throws FieldDataInvalidException
*/
public void setField(Artwork artwork) throws FieldDataInvalidException {
this.setField(createField(artwork));
}
/**
* Create field and then add within tag itself
*
* @param artwork
* @throws FieldDataInvalidException
*/
public void addField(Artwork artwork) throws FieldDataInvalidException {
this.addField(createField(artwork));
}
/**
* Delete all instance of artwork Field
*
* @throws KeyNotFoundException
*/
public void deleteArtworkField() throws KeyNotFoundException {
this.deleteField(FieldKey.COVER_ART);
}
}