Coverage Report - org.jaudiotagger.tag.id3.ID3Tags
 
Classes in this File Line Coverage Branch Coverage Complexity
ID3Tags
51%
60/117
41%
35/86
0
 
 1  
 /*
 2  
  *  MusicTag Copyright (C)2003,2004
 3  
  *
 4  
  *  This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
 5  
  *  General Public  License as published by the Free Software Foundation; either version 2.1 of the License,
 6  
  *  or (at your option) any later version.
 7  
  *
 8  
  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 9  
  *  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 10  
  *  See the GNU Lesser General Public License for more details.
 11  
  *
 12  
  *  You should have received a copy of the GNU Lesser General Public License along with this library; if not,
 13  
  *  you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
 14  
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 15  
  */
 16  
 package org.jaudiotagger.tag.id3;
 17  
 
 18  
 import org.jaudiotagger.tag.TagException;
 19  
 
 20  
 import java.lang.reflect.Constructor;
 21  
 import java.util.logging.Logger;
 22  
 
 23  
 /**
 24  
  * This contains static methods that can be performed on tags
 25  
  * and to convert between tags.
 26  
  *
 27  
  * @author : Paul Taylor
 28  
  * @author : Eric Farng
 29  
  * @version $Id: ID3Tags.java,v 1.20 2008/11/12 16:41:39 paultaylor Exp $
 30  
  */
 31  
 public class ID3Tags
 32  
 {
 33  
     //Logger
 34  45
     public static Logger logger = Logger.getLogger("org.jaudiotagger.tag.id3");
 35  
 
 36  
 
 37  
     private ID3Tags()
 38  0
     {
 39  0
     }
 40  
 
 41  
     /**
 42  
      * Returns true if the identifier is a valid ID3v2.2 frame identifier
 43  
      *
 44  
      * @param identifier string to test
 45  
      * @return true if the identifier is a valid ID3v2.2 frame identifier
 46  
      */
 47  
     public static boolean isID3v22FrameIdentifier(String identifier)
 48  
     {
 49  
         //If less than 3 cant be an identifier
 50  352
         if (identifier.length() < 3)
 51  
         {
 52  0
             return false;
 53  
         }
 54  
         //If 3 is it a known identifier
 55  352
         else if (identifier.length() == 3)
 56  
         {
 57  351
             return ID3v22Frames.getInstanceOf().getIdToValueMap().containsKey(identifier);
 58  
         }
 59  
         else
 60  
         {
 61  1
             return false;
 62  
         }
 63  
     }
 64  
 
 65  
     /**
 66  
      * Returns true if the identifier is a valid ID3v2.3 frame identifier
 67  
      *
 68  
      * @param identifier string to test
 69  
      * @return true if the identifier is a valid ID3v2.3 frame identifier
 70  
      */
 71  
     public static boolean isID3v23FrameIdentifier(String identifier)
 72  
     {
 73  149
         if (identifier.length() < 4)
 74  
         {
 75  2
             return false;
 76  
         }
 77  147
         return ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier.substring(0, 4));
 78  
     }
 79  
 
 80  
     /**
 81  
      * Returns true if the identifier is a valid ID3v2.4 frame identifier
 82  
      *
 83  
      * @param identifier string to test
 84  
      * @return true if the identifier is a valid ID3v2.4 frame identifier
 85  
      */
 86  
     public static boolean isID3v24FrameIdentifier(String identifier)
 87  
     {
 88  138
         if (identifier.length() < 4)
 89  
         {
 90  0
             return false;
 91  
         }
 92  138
         return ID3v24Frames.getInstanceOf().getIdToValueMap().containsKey(identifier.substring(0, 4));
 93  
     }
 94  
 
 95  
     /**
 96  
      * Given an datatype, try to return it as a <code>long</code>. This tries to
 97  
      * parse a string, and takes <code>Long, Short, Byte, Integer</code>
 98  
      * objects and gets their value. An exception is not explicitly thrown
 99  
      * here because it would causes too many other methods to also throw it.
 100  
      *
 101  
      * @param value datatype to find long from.
 102  
      * @return <code>long</code> value
 103  
      * @throws IllegalArgumentException
 104  
      */
 105  
     static public long getWholeNumber(Object value)
 106  
     {
 107  
         long number;
 108  973
         if (value instanceof String)
 109  
         {
 110  0
             number = Long.parseLong((String) value);
 111  
         }
 112  973
         else if (value instanceof Byte)
 113  
         {
 114  0
             number = ((Byte) value).byteValue();
 115  
         }
 116  973
         else if (value instanceof Short)
 117  
         {
 118  0
             number = ((Short) value).shortValue();
 119  
         }
 120  973
         else if (value instanceof Integer)
 121  
         {
 122  0
             number = ((Integer) value).intValue();
 123  
         }
 124  973
         else if (value instanceof Long)
 125  
         {
 126  973
             number = (Long) value;
 127  
         }
 128  
         else
 129  
         {
 130  0
             throw new IllegalArgumentException("Unsupported value class: " + value.getClass().getName());
 131  
         }
 132  973
         return number;
 133  
     }
 134  
 
 135  
     /**
 136  
      * Convert from ID3v22 FrameIdentifier to ID3v23
 137  
      */
 138  
     public static String convertFrameID22To23(String identifier)
 139  
     {
 140  306
         if (identifier.length() < 3)
 141  
         {
 142  0
             return null;
 143  
         }
 144  306
         return ID3Frames.convertv22Tov23.get(identifier.subSequence(0, 3));
 145  
     }
 146  
 
 147  
     /**
 148  
      * Convert from ID3v22 FrameIdentifier to ID3v24
 149  
      */
 150  
     public static String convertFrameID22To24(String identifier)
 151  
     {
 152  
         //Idv22 identifiers are only of length 3 times
 153  195
         if (identifier.length() < 3)
 154  
         {
 155  0
             return null;
 156  
         }
 157  
         //Has idv22 been mapped to v23
 158  195
         String id = ID3Frames.convertv22Tov23.get(identifier.substring(0, 3));
 159  195
         if (id != null)
 160  
         {
 161  
             //has v2.3 been mapped to v2.4
 162  183
             String v23id = ID3Frames.convertv23Tov24.get(id);
 163  183
             if (v23id == null)
 164  
             {
 165  
                 //if not it may be because v2.3 and and v2.4 are same so wont be
 166  
                 //in mapping
 167  183
                 if (ID3v24Frames.getInstanceOf().getIdToValueMap().get(id) != null)
 168  
                 {
 169  164
                     return id;
 170  
                 }
 171  
                 else
 172  
                 {
 173  19
                     return null;
 174  
                 }
 175  
             }
 176  
             else
 177  
             {
 178  0
                 return v23id;
 179  
             }
 180  
         }
 181  
         else
 182  
         {
 183  12
             return null;
 184  
         }
 185  
     }
 186  
 
 187  
     /**
 188  
      * Convert from ID3v23 FrameIdentifier to ID3v22
 189  
      */
 190  
     public static String convertFrameID23To22(String identifier)
 191  
     {
 192  52
         if (identifier.length() < 4)
 193  
         {
 194  0
             return null;
 195  
         }
 196  
 
 197  
         //If it is a v23 identifier
 198  52
         if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier))
 199  
         {
 200  
             //If only name has changed  v22 and modified in v23 return result of.
 201  51
             return ID3Frames.convertv23Tov22.get(identifier.substring(0, 4));
 202  
         }
 203  1
         return null;
 204  
     }
 205  
 
 206  
     /**
 207  
      * Convert from ID3v23 FrameIdentifier to ID3v24
 208  
      */
 209  
     public static String convertFrameID23To24(String identifier)
 210  
     {
 211  1355
         if (identifier.length() < 4)
 212  
         {
 213  2
             return null;
 214  
         }
 215  
 
 216  
         //If it is a ID3v23 identifier
 217  1353
         if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier))
 218  
         {
 219  
             //If no change between ID3v23 and ID3v24 should be in ID3v24 list.
 220  1336
             if (ID3v24Frames.getInstanceOf().getIdToValueMap().containsKey(identifier) == true)
 221  
             {
 222  1206
                 return identifier;
 223  
             }
 224  
             //If only name has changed  ID3v23 and modified in ID3v24 return result of.
 225  
             else
 226  
             {
 227  130
                 return ID3Frames.convertv23Tov24.get(identifier.substring(0, 4));
 228  
             }
 229  
         }
 230  17
         return null;
 231  
     }
 232  
 
 233  
     /**
 234  
      * Force from ID3v22 FrameIdentifier to ID3v23, this is where the frame and structure
 235  
      * has changed from v2 to v3 but we can still do some kind of conversion.
 236  
      */
 237  
     public static String forceFrameID22To23(String identifier)
 238  
     {
 239  62
         return ID3Frames.forcev22Tov23.get(identifier);
 240  
     }
 241  
 
 242  
     /**
 243  
      * Force from ID3v22 FrameIdentifier to ID3v23, this is where the frame and structure
 244  
      * has changed from v2 to v3 but we can still do some kind of conversion.
 245  
      */
 246  
     public static String forceFrameID23To22(String identifier)
 247  
     {
 248  2
         return ID3Frames.forcev23Tov22.get(identifier);
 249  
     }
 250  
 
 251  
     /**
 252  
      * Force from ID3v2.30 FrameIdentifier to ID3v2.40, this is where the frame and structure
 253  
      * has changed from v3 to v4 but we can still do some kind of conversion.
 254  
      */
 255  
     public static String forceFrameID23To24(String identifier)
 256  
     {
 257  76
         return ID3Frames.forcev23Tov24.get(identifier);
 258  
     }
 259  
 
 260  
     /**
 261  
      * Force from ID3v2.40 FrameIdentifier to ID3v2.30, this is where the frame and structure
 262  
      * has changed between v4 to v3 but we can still do some kind of conversion.
 263  
      */
 264  
     public static String forceFrameID24To23(String identifier)
 265  
     {
 266  6
         return ID3Frames.forcev24Tov23.get(identifier);
 267  
     }
 268  
 
 269  
     /**
 270  
      * Convert from ID3v24 FrameIdentifier to ID3v23
 271  
      */
 272  
     public static String convertFrameID24To23(String identifier)
 273  
     {
 274  
         String id;
 275  132
         if (identifier.length() < 4)
 276  
         {
 277  0
             return null;
 278  
         }
 279  132
         id = ID3Frames.convertv24Tov23.get(identifier);
 280  132
         if (id == null)
 281  
         {
 282  132
             if (ID3v23Frames.getInstanceOf().getIdToValueMap().containsKey(identifier) == true)
 283  
             {
 284  126
                 id = identifier;
 285  
             }
 286  
         }
 287  132
         return id;
 288  
     }
 289  
 
 290  
     /**
 291  
      * Unable to instantiate abstract classes, so can't call the copy
 292  
      * constructor. So find out the instantiated class name and call the copy
 293  
      * constructor through reflection (e.g for a a FrameBody would have to have a constructor
 294  
      * that takes another framebody as the same type as a parameter)
 295  
      *
 296  
      * @param copyObject
 297  
      * @return
 298  
      * @throws IllegalArgumentException if no suitable constructor exists
 299  
      */
 300  
     public static Object copyObject(Object copyObject)
 301  
     {
 302  
         Constructor<?> constructor;
 303  
         Class<?>[] constructorParameterArray;
 304  
         Object[] parameterArray;
 305  4056
         if (copyObject == null)
 306  
         {
 307  0
             return null;
 308  
         }
 309  
         try
 310  
         {
 311  4056
             constructorParameterArray = new Class[1];
 312  4056
             constructorParameterArray[0] = copyObject.getClass();
 313  4056
             constructor = copyObject.getClass().getConstructor(constructorParameterArray);
 314  4056
             parameterArray = new Object[1];
 315  4056
             parameterArray[0] = copyObject;
 316  4056
             return constructor.newInstance(parameterArray);
 317  
         }
 318  0
         catch (NoSuchMethodException ex)
 319  
         {
 320  0
             throw new IllegalArgumentException("NoSuchMethodException: Error finding constructor to create copy:"+copyObject.getClass().getName());
 321  
         }
 322  0
         catch (IllegalAccessException ex)
 323  
         {
 324  0
             throw new IllegalArgumentException("IllegalAccessException: No access to run constructor to create copy"+copyObject.getClass().getName());
 325  
         }
 326  0
         catch (InstantiationException ex)
 327  
         {
 328  0
             throw new IllegalArgumentException("InstantiationException: Unable to instantiate constructor to copy"+copyObject.getClass().getName());
 329  
         }
 330  0
         catch (java.lang.reflect.InvocationTargetException ex)
 331  
         {
 332  0
             if (ex.getCause() instanceof Error)
 333  
             {
 334  0
                 throw (Error) ex.getCause();
 335  
             }
 336  0
             else if (ex.getCause() instanceof RuntimeException)
 337  
             {
 338  0
                 throw (RuntimeException) ex.getCause();
 339  
             }
 340  
             else
 341  
             {
 342  0
                 throw new IllegalArgumentException("InvocationTargetException: Unable to invoke constructor to create copy");
 343  
             }
 344  
         }
 345  
     }
 346  
 
 347  
     /**
 348  
      * Find the first whole number that can be parsed from the string
 349  
      *
 350  
      * @param str string to search
 351  
      * @return first whole number that can be parsed from the string
 352  
      * @throws TagException
 353  
      */
 354  
     public static long findNumber(String str) throws TagException
 355  
     {
 356  0
         return findNumber(str, 0);
 357  
     }
 358  
 
 359  
     /**
 360  
      * Find the first whole number that can be parsed from the string
 361  
      *
 362  
      * @param str    string to search
 363  
      * @param offset start seaching from this index
 364  
      * @return first whole number that can be parsed from the string
 365  
      * @throws TagException
 366  
      * @throws NullPointerException
 367  
      * @throws IndexOutOfBoundsException
 368  
      */
 369  
     public static long findNumber(String str, int offset) throws TagException
 370  
     {
 371  0
         if (str == null)
 372  
         {
 373  0
             throw new NullPointerException("String is null");
 374  
         }
 375  0
         if ((offset < 0) || (offset >= str.length()))
 376  
         {
 377  0
             throw new IndexOutOfBoundsException("Offset to image string is out of bounds: offset = " + offset + ", string.length()" + str.length());
 378  
         }
 379  
         int i;
 380  
         int j;
 381  
         long num;
 382  0
         i = offset;
 383  0
         while (i < str.length())
 384  
         {
 385  0
             if (((str.charAt(i) >= '0') && (str.charAt(i) <= '9')) || (str.charAt(i) == '-'))
 386  
             {
 387  0
                 break;
 388  
             }
 389  0
             i++;
 390  
         }
 391  0
         j = i + 1;
 392  0
         while (j < str.length())
 393  
         {
 394  0
             if (((str.charAt(j) < '0') || (str.charAt(j) > '9')))
 395  
             {
 396  0
                 break;
 397  
             }
 398  0
             j++;
 399  
         }
 400  0
         if ((j <= str.length()) && (j > i))
 401  
         {
 402  0
             num = Long.parseLong(str.substring(i, j));
 403  
         }
 404  
         else
 405  
         {
 406  0
             throw new TagException("Unable to find integer in string: " + str);
 407  
         }
 408  0
         return num;
 409  
     }
 410  
 
 411  
     /**
 412  
      * Remove all occurances of the given character from the string argument.
 413  
      *
 414  
      * @param str String to search
 415  
      * @param ch  character to remove
 416  
      * @return new String without the given charcter
 417  
      */
 418  
     static public String stripChar(String str, char ch)
 419  
     {
 420  0
         if (str != null)
 421  
         {
 422  0
             char[] buffer = new char[str.length()];
 423  0
             int next = 0;
 424  0
             for (int i = 0; i < str.length(); i++)
 425  
             {
 426  0
                 if (str.charAt(i) != ch)
 427  
                 {
 428  0
                     buffer[next++] = str.charAt(i);
 429  
                 }
 430  
             }
 431  0
             return new String(buffer, 0, next);
 432  
         }
 433  
         else
 434  
         {
 435  0
             return null;
 436  
         }
 437  
     }
 438  
 
 439  
     /**
 440  
      * truncate a string if it longer than the argument
 441  
      *
 442  
      * @param str String to truncate
 443  
      * @param len maximum desired length of new string
 444  
      * @return
 445  
      */
 446  
     public static String truncate(String str, int len)
 447  
     {
 448  394
         if (str == null)
 449  
         {
 450  0
             return null;
 451  
         }
 452  394
         if (len < 0)
 453  
         {
 454  0
             return null;
 455  
         }
 456  394
         if (str.length() > len)
 457  
         {
 458  0
             return str.substring(0, len);
 459  
         }
 460  
         else
 461  
         {
 462  394
             return str;
 463  
         }
 464  
     }
 465  
 
 466  
 }