Coverage Report - org.jaudiotagger.audio.asf.data.ContentDescriptor
 
Classes in this File Line Coverage Branch Coverage Complexity
ContentDescriptor
64%
77/121
46%
22/48
0
 
 1  
 /*
 2  
  * Entagged Audio Tag library
 3  
  * Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
 4  
  * 
 5  
  * This library is free software; you can redistribute it and/or
 6  
  * modify it under the terms of the GNU Lesser General Public
 7  
  * License as published by the Free Software Foundation; either
 8  
  * version 2.1 of the License, or (at your option) any later version.
 9  
  *  
 10  
  * This library is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
  * Lesser General Public License for more details.
 14  
  * 
 15  
  * You should have received a copy of the GNU Lesser General Public
 16  
  * License along with this library; if not, write to the Free Software
 17  
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 18  
  */
 19  
 package org.jaudiotagger.audio.asf.data;
 20  
 
 21  
 import org.jaudiotagger.audio.asf.util.Utils;
 22  
 
 23  
 import java.io.ByteArrayOutputStream;
 24  
 import java.io.IOException;
 25  
 import java.io.OutputStream;
 26  
 import java.util.Arrays;
 27  
 
 28  
 /**
 29  
  * This class is a wrapper for properties within a
 30  
  * {@link org.jaudiotagger.audio.asf.data.ExtendedContentDescription}.<br>
 31  
  *
 32  
  * Each descriptor consists of the folloowing:
 33  
  *
 34  
  * Descriptor Name Length  16 bits
 35  
  * Descriptor Name UTF16LE format
 36  
  * Descriptor Value Data Type 16 bits
 37  
  * Descriptor Value Length 16 bits
 38  
  * Descriptor Value varies
 39  
  *
 40  
  * @author Christian Laireiter
 41  
  */
 42  0
 public final class ContentDescriptor implements Comparable<ContentDescriptor>
 43  
 {
 44  
     /**
 45  
      * Constant for the content descriptor-type for binary data.
 46  
      */
 47  
     public final static int TYPE_BINARY = 1;
 48  
 
 49  
     /**
 50  
      * Constant for the content descriptor-type for booleans.
 51  
      */
 52  
     public final static int TYPE_BOOLEAN = 2;
 53  
 
 54  
     /**
 55  
      * Constant for the content descriptor-type for integers (32-bit). <br>
 56  
      */
 57  
     public final static int TYPE_DWORD = 3;
 58  
 
 59  
     /**
 60  
      * Constant for the content descriptor-type for integers (64-bit). <br>
 61  
      */
 62  
     public final static int TYPE_QWORD = 4;
 63  
 
 64  
     /**
 65  
      * Constant for the content descriptor-type for Strings.
 66  
      */
 67  
     public final static int TYPE_STRING = 0;
 68  
 
 69  
     /**
 70  
      * Constant for the content descriptor-type for integers (16-bit). <br>
 71  
      */
 72  
     public final static int TYPE_WORD = 5;
 73  
 
 74  
     /**
 75  
      * The name of the content descriptor.
 76  
      */
 77  
     private final String name;
 78  
 
 79  
     /**
 80  
          * This field shows the type of the content descriptor. <br>
 81  
          *
 82  
          * @see #TYPE_BINARY
 83  
          * @see #TYPE_BOOLEAN
 84  
          * @see #TYPE_DWORD
 85  
          * @see #TYPE_QWORD
 86  
          * @see #TYPE_STRING
 87  
          * @see #TYPE_WORD
 88  
          */
 89  
     private int descriptorType;
 90  
 
 91  
 
 92  
      /**
 93  
      * The binary representation of the value.
 94  
      */
 95  5696
     protected byte[] content = new byte[0];
 96  
 
 97  
     /**
 98  
      * Creates an Instance.
 99  
      *
 100  
      * @param propName Name of the ContentDescriptor.
 101  
      * @param propType Type of the content descriptor. See {@link #descriptorType}
 102  
      */
 103  
     public ContentDescriptor(String propName, int propType)
 104  5696
     {
 105  5696
         if (propName == null)
 106  
         {
 107  0
             throw new IllegalArgumentException("Arguments must not be null.");
 108  
         }
 109  5696
         Utils.checkStringLengthNullSafe(propName);
 110  5696
         this.name = propName;
 111  5696
         this.descriptorType = propType;
 112  5696
     }
 113  
 
 114  
     /**
 115  
      * (overridden)
 116  
      *
 117  
      * @see java.lang.Object#clone()
 118  
      */
 119  
     public Object clone() throws CloneNotSupportedException
 120  
     {
 121  0
         return createCopy();
 122  
     }
 123  
 
 124  
     /**
 125  
      * {@inheritDoc}
 126  
      */
 127  
     public int compareTo(ContentDescriptor o)
 128  
     {
 129  0
         int result = getName().compareTo(o.getName());
 130  0
         return result;
 131  
     }
 132  
 
 133  
     /**
 134  
      * This method creates a copy of the current object. <br>
 135  
      * All data will be copied, too. <br>
 136  
      *
 137  
      * @return A new content descriptor containing the same values as the current
 138  
      *         one.
 139  
      */
 140  
     public ContentDescriptor createCopy()
 141  
     {
 142  2620
         ContentDescriptor result = new ContentDescriptor(getName(), getType());
 143  2620
         result.content = getRawData();
 144  2620
         return result;
 145  
     }
 146  
 
 147  
     /**
 148  
      * (overridden)
 149  
      *
 150  
      * @see java.lang.Object#equals(java.lang.Object)
 151  
      */
 152  
     public boolean equals(Object obj)
 153  
     {
 154  0
         boolean result = false;
 155  0
         if (obj instanceof ContentDescriptor)
 156  
         {
 157  0
             if (obj == this)
 158  
             {
 159  0
                 result = true;
 160  
             }
 161  
             else
 162  
             {
 163  0
                 ContentDescriptor other = (ContentDescriptor) obj;
 164  0
                 result = other.getName().equals(getName()) && other.descriptorType == this.descriptorType && Arrays.equals(this.content, other.content);
 165  
             }
 166  
         }
 167  0
         return result;
 168  
     }
 169  
 
 170  
     /**
 171  
      * Returns the value of the ContentDescriptor as a Boolean. <br>
 172  
      * If no Conversion is Possible false is returned. <br>
 173  
      * <code>true</code> if first byte of {@link #content}is not zero.
 174  
      *
 175  
      * @return boolean representation of the current value.
 176  
      */
 177  
     public boolean getBoolean()
 178  
     {
 179  48
         return content.length > 0 && content[0] != 0;
 180  
     }
 181  
 
 182  
     /**
 183  
      * This method will return a byte array, which can directly be written into
 184  
      * an "Extended Content Description"-chunk. <br>
 185  
      *
 186  
      * @return byte[] with the data, that occurs in asf files.
 187  
      */
 188  
     public byte[] getBytes()
 189  
     {
 190  0
         ByteArrayOutputStream result = new ByteArrayOutputStream();
 191  
         try
 192  
         {
 193  0
             byte[] nameBytes = Utils.getBytes(getName(), AsfHeader.ASF_CHARSET);
 194  
             // Write the number of bytes the name needs. +2 because of the
 195  
             // Zero term character.
 196  0
             result.write(Utils.getBytes(nameBytes.length + 2, 2));
 197  
             // Write the name itself
 198  0
             result.write(nameBytes);
 199  
             // Write zero term character
 200  0
             result.write(Utils.getBytes(0, 2));
 201  
             // Write the type of the current descriptor
 202  0
             result.write(Utils.getBytes(getType(), 2));
 203  
             /*
 204  
              * Now the content.
 205  
              */
 206  0
             if (this.getType() == TYPE_STRING)
 207  
             {
 208  
                 // String length +2 for zero termination
 209  0
                 result.write(Utils.getBytes(content.length + 2, 2));
 210  
                 // Value
 211  0
                 result.write(content);
 212  
                 // Zero term
 213  0
                 result.write(Utils.getBytes(0, 2));
 214  
             }
 215  
             else
 216  
             {
 217  0
                 result.write(Utils.getBytes(content.length, 2));
 218  0
                 result.write(content);
 219  
             }
 220  0
         } catch (Exception e)
 221  
         {
 222  0
             e.printStackTrace();
 223  0
         }
 224  0
         return result.toByteArray();
 225  
     }
 226  
 
 227  
     /**
 228  
      * Returns the size (in bytes) this descriptor will take when written to 
 229  
      * an ASF file.<br>
 230  
      * 
 231  
      * @return size of the descriptor in an ASF file.
 232  
      */
 233  
     public int getCurrentAsfSize()
 234  
     {
 235  
         /*
 236  
          * 2 bytes name length, 2 bytes name zero term, 2 bytes type, 2 bytes
 237  
          * content length
 238  
          */
 239  730
         int result = 8;
 240  730
         result += getName().length() * 2;
 241  730
         result += this.content.length;
 242  730
         if (TYPE_STRING == this.getType())
 243  
         {
 244  720
             result += 2; // zero term of content string.
 245  
         }
 246  730
         return result;
 247  
     }
 248  
 
 249  
     /**
 250  
      * This method returns the name of the content descriptor.
 251  
      *
 252  
      * @return Name.
 253  
      */
 254  
     public String getName()
 255  
     {
 256  15656
         return this.name;
 257  
     }
 258  
 
 259  
     /**
 260  
      * This method returns the value of the content descriptor as an integer.
 261  
      * <br>
 262  
      * Converts the needed amount of byte out of {@link #content}to a number.
 263  
      * <br>
 264  
      * Only possible if {@link #getType()}equals on of the following: <br>
 265  
      * <li>
 266  
      *
 267  
      * @return integer value.
 268  
      * @see #TYPE_BOOLEAN </li>
 269  
      *      <li>
 270  
      * @see #TYPE_DWORD </li>
 271  
      *      <li>
 272  
      * @see #TYPE_QWORD </li>
 273  
      *      <li>
 274  
      * @see #TYPE_WORD </li>
 275  
      */
 276  
     public long getNumber()
 277  
     {
 278  42
         long result = 0;
 279  42
         int bytesNeeded = -1;
 280  42
         switch (getType())
 281  
         {
 282  
             case TYPE_BOOLEAN:
 283  0
                 bytesNeeded = 1;
 284  0
                 break;
 285  
             case TYPE_DWORD:
 286  38
                 bytesNeeded = 4;
 287  38
                 break;
 288  
             case TYPE_QWORD:
 289  4
                 bytesNeeded = 8;
 290  4
                 break;
 291  
             case TYPE_WORD:
 292  0
                 bytesNeeded = 2;
 293  0
                 break;
 294  
             default:
 295  0
                 throw new UnsupportedOperationException("The current type doesn't allow an interpretation as a number.");
 296  
         }
 297  42
         if (bytesNeeded > content.length)
 298  
         {
 299  0
             throw new IllegalStateException("The stored data cannot represent the type of current object.");
 300  
         }
 301  226
         for (int i = 0; i < bytesNeeded; i++)
 302  
         {
 303  184
             result |= (content[i] << (i * 8));
 304  
         }
 305  42
         return result;
 306  
     }
 307  
 
 308  
     /**
 309  
      * This method returns a copy of the content of the descriptor. <br>
 310  
      *
 311  
      * @return The content in binary representation, as it would be written to
 312  
      *         asf file. <br>
 313  
      */
 314  
     public byte[] getRawData()
 315  
     {
 316  4460
         byte[] copy = new byte[this.content.length];
 317  4460
         System.arraycopy(this.content, 0, copy, 0, this.content.length);
 318  4460
         return copy;
 319  
     }
 320  
 
 321  
     /**
 322  
      * Returns the value of the ContentDescriptor as a String. <br>
 323  
      *
 324  
      * @return String - Representation Value
 325  
      */
 326  
     public String getString()
 327  
     {
 328  3283
         String result = "";
 329  3283
         switch (getType())
 330  
         {
 331  
             case TYPE_BINARY:
 332  1
                 result = "binary data";
 333  1
                 break;
 334  
             case TYPE_BOOLEAN:
 335  48
                 result = String.valueOf(getBoolean());
 336  48
                 break;
 337  
             case TYPE_QWORD:
 338  
             case TYPE_DWORD:
 339  
             case TYPE_WORD:
 340  42
                 result = String.valueOf(getNumber());
 341  42
                 break;
 342  
             case TYPE_STRING:
 343  
                 try
 344  
                 {
 345  3192
                     result = new String(content, "UTF-16LE");
 346  0
                 } catch (Exception e)
 347  
                 {
 348  0
                     e.printStackTrace();
 349  3192
                 }
 350  0
                 break;
 351  
             default:
 352  0
                 throw new IllegalStateException("Current type is not known.");
 353  
         }
 354  3283
         return result;
 355  
     }
 356  
 
 357  
     /**
 358  
      * Returns the type of the content descriptor. <br>
 359  
      *
 360  
      * @return the value of {@link #descriptorType}
 361  
      * @see #TYPE_BINARY
 362  
      * @see #TYPE_BOOLEAN
 363  
      * @see #TYPE_DWORD
 364  
      * @see #TYPE_QWORD
 365  
      * @see #TYPE_STRING
 366  
      * @see #TYPE_WORD
 367  
      */
 368  
     public int getType()
 369  
     {
 370  8695
         return this.descriptorType;
 371  
     }
 372  
 
 373  
     /**
 374  
      * This method checks if the binary data is empty. <br>
 375  
      * Disregarding the type of the descriptor its content is stored as a byte
 376  
      * array.
 377  
      *
 378  
      * @return <code>true</code> if no value is set.
 379  
      */
 380  
     public boolean isEmpty()
 381  
     {
 382  18
         return this.content.length == 0;
 383  
     }
 384  
 
 385  
     /**
 386  
      * Sets the Value of the current content descriptor. <br>
 387  
      * Using this method will change {@link #descriptorType}to
 388  
      * {@link #TYPE_BINARY}.<br>
 389  
      *
 390  
      * @param data Value to set.
 391  
      * @throws IllegalArgumentException If the byte array is greater that 65535 bytes.
 392  
      */
 393  
     public void setBinaryValue(byte[] data) throws IllegalArgumentException
 394  
     {
 395  28
         if (data.length > 65535)
 396  
         {
 397  0
             throw new IllegalArgumentException("Too many bytes. 65535 is maximum.");
 398  
         }
 399  28
         this.content = data;
 400  28
         this.descriptorType = TYPE_BINARY;
 401  28
     }
 402  
 
 403  
     /**
 404  
      * Sets the Value of the current content descriptor. <br>
 405  
      * Using this method will change {@link #descriptorType}to
 406  
      * {@link #TYPE_BOOLEAN}.<br>
 407  
      *
 408  
      * @param value Value to set.
 409  
      */
 410  
     public void setBooleanValue(boolean value)
 411  
     {
 412  24
         this.content = new byte[]{value ? (byte) 1 : 0, 0, 0, 0};
 413  24
         this.descriptorType = TYPE_BOOLEAN;
 414  24
     }
 415  
 
 416  
     /**
 417  
      * Sets the Value of the current content descriptor. <br>
 418  
      * Using this method will change {@link #descriptorType}to
 419  
      * {@link #TYPE_DWORD}.
 420  
      *
 421  
      * @param value Value to set.
 422  
      */
 423  
     public void setDWordValue(long value)
 424  
     {
 425  27
         this.content = Utils.getBytes(value, 4);
 426  27
         this.descriptorType = TYPE_DWORD;
 427  27
     }
 428  
 
 429  
     /**
 430  
      * Sets the Value of the current content descriptor. <br>
 431  
      * Using this method will change {@link #descriptorType}to
 432  
      * {@link #TYPE_QWORD}
 433  
      *
 434  
      * @param value Value to set.
 435  
      */
 436  
     public void setQWordValue(long value)
 437  
     {
 438  2
         this.content = Utils.getBytes(value, 8);
 439  2
         this.descriptorType = TYPE_QWORD;
 440  2
     }
 441  
 
 442  
     /**
 443  
      * Sets the Value of the current content descriptor. <br>
 444  
      * Using this method will change {@link #descriptorType}to
 445  
      * {@link #TYPE_STRING}.
 446  
      *
 447  
      * @param value Value to set.
 448  
      * @throws IllegalArgumentException If byte representation would take more than 65535 Bytes.
 449  
      */
 450  
     public void setStringValue(String value) throws IllegalArgumentException
 451  
     {
 452  2995
         Utils.checkStringLengthNullSafe(value);
 453  2995
         if (value != null)
 454  
         {
 455  2995
             this.content = Utils.getBytes(value, AsfHeader.ASF_CHARSET);
 456  
         }
 457  
         else
 458  
         {
 459  0
             this.content = new byte[0];
 460  
         }
 461  2995
         this.descriptorType = TYPE_STRING;
 462  2995
     }
 463  
 
 464  
     /**
 465  
      * Sets the Value of the current content descriptor. <br>
 466  
      * Using this method will change {@link #descriptorType}to
 467  
      * {@link #TYPE_WORD}
 468  
      *
 469  
      * @param value Value to set.
 470  
      */
 471  
     public void setWordValue(int value)
 472  
     {
 473  0
         this.content = Utils.getBytes(value, 2);
 474  0
         this.descriptorType = TYPE_WORD;
 475  0
     }
 476  
 
 477  
     /**
 478  
      * (overridden)
 479  
      *
 480  
      * @see java.lang.Object#toString()
 481  
      */
 482  
     public String toString()
 483  
     {
 484  0
         return getName() + " : " + new String[]{"String: ", "Binary: ", "Boolean: ", "DWORD: ", "QWORD:", "WORD:"}[this.descriptorType] + getString();
 485  
     }
 486  
 
 487  
     /**
 488  
      * Writes this descriptor into the specified output stream.<br>
 489  
      * 
 490  
      * @param out stream to write into.
 491  
      * @return amount of bytes written.
 492  
      * @throws IOException on I/O Errors
 493  
      */
 494  
     public int writeInto(OutputStream out) throws IOException
 495  
     {
 496  365
         final int size = getCurrentAsfSize();
 497  365
         Utils.writeUINT16(getName().length() * 2 + 2, out);
 498  365
         out.write(Utils.getBytes(getName(), AsfHeader.ASF_CHARSET));
 499  365
         out.write(AsfHeader.ZERO_TERM);
 500  365
         final int type = getType();
 501  365
         Utils.writeUINT16(type, out);
 502  365
         int contentLen = this.content.length;
 503  365
         if (TYPE_STRING == type)
 504  
         {
 505  360
             contentLen += 2; // Zero Term
 506  
         }
 507  365
         Utils.writeUINT16(contentLen, out);
 508  365
         out.write(this.content);
 509  365
         if (TYPE_STRING == type)
 510  
         {
 511  360
             out.write(AsfHeader.ZERO_TERM);
 512  
         }
 513  365
         return size;
 514  
     }
 515  
 }