Added
Link Here
|
1 |
/* |
2 |
* Sun Public License Notice |
3 |
* |
4 |
* The contents of this file are subject to the Sun Public License |
5 |
* Version 1.0 (the "License"). You may not use this file except in |
6 |
* compliance with the License. A copy of the License is available at |
7 |
* http://www.sun.com/ |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
13 |
|
14 |
package org.netbeans.modules.clazz; |
15 |
|
16 |
import java.io.*; |
17 |
import java.util.*; |
18 |
import org.openide.util.NotImplementedException; |
19 |
|
20 |
/* The following may be useful to Emacs users: |
21 |
|
22 |
(defun comma-sep-decimal-encode () |
23 |
(interactive) |
24 |
(while |
25 |
(let ((p (point))) |
26 |
(and (re-search-forward "\\(-?[0-9][0-9]?[0-9]?\\),?" nil t) |
27 |
(= p (match-beginning 0)))) |
28 |
(replace-match (char-to-string |
29 |
(let ((x (string-to-int (match-string 1)))) |
30 |
(when (< x 0) (setq x (+ x 256))) |
31 |
x)) |
32 |
t t))) |
33 |
|
34 |
After evaluating this, go into an old cpanel.xml or similar |
35 |
(M-x find-file-literally, by the way) and type |
36 |
M-x comma-sep-decimal-encode right after the opening quote |
37 |
of the value. The contents will be converted to raw binary and |
38 |
may be saved as a .ser file parsable by this class. |
39 |
|
40 |
Similar could easily be done for hexadecimal values from .settings |
41 |
files, but for these it is enough in the IDE to select Customize |
42 |
Bean... and serialize as a .ser file. |
43 |
*/ |
44 |
|
45 |
/** Parser for Java serialization files. |
46 |
* Does no classloading or per-class semantics, simply parses the |
47 |
* raw serialization structure. |
48 |
* @author Jesse Glick |
49 |
*/ |
50 |
public final class SerParser implements ObjectStreamConstants { |
51 |
|
52 |
private static final boolean DEBUG = false; |
53 |
|
54 |
private final InputStream is; |
55 |
private int seq = 0; |
56 |
private final List refs = new ArrayList(100); // List<Object> |
57 |
|
58 |
public SerParser(InputStream is) { |
59 |
this.is = is; |
60 |
} |
61 |
|
62 |
private int makeRef(Object o) { |
63 |
refs.add(o); |
64 |
int i = seq; |
65 |
seq++; |
66 |
if (DEBUG) System.err.println("makeRef[" + i + "]=" + o); |
67 |
return i; |
68 |
} |
69 |
|
70 |
private Object getRef(int i) throws CorruptException { |
71 |
int idx = i - baseWireHandle; |
72 |
if (idx < 0 || idx >= seq) throw new CorruptException("Invalid reference: " + i); // NOI18N |
73 |
Object o = refs.get(idx); |
74 |
if (o == null) throw new CorruptException("Invalid reference: " + i); // NOI18N |
75 |
return o; |
76 |
} |
77 |
|
78 |
public Stream parse() throws IOException, CorruptException { |
79 |
Stream s = new Stream(); |
80 |
s.magic = readShort(); |
81 |
s.version = readShort(); |
82 |
if (s.magic != STREAM_MAGIC || s.version != STREAM_VERSION) { |
83 |
throw new CorruptException("stream version mismatch: " + hexify(s.magic) + " != " + hexify(STREAM_MAGIC) + " or " + hexify(s.version) + " != " + hexify(STREAM_VERSION)); // NOI18N |
84 |
} |
85 |
s.contents = new ArrayList(10); |
86 |
while (peek() != -1) { |
87 |
s.contents.add(readContent()); |
88 |
} |
89 |
if (DEBUG) System.err.println("parsed: " + s); |
90 |
return s; |
91 |
} |
92 |
|
93 |
public static final class CorruptException extends IOException { |
94 |
public CorruptException() { |
95 |
} |
96 |
public CorruptException(String m) { |
97 |
super(m); |
98 |
} |
99 |
} |
100 |
|
101 |
private int pushback = -1; |
102 |
private int rb() throws IOException { |
103 |
if (pushback != -1) { |
104 |
int c = pushback; |
105 |
pushback = -1; |
106 |
return c; |
107 |
} |
108 |
int c = is.read(); |
109 |
if (DEBUG) System.err.println("read: " + Integer.toHexString(c)); |
110 |
if (c == -1) { |
111 |
throw new EOFException(); |
112 |
} else { |
113 |
return c; |
114 |
} |
115 |
} |
116 |
private int peek() throws IOException { |
117 |
if (pushback != -1) throw new IllegalStateException("can only peek once"); // NOI18N |
118 |
pushback = is.read(); |
119 |
if (DEBUG) System.err.println("read: " + Integer.toHexString(pushback)); |
120 |
return pushback; |
121 |
} |
122 |
|
123 |
static String hexify(byte b) { |
124 |
int i = b; |
125 |
if (i < 0) i += 256; |
126 |
String s = Integer.toHexString(i).toUpperCase(Locale.US); |
127 |
return "0x" + pad(s, 2); // NOI18N |
128 |
} |
129 |
static String hexify(short s) { |
130 |
int i = s; |
131 |
if (i < 0) i += 65536; |
132 |
String st = Integer.toHexString(i).toUpperCase(Locale.US); |
133 |
return "0x" + pad(st, 4); // NOI18N |
134 |
} |
135 |
static String hexify(int i) { |
136 |
String s = Integer.toHexString(i).toUpperCase(Locale.US); |
137 |
return "0x" + pad(s, 4); // NOI18N |
138 |
} |
139 |
static String hexify(long l) { |
140 |
String s1 = Integer.toHexString((int)((l & 0xFFFFFFFF00000000L) << 32)).toUpperCase(Locale.US); |
141 |
String s2 = Integer.toHexString((int)(l & 0x00000000FFFFFFFFL)).toUpperCase(Locale.US); |
142 |
return "0x" + pad(s1, 4) + pad(s2, 4); // NOI18N |
143 |
} |
144 |
static String hexify(byte[] b) { |
145 |
StringBuffer buf = new StringBuffer(2 + b.length * 2); |
146 |
buf.append("0x"); // NOI18N |
147 |
for (int i = 0; i < b.length; i++) { |
148 |
int x = b[i]; |
149 |
if (x < 0) x += 256; |
150 |
buf.append(pad(Integer.toHexString(x).toUpperCase(Locale.US), 2)); |
151 |
} |
152 |
return buf.toString(); |
153 |
} |
154 |
private static String pad(String s, int size) { |
155 |
int i = s.length(); |
156 |
if (i == size) { |
157 |
return s; |
158 |
} else { |
159 |
StringBuffer b = new StringBuffer(size); |
160 |
for (int k = 0; k < size - i; k++) { |
161 |
b.append('0'); // NOI18N |
162 |
} |
163 |
b.append(s); |
164 |
return b.toString(); |
165 |
} |
166 |
} |
167 |
|
168 |
private long readLong() throws IOException { |
169 |
long x1 = rb(); |
170 |
long x2 = rb(); |
171 |
long x3 = rb(); |
172 |
long x4 = rb(); |
173 |
long x5 = rb(); |
174 |
long x6 = rb(); |
175 |
long x7 = rb(); |
176 |
long x8 = rb(); |
177 |
long l = (x1 << 56) + (x2 << 48) + (x3 << 40) + (x4 << 32) + (x5 << 24) + (x6 << 16) + (x7 << 8) + x8; |
178 |
if (DEBUG) System.err.println("readLong: " + l); |
179 |
return l; |
180 |
} |
181 |
|
182 |
private int readInt() throws IOException { |
183 |
int x1 = rb(); |
184 |
int x2 = rb(); |
185 |
int x3 = rb(); |
186 |
int x4 = rb(); |
187 |
int i = (x1 << 24) + (x2 << 16) + (x3 << 8) + x4; |
188 |
if (DEBUG) System.err.println("readInt: " + i); |
189 |
return i; |
190 |
} |
191 |
|
192 |
private short readShort() throws IOException { |
193 |
int x1 = rb(); |
194 |
int x2 = rb(); |
195 |
short s = (short)((x1 << 8) + x2); |
196 |
//System.err.println("x1=" + hexify(x1) + " x2=" + hexify(x2) + " s=" + hexify(s)); |
197 |
//if (DEBUG) System.err.println("x1=" + x1 + " x2=" + x2 + " s=" + s); |
198 |
if (DEBUG) System.err.println("readShort: " + s); |
199 |
return s; |
200 |
} |
201 |
|
202 |
private byte readByte() throws IOException { |
203 |
return (byte)rb(); |
204 |
} |
205 |
|
206 |
private String readUTF() throws IOException { |
207 |
short len = readShort(); |
208 |
if (len < 0) throw new NotImplementedException();//XXX |
209 |
byte[] buf = new byte[len]; |
210 |
for (int i = 0; i < len; i++) { |
211 |
buf[i] = readByte(); |
212 |
} |
213 |
String s = new String(buf, "UTF-8"); |
214 |
if (DEBUG) System.err.println("readUTF: " + s); |
215 |
return s; |
216 |
} |
217 |
|
218 |
private String readLongUTF() throws IOException { |
219 |
long len = readLong(); |
220 |
if (len < 0) throw new NotImplementedException();//XXX |
221 |
if (len > Integer.MAX_VALUE) throw new NotImplementedException();// XXX |
222 |
int ilen = (int)len; |
223 |
byte[] buf = new byte[ilen]; |
224 |
for (int i = 0; i < ilen; i++) { |
225 |
buf[i] = readByte(); |
226 |
} |
227 |
String s = new String(buf, "UTF-8"); |
228 |
if (DEBUG) System.err.println("readUTF: " + s); |
229 |
return s; |
230 |
} |
231 |
|
232 |
// See "Rules of the Grammar" in Java Object Serialization Specification |
233 |
// for explanation of all these objects. |
234 |
|
235 |
public static final class Stream /*extends Thing*/ { |
236 |
public short magic; |
237 |
public short version; |
238 |
public List contents; // List<Object> |
239 |
public String toString() { |
240 |
return "Stream[contents=" + contents + "]"; // NOI18N |
241 |
} |
242 |
} |
243 |
|
244 |
public static final Object NULL = "null"; // NOI18N |
245 |
|
246 |
private Object readContent() throws IOException { |
247 |
byte tc = readByte(); |
248 |
switch (tc) { |
249 |
case TC_OBJECT: |
250 |
return readNewObject(); |
251 |
case TC_CLASS: |
252 |
return readNewClass(); |
253 |
case TC_ARRAY: |
254 |
return readNewArray(); |
255 |
case TC_CLASSDESC: |
256 |
return readNewClassDesc(); |
257 |
case TC_PROXYCLASSDESC: |
258 |
// XXX too complicated: |
259 |
throw new NotImplementedException("TC_PROXYCLASSDESC"); |
260 |
//return readNewProxyClassDesc(); |
261 |
case TC_STRING: |
262 |
return readNewString(); |
263 |
case TC_LONGSTRING: |
264 |
// XXX later |
265 |
throw new NotImplementedException("TC_LONGSTRING"); |
266 |
//return readNewLongString(); |
267 |
case TC_REFERENCE: |
268 |
return readReference(); |
269 |
case TC_NULL: |
270 |
return NULL; |
271 |
case TC_EXCEPTION: |
272 |
// XXX what is this?? |
273 |
throw new NotImplementedException("TC_EXCEPTION"); |
274 |
case TC_RESET: |
275 |
// XXX what is this?? |
276 |
throw new NotImplementedException("TC_RESET"); |
277 |
case TC_BLOCKDATA: |
278 |
return readBlockData(); |
279 |
case TC_BLOCKDATALONG: |
280 |
return readBlockDataLong(); |
281 |
default: |
282 |
throw new CorruptException("Unknown typecode: " + hexify(tc)); // NOI18N |
283 |
} |
284 |
} |
285 |
|
286 |
public static final class ObjectWrapper { |
287 |
public ClassDesc classdesc; |
288 |
public List data; // List<NameValue|Object> |
289 |
public String toString() { |
290 |
return "Object[class=" + classdesc.name + ",data=" + data + "]"; // NOI18N |
291 |
} |
292 |
} |
293 |
|
294 |
public static final class NameValue { |
295 |
public NameValue(FieldDesc name, Object value) { |
296 |
this.name = name; |
297 |
this.value = value; |
298 |
} |
299 |
public final FieldDesc name; |
300 |
public final Object value; |
301 |
public String toString() { |
302 |
return name.toString() + "=" + value.toString(); // NOI18N |
303 |
} |
304 |
} |
305 |
|
306 |
public static final class ClassDesc { |
307 |
public String name; |
308 |
public long svuid; |
309 |
public boolean writeMethod; |
310 |
public boolean blockData; |
311 |
public boolean serializable; |
312 |
public boolean externalizable; |
313 |
public List fields; // List<FieldDesc> |
314 |
public List annotation; // List<Object> |
315 |
public ClassDesc superclass; |
316 |
public String toString() { |
317 |
return "Class[name=" + name + "]"; // NOI18N |
318 |
} |
319 |
} |
320 |
|
321 |
private ObjectWrapper readNewObject() throws IOException { |
322 |
ObjectWrapper ow = new ObjectWrapper(); |
323 |
ow.classdesc = readClassDesc(); |
324 |
makeRef(ow); |
325 |
ow.data = new ArrayList (10); |
326 |
LinkedList hier = new LinkedList(); |
327 |
for (ClassDesc cd = ow.classdesc; cd != null; cd = cd.superclass) { |
328 |
hier.addFirst(cd); |
329 |
} |
330 |
Iterator it = hier.iterator(); |
331 |
while (it.hasNext()) { |
332 |
ClassDesc cd = (ClassDesc)it.next(); |
333 |
if (cd.serializable) { |
334 |
ow.data.addAll(readNoWrClass(cd)); |
335 |
if (cd.writeMethod) { |
336 |
ow.data.addAll(readContents()); |
337 |
} |
338 |
} else { |
339 |
if (cd.blockData) { |
340 |
ow.data.addAll(readContents()); |
341 |
} else { |
342 |
// Old externalization. If this is not object content, |
343 |
// the stream could now become corrupted. Oh well. |
344 |
ow.data.add(readContent()); |
345 |
} |
346 |
} |
347 |
} |
348 |
if (DEBUG) System.err.println("readNewObject: " + ow); |
349 |
return ow; |
350 |
} |
351 |
|
352 |
private ClassDesc readClassDesc() throws IOException { |
353 |
Object o = readContent(); |
354 |
if (o instanceof ClassDesc) { |
355 |
return (ClassDesc)o; |
356 |
} else if (o == NULL) { |
357 |
return null; |
358 |
} else { |
359 |
throw new CorruptException("Expected class desc, got: " + o); // NOI18N |
360 |
} |
361 |
} |
362 |
|
363 |
private ClassDesc readNewClass() throws IOException { |
364 |
ClassDesc cd = readClassDesc(); |
365 |
makeRef(cd); |
366 |
return cd; |
367 |
} |
368 |
|
369 |
private ClassDesc readNewClassDesc() throws IOException { |
370 |
ClassDesc cd = new ClassDesc(); |
371 |
cd.name = readUTF(); |
372 |
if (! cd.name.startsWith("[") && // NOI18N |
373 |
! (cd.name.length() == 1 && "BSIJFDCZ".indexOf(cd.name) != -1) && // NOI18N |
374 |
! cd.name.endsWith(";")) { // NOI18N |
375 |
// Canonicalize. It seems class names read normally need this; those |
376 |
// read as part of an array do not. ?? |
377 |
cd.name = "L" + cd.name + ";"; // NOI18N |
378 |
} |
379 |
cd.svuid = readLong(); |
380 |
makeRef(cd); |
381 |
byte cdf = readByte(); |
382 |
cd.writeMethod = (cdf & SC_WRITE_METHOD) != 0; |
383 |
cd.blockData = (cdf & SC_BLOCK_DATA) != 0; |
384 |
cd.serializable = (cdf & SC_SERIALIZABLE) != 0; |
385 |
cd.externalizable = (cdf & SC_EXTERNALIZABLE) != 0; |
386 |
short count = readShort(); |
387 |
cd.fields = new ArrayList(count); |
388 |
for (int i = 0; i < count; i++) { |
389 |
cd.fields.add(readFieldDesc()); |
390 |
} |
391 |
cd.annotation = readContents(); |
392 |
cd.superclass = readClassDesc(); |
393 |
if (DEBUG) System.err.println("readNewClassDesc: " + cd); |
394 |
return cd; |
395 |
} |
396 |
|
397 |
public static class FieldDesc { |
398 |
public String name; |
399 |
public String type; |
400 |
public String toString() { |
401 |
return "Field[name=" + name + ",type=" + type + "]"; // NOI18N |
402 |
} |
403 |
} |
404 |
public static final class ObjFieldDesc extends FieldDesc { |
405 |
public boolean array; |
406 |
public String toString() { |
407 |
return "Field[name=" + name + ",type=" + type + (array ? "[]" : "") + "]"; // NOI18N |
408 |
} |
409 |
} |
410 |
|
411 |
private FieldDesc readFieldDesc() throws IOException { |
412 |
char tc = (char)readByte(); |
413 |
FieldDesc fd; |
414 |
switch (tc) { |
415 |
case 'B': |
416 |
case 'C': |
417 |
case 'D': |
418 |
case 'F': |
419 |
case 'I': |
420 |
case 'J': |
421 |
case 'S': |
422 |
case 'Z': |
423 |
fd = new FieldDesc(); |
424 |
fd.type = new String(new char[] {tc}); |
425 |
break; |
426 |
case '[': |
427 |
fd = new ObjFieldDesc(); |
428 |
((ObjFieldDesc)fd).array = true; |
429 |
break; |
430 |
case 'L': |
431 |
fd = new ObjFieldDesc(); |
432 |
((ObjFieldDesc)fd).array = false; |
433 |
break; |
434 |
default: |
435 |
throw new CorruptException("Strange field type: " + tc); // NOI18N |
436 |
} |
437 |
fd.name = readUTF(); |
438 |
if (fd instanceof ObjFieldDesc) { |
439 |
String clazz = (String)readContent(); |
440 |
/* |
441 |
if (((ObjFieldDesc)fd).array) { |
442 |
if (! clazz.startsWith("[")) throw new CorruptException("Field type: " + clazz); // NOI18N |
443 |
clazz = clazz.substring(1, clazz.length()); |
444 |
} |
445 |
if (! (clazz.startsWith("L") && clazz.endsWith(";"))) throw new CorruptException("Field type: " + clazz); // NOI18N |
446 |
fd.type = clazz.substring(1, clazz.length() - 1).replace('/', '.'); // NOI18N |
447 |
*/ |
448 |
fd.type = clazz; |
449 |
} |
450 |
if (DEBUG) System.err.println("readFieldDesc: " + fd); |
451 |
return fd; |
452 |
} |
453 |
|
454 |
private List readContents() throws IOException { |
455 |
List l = new ArrayList(10); |
456 |
while (peek() != TC_ENDBLOCKDATA) { |
457 |
l.add(readContent()); |
458 |
} |
459 |
if (readByte() != TC_ENDBLOCKDATA) throw new IllegalStateException(); |
460 |
if (DEBUG) System.err.println("readContents: " + l); |
461 |
return l; |
462 |
} |
463 |
|
464 |
public static final class ArrayWrapper { |
465 |
public ClassDesc classdesc; |
466 |
public List values; |
467 |
public String toString() { |
468 |
return classdesc.name + "{" + values + "}"; // NOI18N |
469 |
} |
470 |
} |
471 |
|
472 |
private ArrayWrapper readNewArray() throws IOException { |
473 |
ArrayWrapper aw = new ArrayWrapper(); |
474 |
aw.classdesc = readClassDesc(); |
475 |
makeRef(aw); |
476 |
int size = readInt(); |
477 |
if (size < 0) throw new NotImplementedException(); |
478 |
aw.values = new ArrayList(size); |
479 |
for (int i = 0; i < size; i++) { |
480 |
if (aw.classdesc.name.equals("[B")) { // NOI18N |
481 |
aw.values.add(new Byte(readByte())); |
482 |
} else if (aw.classdesc.name.equals("[S")) { // NOI18N |
483 |
aw.values.add(new Short(readShort())); |
484 |
} else if (aw.classdesc.name.equals("[I")) { // NOI18N |
485 |
aw.values.add(new Integer(readInt())); |
486 |
} else if (aw.classdesc.name.equals("[J")) { // NOI18N |
487 |
aw.values.add(new Long(readLong())); |
488 |
} else if (aw.classdesc.name.equals("[F")) { // NOI18N |
489 |
aw.values.add(new Float(Float.intBitsToFloat(readInt()))); |
490 |
} else if (aw.classdesc.name.equals("[D")) { // NOI18N |
491 |
aw.values.add(new Double(Double.longBitsToDouble(readLong()))); |
492 |
} else if (aw.classdesc.name.equals("[C")) { // NOI18N |
493 |
aw.values.add(new Character((char)readShort())); |
494 |
} else if (aw.classdesc.name.equals("[Z")) { // NOI18N |
495 |
aw.values.add(readByte() == 1 ? Boolean.TRUE : Boolean.FALSE); |
496 |
} else { |
497 |
aw.values.add(readContent()); |
498 |
} |
499 |
} |
500 |
if (DEBUG) System.err.println("readNewArray: " + aw); |
501 |
return aw; |
502 |
} |
503 |
|
504 |
private String readNewString() throws IOException { |
505 |
String s = readUTF(); |
506 |
makeRef(s); |
507 |
return s; |
508 |
} |
509 |
|
510 |
private Object readReference() throws IOException { |
511 |
int i = readInt(); |
512 |
Object r = getRef(i); |
513 |
if (DEBUG) System.err.println("readReference: " + r); |
514 |
return r; |
515 |
} |
516 |
|
517 |
private byte[] readBlockData() throws IOException { |
518 |
int size = readByte(); |
519 |
if (size < 0) size += 256; |
520 |
byte[] b = new byte[size]; |
521 |
for (int i = 0; i < size; i++) { |
522 |
b[i] = readByte(); |
523 |
} |
524 |
if (DEBUG) System.err.println("readBlockData: " + size + " bytes"); |
525 |
return b; |
526 |
} |
527 |
|
528 |
private byte[] readBlockDataLong() throws IOException { |
529 |
int size = readInt(); |
530 |
if (size < 0) throw new NotImplementedException(); |
531 |
byte[] b = new byte[size]; |
532 |
for (int i = 0; i < size; i++) { |
533 |
b[i] = readByte(); |
534 |
} |
535 |
if (DEBUG) System.err.println("readBlockDataLong: " + size + " bytes"); |
536 |
return b; |
537 |
} |
538 |
|
539 |
private List/*<NameValue>*/ readNoWrClass(ClassDesc cd) throws IOException { |
540 |
List fields = cd.fields; |
541 |
List values = new ArrayList(fields.size()); |
542 |
for (int i = 0; i < fields.size(); i++) { |
543 |
FieldDesc fd = (FieldDesc)fields.get(i); |
544 |
if (fd.type.equals("B")) { // NOI18N |
545 |
values.add(new NameValue(fd, new Byte(readByte()))); |
546 |
} else if (fd.type.equals("S")) { // NOI18N |
547 |
values.add(new NameValue(fd, new Short(readShort()))); |
548 |
} else if (fd.type.equals("I")) { // NOI18N |
549 |
values.add(new NameValue(fd, new Integer(readInt()))); |
550 |
} else if (fd.type.equals("J")) { // NOI18N |
551 |
values.add(new NameValue(fd, new Long(readLong()))); |
552 |
} else if (fd.type.equals("F")) { // NOI18N |
553 |
values.add(new NameValue(fd, new Float(Float.intBitsToFloat(readInt())))); |
554 |
} else if (fd.type.equals("D")) { // NOI18N |
555 |
values.add(new NameValue(fd, new Double(Double.longBitsToDouble(readLong())))); |
556 |
} else if (fd.type.equals("C")) { // NOI18N |
557 |
values.add(new NameValue(fd, new Character((char)readShort()))); |
558 |
} else if (fd.type.equals("Z")) { // NOI18N |
559 |
values.add(new NameValue(fd, readByte() == 1 ? Boolean.TRUE : Boolean.FALSE)); |
560 |
} else { |
561 |
values.add(new NameValue(fd, readContent())); |
562 |
} |
563 |
} |
564 |
if (DEBUG) System.err.println("readNoWrClass: " + values); |
565 |
return values; |
566 |
} |
567 |
|
568 |
} |