FileDocCategorySizeDatePackage
NumberVariableLength.javaAPI DocJaudiotagger 2.0.47323Wed Mar 30 16:12:12 BST 2011org.jaudiotagger.tag.datatype

NumberVariableLength.java

/**
 *  @author : Paul Taylor
 *  @author : Eric Farng
 *
 *  Version @version:$Id: NumberVariableLength.java 836 2009-11-12 15:44:07Z 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
 *
 * Description:
 *
 */
package org.jaudiotagger.tag.datatype;

import org.jaudiotagger.tag.InvalidDataTypeException;
import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
import org.jaudiotagger.tag.id3.ID3Tags;

/**
 * Represents a number which may span a number of bytes when written to file depending what size is to be represented.
 * <p/>
 * The bitorder in ID3v2 is most significant bit first (MSB). The byteorder in multibyte numbers is most significant
 * byte first (e.g. $12345678 would be encoded $12 34 56 78), also known as big endian and network byte order.
 * <p/>
 * In ID3Specification would be denoted as $xx xx xx xx (xx ...) , this denotes at least four bytes but may be more.
 * Sometimes may be completely optional (zero bytes)
 */
public class NumberVariableLength extends AbstractDataType
{
    private static final int MINIMUM_NO_OF_DIGITS = 1;
    private static final int MAXIMUM_NO_OF_DIGITS = 8;

    int minLength = MINIMUM_NO_OF_DIGITS;


    /**
     * Creates a new ObjectNumberVariableLength datatype, set minimum length to zero
     * if this datatype is optional.
     *
     * @param identifier
     * @param frameBody
     * @param minimumSize
     */
    public NumberVariableLength(String identifier, AbstractTagFrameBody frameBody, int minimumSize)
    {
        super(identifier, frameBody);

        //Set minimum length, which can be zero if optional
        this.minLength = minimumSize;

    }

    public NumberVariableLength(NumberVariableLength copy)
    {
        super(copy);
        this.minLength = copy.minLength;
    }

    /**
     * Return the maximum number of digits that can be used to express the number
     *
     * @return the maximum number of digits that can be used to express the number
     */
    public int getMaximumLenth()
    {
        return MAXIMUM_NO_OF_DIGITS;
    }

    /**
     * Return the  minimum  number of digits that can be used to express the number
     *
     * @return the minimum number of digits that can be used to express the number
     */
    public int getMinimumLength()
    {
        return minLength;
    }

    /**
     * @param minimumSize
     */
    public void setMinimumSize(int minimumSize)
    {
        if (minimumSize > 0)
        {
            this.minLength = minimumSize;
        }
    }

    /**
     * @return the number of bytes required to write this to a file
     */
    public int getSize()
    {
        if (value == null)
        {
            return 0;
        }
        else
        {
            int current;
            long temp = ID3Tags.getWholeNumber(value);
            int size = 0;

            for (int i = MINIMUM_NO_OF_DIGITS; i <= MAXIMUM_NO_OF_DIGITS; i++)
            {
                current = (byte) temp & 0xFF;

                if (current != 0)
                {
                    size = i;
                }

                temp >>= MAXIMUM_NO_OF_DIGITS;
            }

            return (minLength > size) ? minLength : size;
        }
    }

    /**
     * @param obj
     * @return
     */
    public boolean equals(Object obj)
    {
        if (!(obj instanceof NumberVariableLength))
        {
            return false;
        }

        NumberVariableLength object = (NumberVariableLength) obj;

        return this.minLength == object.minLength && super.equals(obj);

    }

    /**
     * Read from Byte Array
     *
     * @param arr
     * @param offset
     * @throws NullPointerException
     * @throws IndexOutOfBoundsException
     */
    public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException
    {
        //Coding error, should never happen
        if (arr == null)
        {
            throw new NullPointerException("Byte array is null");
        }

        //Coding error, should never happen as far as I can see
        if (offset < 0)
        {
            throw new IllegalArgumentException("negativer offset into an array offset:" + offset);
        }

        //If optional then set value to zero, this will mean that if this frame is written back to file it will be created
        //with this additional datatype wheras it didnt exist but I think this is probably an advantage the frame is
        //more likely to be parsed by other applications if it contains optional fields.
        //if not optional problem with this frame
        if (offset >= arr.length)
        {
            if (minLength == 0)
            {
                value = (long) 0;
                return;
            }
            else
            {
                throw new InvalidDataTypeException("Offset to byte array is out of bounds: offset = " + offset + ", array.length = " + arr.length);
            }
        }

        long lvalue = 0;

        //Read the bytes (starting from offset), the most significant byte of the number being constructed is read first,
        //we then shift the resulting long one byte over to make room for the next byte
        for (int i = offset; i < arr.length; i++)
        {
            lvalue <<= 8;
            lvalue += (arr[i] & 0xff);
        }

        value = lvalue;
    }


    /**
     * @return String representation of the number
     */
    public String toString()
    {
        if (value == null)
        {
            return "";
        }
        else
        {
            return value.toString();
        }
    }

    /**
     * Write to Byte Array
     *
     * @return the datatype converted to a byte array
     */
    public byte[] writeByteArray()
    {
        int size = getSize();
        byte[] arr;

        if (size == 0)
        {
            arr = new byte[0];
        }
        else
        {
            long temp = ID3Tags.getWholeNumber(value);
            arr = new byte[size];

            //keeps shifting the number downwards and masking the last 8 bist to get the value for the next byte
            //to be written
            for (int i = size - 1; i >= 0; i--)
            {
                arr[i] = (byte) (temp & 0xFF);
                temp >>= 8;
            }
        }
        return arr;
    }
}