001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.upgrade.systemoptions;
043:
044: import java.io.*;
045: import java.util.*;
046: import org.openide.util.NotImplementedException;
047:
048: /* The following may be useful to Emacs users:
049:
050: (defun comma-sep-decimal-encode ()
051: (interactive)
052: (while
053: (let ((p (point)))
054: (and (re-search-forward "\\(-?[0-9][0-9]?[0-9]?\\),?" nil t)
055: (= p (match-beginning 0))))
056: (replace-match (char-to-string
057: (let ((x (string-to-int (match-string 1))))
058: (when (< x 0) (setq x (+ x 256)))
059: x))
060: t t)))
061:
062: After evaluating this, go into an old cpanel.xml or similar
063: (M-x find-file-literally, by the way) and type
064: M-x comma-sep-decimal-encode right after the opening quote
065: of the value. The contents will be converted to raw binary and
066: may be saved as a .ser file parsable by this class.
067:
068: (defun hex-encode ()
069: (interactive)
070: (while
071: (let ((p (point)))
072: (and (re-search-forward "\\s-*\\([a-fA-F0-9][a-fA-F0-9]\\)" nil t)
073: (= p (match-beginning 0))))
074: (replace-match (char-to-string
075: (string-to-int (match-string 1) 16))
076: t t)))
077:
078: Same for hexadecimal serialized data. For .settings files, it is
079: easier to select Customize Bean... in the IDE and save as *.ser.
080: */
081:
082: /** Parser for Java serialization files.
083: * Does no classloading or per-class semantics, simply parses the
084: * raw serialization structure.
085: * @author Jesse Glick
086: */
087: public final class SerParser implements ObjectStreamConstants {
088:
089: private static final boolean DEBUG = Boolean
090: .getBoolean("org.netbeans.modules.clazz.SerParser.DEBUG"); // NOI18N
091:
092: private final InputStream is;
093: private int seq = 0;
094: private final List<Object> refs = new ArrayList<Object>(100);
095:
096: public SerParser(InputStream is) {
097: this .is = is;
098: }
099:
100: private int makeRef(Object o) {
101: refs.add(o);
102: int i = seq;
103: seq++;
104: if (DEBUG)
105: System.err.println("makeRef[" + i + "]=" + o); // NOI18N
106: return i;
107: }
108:
109: private Object getRef(int i) throws CorruptException {
110: int idx = i - baseWireHandle;
111: if (idx < 0 || idx >= seq)
112: throw new CorruptException("Invalid reference: " + i); // NOI18N
113: Object o = refs.get(idx);
114: if (o == null)
115: throw new CorruptException("Invalid reference: " + i); // NOI18N
116: return o;
117: }
118:
119: public Stream parse() throws IOException, CorruptException {
120: Stream s = new Stream();
121: s.magic = readShort();
122: s.version = readShort();
123: if (s.magic != STREAM_MAGIC || s.version != STREAM_VERSION) {
124: throw new CorruptException("stream version mismatch: "
125: + hexify(s.magic) + " != " + hexify(STREAM_MAGIC)
126: + " or " + hexify(s.version) + " != "
127: + hexify(STREAM_VERSION)); // NOI18N
128: }
129: s.contents = new ArrayList<Object>(10);
130: while (peek() != -1) {
131: s.contents.add(readContent());
132: }
133: if (DEBUG)
134: System.err.println("parsed: " + s); // NOI18N
135: return s;
136: }
137:
138: public static final class CorruptException extends IOException {
139: public CorruptException() {
140: }
141:
142: public CorruptException(String m) {
143: super (m);
144: }
145: }
146:
147: private int pushback = -1;
148:
149: private int rb() throws IOException {
150: if (pushback != -1) {
151: int c = pushback;
152: pushback = -1;
153: return c;
154: }
155: int c = is.read();
156: if (DEBUG)
157: System.err.println("read: " + Integer.toHexString(c)); // NOI18N
158: if (c == -1) {
159: throw new EOFException();
160: } else {
161: return c;
162: }
163: }
164:
165: private int peek() throws IOException {
166: if (pushback != -1)
167: throw new IllegalStateException("can only peek once"); // NOI18N
168: pushback = is.read();
169: if (DEBUG)
170: System.err
171: .println("read: " + Integer.toHexString(pushback)); // NOI18N
172: return pushback;
173: }
174:
175: static String hexify(byte b) {
176: int i = b;
177: if (i < 0)
178: i += 256;
179: String s = Integer.toHexString(i).toUpperCase(Locale.US);
180: return "0x" + pad(s, 2); // NOI18N
181: }
182:
183: static String hexify(short s) {
184: int i = s;
185: if (i < 0)
186: i += 65536;
187: String st = Integer.toHexString(i).toUpperCase(Locale.US);
188: return "0x" + pad(st, 4); // NOI18N
189: }
190:
191: static String hexify(int i) {
192: String s = Integer.toHexString(i).toUpperCase(Locale.US);
193: return "0x" + pad(s, 4); // NOI18N
194: }
195:
196: static String hexify(long l) {
197: String s1 = Integer.toHexString(
198: (int) ((l & 0xFFFFFFFF00000000L) << 32)).toUpperCase(
199: Locale.US);
200: String s2 = Integer
201: .toHexString((int) (l & 0x00000000FFFFFFFFL))
202: .toUpperCase(Locale.US);
203: return "0x" + pad(s1, 4) + pad(s2, 4); // NOI18N
204: }
205:
206: static String hexify(byte[] b) {
207: StringBuffer buf = new StringBuffer(2 + b.length * 2);
208: buf.append("0x"); // NOI18N
209: for (int i = 0; i < b.length; i++) {
210: int x = b[i];
211: if (x < 0)
212: x += 256;
213: buf.append(pad(Integer.toHexString(x)
214: .toUpperCase(Locale.US), 2));
215: }
216: return buf.toString();
217: }
218:
219: private static String pad(String s, int size) {
220: int i = s.length();
221: if (i == size) {
222: return s;
223: } else {
224: StringBuffer b = new StringBuffer(size);
225: for (int k = 0; k < size - i; k++) {
226: b.append('0'); // NOI18N
227: }
228: b.append(s);
229: return b.toString();
230: }
231: }
232:
233: private long readLong() throws IOException {
234: long x1 = rb();
235: long x2 = rb();
236: long x3 = rb();
237: long x4 = rb();
238: long x5 = rb();
239: long x6 = rb();
240: long x7 = rb();
241: long x8 = rb();
242: long l = (x1 << 56) + (x2 << 48) + (x3 << 40) + (x4 << 32)
243: + (x5 << 24) + (x6 << 16) + (x7 << 8) + x8;
244: if (DEBUG)
245: System.err.println("readLong: " + l); // NOI18N
246: return l;
247: }
248:
249: private int readInt() throws IOException {
250: int x1 = rb();
251: int x2 = rb();
252: int x3 = rb();
253: int x4 = rb();
254: int i = (x1 << 24) + (x2 << 16) + (x3 << 8) + x4;
255: if (DEBUG)
256: System.err.println("readInt: " + i); // NOI18N
257: return i;
258: }
259:
260: private short readShort() throws IOException {
261: int x1 = rb();
262: int x2 = rb();
263: short s = (short) ((x1 << 8) + x2);
264: //System.err.println("x1=" + hexify(x1) + " x2=" + hexify(x2) + " s=" + hexify(s));
265: //if (DEBUG) System.err.println("x1=" + x1 + " x2=" + x2 + " s=" + s);
266: if (DEBUG)
267: System.err.println("readShort: " + s); // NOI18N
268: return s;
269: }
270:
271: private byte readByte() throws IOException {
272: return (byte) rb();
273: }
274:
275: private String readUTF() throws IOException {
276: short len = readShort();
277: if (len < 0)
278: throw new NotImplementedException();//XXX
279: byte[] buf = new byte[len];
280: for (int i = 0; i < len; i++) {
281: buf[i] = readByte();
282: }
283: String s = new String(buf, "UTF-8"); // NOI18N
284: if (DEBUG)
285: System.err.println("readUTF: " + s); // NOI18N
286: return s;
287: }
288:
289: /*
290: private String readLongUTF() throws IOException {
291: long len = readLong();
292: if (len < 0) throw new NotImplementedException();//XXX
293: if (len > Integer.MAX_VALUE) throw new NotImplementedException();// XXX
294: int ilen = (int)len;
295: byte[] buf = new byte[ilen];
296: for (int i = 0; i < ilen; i++) {
297: buf[i] = readByte();
298: }
299: String s = new String(buf, "UTF-8"); // NOI18N
300: if (DEBUG) System.err.println("readUTF: " + s); // NOI18N
301: return s;
302: }
303: */
304:
305: // See "Rules of the Grammar" in Java Object Serialization Specification
306: // for explanation of all these objects.
307: public static final class Stream /*extends Thing*/{
308: public short magic;
309: public short version;
310: public List<Object> contents;
311:
312: public String toString() {
313: return "Stream[contents=" + contents + "]"; // NOI18N
314: }
315: }
316:
317: public static final Object NULL = "null"; // NOI18N
318:
319: private Object readContent() throws IOException {
320: byte tc = readByte();
321: switch (tc) {
322: case TC_OBJECT:
323: return readNewObject();
324: case TC_CLASS:
325: return readNewClass();
326: case TC_ARRAY:
327: return readNewArray();
328: case TC_CLASSDESC:
329: return readNewClassDesc();
330: case TC_PROXYCLASSDESC:
331: // XXX too complicated:
332: throw new NotImplementedException("TC_PROXYCLASSDESC"); // NOI18N
333: //return readNewProxyClassDesc();
334: case TC_STRING:
335: return readNewString();
336: case TC_LONGSTRING:
337: // XXX later
338: throw new NotImplementedException("TC_LONGSTRING"); // NOI18N
339: //return readNewLongString();
340: case TC_REFERENCE:
341: return readReference();
342: case TC_NULL:
343: return NULL;
344: case TC_EXCEPTION:
345: // XXX what is this??
346: throw new NotImplementedException("TC_EXCEPTION"); // NOI18N
347: case TC_RESET:
348: // XXX what is this??
349: throw new NotImplementedException("TC_RESET"); // NOI18N
350: case TC_BLOCKDATA:
351: return readBlockData();
352: case TC_BLOCKDATALONG:
353: return readBlockDataLong();
354: default:
355: throw new CorruptException("Unknown typecode: "
356: + hexify(tc)); // NOI18N
357: }
358: }
359:
360: public static final class ObjectWrapper {
361: public ClassDesc classdesc;
362: public List<Object> data; // <Union2<NameValue,Object>>
363:
364: public String toString() {
365: return "Object[class=" + classdesc.name
366: + ",data=<omitted>]"; // NOI18N
367: }
368: }
369:
370: public static final class NameValue {
371: public NameValue(FieldDesc name, Object value) {
372: this .name = name;
373: this .value = value;
374: }
375:
376: public final FieldDesc name;
377: public final Object value;
378:
379: public String toString() {
380: return name.toString() + "=" + value.toString(); // NOI18N
381: }
382: }
383:
384: public static final class ClassDesc {
385: public String name;
386: public long svuid;
387: public boolean writeMethod;
388: public boolean blockData;
389: public boolean serializable;
390: public boolean externalizable;
391: public List<FieldDesc> fields;
392: public List annotation; // List<Object>
393: public ClassDesc super class;
394:
395: public String toString() {
396: return "Class[name=" + name + "]"; // NOI18N
397: }
398: }
399:
400: private ObjectWrapper readNewObject() throws IOException {
401: ObjectWrapper ow = new ObjectWrapper();
402: ow.classdesc = readClassDesc();
403: makeRef(ow);
404: ow.data = new ArrayList<Object>(10);
405: LinkedList<ClassDesc> hier = new LinkedList<ClassDesc>();
406: for (ClassDesc cd = ow.classdesc; cd != null; cd = cd.super class) {
407: hier.addFirst(cd);
408: }
409: for (ClassDesc cd : hier) {
410: if (cd.serializable) {
411: ow.data.addAll(readNoWrClass(cd));
412: if (cd.writeMethod) {
413: ow.data.addAll(readContents());
414: }
415: } else {
416: if (cd.blockData) {
417: ow.data.addAll(readContents());
418: } else {
419: // Old externalization. If this is not object content,
420: // the stream could now become corrupted. Oh well.
421: ow.data.add(readContent());
422: }
423: }
424: }
425: if (DEBUG)
426: System.err.println("readNewObject: " + ow); // NOI18N
427: return ow;
428: }
429:
430: private ClassDesc readClassDesc() throws IOException {
431: Object o = readContent();
432: if (o instanceof ClassDesc) {
433: return (ClassDesc) o;
434: } else if (o == NULL) {
435: return null;
436: } else {
437: throw new CorruptException("Expected class desc, got: " + o); // NOI18N
438: }
439: }
440:
441: private ClassDesc readNewClass() throws IOException {
442: ClassDesc cd = readClassDesc();
443: makeRef(cd);
444: return cd;
445: }
446:
447: private ClassDesc readNewClassDesc() throws IOException {
448: ClassDesc cd = new ClassDesc();
449: cd.name = readUTF();
450: if (!cd.name.startsWith("[")
451: && // NOI18N
452: !(cd.name.length() == 1 && "BSIJFDCZ".indexOf(cd.name) != -1)
453: && // NOI18N
454: !cd.name.endsWith(";")) { // NOI18N
455: // Canonicalize. It seems class names read normally need this; those
456: // read as part of an array do not. ??
457: cd.name = "L" + cd.name + ";"; // NOI18N
458: }
459: cd.svuid = readLong();
460: makeRef(cd);
461: byte cdf = readByte();
462: cd.writeMethod = (cdf & SC_WRITE_METHOD) != 0;
463: cd.blockData = (cdf & SC_BLOCK_DATA) != 0;
464: cd.serializable = (cdf & SC_SERIALIZABLE) != 0;
465: cd.externalizable = (cdf & SC_EXTERNALIZABLE) != 0;
466: short count = readShort();
467: cd.fields = new ArrayList<FieldDesc>(count);
468: for (int i = 0; i < count; i++) {
469: cd.fields.add(readFieldDesc());
470: }
471: cd.annotation = readContents();
472: cd.super class = readClassDesc();
473: if (DEBUG)
474: System.err.println("readNewClassDesc: " + cd); // NOI18N
475: return cd;
476: }
477:
478: public static class FieldDesc {
479: public String name;
480: public String type;
481:
482: public String toString() {
483: return "Field[name=" + name + ",type=" + type + "]"; // NOI18N
484: }
485: }
486:
487: public static final class ObjFieldDesc extends FieldDesc {
488: public boolean array;
489:
490: public String toString() {
491: return "Field[name=" + name + ",type=" + type
492: + (array ? "[]" : "") + "]"; // NOI18N
493: }
494: }
495:
496: private FieldDesc readFieldDesc() throws IOException {
497: char tc = (char) readByte();
498: FieldDesc fd;
499: switch (tc) {
500: case 'B':
501: case 'C':
502: case 'D':
503: case 'F':
504: case 'I':
505: case 'J':
506: case 'S':
507: case 'Z':
508: fd = new FieldDesc();
509: fd.type = new String(new char[] { tc });
510: break;
511: case '[':
512: fd = new ObjFieldDesc();
513: ((ObjFieldDesc) fd).array = true;
514: break;
515: case 'L':
516: fd = new ObjFieldDesc();
517: ((ObjFieldDesc) fd).array = false;
518: break;
519: default:
520: throw new CorruptException("Strange field type: " + tc); // NOI18N
521: }
522: fd.name = readUTF();
523: if (fd instanceof ObjFieldDesc) {
524: String clazz = (String) readContent();
525: /*
526: if (((ObjFieldDesc)fd).array) {
527: if (! clazz.startsWith("[")) throw new CorruptException("Field type: " + clazz); // NOI18N
528: clazz = clazz.substring(1, clazz.length());
529: }
530: if (! (clazz.startsWith("L") && clazz.endsWith(";"))) throw new CorruptException("Field type: " + clazz); // NOI18N
531: fd.type = clazz.substring(1, clazz.length() - 1).replace('/', '.'); // NOI18N
532: */
533: fd.type = clazz;
534: }
535: if (DEBUG)
536: System.err.println("readFieldDesc: " + fd); // NOI18N
537: return fd;
538: }
539:
540: private List<Object> readContents() throws IOException {
541: List<Object> l = new ArrayList<Object>(10);
542: while (peek() != TC_ENDBLOCKDATA) {
543: l.add(readContent());
544: }
545: if (readByte() != TC_ENDBLOCKDATA)
546: throw new IllegalStateException();
547: if (DEBUG)
548: System.err.println("readContents: " + l); // NOI18N
549: return l;
550: }
551:
552: public static final class ArrayWrapper {
553: public ClassDesc classdesc;
554: public List<Object> values;
555:
556: public String toString() {
557: return classdesc.name + "{" + values + "}"; // NOI18N
558: }
559: }
560:
561: private ArrayWrapper readNewArray() throws IOException {
562: ArrayWrapper aw = new ArrayWrapper();
563: aw.classdesc = readClassDesc();
564: makeRef(aw);
565: int size = readInt();
566: if (size < 0)
567: throw new NotImplementedException();
568: aw.values = new ArrayList<Object>(size);
569: for (int i = 0; i < size; i++) {
570: if (aw.classdesc.name.equals("[B")) { // NOI18N
571: aw.values.add(new Byte(readByte()));
572: } else if (aw.classdesc.name.equals("[S")) { // NOI18N
573: aw.values.add(new Short(readShort()));
574: } else if (aw.classdesc.name.equals("[I")) { // NOI18N
575: aw.values.add(new Integer(readInt()));
576: } else if (aw.classdesc.name.equals("[J")) { // NOI18N
577: aw.values.add(new Long(readLong()));
578: } else if (aw.classdesc.name.equals("[F")) { // NOI18N
579: aw.values
580: .add(new Float(Float.intBitsToFloat(readInt())));
581: } else if (aw.classdesc.name.equals("[D")) { // NOI18N
582: aw.values.add(new Double(Double
583: .longBitsToDouble(readLong())));
584: } else if (aw.classdesc.name.equals("[C")) { // NOI18N
585: aw.values.add(new Character((char) readShort()));
586: } else if (aw.classdesc.name.equals("[Z")) { // NOI18N
587: aw.values.add(readByte() == 1 ? Boolean.TRUE
588: : Boolean.FALSE);
589: } else {
590: aw.values.add(readContent());
591: }
592: }
593: if (DEBUG)
594: System.err.println("readNewArray: " + aw); // NOI18N
595: return aw;
596: }
597:
598: private String readNewString() throws IOException {
599: String s = readUTF();
600: makeRef(s);
601: return s;
602: }
603:
604: private Object readReference() throws IOException {
605: int i = readInt();
606: Object r = getRef(i);
607: if (DEBUG)
608: System.err.println("readReference: " + r); // NOI18N
609: return r;
610: }
611:
612: private byte[] readBlockData() throws IOException {
613: int size = readByte();
614: if (size < 0)
615: size += 256;
616: byte[] b = new byte[size];
617: for (int i = 0; i < size; i++) {
618: b[i] = readByte();
619: }
620: if (DEBUG)
621: System.err.println("readBlockData: " + size + " bytes"); // NOI18N
622: return b;
623: }
624:
625: private byte[] readBlockDataLong() throws IOException {
626: int size = readInt();
627: if (size < 0)
628: throw new NotImplementedException();
629: byte[] b = new byte[size];
630: for (int i = 0; i < size; i++) {
631: b[i] = readByte();
632: }
633: if (DEBUG)
634: System.err.println("readBlockDataLong: " + size + " bytes"); // NOI18N
635: return b;
636: }
637:
638: private List<NameValue> readNoWrClass(ClassDesc cd)
639: throws IOException {
640: List<FieldDesc> fields = cd.fields;
641: List<NameValue> values = new ArrayList<NameValue>(fields.size());
642: for (int i = 0; i < fields.size(); i++) {
643: FieldDesc fd = (FieldDesc) fields.get(i);
644: if (fd.type.equals("B")) { // NOI18N
645: values.add(new NameValue(fd, new Byte(readByte())));
646: } else if (fd.type.equals("S")) { // NOI18N
647: values.add(new NameValue(fd, new Short(readShort())));
648: } else if (fd.type.equals("I")) { // NOI18N
649: values.add(new NameValue(fd, new Integer(readInt())));
650: } else if (fd.type.equals("J")) { // NOI18N
651: values.add(new NameValue(fd, new Long(readLong())));
652: } else if (fd.type.equals("F")) { // NOI18N
653: values.add(new NameValue(fd, new Float(Float
654: .intBitsToFloat(readInt()))));
655: } else if (fd.type.equals("D")) { // NOI18N
656: values.add(new NameValue(fd, new Double(Double
657: .longBitsToDouble(readLong()))));
658: } else if (fd.type.equals("C")) { // NOI18N
659: values.add(new NameValue(fd, new Character(
660: (char) readShort())));
661: } else if (fd.type.equals("Z")) { // NOI18N
662: values
663: .add(new NameValue(fd,
664: readByte() == 1 ? Boolean.TRUE
665: : Boolean.FALSE));
666: } else {
667: values.add(new NameValue(fd, readContent()));
668: }
669: }
670: if (DEBUG)
671: System.err.println("readNoWrClass: " + values); // NOI18N
672: return values;
673: }
674:
675: }
|