/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphael Slinckx <raphael@slinckx.net>
*
* 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.audio.AudioFile;
import java.io.DataInput;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
/**
* Contains various frequently used static functions in the different tag
* formats
*
* @author Raphael Slinckx
*/
public class Utils {
/**
* Copies the bytes of <code>srd</code> to <code>dst</code> at the
* specified offset.
*
* @param src The byte to be copied.
* @param dst The array to copy to
* @param dstOffset The start offset for the bytes to be copied.
*/
public static void copy(byte[] src, byte[] dst, int dstOffset) {
System.arraycopy(src, 0, dst, dstOffset, src.length);
}
/**
* Returns {@link String#getBytes()}.<br>
*
* @param s The String to call, decode bytes using the specfied charset
* @return The bytes.
*/
public static byte[] getDefaultBytes(String s, String charSet) {
try {
return s.getBytes(charSet);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
}
/*
* Returns the extension of the given file.
* The extension is empty if there is no extension
* The extension is the string after the last "."
*
* @param f The file whose extension is requested
* @return The extension of the given file
*/
public static String getExtension(File f) {
String name = f.getName().toLowerCase();
int i = name.lastIndexOf(".");
if (i == -1) {
return "";
}
return name.substring(i + 1);
}
/*
* Computes a number whereby the 1st byte is the least signifcant and the last
* byte is the most significant.
*
* @param b The byte array @param start The starting offset in b
* (b[offset]). The less significant byte @param end The end index
* (included) in b (b[end]). The most significant byte @return a long number
* represented by the byte sequence.
*
* So if storing a number which only requires one byte it will be stored in the first
* byte.
*/
public static long getLongLE(ByteBuffer b, int start, int end) {
long number = 0;
for (int i = 0; i < (end - start + 1); i++) {
number += ((b.get(start + i) & 0xFF) << i * 8);
}
return number;
}
/*
* Computes a number whereby the 1st byte is the most significant and the last
* byte is the least significant.
*
* So if storing a number which only requires one byte it will be stored in the last
* byte.
*/
public static long getLongBE(ByteBuffer b, int start, int end) {
int number = 0;
for (int i = 0; i < (end - start + 1); i++) {
number += ((b.get(end - i) & 0xFF) << i * 8);
}
return number;
}
public static int getIntLE(byte[] b) {
return (int) getLongLE(ByteBuffer.wrap(b), 0, b.length - 1);
}
/*
* same as above, but returns an int instead of a long @param b The byte
* array @param start The starting offset in b (b[offset]). The less
* significant byte @param end The end index (included) in b (b[end]). The
* most significant byte @return a int number represented by the byte
* sequence.
*/
public static int getIntLE(byte[] b, int start, int end) {
return (int) getLongLE(ByteBuffer.wrap(b), start, end);
}
public static int getIntBE(byte[] b, int start, int end) {
return (int) getLongBE(ByteBuffer.wrap(b), start, end);
}
public static int getIntBE(ByteBuffer b, int start, int end) {
return (int) getLongBE(b, start, end);
}
public static short getShortBE(ByteBuffer b, int start, int end) {
return (short) getIntBE(b, start, end);
}
/**
* Convert int to byte representation - Big Endian (as used by mp4)
*
* @param size
* @return byte represenetation
*/
public static byte[] getSizeBEInt32(int size) {
byte[] b = new byte[4];
b[0] = (byte) ((size >> 24) & 0xFF);
b[1] = (byte) ((size >> 16) & 0xFF);
b[2] = (byte) ((size >> 8) & 0xFF);
b[3] = (byte) (size & 0xFF);
return b;
}
/**
* Convert short to byte representation - Big Endian (as used by mp4)
*
* @param size
* @return byte represenetation
*/
public static byte[] getSizeBEInt16(short size) {
byte[] b = new byte[2];
b[0] = (byte) ((size >> 8) & 0xFF);
b[1] = (byte) (size & 0xFF);
return b;
}
/**
* Convert int to byte representation - Little Endian (as used by ogg vorbis)
*
* @param size
* @return byte represenetation
*/
public static byte[] getSizeLEInt32(int size) {
byte[] b = new byte[4];
b[0] = (byte) (size & 0xff);
b[1] = (byte) ((size >>> 8) & 0xffL);
b[2] = (byte) ((size >>> 16) & 0xffL);
b[3] = (byte) ((size >>> 24) & 0xffL);
return b;
}
/**
* Create String starting from offset upto length using encoding
*
* @param b
* @param offset
* @param length
* @param encoding
* @return
* @throws UnsupportedEncodingException
*/
public static String getString(byte[] b, int offset, int length, String encoding) {
try {
return new String(b, offset, length, encoding);
} catch (UnsupportedEncodingException ue) {
//Shouldnt have to worry about this exception as should only be calling with well defined charsets
throw new RuntimeException(ue);
}
}
/**
* Create String offset from position by offset upto length using encoding, and position of buffer
* is moved to after position + offset + length
*
* @param buffer
* @param offset
* @param length
* @param encoding
* @return
*/
public static String getString(ByteBuffer buffer, int offset, int length, String encoding) {
byte[] b = new byte[length];
buffer.position(buffer.position() + offset);
buffer.get(b);
try {
return new String(b, 0, length, encoding);
} catch (UnsupportedEncodingException uee) {
//TODO, will we ever use unsupported encodings
throw new RuntimeException(uee);
}
}
/*
* Tries to convert a string into an UTF8 array of bytes If the conversion
* fails, return the string converted with the default encoding.
*
* @param s The string to convert @return The byte array representation of
* this string in UTF8 encoding
*/
public static byte[] getUTF8Bytes(String s) throws UnsupportedEncodingException {
return s.getBytes("UTF-8");
}
/**
* Overflow checking since java can't handle unsigned numbers.
*/
public static int readUint32AsInt(DataInput di) throws IOException {
final long l = readUint32(di);
if (l > Integer.MAX_VALUE) {
throw new IOException("uint32 value read overflows int");
}
return (int) l;
}
public static long readUint32(DataInput di) throws IOException {
final byte[] buf8 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
di.readFully(buf8, 4, 4);
final long l = ByteBuffer.wrap(buf8).getLong();
return l;
}
public static int readUint16(DataInput di) throws IOException {
final byte[] buf = {0x00, 0x00, 0x00, 0x00};
di.readFully(buf, 2, 2);
final int i = ByteBuffer.wrap(buf).getInt();
return i;
}
public static String readString(DataInput di, int charsToRead) throws IOException {
final byte[] buf = new byte[charsToRead];
di.readFully(buf);
return new String(buf);
}
public static long readUInt64(ByteBuffer b) {
long result = 0;
result += (readUBEInt32(b) << 32);
result += readUBEInt32(b);
return result;
}
public static int readUBEInt32(ByteBuffer b) {
int result = 0;
result += readUBEInt16(b) << 16;
result += readUBEInt16(b);
return result;
}
public static int readUBEInt24(ByteBuffer b) {
int result = 0;
result += readUBEInt16(b) << 16;
result += readUInt8(b);
return result;
}
public static int readUBEInt16(ByteBuffer b) {
int result = 0;
result += readUInt8(b) << 8;
result += readUInt8(b);
return result;
}
public static int readUInt8(ByteBuffer b) {
return read(b);
}
public static int read(ByteBuffer b) {
int result = (b.get() & 0xFF);
return result;
}
/**
* @param file
* @return filename with audioformat seperator stripped of, lengthened to ensure not too small for calid tempfile
* creation.
*/
public static String getMinBaseFilenameAllowedForTempFile(File file) {
String s = AudioFile.getBaseFilename(file);
if (s.length() >= 3) {
return s;
}
if (s.length() == 1) {
return s + "000";
} else if (s.length() == 1) {
return s + "00";
} else if (s.length() == 2) {
return s + "0";
}
return s;
}
}