/** * @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; } }