1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package entagged.audioformats.asf.util;
20
21 import java.io.IOException;
22 import java.io.RandomAccessFile;
23 import java.io.UnsupportedEncodingException;
24 import java.math.BigInteger;
25 import java.util.Calendar;
26 import java.util.GregorianCalendar;
27
28 import entagged.audioformats.asf.data.GUID;
29
30 /***
31 * Some static Methods which are used in several Classes. <br>
32 *
33 * @author Christian Laireiter
34 */
35 public class Utils {
36
37 /***
38 * Stores the default line seperator of the current underlying system.
39 */
40 public final static String LINE_SEPARATOR = System
41 .getProperty("line.separator");
42
43 /***
44 * Reads chars out of <code>raf</code> until <code>chars</code> is
45 * filled.
46 *
47 * @param chars
48 * to be filled
49 * @param raf
50 * to be read
51 * @throws IOException
52 * read error, or file at end before <code>chars</code> is
53 * filled.
54 */
55 public static void fillChars(char[] chars, RandomAccessFile raf)
56 throws IOException {
57 if (chars == null) {
58 throw new IllegalArgumentException("Argument must not be null.");
59 }
60 for (int i = 0; i < chars.length; i++) {
61 chars[i] = raf.readChar();
62 }
63 }
64
65 /***
66 * This method will create a byte[] at the size of <code>byteCount</code>
67 * and insert the bytes of <code>value</code> (starting from lowset byte)
68 * into it. <br>
69 * You can easily create a Word (16-bit), DWORD (32-bit), QWORD (64 bit) out
70 * of the value, ignoring the original type of value, since java
71 * automatically performs transformations. <br>
72 * <b>Warning: </b> This method works with unsigned numbers only.
73 *
74 * @param value
75 * The value to be written into the result.
76 * @param byteCount
77 * The number of bytes the array has got.
78 * @return A byte[] with the size of <code>byteCount</code> containing the
79 * lower byte values of <code>value</code>.
80 */
81 public static byte[] getBytes(long value, int byteCount) {
82 byte[] result = new byte[byteCount];
83 for (int i = 0; i < result.length; i++) {
84 result[i] = (byte) (value & 0xFF);
85 value >>>= 8;
86 }
87 return result;
88 }
89
90 /***
91 * Since date values in asf files are given in 100 ns steps since first
92 * january of 1601 a little conversion must be done. <br>
93 * This method converts a date given in described manner to a calendar.
94 *
95 * @param fileTime
96 * Time in 100ns since 1 jan 1601
97 * @return Calendar holding the date representation.
98 */
99 public static GregorianCalendar getDateOf(BigInteger fileTime) {
100 GregorianCalendar result = new GregorianCalendar(1601, 0, 1);
101
102
103 fileTime = fileTime.divide(new BigInteger("10000"));
104 BigInteger maxInt = new BigInteger(String.valueOf(Integer.MAX_VALUE));
105 while (fileTime.compareTo(maxInt) > 0) {
106 result.add(Calendar.MILLISECOND, Integer.MAX_VALUE);
107 fileTime = fileTime.subtract(maxInt);
108 }
109 result.add(Calendar.MILLISECOND, fileTime.intValue());
110 return result;
111 }
112
113 /***
114 * This method reads one byte from <code>raf</code> and creates an
115 * unsigned value of it. <br>
116 *
117 * @param raf
118 * The file to read from.
119 * @return next 7 bits as number.
120 * @throws IOException
121 * read errors.
122 */
123 public static int read7Bit(RandomAccessFile raf) throws IOException {
124 int result = raf.read();
125 return result & 127;
126 }
127
128 /***
129 * This method reads 8 bytes, interprets them as an unsigned number and
130 * creates a {@link BigInteger}
131 *
132 * @param raf
133 * Input source
134 * @return 8 bytes unsigned number
135 * @throws IOException
136 * read errors.
137 */
138 public static BigInteger readBig64(RandomAccessFile raf) throws IOException {
139 byte[] bytes = new byte[8];
140 byte[] oa = new byte[8];
141 raf.readFully(bytes);
142 for (int i = 0; i < bytes.length; i++) {
143 oa[7 - i] = bytes[i];
144 }
145 BigInteger result = new BigInteger(oa);
146 return result;
147 }
148
149 /***
150 * This method reads a UTF-16 String, which legth is given on the number of
151 * characters it consits of. <br>
152 * The filepointer of <code>raf</code> must be at the number of
153 * characters. This number contains the terminating zero character (UINT16).
154 *
155 * @param raf
156 * Input source
157 * @return String
158 * @throws IOException
159 * read errors
160 */
161 public static String readCharacterSizedString(RandomAccessFile raf)
162 throws IOException {
163 StringBuffer result = new StringBuffer();
164 int strLen = readUINT16(raf);
165 int character = raf.read();
166 character |= raf.read() << 8;
167 do {
168 if (character != 0) {
169 result.append((char) character);
170 character = raf.read();
171 character |= raf.read() << 8;
172 }
173 } while (character != 0 || (result.length() + 1) > strLen);
174 if (strLen != (result.length() + 1)) {
175 throw new IllegalStateException(
176 "Invalid Data for current interpretation");
177 }
178 return result.toString();
179 }
180
181 /***
182 * This Method reads a GUID (which is a 16 byte long sequence) from the
183 * given <code>raf</code> and creates a wrapper. <br>
184 * <b>Warning </b>: <br>
185 * There is no way of telling if a byte sequence is a guid or not. The next
186 * 16 bytes will be interpreted as a guid, whether it is or not.
187 *
188 * @param raf
189 * Input source.
190 * @return A class wrapping the guid.
191 * @throws IOException
192 * happens when the file ends before guid could be extracted.
193 */
194 public static GUID readGUID(RandomAccessFile raf) throws IOException {
195 if (raf == null) {
196 throw new IllegalArgumentException("Argument must not be null");
197 }
198 int[] binaryGuid = new int[GUID.GUID_LENGTH];
199 for (int i = 0; i < binaryGuid.length; i++) {
200 binaryGuid[i] = raf.read();
201 }
202 return new GUID(binaryGuid);
203 }
204
205 /***
206 * @see #readUINT64(RandomAccessFile)
207 * @param raf
208 * @return number
209 * @throws IOException
210 */
211 public static int readUINT16(RandomAccessFile raf) throws IOException {
212 int result = raf.read();
213 result |= raf.read() << 8;
214 return result;
215 }
216
217 /***
218 * @see #readUINT64(RandomAccessFile)
219 * @param raf
220 * @return number
221 * @throws IOException
222 */
223 public static long readUINT32(RandomAccessFile raf) throws IOException {
224 long result = 0;
225 for (int i = 0; i <= 24; i += 8)
226 result |= raf.read() << i;
227 return result;
228 }
229
230 /***
231 * Reads long as little endian.
232 *
233 * @param raf
234 * Data source
235 * @return long value
236 * @throws IOException
237 * read error, or eof is reached before long is completed
238 */
239 public static long readUINT64(RandomAccessFile raf) throws IOException {
240 long result = 0;
241 for (int i = 0; i <= 56; i += 8)
242 result |= raf.read() << i;
243 return result;
244 }
245
246 /***
247 * This method reads a UTF-16 encoded String, beginning with a 16-bit value
248 * representing the number of bytes needed. The String is terminated with as
249 * 16-bit ZERO. <br>
250 *
251 * @param raf
252 * Input source
253 * @return read String.
254 * @throws IOException
255 * read errors.
256 */
257 public static String readUTF16LEStr(RandomAccessFile raf)
258 throws IOException {
259 int strLen = readUINT16(raf);
260 byte[] buf = new byte[strLen];
261 int read = raf.read(buf);
262 if (read == buf.length) {
263
264
265
266 if (buf.length >= 2) {
267 if (buf[buf.length - 1] == 0 && buf[buf.length - 2] == 0) {
268 byte[] copy = new byte[buf.length - 2];
269 System.arraycopy(buf, 0, copy, 0, buf.length - 2);
270 buf = copy;
271 }
272 }
273 return new String(buf, "UTF-16LE");
274 }
275 throw new IllegalStateException(
276 "Invalid Data for current interpretation");
277 }
278
279 /***
280 * This method converts the given string into a byte[] in UTF-16LE encoding
281 * and checks whether the length doesn't exceed 65535 bytes. <br>
282 *
283 * @param value
284 * The string to check.
285 * @throws IllegalArgumentException
286 * If byte representation takes more than 65535 bytes.
287 */
288 public static void checkStringLengthNullSafe(String value)
289 throws IllegalArgumentException {
290 if (value != null) {
291 try {
292 byte[] tmp = value.getBytes("UTF-16LE");
293 if (tmp.length > 65533) {
294 throw new IllegalArgumentException(
295 "\"UTF-16LE\" representation exceeds 65535 bytes."
296 + " (Including zero term character)");
297 }
298 } catch (UnsupportedEncodingException e) {
299 e.printStackTrace();
300 }
301 }
302 }
303 }