1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
88
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
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
148
149
150 int[] stringSizes = getStringSizes(raf);
151
152
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 }