View Javadoc

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 entagged.audioformats.asf.io;
20  
21  import java.io.IOException;
22  import java.io.RandomAccessFile;
23  import java.math.BigInteger;
24  
25  import entagged.audioformats.asf.data.ContentDescription;
26  import entagged.audioformats.asf.data.Chunk;
27  import entagged.audioformats.asf.data.GUID;
28  import entagged.audioformats.asf.util.Utils;
29  
30  /***
31   * Reads and interprets the data of a asf chunk containing title, author... <br>
32   * 
33   * @see entagged.audioformats.asf.data.ContentDescription
34   * 
35   * @author Christian Laireiter
36   */
37  public class ContentDescriptionReader {
38  
39      /***
40       * Creates and fills a
41       * {@link entagged.audioformats.asf.data.ContentDescription}from given
42       * file. <br>
43       * 
44       * @param raf
45       *                  Input
46       * @param candidate
47       *                  Chunk which possibly is a file header.
48       * @return FileHeader if filepointer of <code>raf</code> is at valid
49       *              fileheader.
50       * @throws IOException
51       *                   Read errors.
52       */
53      public static ContentDescription read(RandomAccessFile raf, Chunk candidate)
54              throws IOException {
55          if (raf == null || candidate == null) {
56              throw new IllegalArgumentException("Arguments must not be null.");
57          }
58          if (GUID.GUID_CONTENTDESCRIPTION.equals(candidate.getGuid())) {
59              raf.seek(candidate.getPosition());
60              return new ContentDescriptionReader().parseData(raf);
61          }
62          return null;
63      }
64  
65      /***
66       * This method reads a UTF-16 encoded String. <br>
67       * For the use this method the number of bytes used by current string must
68       * be known. <br>
69       * The ASF spec recommends that those strings end with a terminating zero.
70       * However it also says that it is not always the case.
71       * 
72       * @param raf
73       *                  Input source
74       * @param strLen
75       *                  Number of bytes the String may take.
76       * @return read String.
77       * @throws IOException
78       *                   read errors.
79       */
80      public static String readFixedSizeUTF16Str(RandomAccessFile raf, int strLen)
81              throws IOException {
82          byte[] strBytes = new byte[strLen];
83          int read = raf.read(strBytes);
84          if (read == strBytes.length) {
85              if (strBytes.length >= 2) {
86                  /*
87                   * Zero termination is recommended but optional.
88                   * So check and if, remove.
89                   */
90                  if (strBytes[strBytes.length-1] == 0 && strBytes[strBytes.length-2] == 0) {
91                      byte[] copy = new byte[strBytes.length-2];
92                      System.arraycopy(strBytes, 0, copy, 0, strBytes.length-2);
93                      strBytes = copy;
94                  }
95              }
96              return new String(strBytes, "UTF-16LE");
97          }
98          throw new IllegalStateException(
99                  "Couldn't read the necessary amount of bytes.");
100     }
101 
102     /***
103      * Should not be used for now.
104      *  
105      */
106     protected ContentDescriptionReader() {
107         // NOTHING toDo
108     }
109 
110     /***
111      * Directly behind the GUID and chunkSize of the current chunck comes 5
112      * sizes (16-bit) of string lengths. <br>
113      * 
114      * @param raf
115      *                  input source
116      * @return Number and length of Strings, which are directly behind
117      *              filepointer if method exits.
118      * @throws IOException
119      *                   read errors.
120      */
121     private int[] getStringSizes(RandomAccessFile raf) throws IOException {
122         int[] result = new int[5];
123         for (int i = 0; i < result.length; i++) {
124             result[i] = Utils.readUINT16(raf);
125         }
126         return result;
127     }
128 
129     /***
130      * Does the job of {@link #read(RandomAccessFile, Chunk)}
131      * 
132      * @param raf
133      *                  input source
134      * @return Contentdescription
135      * @throws IOException
136      *                   read errors.
137      */
138     private ContentDescription parseData(RandomAccessFile raf)
139             throws IOException {
140         ContentDescription result = null;
141         long chunkStart = raf.getFilePointer();
142         GUID guid = Utils.readGUID(raf);
143         if (GUID.GUID_CONTENTDESCRIPTION.equals(guid)) {
144             BigInteger chunkLen = Utils.readBig64(raf);
145             result = new ContentDescription(chunkStart, chunkLen);
146             /*
147              * Now comes 16-Bit values representing the length of the Strings
148              * which follows.
149              */
150             int[] stringSizes = getStringSizes(raf);
151             /*
152              * Now we know the String length of each occuring String.
153              */
154             String[] strings = new String[stringSizes.length];
155             for (int i = 0; i < strings.length; i++) {
156                 if (stringSizes[i] > 0)
157                     strings[i] = readFixedSizeUTF16Str(raf, stringSizes[i]);
158             }
159             if (stringSizes[0] > 0)
160                 result.setTitle(strings[0]);
161             if (stringSizes[1] > 0)
162                 result.setAuthor(strings[1]);
163             if (stringSizes[2] > 0)
164                 result.setCopyRight(strings[2]);
165             if (stringSizes[3] > 0)
166                 result.setComment(strings[3]);
167             if (stringSizes[4] > 0)
168                 result.setRating(strings[4]);
169         }
170         return result;
171     }
172 }