/*
* 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.id3;
import org.jaudiotagger.tag.TagException;
import java.lang.reflect.Constructor;
import java.util.logging.Logger;
/**
* This contains static methods that can be performed on tags
* and to convert between tags.
*
* @author : Paul Taylor
* @author : Eric Farng
* @version $Id: ID3Tags.java 919 2010-10-04 13:46:39Z paultaylor $
*/
public class ID3Tags {
//Logger
public static Logger logger = Logger.getLogger("org.jaudiotagger.tag.id3");
private ID3Tags() {
}
/**
* Returns true if the identifier is a valid ID3v2.2 frame identifier
*
* @param identifier string to test
* @return true if the identifier is a valid ID3v2.2 frame identifier
*/
public static boolean isID3v22FrameIdentifier(String identifier) {
//If less than 3 cant be an identifier
if (identifier.length() < 3) {
return false;
}
//If 3 is it a known identifier
else return identifier.length() == 3 && ID3v22Frames.getInstanceOf().getIdToValueMap().containsKey(identifier);
}
/**
* Returns true if the identifier is a valid ID3v2.3 frame identifier
*
* @param identifier string to test
* @return true if the identifier is a valid ID3v2.3 frame identifier
*/
public static boolean isID3v23FrameIdentifier(String identifier) {
return identifier.length() >= 4 && ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier.substring(0, 4));
}
/**
* Returns true if the identifier is a valid ID3v2.4 frame identifier
*
* @param identifier string to test
* @return true if the identifier is a valid ID3v2.4 frame identifier
*/
public static boolean isID3v24FrameIdentifier(String identifier) {
return identifier.length() >= 4 && ID3v24Frames.getInstanceOf().getIdToValueMap().containsKey(identifier.substring(0, 4));
}
/**
* Given an datatype, try to return it as a <code>long</code>. This tries to
* parse a string, and takes <code>Long, Short, Byte, Integer</code>
* objects and gets their value. An exception is not explicitly thrown
* here because it would causes too many other methods to also throw it.
*
* @param value datatype to find long from.
* @return <code>long</code> value
* @throws IllegalArgumentException
*/
static public long getWholeNumber(Object value) {
long number;
if (value instanceof String) {
number = Long.parseLong((String) value);
} else if (value instanceof Byte) {
number = (Byte) value;
} else if (value instanceof Short) {
number = (Short) value;
} else if (value instanceof Integer) {
number = (Integer) value;
} else if (value instanceof Long) {
number = (Long) value;
} else {
throw new IllegalArgumentException("Unsupported value class: " + value.getClass().getName());
}
return number;
}
/**
* Convert from ID3v22 FrameIdentifier to ID3v23
*
* @param identifier
* @return
*/
public static String convertFrameID22To23(String identifier) {
if (identifier.length() < 3) {
return null;
}
return ID3Frames.convertv22Tov23.get((String) identifier.subSequence(0, 3));
}
/**
* Convert from ID3v22 FrameIdentifier to ID3v24
*
* @param identifier
* @return
*/
public static String convertFrameID22To24(String identifier) {
//Idv22 identifiers are only of length 3 times
if (identifier.length() < 3) {
return null;
}
//Has idv22 been mapped to v23
String id = ID3Frames.convertv22Tov23.get(identifier.substring(0, 3));
if (id != null) {
//has v2.3 been mapped to v2.4
String v23id = ID3Frames.convertv23Tov24.get(id);
if (v23id == null) {
//if not it may be because v2.3 and and v2.4 are same so wont be
//in mapping
if (ID3v24Frames.getInstanceOf().getIdToValueMap().get(id) != null) {
return id;
} else {
return null;
}
} else {
return v23id;
}
} else {
return null;
}
}
/**
* Convert from ID3v23 FrameIdentifier to ID3v22
*
* @param identifier
* @return
*/
public static String convertFrameID23To22(String identifier) {
if (identifier.length() < 4) {
return null;
}
//If it is a v23 identifier
if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier)) {
//If only name has changed v22 and modified in v23 return result of.
return ID3Frames.convertv23Tov22.get(identifier.substring(0, 4));
}
return null;
}
/**
* Convert from ID3v23 FrameIdentifier to ID3v24
*
* @param identifier
* @return
*/
public static String convertFrameID23To24(String identifier) {
if (identifier.length() < 4) {
return null;
}
//If it is a ID3v23 identifier
if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier)) {
//If no change between ID3v23 and ID3v24 should be in ID3v24 list.
if (ID3v24Frames.getInstanceOf().getIdToValueMap().containsKey(identifier)) {
return identifier;
}
//If only name has changed ID3v23 and modified in ID3v24 return result of.
else {
return ID3Frames.convertv23Tov24.get(identifier.substring(0, 4));
}
}
return null;
}
/**
* Force from ID3v22 FrameIdentifier to ID3v23, this is where the frame and structure
* has changed from v2 to v3 but we can still do some kind of conversion.
*
* @param identifier
* @return
*/
public static String forceFrameID22To23(String identifier) {
return ID3Frames.forcev22Tov23.get(identifier);
}
/**
* Force from ID3v22 FrameIdentifier to ID3v23, this is where the frame and structure
* has changed from v2 to v3 but we can still do some kind of conversion.
*
* @param identifier
* @return
*/
public static String forceFrameID23To22(String identifier) {
return ID3Frames.forcev23Tov22.get(identifier);
}
/**
* Force from ID3v2.30 FrameIdentifier to ID3v2.40, this is where the frame and structure
* has changed from v3 to v4 but we can still do some kind of conversion.
*
* @param identifier
* @return
*/
public static String forceFrameID23To24(String identifier) {
return ID3Frames.forcev23Tov24.get(identifier);
}
/**
* Force from ID3v2.40 FrameIdentifier to ID3v2.30, this is where the frame and structure
* has changed between v4 to v3 but we can still do some kind of conversion.
*
* @param identifier
* @return
*/
public static String forceFrameID24To23(String identifier) {
return ID3Frames.forcev24Tov23.get(identifier);
}
/**
* Convert from ID3v24 FrameIdentifier to ID3v23
*
* @param identifier
* @return
*/
public static String convertFrameID24To23(String identifier) {
String id;
if (identifier.length() < 4) {
return null;
}
id = ID3Frames.convertv24Tov23.get(identifier);
if (id == null) {
if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier)) {
id = identifier;
}
}
return id;
}
/**
* Unable to instantiate abstract classes, so can't call the copy
* constructor. So find out the instantiated class name and call the copy
* constructor through reflection (e.g for a a FrameBody would have to have a constructor
* that takes another frameBody as the same type as a parameter)
*
* @param copyObject
* @return
* @throws IllegalArgumentException if no suitable constructor exists
*/
public static Object copyObject(Object copyObject) {
Constructor<?> constructor;
Class<?>[] constructorParameterArray;
Object[] parameterArray;
if (copyObject == null) {
return null;
}
try {
constructorParameterArray = new Class[1];
constructorParameterArray[0] = copyObject.getClass();
constructor = copyObject.getClass().getConstructor(constructorParameterArray);
parameterArray = new Object[1];
parameterArray[0] = copyObject;
return constructor.newInstance(parameterArray);
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException("NoSuchMethodException: Error finding constructor to create copy:" + copyObject.getClass().getName());
} catch (IllegalAccessException ex) {
throw new IllegalArgumentException("IllegalAccessException: No access to run constructor to create copy" + copyObject.getClass().getName());
} catch (InstantiationException ex) {
throw new IllegalArgumentException("InstantiationException: Unable to instantiate constructor to copy" + copyObject.getClass().getName());
} catch (java.lang.reflect.InvocationTargetException ex) {
if (ex.getCause() instanceof Error) {
throw (Error) ex.getCause();
} else if (ex.getCause() instanceof RuntimeException) {
throw (RuntimeException) ex.getCause();
} else {
throw new IllegalArgumentException("InvocationTargetException: Unable to invoke constructor to create copy");
}
}
}
/**
* Find the first whole number that can be parsed from the string
*
* @param str string to search
* @return first whole number that can be parsed from the string
* @throws TagException
*/
public static long findNumber(String str) throws TagException {
return findNumber(str, 0);
}
/**
* Find the first whole number that can be parsed from the string
*
* @param str string to search
* @param offset start seaching from this index
* @return first whole number that can be parsed from the string
* @throws TagException
* @throws NullPointerException
* @throws IndexOutOfBoundsException
*/
public static long findNumber(String str, int offset) throws TagException {
if (str == null) {
throw new NullPointerException("String is null");
}
if ((offset < 0) || (offset >= str.length())) {
throw new IndexOutOfBoundsException("Offset to image string is out of bounds: offset = " + offset + ", string.length()" + str.length());
}
int i;
int j;
long num;
i = offset;
while (i < str.length()) {
if (((str.charAt(i) >= '0') && (str.charAt(i) <= '9')) || (str.charAt(i) == '-')) {
break;
}
i++;
}
j = i + 1;
while (j < str.length()) {
if (((str.charAt(j) < '0') || (str.charAt(j) > '9'))) {
break;
}
j++;
}
if ((j <= str.length()) && (j > i)) {
num = Long.parseLong(str.substring(i, j));
} else {
throw new TagException("Unable to find integer in string: " + str);
}
return num;
}
/**
* Remove all occurances of the given character from the string argument.
*
* @param str String to search
* @param ch character to remove
* @return new String without the given charcter
*/
static public String stripChar(String str, char ch) {
if (str != null) {
char[] buffer = new char[str.length()];
int next = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != ch) {
buffer[next++] = str.charAt(i);
}
}
return new String(buffer, 0, next);
} else {
return null;
}
}
/**
* truncate a string if it longer than the argument
*
* @param str String to truncate
* @param len maximum desired length of new string
* @return
*/
public static String truncate(String str, int len) {
if (str == null) {
return null;
}
if (len < 0) {
return null;
}
if (str.length() > len) {
return str.substring(0, len);
} else {
return str;
}
}
}