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