/**
* @author : Paul Taylor
* @author : Eric Farng
*
* Version @version:$Id: AbstractDataType.java 923 2010-10-16 21:59:49Z paultaylor $
*
* MusicTag Copyright (C)2003,2004
*
* 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,
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*/
package org.jaudiotagger.tag.datatype;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.InvalidDataTypeException;
import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
import java.util.Arrays;
import java.util.logging.Logger;
/**
* Represents a field/data type that can be held within a frames body, these map loosely onto
* Section 4. ID3v2 frame overview at http://www.id3.org/id3v2.4.0-structure.txt
*/
public abstract class AbstractDataType
{
protected static final String TYPE_ELEMENT = "element";
//Logger
public static Logger logger = Logger.getLogger("org.jaudiotagger.tag.datatype");
/**
* Holds the data
*/
protected Object value = null;
/**
* Holds the key such as "Text" or "PictureType", the naming of keys are fairly arbitary but are intended
* to make it easier to for the developer, the keys themseleves are not written to the tag.
*/
protected String identifier = "";
/**
* Holds the calling body, allows an datatype to query other objects in the
* body such as the Text Encoding of the frame
*/
protected AbstractTagFrameBody frameBody = null;
/**
* Holds the size of the data in file when read/written
*/
protected int size;
/**
* Construct an abstract datatype identified by identifier and linked to a framebody without setting
* an initial value.
*
* @param identifier to allow retrieval of this datatype by name from framebody
* @param frameBody that the dataype is associated with
*/
protected AbstractDataType(String identifier, AbstractTagFrameBody frameBody)
{
this.identifier = identifier;
this.frameBody = frameBody;
}
/**
* Construct an abstract datatype identified by identifier and linked to a framebody initilised with a value
*
* @param identifier to allow retrieval of this datatype by name from framebody
* @param frameBody that the dataype is associated with
* @param value of this DataType
*/
protected AbstractDataType(String identifier, AbstractTagFrameBody frameBody, Object value)
{
this.identifier = identifier;
this.frameBody = frameBody;
setValue(value);
}
/**
* This is used by subclasses, to clone the data within the copyObject
* <p/>
* TODO:It seems to be missing some of the more complex value types.
* @param copyObject
*/
public AbstractDataType(AbstractDataType copyObject)
{
// no copy constructor in super class
this.identifier = copyObject.identifier;
if (copyObject.value == null)
{
this.value = null;
}
else if (copyObject.value instanceof String)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Boolean)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Byte)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Character)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Double)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Float)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Integer)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Long)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof Short)
{
this.value = copyObject.value;
}
else if(copyObject.value instanceof MultipleTextEncodedStringNullTerminated.Values)
{
this.value = copyObject.value;
}
else if(copyObject.value instanceof PairedTextEncodedStringNullTerminated.ValuePairs)
{
this.value = copyObject.value;
}
else if(copyObject.value instanceof PartOfSet.PartOfSetValue)
{
this.value = copyObject.value;
}
else if (copyObject.value instanceof boolean[])
{
this.value = ((boolean[]) copyObject.value).clone();
}
else if (copyObject.value instanceof byte[])
{
this.value = ((byte[]) copyObject.value).clone();
}
else if (copyObject.value instanceof char[])
{
this.value = ((char[]) copyObject.value).clone();
}
else if (copyObject.value instanceof double[])
{
this.value = ((double[]) copyObject.value).clone();
}
else if (copyObject.value instanceof float[])
{
this.value = ((float[]) copyObject.value).clone();
}
else if (copyObject.value instanceof int[])
{
this.value = ((int[]) copyObject.value).clone();
}
else if (copyObject.value instanceof long[])
{
this.value = ((long[]) copyObject.value).clone();
}
else if (copyObject.value instanceof short[])
{
this.value = ((short[]) copyObject.value).clone();
}
else if (copyObject.value instanceof Object[])
{
this.value = ((Object[]) copyObject.value).clone();
}
else
{
throw new UnsupportedOperationException("Unable to create copy of class " + copyObject.getClass());
}
}
/**
* Set the framebody that this datatype is associated with
*
* @param frameBody
*/
public void setBody(AbstractTagFrameBody frameBody)
{
this.frameBody = frameBody;
}
/**
* Get the framebody associated with this datatype
*
* @return the framebody that this datatype is associated with
*/
public AbstractTagFrameBody getBody()
{
return frameBody;
}
/**
* Return the key as declared by the frame bodies datatype list
*
* @return the key used to reference this datatype from a framebody
*/
public String getIdentifier()
{
return identifier;
}
/**
* Set the value held by this datatype, this is used typically used when the
* user wants to modify the value in an existing frame.
*
* @param value
*/
public void setValue(Object value)
{
this.value = value;
}
/**
* Get value held by this Object
*
* @return value held by this Object
*/
public Object getValue()
{
return value;
}
/**
* Simplified wrapper for reading bytes from file into Object.
* Used for reading Strings, this class should be overridden
* for non String Objects
*
* @param arr
* @throws org.jaudiotagger.tag.InvalidDataTypeException
*/
final public void readByteArray(byte[] arr) throws InvalidDataTypeException
{
readByteArray(arr, 0);
}
/**
* This defines the size in bytes of the datatype being
* held when read/written to file.
*
* @return the size in bytes of the datatype
*/
abstract public int getSize();
/**
* @param obj
* @return whether this and obj are deemed equivalent
*/
public boolean equals(Object obj)
{
if(this==obj)
{
return true;
}
if (!(obj instanceof AbstractDataType))
{
return false;
}
AbstractDataType object = (AbstractDataType) obj;
if (!this.identifier.equals(object.identifier))
{
return false;
}
if ((this.value == null) && (object.value == null))
{
return true;
}
else if ((this.value == null) || (object.value == null))
{
return false;
}
// boolean[]
if (this.value instanceof boolean[] && object.value instanceof boolean[])
{
if (!Arrays.equals((boolean[]) this.value, (boolean[]) object.value))
{
return false;
}
// byte[]
}
else if (this.value instanceof byte[] && object.value instanceof byte[])
{
if (!Arrays.equals((byte[]) this.value, (byte[]) object.value))
{
return false;
}
// char[]
}
else if (this.value instanceof char[] && object.value instanceof char[])
{
if (!Arrays.equals((char[]) this.value, (char[]) object.value))
{
return false;
}
// double[]
}
else if (this.value instanceof double[] && object.value instanceof double[])
{
if (!Arrays.equals((double[]) this.value, (double[]) object.value))
{
return false;
}
// float[]
}
else if (this.value instanceof float[] && object.value instanceof float[])
{
if (!Arrays.equals((float[]) this.value, (float[]) object.value))
{
return false;
}
// int[]
}
else if (this.value instanceof int[] && object.value instanceof int[])
{
if (!Arrays.equals((int[]) this.value, (int[]) object.value))
{
return false;
}
// long[]
}
else if (this.value instanceof long[] && object.value instanceof long[])
{
if (!Arrays.equals((long[]) this.value, (long[]) object.value))
{
return false;
}
// Object[]
}
else if (this.value instanceof Object[] && object.value instanceof Object[])
{
if (!Arrays.equals((Object[]) this.value, (Object[]) object.value))
{
return false;
}
// short[]
}
else if (this.value instanceof short[] && object.value instanceof short[])
{
if (!Arrays.equals((short[]) this.value, (short[]) object.value))
{
return false;
}
}
else if (!this.value.equals(object.value))
{
return false;
}
return true;
}
/**
* This is the starting point for reading bytes from the file into the ID3 datatype
* starting at offset.
* This class must be overridden
*
* @param arr
* @param offset
* @throws org.jaudiotagger.tag.InvalidDataTypeException
*/
public abstract void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException;
/**
* Starting point write ID3 Datatype back to array of bytes.
* This class must be overridden.
*
* @return the array of bytes representing this datatype that should be written to file
*/
public abstract byte[] writeByteArray();
/**
* Return String Representation of Datatype *
*/
public void createStructure()
{
MP3File.getStructureFormatter().addElement(identifier, getValue().toString());
}
}
|