FileDocCategorySizeDatePackage
ID3Tags.javaAPI DocJaudiotagger 2.0.414826Wed Mar 30 16:12:06 BST 2011org.jaudiotagger.tag.id3

ID3Tags.java

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

}