001: /*
002: * @(#)BinaryPListParser.java 1.0 2005-11-06
003: *
004: * Copyright (c) 2005 Werner Randelshofer
005: * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
006: * All rights reserved.
007: *
008: * This software is the confidential and proprietary information of
009: * Werner Randelshofer. ("Confidential Information"). You shall not
010: * disclose such Confidential Information and shall use it only in
011: * accordance with the terms of the license agreement you entered into
012: * with Werner Randelshofer.
013: */
014:
015: package com.jidesoft.plaf.aqua;
016:
017: import com.jidesoft.utils.Base64;
018: import com.jidesoft.utils.SecurityUtils;
019:
020: import java.io.*;
021: import java.text.DateFormat;
022: import java.text.SimpleDateFormat;
023: import java.util.ArrayList;
024: import java.util.Date;
025: import java.util.HashMap;
026:
027: /**
028: * Reads a binary PList file and returns it as a NanoXML XMLElement.
029: * <p/>
030: * The NanoXML XMLElement returned by this reader is equivalent to the
031: * XMLElement returned, if a PList file in XML format is parsed with
032: * NanoXML.
033: * <p/>
034: * Description about property list taken from <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/index.html#//apple_ref/doc/uid/10000048i">
035: * Apple's online documentation</a>:
036: * <p/>
037: * "A property list is a data representation used by Mac OS X Cocoa and Core
038: * Foundation as a convenient way to store, organize, and access standard object
039: * types. Frequently called a Òplist,Ó a property list is an object of one of
040: * several certain Cocoa or Core Foundation types, including arrays,
041: * dictionaries, strings, binary data, numbers, dates, and Boolean values. If
042: * the object is a container (an array or dictionary), all objects contained
043: * within it must also be supported property list objects. (Arrays and
044: * dictionaries can contain objects not supported by the architecture, but are
045: * then not property lists, and cannot be saved and restored with the various
046: * property list methods.)"
047: * <p/>
048: * XXX - This implementation can not read date values. Date values will always
049: * have the current date.
050: *
051: * @author Werner Randelshofer
052: * @version 0.1 June 18, 2005 Created.
053: * @see XMLElement
054: */
055: class BinaryPListParser {
056: /* Description of the binary plist format derived from
057: * http://cvs.opendarwin.org/cgi-bin/cvsweb.cgi/~checkout~/src/CoreFoundation/Parsing.subproj/CFBinaryPList.c?rev=1.1.1.3&content-type=text/plain
058: *
059: * EBNF description of the file format:
060: * <pre>
061: * bplist ::= header objectTable offsetTable trailer
062: *
063: * header ::= magicNumber fileFormatVersion
064: * magicNumber ::= "bplist"
065: * fileFormatVersion ::= "00"
066: *
067: * objectTable ::= { null | bool | fill | number | date | data |
068: * string | uid | array | dict }
069: *
070: * null ::= 0b0000 0b0000
071: *
072: * bool ::= false | true
073: * false ::= 0b0000 0b1000
074: * true ::= 0b0000 0b1001
075: *
076: * fill ::= 0b0000 0b1111 // fill byte
077: *
078: * number ::= int | real
079: * int ::= 0b0001 0bnnnn byte*(2^nnnn) // 2^nnnn big-endian bytes
080: * real ::= 0b0010 0bnnnn byte*(2^nnnn) // 2^nnnn big-endian bytes
081: *
082: * date ::= 0b0011 0b0011 byte*8 // 8 byte float big-endian bytes
083: *
084: * data ::= 0b0100 0bnnnn [int] byte* // nnnn is number of bytes
085: * // unless 0b1111 then a int
086: * // variable-sized object follows
087: * // to indicate the number of bytes
088: *
089: * string ::= asciiString | unicodeString
090: * asciiString ::= 0b0101 0bnnnn [int] byte*
091: * unicodeString ::= 0b0110 0bnnnn [int] short*
092: * // nnnn is number of bytes
093: * // unless 0b1111 then a int
094: * // variable-sized object follows
095: * // to indicate the number of bytes
096: *
097: * uid ::= 0b1000 0bnnnn byte* // nnnn+1 is # of bytes
098: *
099: * array ::= 0b1010 0bnnnn [int] objref* //
100: * // nnnn is number of objref
101: * // unless 0b1111 then a int
102: * // variable-sized object follows
103: * // to indicate the number of objref
104: *
105: * dict ::= 0b1010 0bnnnn [int] keyref* objref*
106: * // nnnn is number of keyref and
107: * // objref pairs
108: * // unless 0b1111 then a int
109: * // variable-sized object follows
110: * // to indicate the number of pairs
111: *
112: * objref = byte | short // if refCount
113: * // is less than 256 then objref is
114: * // an unsigned byte, otherwise it
115: * // is an unsigned big-endian short
116: *
117: * keyref = byte | short // if refCount
118: * // is less than 256 then objref is
119: * // an unsigned byte, otherwise it
120: * // is an unsigned big-endian short
121: *
122: * unused ::= 0b0111 0bxxxx | 0b1001 0bxxxx |
123: * 0b1011 0bxxxx | 0b1100 0bxxxx |
124: * 0b1110 0bxxxx | 0b1111 0bxxxx
125: *
126: *
127: * offsetTable ::= { int } // list of ints, byte size of which
128: * // is given in trailer
129: * // these are the byte offsets into
130: * // the file
131: * // number of these is in the trailer
132: *
133: * trailer ::= refCount offsetCount objectCount topLevelOffset
134: *
135: * refCount ::= byte*8 // unsigned big-endian long
136: * offsetCount ::= byte*8 // unsigned big-endian long
137: * objectCount ::= byte*8 // unsigned big-endian long
138: * topLevelOffset ::= byte*8 // unsigned big-endian long
139: * </pre>
140: */
141:
142: /**
143: * Total count of objrefs and keyrefs.
144: */
145: private int refCount;
146: /**
147: * Total count of ofsets.
148: */
149: private int offsetCount;
150: /**
151: * Total count of objects.
152: */
153: private int objectCount;
154: /**
155: * Offset in file of top level offset in offset table.
156: */
157: private int topLevelOffset;
158:
159: /**
160: * Object table.
161: * We gradually fill in objects from the binary PList object table into
162: * this list.
163: */
164: private ArrayList objectTable;
165:
166: /**
167: * Holder for a binary PList array element.
168: */
169: private static class BPLArray {
170: ArrayList objectTable;
171: int[] objref;
172:
173: public Object getValue(int i) {
174: return objectTable.get(objref[i]);
175: }
176:
177: @Override
178: public String toString() {
179: StringBuffer buf = new StringBuffer("Array{");
180: for (int i = 0; i < objref.length; i++) {
181: if (i > 0) {
182: buf.append(',');
183: }
184: if (objectTable.size() > objref[i]
185: && objectTable.get(objref[i]) != this ) {
186: buf.append(objectTable.get(objref[i]));
187: } else {
188: buf.append("*" + objref[i]);
189: }
190: }
191: buf.append('}');
192: return buf.toString();
193: }
194: }
195:
196: /**
197: * Holder for a binary PList dict element.
198: */
199: private static class BPLDict {
200: ArrayList objectTable;
201: int[] keyref;
202: int[] objref;
203:
204: public String getKey(int i) {
205: return objectTable.get(keyref[i]).toString();
206: }
207:
208: public Object getValue(int i) {
209: return objectTable.get(objref[i]);
210: }
211:
212: @Override
213: public String toString() {
214: StringBuffer buf = new StringBuffer("BPLDict{");
215: for (int i = 0; i < keyref.length; i++) {
216: if (i > 0) {
217: buf.append(',');
218: }
219: if (keyref[i] < 0 || keyref[i] >= objectTable.size()) {
220: buf.append("#" + keyref[i]);
221: } else if (objectTable.get(keyref[i]) == this ) {
222: buf.append("*" + keyref[i]);
223: } else {
224: buf.append(objectTable.get(keyref[i]));
225: //buf.append(keyref[i]);
226: }
227: buf.append(":");
228: if (objref[i] < 0 || objref[i] >= objectTable.size()) {
229: buf.append("#" + objref[i]);
230: } else if (objectTable.get(objref[i]) == this ) {
231: buf.append("*" + objref[i]);
232: } else {
233: buf.append(objectTable.get(objref[i]));
234: //buf.append(objref[i]);
235: }
236: }
237: buf.append('}');
238: return buf.toString();
239: }
240: }
241:
242: /**
243: * Creates a new instance.
244: */
245: public BinaryPListParser() {
246: }
247:
248: /**
249: * Parses a binary PList file and turns it into a XMLElement.
250: * The XMLElement is equivalent with a XML PList file parsed using
251: * NanoXML.
252: *
253: * @param file A file containing a binary PList.
254: * @return Returns the parsed XMLElement.
255: */
256: public XMLElement parse(File file) throws IOException {
257: RandomAccessFile raf = null;
258: byte[] buf = null;
259: try {
260: raf = new RandomAccessFile(file, "r");
261:
262: // Parse the HEADER
263: // ----------------
264: // magic number ("bplist")
265: // file format version ("00")
266: int bpli = raf.readInt();
267: int st00 = raf.readInt();
268: if (bpli != 0x62706c69 || st00 != 0x73743030) {
269: throw new IOException(
270: "parseHeader: File does not start with 'bplist00' magic.");
271: }
272:
273: // Parse the TRAILER
274: // ----------------
275: // byte size of offset ints in offset table
276: // byte size of object refs in arrays and dicts
277: // number of offsets in offset table (also is number of objects)
278: // element # in offset table which is top level object
279: raf.seek(raf.length() - 32);
280: // count of offset ints in offset table
281: offsetCount = (int) raf.readLong();
282: // count of object refs in arrays and dicts
283: refCount = (int) raf.readLong();
284: // count of offsets in offset table (also is number of objects)
285: objectCount = (int) raf.readLong();
286: // element # in offset table which is top level object
287: topLevelOffset = (int) raf.readLong();
288: buf = new byte[topLevelOffset - 8];
289: raf.seek(8);
290: raf.readFully(buf);
291: } finally {
292: if (raf != null) {
293: raf.close();
294: }
295: }
296:
297: // Parse the OBJECT TABLE
298: // ----------------------
299: objectTable = new ArrayList();
300: DataInputStream in = null;
301: try {
302: in = new DataInputStream(new ByteArrayInputStream(buf));
303: parseObjectTable(in);
304: } finally {
305: if (in != null) {
306: in.close();
307: }
308: }
309:
310: // Convert the object table to XML and return it
311: XMLElement root = new XMLElement(new HashMap(), false, false);
312: root.setName("plist");
313: root.setAttribute("version", "1.0");
314: convertObjectTableToXML(root, objectTable.get(0));
315: return root;
316: }
317:
318: /**
319: * Converts the object table in the binary PList into an XMLElement.
320: */
321: private void convertObjectTableToXML(XMLElement parent,
322: Object object) {
323: XMLElement elem = parent.createAnotherElement();
324: if (object instanceof BPLDict) {
325: BPLDict dict = (BPLDict) object;
326: elem.setName("dict");
327: for (int i = 0; i < dict.keyref.length; i++) {
328: XMLElement key = parent.createAnotherElement();
329: key.setName("key");
330: key.setContent(dict.getKey(i));
331: elem.addChild(key);
332: convertObjectTableToXML(elem, dict.getValue(i));
333: }
334: } else if (object instanceof BPLArray) {
335: BPLArray arr = (BPLArray) object;
336: elem.setName("array");
337: for (int i = 0; i < arr.objref.length; i++) {
338: convertObjectTableToXML(elem, arr.getValue(i));
339: }
340:
341: } else if (object instanceof String) {
342: elem.setName("string");
343: elem.setContent((String) object);
344: } else if (object instanceof Integer) {
345: elem.setName("integer");
346: elem.setContent(object.toString());
347: } else if (object instanceof Long) {
348: elem.setName("integer");
349: elem.setContent(object.toString());
350: } else if (object instanceof Float) {
351: elem.setName("real");
352: elem.setContent(object.toString());
353: } else if (object instanceof Double) {
354: elem.setName("real");
355: elem.setContent(object.toString());
356: } else if (object instanceof Boolean) {
357: elem.setName("boolean");
358: elem.setContent(object.toString());
359: } else if (object instanceof byte[]) {
360: elem.setName("data");
361: elem.setContent(Base64.encodeBytes((byte[]) object));
362: } else if (object instanceof Date) {
363: elem.setName("date");
364: DateFormat format = new SimpleDateFormat(
365: "yyyy-MM-dd'T'HH:mm:ss'Z'");
366: elem.setContent(format.format((Date) object));
367: } else {
368: elem.setName("unsupported");
369: elem.setContent(object.toString());
370: }
371: parent.addChild(elem);
372: }
373:
374: /**
375: * Object Formats (marker byte followed by additional info in some cases)
376: * null 0000 0000
377: * bool 0000 1000 // false
378: * bool 0000 1001 // true
379: * fill 0000 1111 // fill byte
380: * int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
381: * real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
382: * date 0011 0011 ... // 8 byte float follows, big-endian bytes
383: * data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
384: * string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
385: * string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte shorts
386: * 0111 xxxx // unused
387: * uid 1000 nnnn ... // nnnn+1 is # of bytes
388: * 1001 xxxx // unused
389: * array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
390: * 1011 xxxx // unused
391: * 1100 xxxx // unused
392: * dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
393: * 1110 xxxx // unused
394: * 1111 xxxx // unused
395: */
396: private void parseObjectTable(DataInputStream in)
397: throws IOException {
398: int marker;
399: while ((marker = in.read()) != -1) {
400: //System.out.println("parseObjectTable "+objectTable.size()+": marker="+Integer.toHexString(marker));
401: switch ((marker & 0xf0) >> 4) {
402: case 0: {
403: parsePrimitive(in, marker & 0xf);
404: break;
405: }
406: case 1: {
407: int count = 1 << (marker & 0xf);
408: parseInt(in, count);
409: break;
410: }
411: case 2: {
412: int count = 1 << (marker & 0xf);
413: parseReal(in, count);
414: break;
415: }
416: case 3: {
417: if ((marker & 0xf) != 3) {
418: throw new IOException(
419: "parseObjectTable: illegal marker "
420: + Integer.toBinaryString(marker));
421: }
422: parseDate(in);
423: break;
424: }
425: case 4: {
426: int count = marker & 0xf;
427: if (count == 15) {
428: count = readCount(in);
429: }
430: parseData(in, count);
431: break;
432: }
433: case 5: {
434: int count = marker & 0xf;
435: if (count == 15) {
436: count = readCount(in);
437: }
438: parseAsciiString(in, count);
439: break;
440: }
441: case 6: {
442: int count = marker & 0xf;
443: if (count == 15) {
444: count = readCount(in);
445: }
446: parseUnicodeString(in, count);
447: break;
448: }
449: case 7: {
450: System.out.println("parseObjectTable: illegal marker "
451: + Integer.toBinaryString(marker));
452: return;
453: // throw new IOException("parseObjectTable: illegal marker "+Integer.toBinaryString(marker));
454: //break;
455: }
456: case 8: {
457: int count = (marker & 0xf) + 1;
458: System.out.println("uid " + count);
459: break;
460: }
461: case 9: {
462: throw new IOException(
463: "parseObjectTable: illegal marker "
464: + Integer.toBinaryString(marker));
465: //break;
466: }
467: case 10: {
468: int count = marker & 0xf;
469: if (count == 15) {
470: count = readCount(in);
471: }
472: if (refCount > 255) {
473: parseShortArray(in, count);
474: } else {
475: parseByteArray(in, count);
476: }
477: break;
478: }
479: case 11: {
480: throw new IOException(
481: "parseObjectTable: illegal marker "
482: + Integer.toBinaryString(marker));
483: //break;
484: }
485: case 12: {
486: throw new IOException(
487: "parseObjectTable: illegal marker "
488: + Integer.toBinaryString(marker));
489: //break;
490: }
491: case 13: {
492: int count = marker & 0xf;
493: if (count == 15) {
494: count = readCount(in);
495: }
496: if (refCount > 256) {
497: parseShortDict(in, count);
498: } else {
499: parseByteDict(in, count);
500: }
501: break;
502: }
503: case 14: {
504: throw new IOException(
505: "parseObjectTable: illegal marker "
506: + Integer.toBinaryString(marker));
507: //break;
508: }
509: case 15: {
510: throw new IOException(
511: "parseObjectTable: illegal marker "
512: + Integer.toBinaryString(marker));
513: //break;
514: }
515: }
516: // System.out.println(objectTable.get(objectTable.size() - 1));
517: }
518: }
519:
520: /**
521: * Reads a count value from the object table. Count values are encoded
522: * using the following scheme:
523: * <p/>
524: * int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
525: */
526: private int readCount(DataInputStream in) throws IOException {
527: int marker = in.read();
528: if (marker == -1) {
529: throw new IOException(
530: "variableLengthInt: Illegal EOF in marker");
531: }
532: if (((marker & 0xf0) >> 4) != 1) {
533: throw new IOException("variableLengthInt: Illegal marker "
534: + Integer.toBinaryString(marker));
535: }
536: int count = 1 << (marker & 0xf);
537: int value = 0;
538: for (int i = 0; i < count; i++) {
539: int b = in.read();
540: if (b == -1) {
541: throw new IOException(
542: "variableLengthInt: Illegal EOF in value");
543: }
544: value = (value << 8) | b;
545: }
546: return value;
547: }
548:
549: /**
550: * null 0000 0000
551: * bool 0000 1000 // false
552: * bool 0000 1001 // true
553: * fill 0000 1111 // fill byte
554: */
555: private void parsePrimitive(DataInputStream in, int primitive)
556: throws IOException {
557: switch (primitive) {
558: case 0:
559: objectTable.add(null);
560: break;
561: case 8:
562: objectTable.add(Boolean.FALSE);
563: break;
564: case 9:
565: objectTable.add(Boolean.TRUE);
566: break;
567: case 15:
568: // fill byte: don't add to object table
569: break;
570: default:
571: throw new IOException("parsePrimitive: illegal primitive "
572: + Integer.toBinaryString(primitive));
573: }
574: }
575:
576: /**
577: * array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
578: */
579: private void parseByteArray(DataInputStream in, int count)
580: throws IOException {
581: BPLArray arr = new BPLArray();
582: arr.objectTable = objectTable;
583: arr.objref = new int[count];
584:
585: for (int i = 0; i < count; i++) {
586: arr.objref[i] = in.readByte() & 0xff;
587: if (arr.objref[i] == -1) {
588: throw new IOException(
589: "parseByteArray: illegal EOF in objref*");
590: }
591: }
592:
593: objectTable.add(arr);
594: }
595:
596: /**
597: * array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
598: */
599: private void parseShortArray(DataInputStream in, int count)
600: throws IOException {
601: BPLArray arr = new BPLArray();
602: arr.objectTable = objectTable;
603: arr.objref = new int[count];
604:
605: for (int i = 0; i < count; i++) {
606: arr.objref[i] = in.readShort() & 0xffff;
607: if (arr.objref[i] == -1) {
608: throw new IOException(
609: "parseShortArray: illegal EOF in objref*");
610: }
611: }
612:
613: objectTable.add(arr);
614: }
615:
616: /*
617: * data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
618: */
619: private void parseData(DataInputStream in, int count)
620: throws IOException {
621: byte[] data = new byte[count];
622: in.readFully(data);
623: objectTable.add(data);
624: }
625:
626: /**
627: * byte dict 1101 nnnn keyref* objref* // nnnn is less than '1111'
628: */
629: private void parseByteDict(DataInputStream in, int count)
630: throws IOException {
631: BPLDict dict = new BPLDict();
632: dict.objectTable = objectTable;
633: dict.keyref = new int[count];
634: dict.objref = new int[count];
635:
636: for (int i = 0; i < count; i++) {
637: dict.keyref[i] = in.readByte() & 0xff;
638: }
639: for (int i = 0; i < count; i++) {
640: dict.objref[i] = in.readByte() & 0xff;
641: }
642: objectTable.add(dict);
643: }
644:
645: /**
646: * short dict 1101 ffff int keyref* objref* // int is count
647: */
648: private void parseShortDict(DataInputStream in, int count)
649: throws IOException {
650: BPLDict dict = new BPLDict();
651: dict.objectTable = objectTable;
652: dict.keyref = new int[count];
653: dict.objref = new int[count];
654:
655: for (int i = 0; i < count; i++) {
656: dict.keyref[i] = in.readShort() & 0xffff;
657: }
658: for (int i = 0; i < count; i++) {
659: dict.objref[i] = in.readShort() & 0xffff;
660: }
661: objectTable.add(dict);
662: }
663:
664: /**
665: * string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
666: */
667: private void parseAsciiString(DataInputStream in, int count)
668: throws IOException {
669: byte[] buf = new byte[count];
670: in.readFully(buf);
671: String str = new String(buf, "ASCII");
672: objectTable.add(str);
673: }
674:
675: /**
676: * int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
677: */
678: private void parseInt(DataInputStream in, int count)
679: throws IOException {
680: if (count > 8) {
681: throw new IOException("parseInt: unsupported byte count:"
682: + count);
683: }
684: long value = 0;
685: for (int i = 0; i < count; i++) {
686: int b = in.read();
687: if (b == -1) {
688: throw new IOException("parseInt: Illegal EOF in value");
689: }
690: value = (value << 8) | b;
691: }
692: objectTable.add(value);
693: }
694:
695: /**
696: * real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
697: */
698: private void parseReal(DataInputStream in, int count)
699: throws IOException {
700: switch (count) {
701: case 4:
702: objectTable.add(in.readFloat());
703: break;
704: case 8:
705: objectTable.add(in.readDouble());
706: break;
707: default:
708: throw new IOException("parseReal: unsupported byte count:"
709: + count);
710: }
711: }
712:
713: /**
714: * date 0011 0011 ... // 8 byte float follows, big-endian bytes
715: */
716: private void parseDate(DataInputStream in) throws IOException {
717: // XXX - This does not yield a date :(
718: double date = in.readDouble();
719: //objectTable.add(new Date((long) date));
720: objectTable.add(new Date());
721: }
722:
723: /**
724: * string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte shorts
725: */
726: private void parseUnicodeString(DataInputStream in, int count)
727: throws IOException {
728: char[] buf = new char[count];
729: for (int i = 0; i < count; i++) {
730: buf[i] = in.readChar();
731: }
732: String str = new String(buf);
733: objectTable.add(str);
734: }
735:
736: public static void main(String[] args) {
737:
738: try {
739: File[] list = new File(SecurityUtils.getProperty(
740: "user.home", ""), "Library/Preferences")
741: .listFiles();
742: /*
743: File[] list = {
744: //new File(QuaquaManager.getProperty("user.home"), "Documents/BPList/date.plist")
745: new File(QuaquaManager.getProperty("user.home"), "Library/Preferences/ChristopheGlobalPreferences.plist")
746: };*/
747: for (int i = 0; i < list.length; i++) {
748: String name = list[i].getName();
749: if (list[i].isDirectory()
750: //|| name.startsWith(".")
751: || !name.endsWith(".plist")
752: || name.endsWith("internetconfig.plist")) {
753: continue;
754: }
755: try {
756: System.out.println(list[i]);
757: BinaryPListParser bplr = new BinaryPListParser();
758: XMLElement xml = bplr.parse(list[i]);
759: System.out.println(xml);
760: } catch (IOException e) {
761: if (e.getMessage() != null
762: && (e.getMessage()
763: .startsWith("parseHeader") || e
764: .getMessage().startsWith(
765: "parseTrailer"))) {
766: System.out.println(e);
767:
768: continue;
769: } else {
770: throw e;
771: }
772: }
773: }
774: } catch (Throwable e) {
775: e.printStackTrace();
776: }
777: }
778: }
|