KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JSX > XMLDeserialize


1 /** (c) Brendan Macmillan 2001, licensed under GNU's GPL 2
2         (If your project is not GPL, then a fee is due - see license on website)
3     Website: http://www.csse.monash.edu.au/~bren/JSX
4     Website: http://freshmeat.net/projects/jsx
5     List (can read without subscribing, need to join to post):
6       http://groups.yahoo.com/group/JSX-ideas/messages
7         Commercial licensing enquiries only:
8             bren@mail.csse.monash.edu.au */

9
10 /** ===========================================================================
11   * XMLDeserialize (JSX0.8.21).
12     * ==============
13     *
14     * Creates the object described by an XML document.
15
16     * Note that a basic limitation of Java is that an object cannot be created
17     * unless the JVM has access to the class file defining it. That is, Java
18     * cannot *define* classes at runtime; it can only instantiate pre-defined
19     * classes.
20     * However, a class loader could be written to "read" in class files which
21     * do not actually exist; thus giving Java the ability to define new
22     * classes dynamically. See also JavaClass (link on JSX homepage).
23
24     * Please email me: for help, to suggest features, or to report bugs.
25         bren@csse.monash.edu.au <Brendan Macmillan>
26
27     * ISSUES:
28     * What about: subclassing wrappers or containers?
29     * What if root object is a wrapper? Or a field is a wrapper?
30     
31     **/

32
33
34 package JSX;
35 import java.lang.reflect.*;
36 import java.io.*;
37 import java.util.*; //for Hashtable
38

39 class XMLDeserialize {
40
41 /** --------------------------------------------------------------------------
42     * debug constants
43     * ---------------
44     **/

45     private static final boolean VERSION_DEBUG = false;
46     //private static final boolean VERSION_DEBUG = true;
47

48     private static final boolean DEBUG_CUSTOM = false;
49     //private static final boolean DEBUG_CUSTOM = true;
50

51     private static final boolean DEBUG = false;
52     //private static final boolean DEBUG = true;
53

54     private static final boolean TESTCIRC = false;
55     //private static final boolean TESTCIRC = true;
56

57     private static final boolean ALIASDEBUG = false;
58     //private static final boolean ALIASDEBUG = true;
59

60     private static final boolean INTERNAL_DEBUG = false;
61     //private static final boolean INTERNAL_DEBUG = true;
62

63     private static final boolean DEBUG_CLASS = false;
64     //private static final boolean DEBUG_CLASS = true;
65

66     private static final boolean FINAL_DEBUG = false;
67     //private static final boolean FINAL_DEBUG = true;
68

69     static final boolean GETFIELD_DEBUG = false;
70     //static final boolean GETFIELD_DEBUG = true;
71

72     static final boolean READRESOLVE_DEBUG = false;
73     //static final boolean READRESOLVE_DEBUG = true;
74

75     static final boolean IBM_DEBUG = false;
76     //static final boolean IBM_DEBUG = true;
77

78     static final boolean KELLY_DEBUG = false;
79     //static final boolean KELLY_DEBUG = true;
80

81     private static final boolean SUPER_DEBUG = false;
82     //private static final boolean SUPER_DEBUG = true;
83

84     private static final boolean NESTED_DEBUG = false; //part of super
85
//private static final boolean NESTED_DEBUG = true; //part of super
86

87     private static final boolean NESTEDREADOBJECT_DEBUG = false; //super
88
//private static final boolean NESTEDREADOBJECT_DEBUG = true; //super
89

90     private static final boolean ADDATTR_NULL_DEBUG = false;
91     //private static final boolean ADDATTR_NULL_DEBUG = true;
92

93     private static final boolean KELLYSUPER_DEBUG = false;
94     //private static final boolean KELLYSUPER_DEBUG = true;
95

96     private static final boolean NOV_DEBUG = false;
97     //private static final boolean NOV_DEBUG = true;
98

99     private static final boolean CFG_DEBUG = false;
100     //private static final boolean CFG_DEBUG = true;
101

102     private static final boolean MIKE_DEBUG = false;
103     //private static final boolean MIKE_DEBUG = true;
104

105 /** --------------------------------------------------------------------------
106     * Deserialization fields
107     * ----------------------
108     * NB: there are others scattered through the code, proximate to their use.
109     **/

110     private ParserXML p;
111     private static JSX.magic.MagicClassI magic = JSX.magic.MagicClassFactory.newInstance();
112
113 /** --------------------------------------------------------------------------
114     * alias fields
115     * ------------
116     * Consider: extract as an object. It has state, init, and API.
117     * Consider: change name from 'aliasHash' to 'aliasTable'.
118     **/

119     private int aliasSerialNumber; //NB: *not* static (cs serialization)
120
private Hashtable aliasHash = new Hashtable();
121         //private static final int ALIAS_INIT = -1;//so pre-increment in serializeObject
122
//get INIT directly from XMLSerialize, so guaranteed to be synchronized.
123

124
125     
126     /**--------------------------------------------------------------------------
127         * main
128         * ----
129         * Version information only; for test code, see JSXTest.java
130         **/

131     public static void main(String JavaDoc args[]) {
132         System.err.println(XMLSerialize.VERSION);
133     }
134
135
136
137 /**==========================================================================
138     * CONSTRUCTORS
139     * ============
140     **/

141     /**--------------------------------------------------------------------------
142         * XMLDeserialize(in) - core constructor
143         * --------------
144         * Core constructor (others call it)
145         * init alias *only* here, or confuses re-enterant code (internal ser)
146         * ALT: we could have an explicit null, instead of with/without CoNfig.
147         **/

148     XMLDeserialize(Reader in) {
149         reset(); //whenever create, always reset alias table.
150
p = new ParserXML(in);
151     }
152
153     private Config cfg = new Config(); //so never null!
154
XMLDeserialize(Reader in, Config cfg) {
155         this.cfg = cfg;
156         reset(); //whenever create, always reset alias table.
157
p = new ParserXML(in);
158     }
159
160     //--------------------------------------------------------------------------
161
/** XMLDeserialize()
162         * --------------
163         * Default to System.in
164         **/

165     XMLDeserialize() { //default to System.in
166
this(new InputStreamReader(System.in));
167     }
168
169
170
171 /**==========================================================================
172     * METHODS
173     * =======
174     **/

175
176         //use explicit subclass, so we can access our "putObj()" method
177
GetFieldImpl currentGetField = new GetFieldImpl(); //NESTED FIELDS - needed?
178
java.io.ObjectInputStream.GetField getCurrentGetField() {
179         currentGetField.setup(); // need to init it with current class
180
return currentGetField;
181     }
182     
183         //this is set, ready for ObjIn to grab it
184
// *non* static, so can access current class from getFieldClassStack
185
final class GetFieldImpl extends java.io.ObjectInputStream.GetField {
186         //interal - *not* part of public API
187
HashMap objMap = new HashMap(); //need to distinguish obj and prim?
188
Class JavaDoc c;
189         Map fmap;
190         
191         public String JavaDoc toString() {
192             return "GetFieldImpl state: "+objMap;
193         }
194         GetFieldImpl() {
195         }
196
197         void setup() {
198                 // (1). we need a reference to the parent - so non-static
199
c = (Class JavaDoc) getFieldClassStack.peek(); // clearer here than in decl
200
// (2). it needs to know the class, so we can get it
201
fmap = SerialPF.getFieldNameType(c);
202         }
203
204         void putObj(String JavaDoc name, Object JavaDoc value) {
205             if (GETFIELD_DEBUG) System.err.println("putObj("+name+", "+value+");");
206             objMap.put(name, value);
207         }
208         //this seems to put primitive values in as Strings, and parse later.
209

210         private boolean isSF(String JavaDoc name) {
211             if (fmap.containsKey(name))
212                 return true;
213             else
214                 return false; // verbose but clear
215
}
216
217         public ObjectStreamClass getObjectStreamClass() {
218             return null; //DEFER: this will be tricky to implement... or a
219
//convenience, to encourage me to implement it closer to their way?
220
}
221         public boolean defaulted(String JavaDoc name)
222             throws IOException, IllegalArgumentException JavaDoc {
223             return !objMap.containsKey(name); //if containsKey, then not default...
224
}
225         public boolean get(String JavaDoc name, boolean defvalue)
226                 throws IOException, IllegalArgumentException JavaDoc {
227             if (objMap.containsKey(name))
228                 return ParseUtilities.parseBoolean((String JavaDoc)objMap.get(name)); //how to tell if default?
229
else if (isSF(name))
230                 return defvalue;
231             else
232                 throw new IllegalArgumentException JavaDoc("no such field");
233         }
234         public char get(String JavaDoc name, char defvalue)
235                 throws IOException, IllegalArgumentException JavaDoc {
236             if (objMap.containsKey(name))
237                 return ((String JavaDoc)objMap.get(name)).charAt(0);
238             else if (isSF(name))
239                 return defvalue;
240             else
241                 throw new IllegalArgumentException JavaDoc("no such field");
242         }
243         public byte get(String JavaDoc name, byte defvalue)
244                 throws IOException, IllegalArgumentException JavaDoc {
245             if (objMap.containsKey(name))
246                 return Byte.parseByte((String JavaDoc)objMap.get(name));
247             else if (isSF(name))
248                 return defvalue;
249             else
250                 throw new IllegalArgumentException JavaDoc("no such field");
251         }
252         public short get(String JavaDoc name, short defvalue)
253                 throws IOException, IllegalArgumentException JavaDoc {
254             if (objMap.containsKey(name))
255                 return Short.parseShort((String JavaDoc)objMap.get(name));
256             else if (isSF(name))
257                 return defvalue;
258             else
259                 throw new IllegalArgumentException JavaDoc("no such field");
260         }
261         public int get(String JavaDoc name, int defvalue)
262                 throws IOException, IllegalArgumentException JavaDoc {
263             if (objMap.containsKey(name))
264                 return Integer.parseInt((String JavaDoc)objMap.get(name));
265             else if (isSF(name))
266                 return defvalue;
267             else
268                 throw new IllegalArgumentException JavaDoc("no such field");
269         }
270         public long get(String JavaDoc name, long defvalue)
271                 throws IOException, IllegalArgumentException JavaDoc {
272             if (objMap.containsKey(name))
273                 return Long.parseLong((String JavaDoc)objMap.get(name));
274             else if (isSF(name))
275                 return defvalue;
276             else
277                 throw new IllegalArgumentException JavaDoc("no such field");
278         }
279         public float get(String JavaDoc name, float defvalue)
280                 throws IOException, IllegalArgumentException JavaDoc {
281             if (objMap.containsKey(name))
282                 return ParseUtilities.parseFloat((String JavaDoc)objMap.get(name));
283             else if (isSF(name))
284                 return defvalue;
285             else
286                 throw new IllegalArgumentException JavaDoc("no such field");
287         }
288         public double get(String JavaDoc name, double defvalue)
289                 throws IOException, IllegalArgumentException JavaDoc {
290             if (objMap.containsKey(name))
291                 return ParseUtilities.parseDouble((String JavaDoc)objMap.get(name));
292             else if (isSF(name))
293                 return defvalue;
294             else
295                 throw new IllegalArgumentException JavaDoc("no such field");
296         }
297         public Object JavaDoc get(String JavaDoc name, Object JavaDoc defvalue)
298                 throws IOException, IllegalArgumentException JavaDoc {
299             if (objMap.containsKey(name))
300                 return objMap.get(name);
301             else if (isSF(name))
302                 return defvalue;
303             else
304                 throw new IllegalArgumentException JavaDoc("no such field");
305         }
306     }
307
308
309
310     int invocationCount = 0;
311     //--------------------------------------------------------------------------
312
/** deserialize()
313         * -----------
314         * Entry point.
315         * NB: invocationCount distinguisges between first time callers, and
316         * calls from internal serialization. This allows both old and new
317         * API's to work.
318         **/

319     String JavaDoc jsxVersion; //what will happen to this information?
320
//After decoding information, need to interpret it somewhere. Where?
321
boolean superVersion = false; //default (for now)
322

323 /*
324     class Header {
325         public Header(String target) { this.target = target; }
326         final String target;
327         final String encoding;
328         final String version; //dangerous? if move to 1.0.1? Eg for XML, it is
329             //"1.0"... tests for exact equivalence? Best to use Strings..
330     }
331     //Header xmlHeader;
332     //Header jsxHeader;
333 */

334     Object JavaDoc deserialize() throws ParserXML.ExceptionXML,
335                                            ClassNotFoundException JavaDoc,
336                                            //NoSuchFieldException,
337
IOException {
338                                      //Exception {
339
if (MIKE_DEBUG) System.err.println("LOGGING: entering *deserialize* of the Mike Special");
340         if (INTERNAL_DEBUG) System.err.println("RETRIEVE optional data object: "+objStoreIndex);
341         if (invocationCount==0) {
342             reset();
343             invocationCount++;
344             ParserXML.Tag tag = p.readTag();
345             if (tag==null) throw new EOFException("No XML found in input"); //no more objects left
346
while (tag.isPI) { //many PI's to process (eg ?xml, ?jsx)
347
//Polymorphic information storage:
348
// Header currentHeader = new Header(tag.name);
349
// if (tag.name.equals(XMLSerialize.JSX_HEADER_TARGET)) {//"factory if"
350
// jsxHeader = currentHeader; //set this field to it.
351
// }
352
// else if (tag.name.equals(XMLSerialize.XML_HEADER_TARGET)) {
353
// xmlHeader = currentHeader; //set this field to it.
354
// }
355
// else { //unrecognized target: info is lost.
356
// }
357
//
358
if (VERSION_DEBUG) System.err.println("start PI: "+ tag);
359                 ParserXML.Attr attr;
360                 while ( !(attr=p.readAttr()).isEndPI) { //attrs (eg version, encoding)
361
if (attr.isEnd)
362                         throw new IOException("PI should end with '?>', not '/>'");
363                     if (attr.name.equals("version")) { //this is kinda cool ;-0 oh yeah
364
if (VERSION_DEBUG) System.err.println("\tversion: "+attr.value);
365                         //(1). set version
366
// currentHeader.version = attr.value;
367
if (tag.name.equals(XMLSerialize.JSX_HEADER_TARGET)) {
368                             jsxVersion = attr.value;
369                             if (jsxVersion.equals("1")) {
370                                 superVersion = false;
371                             } else if (jsxVersion.equals("2")) {
372                                 superVersion = true;
373                             } else {
374                                 throw new IOException("Unknown jsx version=\""+jsxVersion+"\". You need to upgrade to read this later version");
375                             }
376                         }
377                     } else if (attr.name.equals("encoding")) {
378                         if (VERSION_DEBUG) System.err.println("\tencoding: "+attr.value);
379                         //(2). set encoding
380
// currentHeader.encoding = attr.value;
381
} else {
382                         if (VERSION_DEBUG) System.err.println("\tunknown attr: "+attr+" in PI: "+tag);
383                         //ignore unknown options.
384
}
385                 }
386                 if (VERSION_DEBUG) System.err.println("end PI: ?>");
387                 tag = p.readTag(); //set up for root (or another PI)
388
//NB: error if null (root *must* follow PI)
389
}
390         //Version info is needed where?
391
//(1). Pass it back to the caller? (ie ObjIn)
392
// - perhaps easiest to register a call-back, listener: std architecture
393
//(2). Set it directly with the registered back-reference?
394
//(3). Set it directly with a setVersion(version) on the back-reference?
395
//NOTE: No point planning too much. Wait for JAXP - just do it.
396
//(4). We could send it back to ObjIn, along with ParserXML p, which
397
//could then easily be plugged into another XMLDeserialize. Note that
398
//this won't help for version changes to the *parser* - but that should
399
//be OK.
400
//(5). Perhaps the key thing is to swap streams with JAXP... actually,
401
//that will be the one reading the stream, when it finds it needs to
402
//swap. At that point, easiest might be to reset the stream to the
403
//start, and start all over again.
404

405             if (!tag.start)
406                 throw new ParserXML.ExceptionXML("Expected start tag, got \"</" +tag.name+ ">\"");
407             //Object o = createObject(tag.name, null); //no parent
408
Object JavaDoc o = createObject(tag, null); //no parent //10 Apr 2001
409
if (TESTCIRC)
410                 System.err.println("Count was: " + (aliasSerialNumber-XMLSerialize.ALIAS_INIT));
411                 if (ALIASDEBUG) System.err.println(aliasHash);
412             invocationCount--;
413             return o; //separate statement into 2 lines so we can print the count
414
}
415         else {//ie. if from a customization
416
if (superVersion) {
417                 defROSubstitute(); //eat any header stuff, if defRO not called
418
ParserXML.Tag tag = p.readTag();
419                 if (NOV_DEBUG)
420                     System.err.println("readObject: just read a "+ tag);
421                 return createObject(tag, null);
422             }
423             else
424                 return getObj();
425         }
426     }
427
428     void defROSubstitute() throws IOException {
429         try {
430             if (NOV_DEBUG) System.err.println("entering defROSubstitute:");
431             if (defROCalledStack.get()==false) {
432                 if (NOV_DEBUG) System.err.println("\tfor first time");
433                 defROCalledStack.set(true);
434                 defaultReadObject_ver2(); //to just eat!
435
/*
436                     //we don't actually use "Boolean true" - but nice description
437             //need to read the header stuff that defRO would normally read:
438                 ParserXML.Attr[] addAttrTmp = defaultReadObject_attrs_in;
439                         //stack unnec, as use immediately
440                 addPrim(addAttrTmp, null, false); //should not return a name
441                 ParserXML.Tag tag = p.readTag(); //should be <opt-data> section
442             checkOpenTag(XMLSerialize.OPT_DATA_TOKEN, tag);
443                 System.err.println("reading <opt-data> from within readObject()");
444                 addPrim(addName(), null, false); //just clear end of opt tag
445                 defROStack.set(defROStack.size()-1, tag); //mimic defRO
446     */

447             }
448         } catch (ClassNotFoundException JavaDoc e) {
449             System.err.println("unexpected default fields - that's OK in itself,"
450                 + "the problem is: "+e);
451             e.printStackTrace();
452         }
453     }
454
455     //NB: same as XMLSerialize
456
void reset() {
457         if (invocationCount!=0)
458             throw new Error JavaDoc("Deserialize: Attempt to reset alias table mid-way.");
459         initAlias();
460     }
461     void close() { p.close(); }
462
463
464
465     /** =========================================================================
466         * alias nascent-object
467         * --------------------
468         * NB: init, fields and API: extract into a class.
469         * Should move this into a separate, pluggable class, so that
470         * (1). the implementation can be changed easily
471         * (2). a co-developer can work on it in isolation
472         * (3). it can be recompiled separately - faster recompilation
473         * (4). it can be reused elsewhere
474         * (5). a different implementation can be plugged in (including a subclass)
475         *
476         * All are aspects of "divide and conquer" - the danger is to divide
477         * overzealously, in an inappropriate way, which counterproductively
478         * increases complexity. However, when a division is clear, it really
479         * should be done.
480         * The reluctance to do it is that the change may introduce bugs. It is
481         * work, and uncertainty, and fear of opening a can of worms - better to
482         * let sleeping dogs lie.
483         * And yet, because it has been used as it were a separate object, it
484         * should not be too hard. And if there are ripples in the force, it is
485         * good to find them and tame them, rather than fear them.
486         *
487         * It would also be good to have an "alias" API - javadocs could produce
488         * this automatically. Makes it easier to work with. Looks more
489         * professional (to employers, licensees), and more welcoming to
490         * co-developers. Reduces barriers to contribution. Increases chances
491         * of patches being offered.
492         **/

493     private void initAlias() {
494         aliasSerialNumber = XMLSerialize.ALIAS_INIT;
495         aliasHash.clear();
496     }
497     /**--------------------------------------------------------------------------
498         * put Alias
499         * --------
500         * inline = !serialized;
501         * if (inline)
502         * //add to hash
503         * else
504         * //write as alias
505         * return (inline); //to tell caller to serialize it inline!
506         * //else, caller leaves the ref writing up to this routine
507         **/

508     //private static boolean putAlias(Object o, String fieldName, boolean isStringAttr, String indent) {
509

510     /**--------------------------------------------------------------------------
511         * getAlias
512         * --------
513         * Much of this is duplicated code from addAttr(). This should be
514         * rationalized and refactored at some point.
515         **/

516     boolean aliasNameMissing; //clumsy message passing
517
private NamedObject getAlias(ParserXML.Attr[] addAttrTmp, String JavaDoc flag, Object JavaDoc parent) throws
518             //NoSuchFieldException,
519
IOException {
520         aliasNameMissing = false;
521         ParserXML.Attr attr = addAttrTmp[0]; //now passed in above
522
String JavaDoc fieldName = null;
523         String JavaDoc aliasName = null;
524         if (flag.equals(XMLSerialize.ALIAS_TAG_TOKEN)) { //parse obj-name and name
525
//fieldName = p.getObjName(); //idea.
526
//while ( !(attr=p.readAttr()).isEnd)
527
if (!attr.isEnd) {
528                 do {
529                     if (attr.name.equals(XMLSerialize.NAME_TOKEN)) {
530                         if (fieldName!=null)
531                             throw new ParserXML.ExceptionXML("just one name field");
532                         fieldName = attr.value;
533                     }
534                     else if (attr.name.equals(XMLSerialize.ALIAS_ATTR_TOKEN)) {
535                         if (aliasName!=null)
536                             throw new ParserXML.ExceptionXML("just one alias name");
537                         aliasName = attr.value; //NB: won't flag duplicates
538
} else
539                             throw new ParserXML.ExceptionXML("unknown attribute: "+attr.name);
540                 } while (!(attr=p.readAttr()).isEnd);
541             }//if
542
if (!attr.emptyTag) { //if not the empty tag, read in the close tag
543
ParserXML.Tag closeTag = p.readTag();
544                 if (!closeTag.name.equals(XMLSerialize.ALIAS_TAG_TOKEN))
545                     throw new ParserXML.ExceptionXML(XMLSerialize.ALIAS_TAG_TOKEN, closeTag.name);
546             }
547             Object JavaDoc me = aliasHash.get(aliasName);
548             if (me==null) { //null is never a valid value - it means alias not found
549
throw new ParserXML.ExceptionXML("alias target '"+aliasName+"' not found");
550             }
551             if (parent!=null) { //ie, if not an orphan object (as in Vector, Array)
552
if (fieldName==null)
553                     aliasNameMissing = true; //need to defer it, so context is correct
554
else {
555                     //try { //was the "try" removed for catching; but forgetting putObj?
556
currentGetField.putObj(fieldName, me);
557                         Field f = getAllField(parent.getClass(), fieldName);
558                         magic.setFinal(f, parent, me);
559                         //f.set(parent, me);
560
//} catch (NoSuchFieldException e) {
561
//}
562
}
563             }
564                 //System.err.println("alias " +aliasName+ ":");
565
//System.err.println("me: " +me);
566
return new NamedObject(fieldName, me, !aliasNameMissing);
567         }
568         else
569             return null; //indicates that the tag was not an alias
570
}
571
572
573
574     /** ------------------------------------------------------------------------
575         * putAlias
576         * --------
577         * Change to return the alias - this should be a "back-compatible
578         * refactoring" for callers (tho all return statements need to be changed),
579         * and it allows it to be stored, and used to later update it (for
580         * readResolve()).
581         **/

582     private String JavaDoc putAlias(Object JavaDoc o) throws ParserXML.ExceptionXML {
583         if (o==null) return null; //a flag: null not a valid alias - but...
584
//better to throw an exception?
585
String JavaDoc alias;
586         if (cfg.aliasID) { //Config switch; sh be from version?
587
alias = p.getAlias().value; //if no config, defaults to old system
588
} else {
589             ++aliasSerialNumber; //pre-inc (use this syntax to highlight it)
590
alias = aliasSerialNumber+""; //commented out on 11Feb2002
591
try {
592                 p.getAlias();
593             } catch (ParserXML.ExceptionXML e) {} //ineff way to ignore exception
594
}
595         aliasHash.put(alias, o); //add (NB: inverse of serialize)
596
//put() returns the previous key - it *shouldn't* have had one (ie null)
597
//if (TESTCIRC) System.err.println("inline " +aliasSerialNumber+ ":");
598
return alias;
599     }
600 /** 11Feb200 ISSUE for aliasID: awkward to tell reader whether to ignore or
601     * I think it is safer to fail noisily; and it is up to the serializer to
602     * add in appropriate version information. If version says "aliasIDs used",
603     * any seen will signal an error (or alternatively be ignored). Conversely,
604     * if version says "no aliasIDs", then any found will signal an error.
605     *
606     * The present system, of a config flag to explicitly specify seems
607     * destined to confuse - aliasID are changed, but ignored!
608     * However, big problems can be avoided by making it default to ignoring
609     * them.
610     *
611     * I suggest this be orthogonal to the other changes - specifically, to
612     * super. Eg: aliasID="true". Then, we have aliasID="ignore", if we
613     * really want to.
614     * Other alternative is to have a special surrounding <jsx> tag - which is
615     * a complete change.
616     **/

617
618 /** Config: big confusion is that there are two defaults: one specified in
619     * Config itself, and one in the code, for when no config object given.
620     * I think it's better to supply a "vanilla" cfg by default, so that all
621     * the default behaviour is collected into that one spot.
622     **/

623
624
625 /* the follow looks like it came from the XMLSerialize version of putAlias
626         Object alias; //will be String, but save the conversion
627         if ( (alias = aliasHash.get(o))==null) {
628             ++aliasSerialNumber; //pre-inc (use this syntax to highlight it)
629             aliasHash.put(o, aliasSerialNumber+""); //add (convert to String)
630                 //System.err.println(indent +"inline " +aliasSerialNumber+ ":");
631             //Need to:
632             // indicate "inline" somehow.
633             // write as inline (same as now).
634             return true; //yes, should serialize the object inline
635         }
636         else
637         {
638             if (isStringAttr) {
639                 printAttr(fieldName + STRING_ALIAS_TOKEN, alias+"");
640             }
641             else {
642                     //System.err.println(indent+ "alias " +alias+ ":");
643                 printOpenTag(ALIAS_TAG_TOKEN, indent);
644                 if (fieldName!=null) printAttr(NAME_TOKEN, fieldName); //nice to reuse
645                 printAttr(ALIAS_ATTR_TOKEN, alias+"");
646                 out.println("/>"); //empty tag - under control of caller
647             }
648             return false; //no, don't serialize inline; we (or you?) write the alias
649         }
650 */

651
652     /** ------------------------------------------------------------------------
653         * updateAlias
654         * -----------
655         * for readResolve() - easy to implement, because Hashtable already
656         * allows for updates
657         **/

658     private String JavaDoc updateAlias(Object JavaDoc o, String JavaDoc alias) throws IOException {
659         if (o==null) return null; //a flag: null not a valid alias - but...
660
//better to throw an exception?
661
if (aliasHash.put(alias, o)==null) //add (NB: inverse of serialize)
662
throw new IOException("update called, but no existing alias");
663             //this returns the previous key - it *should* have had one.
664
return alias;
665     }
666
667
668     private ObjIn oisSubclass; //we can call protected, because same package
669
//the problem before was with ObjectInputStream - not same (JSX) package!
670
static final Class JavaDoc[] OIS_ARGS = {ObjectInputStream.class};
671     Object JavaDoc[] readObjectArglist;
672     java.util.Stack JavaDoc getFieldClassStack = new java.util.Stack JavaDoc();
673     /**--------------------------------------------------------------------------
674         * setArg()
675         * -------
676         * Consider: putting this in constructor; or caller in same package or same
677         * class.
678         **/

679     void setArg(Object JavaDoc[] argList) {
680         readObjectArglist = argList;
681         oisSubclass = (ObjIn) argList[0]; //kill two birds with one stone
682
}
683     Vector primStore = null; //Note: not yet set! Consider: best place to define?
684
int primStoreIndex = -1; //fail fast
685
/**--------------------------------------------------------------------------
686         * getPrimString()
687         * ---------------
688         * Called by readInt(), readFloat(), etc to get the String that was
689         * stored by addAttr
690         **/

691     String JavaDoc getPrimString() throws IOException { //note: we *know* what it is meant to be...
692
if (superVersion) { //what to do here?
693
defROSubstitute(); //eat any header stuff, if defRO not called
694
return deserializePrimOptData();
695         } else {
696             return (String JavaDoc) primStore.get(primStoreIndex++);
697         }
698     }
699
700     // <prim-opt-data valueOf="___"/> <---- only this version is expected
701
// <prim-opt-data valueOf="___"> <---- but we can handle this one too
702
// </prim-opt-data>
703
private String JavaDoc deserializePrimOptData() throws IOException {
704             // <prim-opt-data..
705
ParserXML.Tag tag = p.readTag();
706         checkOpenTag(XMLSerialize.OPT_PRIM_DATA_TOKEN, tag);
707             // ..valueOf="___"..
708
// ../>
709
ParserXML.Attr attr;
710         String JavaDoc value = null;
711         while (!(attr=p.readAttr()).isEnd) { //slurp the lot... so can ignore some
712
if (attr.name.equals(XMLSerialize.VALUE_TOKEN))
713                 value = attr.value;
714         }
715         if (value==null)
716             throw new ParserXML.ExceptionXML("Expected: "+XMLSerialize.VALUE_TOKEN);
717
718         if (!attr.emptyTag) //the final one
719
// </prim-opt-data>
720
checkCloseTag(XMLSerialize.OPT_PRIM_DATA_TOKEN, p.readTag()); //cool!
721
return value; //my work here is done...
722
}
723
724
725     Vector objStore = new Vector(); //Just to debug superVersion
726
//Consider: best place to define?
727
int objStoreIndex;
728     /**--------------------------------------------------------------------------
729         * getObj()
730         * --------
731         * Called by readObject(), when it is a from a customization (not the
732         * top-level call). The Vector is populated by createObject.
733         **/

734     Object JavaDoc getObj() {
735         return objStore.get(objStoreIndex++);
736             //seems to cause problems for "stub" classes...
737
}
738
739     /**--------------------------------------------------------------------------
740         * NamedObject - message object (to return more than one thing)
741         * -----------
742         * Defn: Wraps an "object" and its (field)"name".
743         * Used by: createObject. null, alias, array etc routines return it.
744         *
745         * Future: We return a message object. Object creation is inefficient.
746         * Reuse a static object - a global variable with cleaner semantics (usage).
747         * That is, set global/static object fields, and return it. Problem is that
748         * not restricted to this use - others could still access it as global -
749         * unless the orginator of it was in a separate class. An advantage of
750         * small modules.
751         **/

752     class NamedObject {
753         String JavaDoc name;
754         Object JavaDoc object; //better to not use abbreviations
755
boolean named;
756         NamedObject(String JavaDoc name, Object JavaDoc object, boolean named) {
757             this.name = name;
758             this.object = object;
759             this.named = named;
760         }
761     }
762
763
764     /**--------------------------------------------------------------------------
765         * createObject()
766         * ------------
767         * Returns the deserialized object (caller needs to cast at runtime)
768         * The object created is "me".
769         * USAGE: the open tag is assumed to have been just read
770         * LOGIC: A new object is instantiated: "me". Attr and elements are
771         * added to me. This new object is then added to its parent. NB: that
772         * the name for me (given by parents) is only discoverable by reading the
773         * attr; which is done in "addAttr" (second arg is boolean for isArray)
774         * NB: a null object may still have a name.
775         * NOTE: tag problem for lengths - not actually a field.
776         *
777         * Issues are:
778         * - custom data? Unnamed fields.
779         * - Strings - attr or tags? strings in Object fields.
780         *
781         * parentClassFrame
782         * ----------------
783         * Parent class frame is needed when we want to assign this tag to its
784         * parent - it's a namespacing thing. We can rely on the shadow namespacer
785         * as scaffolding, without actually using this one for now.
786         **/

787     //private Object createObject(ParserXML.Tag tag, Object parent, Class parentClassFrame)
788

789
790     private Object JavaDoc createObject(ParserXML.Tag tag, Object JavaDoc parent)
791             throws IOException,
792                          //NoSuchFieldException,
793
ClassNotFoundException JavaDoc {
794                    //Exception
795
ParserXML.Attr[] addAttrTmp = addName(); //Too early for special cases?
796
return createObject(tag, addAttrTmp, parent);
797         //return createObject(tag, addName(), parent); //consider this...
798
}
799
800     private Object JavaDoc createObject(ParserXML.Tag tag, ParserXML.Attr[] addAttrTmp, Object JavaDoc parent)
801             throws IOException, ClassNotFoundException JavaDoc {
802         String JavaDoc elementName = tag.name; //loosely coupled argument passing
803
if (KELLY_DEBUG)
804             System.err.println("adding "+elementName);
805
806         //Notes on "sub-class" field:
807
//---------------------------
808
//Theory: For subclasses, the "sub-class" field is actually the same
809
//object, just with a different classFrame through which it is viewed
810
//(revealing different fields, and also a different readObject() to call).
811
//Thus, for a subclass tag, its "parent" is actually the same object.
812
//Implementation: As a hack, instead of explicitly checking if the present
813
//tag is a subclass field, we set it as a default - if the tag we are in
814
//turns out to *not* be a subclass, these default values will be written
815
//over.
816
//For special-case tags: A subclass can never be null, or an alias (each
817
//instance occurs exactly once. It cannot be String or Class, because
818
//these are final. However, it could be a Vector or Hashtable - not
819
//sure how these will be coped with.
820
Object JavaDoc me = parent; //default; me is overwritten if not a subclass.
821
//Needed? No:
822
// - if subclass, this tag sets a new classFrame (the "next" one);
823
// - if not subclass (ie new header), will set an entirely new frame.
824
//Class meClassFrame = parentClassFrame; //again, default/overwritten
825

826         boolean nameMissing = false; //named assumed to be present
827
NamedObject namedObject; //used in many sections below:
828

829 //NULL
830
if (elementName.equals(XMLSerialize.NULL_TOKEN)) {
831             //namedObject = deserializeNull(parent); //code dup for array and alias
832
namedObject = deserializeNull(addAttrTmp, parent); //code dup for array and alias
833
me = namedObject.object; //it will be null
834
nameMissing = !namedObject.named; //this is the info we need
835
if (nameMissing) { //only consider objStore, if name is missing...
836
if (parent!=null) { //if not an orphan, for all these...
837
if (parent.getClass()!=Vector.class && //not vector
838
parent.getClass()!=Hashtable.class && //not hashtable
839
!parent.getClass().isArray()) { //not array
840
objStore.add(me);
841                     }
842                 }
843             }
844             //NOTE: a bug was fixed in this refactoring: before, such a string was
845
//added to objStore, regardless of whether named or not. However, this
846
//wouldn't confuse any custom objects, because this extra one would just
847
//be tacked on the end.
848
return me;
849         }
850
851 //ARRAY
852
else if (elementName.startsWith(XMLSerialize.ARRAY_TOKEN)) {
853             //namedObject = createArray(elementName, parent);
854
namedObject = createArray(addAttrTmp, elementName, parent);
855             me = namedObject.object;
856             nameMissing = !namedObject.named; //this is the info we need
857
if (nameMissing) { //only consider objStore, if name is missing...
858
if (parent!=null) { //if not an orphan, for all these...
859
if (parent!=null && //not root (MEANT to be "not orphan"?)
860
parent.getClass()!=Hashtable.class &&
861                             parent.getClass()!=Vector.class &&
862                             !parent.getClass().isArray()) {
863                         objStore.add(me); //old bug: no objStore exists.
864
}
865                 }
866             }
867             //Only put in objStore if custom data. That is, if an "unnamed field":
868
//parent is a classInstance and object has no name.
869
return me; //Limit interaction with rest of routine.
870
//NOTE createArray() reads close tag, to completely parses the element.
871
//validation of nested of array types is left to Java.
872
}
873
874 //ALIAS
875
else if ( (namedObject=getAlias(addAttrTmp, elementName, parent))!=null ) {
876             me = namedObject.object;
877             nameMissing = !namedObject.named; //if named, plugged inside already
878
if (nameMissing) { //only consider objStore, if name is missing...
879
if (parent!=null) { //if not an orphan, for all these...
880
if (parent.getClass()!=Vector.class && //not vector
881
parent.getClass()!=Hashtable.class && //not hashtable
882
!parent.getClass().isArray()) { //not array
883
objStore.add(me);
884                     }
885                 }
886             }
887             return me;
888         }
889
890 //BINARY DATA (similar design to Class and String)
891
else if (elementName.equals(XMLSerialize.BINARY_DATA_TOKEN)) {
892             namedObject = deserializeBinary(addAttrTmp, tag, parent);
893                 //(1). sets the field, if name is null.
894
//(2). putAlias()
895
nameMissing = !namedObject.named; //if named, already plugged
896
me = namedObject.object;
897             if (parent!=null) { //if not an orphan, for all these...
898
if (nameMissing) { //only consider objStore, if name is missing...
899
if (parent.getClass()!=Vector.class && //not vector
900
parent.getClass()!=Hashtable.class && //not hashtable
901
!parent.getClass().isArray()) { //not array
902
objStore.add(me); //NOTE: we store it as an Object, not prim!
903
}
904                 }
905             }
906             return me;
907         }
908
909 //STRING
910
else if (elementName.equals("java.lang.String")) {
911                 //EXAMPLE: Object eg = "a string"; //<- String as named object
912
//un-named: element of Hash or Vect, array of Object(?), root
913
namedObject = deserializeWrapper(addAttrTmp, tag, parent);
914                 //(1). sets the field, if name is null.
915
//(2). putAlias()
916
nameMissing = !namedObject.named; //if named, already plugged
917
me = namedObject.object;
918             if (parent!=null) { //if not an orphan, for all these...
919
if (nameMissing) { //only consider objStore, if name is missing...
920
if (parent.getClass()!=Vector.class && //not vector
921
parent.getClass()!=Hashtable.class && //not hashtable
922
!parent.getClass().isArray()) { //not array
923
objStore.add(me); //NOTE: we store it as an Object, not prim!
924
}
925                 }
926             }
927             return me;
928         }
929
930 //CLASS
931
else if (elementName.equals("java.lang.Class")) {
932             if (DEBUG_CLASS)
933                 System.err.println("Got a Class tag");
934             namedObject = deserializeClass(addAttrTmp, tag, parent);
935                 //(1). sets the field, if name is null.
936
//(2). putAlias()
937
nameMissing = !namedObject.named; //if named, already plugged
938
me = namedObject.object;
939             if (parent!=null) { //if not an orphan, for all these...
940
if (nameMissing) { //only consider objStore, if name is missing...
941
if (parent.getClass()!=Vector.class && //not vector
942
parent.getClass()!=Hashtable.class && //not hashtable
943
!parent.getClass().isArray()) { //not array
944
objStore.add(me); //NOTE: we store it as an Object, not prim!
945
}
946                 }
947             }
948             if (DEBUG_CLASS) {
949                 System.err.println(">>>>start>>>>>");
950                 System.err.println(me);
951                 try {
952                     System.err.println( ((Class JavaDoc)me).newInstance());
953                     new JSX.ObjOut(System.err).writeObject( ((Class JavaDoc)me).newInstance());
954                 } catch (InstantiationException JavaDoc e) {
955                     System.err.println(e);
956                     e.printStackTrace(); //OK, because within DEBUG block
957
} catch (IllegalAccessException JavaDoc e) {
958                     System.err.println("Should never get here - it means that we couldn't create a Hashtable with the no-arg constructor");
959                     e.printStackTrace();
960                 }
961                 System.err.println("<<<<end<<<<");
962             }
963             return me;
964         }
965
966 //VECTOR
967
else if (elementName.equals("java.util.Vector")) {
968             Class JavaDoc clazz = Class.forName(ParseUtilities.descapeDollar(elementName)); //2 lines, for debugging
969
me = null; //why not just make one, if we are so sure it is OK?
970
try {
971                 me = clazz.newInstance();
972             } catch (InstantiationException JavaDoc e) {
973                 if (DEBUG) {
974                     System.err.println("Should never get here - it means that we couldn't create a Hashtable with the no-arg constructor");
975                     e.printStackTrace();
976                 }
977             } catch (IllegalAccessException JavaDoc e) {
978                 if (DEBUG) {
979                     System.err.println("Should never get here - it means that we couldn't create a Hashtable with the no-arg constructor");
980                     e.printStackTrace();
981                 }
982             }
983             ParserXML.Attr name = null;
984             name = addPrim(addAttrTmp, me, false);//gets NAME_TOKEN - should be no primitive data
985
nameMissing = name.nameMissing;
986              if (parent!=null) { //can't plug if no parent (vect, hash, array do sep)
987
//(1). BUFFER (CUSTOM DATA)
988
if (nameMissing) {//no name
989
if (parent.getClass()!=Vector.class && //not vector
990
parent.getClass()!=Hashtable.class && //not hashtable
991
!parent.getClass().isArray()) { //not array
992
objStore.add(me); //store custom *Object* in buffer
993
}
994                 }
995                 else { //if named...
996
//(2). INTO CLASS AS FIELD
997
//try {
998
currentGetField.putObj(name.value, me); //prim/obj?
999
Field f = getAllField(parent.getClass(), name.value);
1000                        magic.setFinal(f, parent, me);
1001                        //f.set(parent, me);
1002
//} catch (NoSuchFieldException e) {
1003
//}
1004
}
1005            }
1006            putAlias(me); //to sync with ser: do before putAlias for elements
1007
if (!name.emptyTag) { //<java.util.Vector/> means no elements to add
1008
addVectorElements((Vector) me);
1009            }
1010            return me; //note: could return null, if already plugged with name?
1011
}
1012
1013//HASHTABLE
1014
else if (elementName.equals("java.util.Hashtable")) {
1015            Class JavaDoc clazz = Class.forName(ParseUtilities.descapeDollar(elementName)); //2 lines, for debugging
1016
me = null; //why not just make one, if we are so sure it is OK?
1017
try {
1018            me = clazz.newInstance();
1019            } catch (InstantiationException JavaDoc e) {
1020                if (DEBUG) {
1021                    System.err.println("Should never get here - it means that we couldn't create a Hashtable with the no-arg constructor");
1022                    e.printStackTrace();
1023                }
1024            } catch (IllegalAccessException JavaDoc e) {
1025                if (DEBUG) {
1026                    System.err.println("Should never get here - it means that we couldn't create a Hashtable with the no-arg constructor");
1027                    e.printStackTrace();
1028                }
1029            }
1030            ParserXML.Attr name = null;
1031            name = addPrim(addAttrTmp, me, false);//gets NAME_TOKEN - should be no primitive data
1032
nameMissing = name.nameMissing;
1033            if (parent!=null) { //can't plug if no parent (vect, hash, array do sep)
1034
//(1). BUFFER (CUSTOM DATA)
1035
if (nameMissing) {//no name
1036
if (parent.getClass()!=Vector.class && //not vector
1037
parent.getClass()!=Hashtable.class && //not hashtable
1038
!parent.getClass().isArray()) { //not array
1039
objStore.add(me); //store custom *Object* in buffer
1040
}
1041                }
1042                else { //if named...
1043
//(2). INTO CLASS AS FIELD
1044
//try {
1045
currentGetField.putObj(name.value, me); //prim/obj?
1046
Field f = getAllField(parent.getClass(), name.value);
1047                        magic.setFinal(f, parent, me);
1048                        //f.set(parent, me);
1049
//} catch (NoSuchFieldException e) {
1050
//}
1051
}
1052            }
1053            putAlias(me); //to sync with ser: do before putAlias for elements
1054
if (!name.emptyTag) { //<java.util.Hashtable/> means no elements to add
1055
addHashtableElements((Hashtable) me);
1056            }
1057            return me; //note: could return null, if already plugged with name?
1058
}
1059
1060
1061//CLASSINSTANCE (the entire remainder of routine)
1062
//-------------
1063
// ---just written: 2 Aug 2001---
1064
//proposed change - just a switch in order, and move some to default() method.
1065
// - create
1066
// - foreach class, call readObject
1067
// - else default
1068
// - default is addAttr, plug, addTag
1069
// NB: need addAttr for name, to decide how to plug.
1070
// NB: always call default *exactly* once
1071
// NB: maybe problems with stores (for custom data)
1072
// NB: this fix only works if only one class in the inheritance path
1073
// has a readObject() that relies on the defaultReadObject() order.
1074
// - and fortunately, this does seem to be the case.
1075
else {
1076            //ParserXML.Attr[] addAttrTmp = addName();//head of old "addAttr()"
1077
ParserXML.Attr name = addAttrTmp[1];
1078            boolean subclass = false; //ie "not header" the default
1079
if ( name!=null && name.value.equals(XMLSerialize.SUPER_TOKEN) ) {
1080                subclass = true;
1081            }
1082            String JavaDoc alias = null; //store for (possible) readResolve
1083
Class JavaDoc meClassFrame; //we may need to set this for first call?
1084

1085         if (!subclass) {
1086            Class JavaDoc clazz = null;
1087            //clazz = Class.forName(ParseUtilities.descapeDollar(elementName)); //note: code dup with above
1088
//System.err.println("forName: "+clazz+", "+clazz.getClassLoader());
1089

1090            //System.err.println("XMLDeserialize: About to load class");
1091

1092            //front-end to for java.io.ObjectInputStream.resolveClass()
1093
//construct an osc object with "name" of desired class to load.
1094
String JavaDoc escapedName = ParseUtilities.descapeDollar(elementName);
1095            ObjectStreamClass osc = magic.getOsc(escapedName);
1096            try {
1097                clazz = oisSubclass.resolveClass(osc); //done!
1098
} catch (ClassNotFoundException JavaDoc noClass) {
1099                throw new ClassNotFoundException JavaDoc("Could not load "+escapedName+": "+noClass);
1100            }
1101        /* ----- auto Memento substitute ----- */
1102         /* NB: we let resolveClass() have a go at it first.
1103            * Note: for resolve class to work, the serialized class must actually
1104            * be defined. Thus, resolveClass() works for read back-compatibility.
1105            * But not for forward compatibility (enabling the old version to read
1106            * the memento of the new version, even tho not defined).
1107            */

1108
1109            if (cfg.autoMemento) {
1110                Class JavaDoc[] innerClasses = clazz.getDeclaredClasses();
1111                for (int i=0; i<innerClasses.length; i++) {
1112                    if ( innerClasses[i].getName().endsWith("$Memento") ) { // bingo!
1113
// It's a shame to have to rely on the "$" syntax...
1114
// Possibly should check beginning matches parent class, so
1115
// that class A { class hahaha$Memento {} } doesn't confuse it
1116
Class JavaDoc memento = innerClasses[i];
1117                        // if (JSX.Memento.class.isAssignableFrom(memento)) // a refinement
1118
clazz = memento;
1119                        escapedName = memento.getName();
1120                        elementName = ParseUtilities.escapeDollar(escapedName);
1121                            // for blind safety - switch everything over
1122
break;
1123                    }
1124                }
1125            }
1126            
1127
1128            //System.err.println("resolveClass: "+clazz+", "+clazz.getClassLoader());
1129

1130/*
1131        try {
1132            Method latest = ObjectInputStream.class.getDeclaredMethod("latestUserDefinedLoader", new Class[0]);
1133            latest.setAccessible(true); //private
1134            ClassLoader loader = (ClassLoader)latest.invoke(null, null);
1135            System.err.println(" --- latest loader ---> "+loader);
1136            clazz = Class.forName(elementName, false, loader);
1137            System.err.println("XMLDeserialize latest: "+clazz+", "+clazz.getClassLoader());
1138        } catch (Exception e) {e.printStackTrace();}
1139            System.err.println("XMLDeserialize call to Latest.get");
1140        classLoading.Latest.get();
1141
1142            System.err.println("XMLDeserialize IN LINE CODE in line code");
1143    //does it make a difference being right here? YES!! crazy...
1144    try {
1145      java.lang.reflect.Method latest = java.io.ObjectInputStream.class.getDeclaredMethod("latestUserDefinedLoader", null);
1146  //d("latestUserDefinedLoader", new Class[0]);
1147      latest.setAccessible(true); //private
1148      ClassLoader loader = (ClassLoader)latest.invoke(null, null);
1149      System.err.println("latest loader: "+loader);
1150    } catch (Exception e) {
1151      e.printStackTrace();
1152    }
1153*/

1154/*
1155            clazz = Class.forName(elementName, false, ObjIn.cl); //cheat
1156*/

1157
1158
1159            me = null; //is there a better way to do this?
1160
try {
1161                if (Externalizable.class.isAssignableFrom(clazz)) {
1162                    try {
1163                        Constructor cons = clazz.getConstructor(new Class JavaDoc[0]);
1164                        //cons.setAccessible(true); //that's why we can't clazz.newInstance()
1165
me = cons.newInstance(new Object JavaDoc[0]); //Externalizable requires no-arg constructor
1166
} catch (NoSuchMethodException JavaDoc e) {
1167                        throw new InvalidClassException("Missing public no-arg constructor for class "+ clazz.getName());
1168                    } catch (InstantiationException JavaDoc e) {
1169                        throw new InvalidClassException("An interface or abstract class "+ clazz.getName());
1170                    }
1171                } else {
1172                    me = magic.newInstance(clazz); //use anon
1173
if (me==null)
1174                        throw new ClassNotFoundException JavaDoc("magic.newInstance() failed to create a "+clazz+", from the name "+escapedName);
1175                }
1176            } catch (InvocationTargetException e) {
1177                    //a crucial exception
1178
Throwable JavaDoc thrown = e.getTargetException();
1179                if (thrown instanceof ClassNotFoundException JavaDoc)
1180                    throw (ClassNotFoundException JavaDoc) thrown; //rethrow it
1181
else if (thrown instanceof IOException)
1182                    throw (IOException) thrown; //rethrow it
1183
else if (thrown instanceof RuntimeException JavaDoc)
1184                    throw (RuntimeException JavaDoc) thrown; //rethrow it
1185
else {
1186                    System.err.println("\nWrapper Exception:");
1187                    e.printStackTrace();
1188                    System.err.println("\nException wrapped up:");
1189                    thrown.printStackTrace();
1190                }
1191            } catch (IllegalAccessException JavaDoc e) {
1192                    e.printStackTrace();
1193            }
1194            if (IBM_DEBUG) System.err.println("IBM: me is "+
1195                                (me==null?"null":"not null") );
1196            if (KELLY_DEBUG) {
1197                //change from "instanceof java.awt.Container", to avoid link error,
1198
//if awt not avail (eg OTI/IBM's J9 JVM). Bug report by Alex Lennon.
1199
if (me.getClass() == Class.forName("java.awt.Container")) {
1200                    System.err.println("Got a java.awt.Container");
1201                }
1202            }
1203            if (DEBUG) System.err.println("Made class \"" +elementName+ "\"");
1204                //Constructor con = clazz.getConstructor(new Class[] {}); //no-arg con
1205
//con.setAccessible(true);
1206
//me = con.newInstance(new Object[] {}); //no-arg again
1207

1208    //6 Aug 2001 - need to store present alias *if* has readResolve.
1209
alias = putAlias(me); //6 Aug 2001
1210
//putAlias(me); //NOTE: do before any Strings in addAttr()
1211
//Be careful of this complexity!
1212
//To make consistent, may need to change the above
1213
meClassFrame = clazz;
1214            
1215            if (superVersion) {
1216                if (false) throw new RuntimeException JavaDoc("superVersion is disabled in this version of JSX");
1217                lookahead.set(tag, addAttrTmp);
1218                f(me); //pass in <F>
1219
}
1220        } else { //a subclass - never happens, except when superVersion.
1221
meClassFrame = Class.forName(ParseUtilities.descapeDollar(elementName));
1222        }
1223
1224    if (!superVersion) {
1225            Vector localStackPrimStore = primStore; //push old (see Explanatory memo)
1226
int localStackPrimStoreIndex = primStoreIndex;
1227            primStore = new Vector(); //new context - NOTE: need for addAttr
1228
primStoreIndex = 0;
1229                //DESIGN DECISION: custom data Strings never in attributes!
1230

1231            //Move up, along with addName, to before createObject
1232
// ParserXML.Attr name = null;
1233

1234            //Expand and slice old "addAttr":
1235
// name = addAttr(me, false); //adds in all attr and gets NAME_TOKEN
1236
//Move getting the name part up to before instance is created:
1237
// ParserXML.Attr[] addAttrTmp = addName(); //split it up
1238
name = addPrim(addAttrTmp, me, false);
1239
1240            nameMissing = name.nameMissing;
1241            if (DEBUG && name!=null) System.err.println("Name: "+name);
1242            if (DEBUG) System.err.println("nameMissing: "+nameMissing);
1243
1244         /* Explanatory memorandum for custom data:
1245            * --------------------------------------
1246            * Custom data only occurs in classInstances (not null, alias, String,
1247            * hash or vect). It has two phases: (1). Store custom data in buffers
1248            * (all attrs and contained, nested tags). (2). Callback the objects's
1249            * readObject() method, drawing from the buffers, via calling readObject
1250            * within it readInt(), readLong() etc. The buffer context needs to be
1251            * stacked, because contained tags (field or custom data objects) may
1252            * themselves contain custom data. Both fields and custom data object are
1253            * created the same; the difference is that custom data is stored in a
1254            * stacked buffer. This stack is implemented with the local var
1255            * stackframe (CONSIDER: would an explicit stack be clearer?).
1256            *
1257            * In effect, each classinstance has two potential kinds of data - in
1258            * fields, and in buffers. The buffer is therefore like a kind of series
1259            * of positional field, or typeless array. This provides another view of
1260            * the need for stacking: each object has its own buffer, just as it has
1261            * its own field.
1262            *
1263            * Attributes are stored into their parent buffer when we are in their
1264            * "parent" object. This is because attributes can never be parents -
1265            * there is no possibility of nesting (tho we still need to stack). In
1266            * contrast, objects are stored into the parent buffer when we are in the
1267            * "child" object. This is because we always change the context for
1268            * objects.
1269            *
1270            * This difference is caused by not changing context for attributes - it
1271            * might be good to regularise it by representing the present object
1272            * context as a "parent" when doing the attributes.
1273            *
1274            * Note: custom data String objects are never stored in attributes. This
1275            * avoids the problem of determining that it is a String (and so needs to
1276            * create an alias, and be put in objStore, not primStore). The type of
1277            * fields can be determined from the class; but not so for custom data.
1278            * Although the position does ultimately determine how it is read, this is
1279            * deferred (and so can cause problems for aliasing), and the attr/tag
1280            * split (seems) to not preserve the ordering - unless we know into which
1281            * to put the String beforehand (begging the question)! Easier to simply
1282            * put it in a tag!! Previous design comments:
1283            *
1284            * >This order is in fact lost already - it was lost when the Strings were
1285            * >included as Attr, instead of as objects... the simplest thing may well
1286            * >be to represetn *every* string as a wrapper... or, at least, to include
1287            * >every customized string as a wrapper...
1288            *
1289            * Note that we still need to stack attributes, even though they
1290            * themselves don't nest, since the object containing them will have
1291            * invocation of its readObject() method deferred until after any
1292            * contained tags (objects) are also processed, which need their own
1293            * "buffer field" for any custom data.
1294            *
1295            */

1296    
1297            if (INTERNAL_DEBUG) {
1298                if (primStore!=null) {
1299                    for (Enumeration keys=primStore.elements(); keys.hasMoreElements();) {
1300                        Object JavaDoc a = keys.nextElement();
1301                        System.err.println("In primStore: " +a+ " -- "
1302                        + a.getClass() + " -- " +a.hashCode());
1303                    }
1304                }
1305            }
1306
1307//CHILDREN TAGS; recursion, and a new context for buffering custom data
1308
/*
1309            * A new custom data context is created, and then the children are done.
1310            * Each custom data child is buffered into this context - all siblings
1311            * into the same one. This buffering is done in this routine
1312            * (createObject), with objStore.add(). Incidentally, they nest, so
1313            * memory usage is (aveObjectSize*depth).
1314            */

1315            Vector localStackObjStore = objStore; //save locally, so recurse
1316
objStore = new Vector(); //this will get all the enclosed tags of "me"
1317
int localStackObjStoreIndex = objStoreIndex; //also save it.
1318
objStoreIndex = 0;
1319
1320            Object JavaDoc localMeStore = meStore; //Push onto program stack (frame)
1321
meStore = me; //"pass arg", to defaultReadObject
1322
Class JavaDoc localMeClassFrameStore = meClassFrameStore; //Push
1323
meClassFrameStore = meClassFrame;
1324            boolean localEmptyTagStore = emptyTagStore; //Push
1325
emptyTagStore = name.emptyTag;
1326//addTags *is* the default!! -- now called from ObjIn!
1327
// if (!name.emptyTag) { //ie if has elements (not <xxxx/>)
1328
// addTags(me); //CENTRAL RECURSIVE dispatch: me am become the parent.
1329
// }
1330

1331//CALLBACK object's readObject()
1332
/*
1333            * The defaultReadObject() problem
1334            * -------------------------------
1335            * All contained tags are read; now invoke readObject() to get them.
1336            * Strictly, any fields should only be set if defaultReadObject() is
1337            * called. But JSX doesn't work like this. JSX sets them all if they are
1338            * present in the XML, before readObject() is even called.
1339            *
1340            * This rarely seems to be a problem - it would only be so if a class
1341            * *depended* on the recorded values not being set. Usually, such a class
1342            * wouldn't write them in the first place - that is, it wouldn't call
1343            * defaultWriteObject() in writeObject(), and so the fields wouldn't be
1344            * present. However, it might be relied upon in evolution of classes.
1345            *
1346            * A solution to this would be to not set the fields until after
1347            * readObject() is invoked; similar to as is presently done for the
1348            * custom data. This means processing all fields twice - once to store,
1349            * and once to set into field. Although it might also seem to create
1350            * a complete DOM tree replica of the object graph, it is not all in memory
1351            * at once, but only the path to the present object (on the stack).
1352            * (This does trace out an entire DOM tree, just not all at once).
1353
1354            * Efficiency ("Don't" and "Not yet") - needn't do this when:
1355            * - the parent is an Arrays, Vect or Hash.
1356            * - there is no readObject() method (and so all fields are read)
1357
1358            * The custom data Strings as attributes problem
1359            * ---------------------------------------------
1360            * There is a separate problem for custom data Strings in attributes:
1361            * There is no markup to indicate the type of attributes, so that we
1362            * don't know whether they are Strings or primitives, and therefore
1363            * don't know what to do with them. For fields, we can derive the type
1364            * via Java reflection, based on the name. But for custom data, we
1365            * don't know until the readObject() requests it. However, for this to
1366            * work, we would need to have the custom data all in one buffer, rather
1367            * than split in two.
1368            *
1369            * [The difficulty is to do with splitting it into object/primitive, which
1370            * loses the relative ordering between them, before we can decide whether
1371            * it is an object or primitive! Catch 22. Begging the question. This
1372            * information is in fact lost in the serialization into primitive vs
1373            * object - that's the split, not the buffering. I'm a still a bit
1374            * confused about *exactly* what is wrong with this, which is why it's
1375            * not quite expressed right. However, I'm convinced that it is an
1376            * insurmountable problem, due to the attr/object split. The simple
1377            * resolution is just to always put custom data Strings into tags.]
1378            *
1379            * A solution to the buffering problem is to just do all the custom data
1380            * in one section, and so not need to buffer. The readObject() could read
1381            * this directly, skipping named data. This would require putting the
1382            * custom data at the end (so no buffering needed), or putting it into a
1383            * new "<custom>" tag - which would also need to go at the end. However,
1384            * the attr/tag split problem for Strings still remains. This could be
1385            * solved by putting all custom data into tags - though we would need
1386            * someway to distinguish between primitives and their object wrappers.
1387            * Ie between Byte and byte. Of course, we simply introduce <byte
1388            * valueOf=""/> tags and so on.
1389
1390            * NOTE: in practice, a class not calling defaultReadObject() also should
1391            * not call defaultWriteObject(). If so, the fields are *not* serialized,
1392            * and so the defaultReadObject() has no effect, in practice. This is not
1393            * the proper solution, but it may well work in all cases in practice.
1394            * Something that *does* need to be fixed for correct implementation of
1395            * the Java Serialization API.
1396
1397            * SUMMARY (rough, first draft)
1398            * -------
1399            * (1). Buffering of fields (as well as custom data) is *not* too memory
1400            * intensive - just a little stack overhead. It will be *slightly* less
1401            * efficient. It will solve the defaultReadObject() problem (though which
1402            * should never occur in practice, anyway). NOT PLANNING TO DO YET.
1403            * (2). The attr/tag custom data String problem needs a dramatic change
1404            * to fix properly. Since there seems no disadvantage to simply
1405            * serializing in a tag, that is what has been done.
1406            * (3). the custom data represents the positional fields of each object.
1407            * (4). fields are created and set as they are read.
1408            * (5). custom data is fully created and stored as it is read - then
1409            * processed after *all* tags have been read.
1410            * (6). tags (objects) are stored into a buffer belonging to the parent
1411            * object.
1412            * (7). attr (primitives) are stored into a buffer belonging to the
1413            * present object.
1414            */

1415
1416//CALLBACK OF readObject() for CUSTOM DATA
1417
boolean localDefaultCalledStore = defaultCalledStore; //Push
1418
defaultCalledStore = false;
1419            Method m;
1420            if (me instanceof Externalizable) {
1421                //try {
1422
((Externalizable)me).readExternal(oisSubclass);
1423                //} catch (IOException e) {
1424
//should propagate, but awkward in present architecture
1425
//} catch (ClassNotFoundException e) {
1426
//}
1427
} else {// regular treatment
1428
Vector classes = new Vector(); //NB: Mirror order - reverse.
1429
Class JavaDoc[] superClasses=XMLSerialize.getReversedSuperClasses(me.getClass());
1430                    if (SUPER_DEBUG)
1431                        System.err.println(me.getClass().getName()+" "+(!nameMissing?name.value:"<unnamed>")+ ": superClasses.length = "+ superClasses.length);
1432                    for (int j=0; j<superClasses.length; j++) { //traverse root to leaf
1433
Class JavaDoc c = superClasses[j];
1434                        if ((m=XMLSerialize.getDeclaredMethod(c, "readObject", OIS_ARGS,
1435                                Modifier.PRIVATE, Modifier.STATIC)) != null) { //NB: ?clazz->c
1436
if (INTERNAL_DEBUG) System.err.print("\t--JSX deserialize--");
1437                            try {
1438                                getFieldClassStack.push(c);
1439                                    m.invoke(me, readObjectArglist);//might as well do them all!
1440
getFieldClassStack.pop();
1441                                if (INTERNAL_DEBUG)
1442                                    System.err.println("\tinvoke "+m+" on "+me+" of " +me.getClass());
1443                            } catch (InvocationTargetException e) {
1444                                if (DEBUG) {
1445                                    System.err.println("JSX InvocationTargetException:");
1446                                    System.err.println("Object which is a: " +me.getClass());
1447                                    e.printStackTrace(); //should we just rethrow this? YES!
1448
}
1449                                //we just copied Java's ObjectInputStream handling of it!
1450
//It does all this in an "invokeObjectReader" method
1451
Throwable JavaDoc t = e.getTargetException();
1452                                if (t instanceof ClassNotFoundException JavaDoc)
1453                                    throw (ClassNotFoundException JavaDoc)t;
1454                                else if (t instanceof IOException)
1455                                    throw (IOException)t;
1456                                else if (t instanceof RuntimeException JavaDoc)
1457                                    throw (RuntimeException JavaDoc) t;
1458                                else if (t instanceof Error JavaDoc)
1459                                    throw (Error JavaDoc) t;
1460                                else
1461                                    throw new Error JavaDoc("interal error");
1462                            } catch (IllegalAccessException JavaDoc e) {
1463                                defaultReadObject(); //just as if it were not there.
1464
}
1465                        } else {
1466                            defaultReadObject(); //call if no readObject() declared
1467
//potential BUG: will JSX work if the readObject doesn't call
1468
//defaultReadObject? Doesn't there need to be some check, so
1469
//that it does get called, if it hasn't been already?
1470
//This is like the other side of ensuring it is only called
1471
//once.
1472
}
1473                    }
1474                    if (superClasses.length==0) //ie for java.lang.Object
1475
defaultReadObject(); //a more elegant placement?
1476
}//end: regular (non-Externalizable) treatment
1477
//BTW: "Externalizable extends Serializable"
1478

1479                //how is the best way (most modular) for readResolve to operate?
1480
//Can we move readResolve out of this nested position?
1481
if (!subclass) {
1482                if (NOV_DEBUG) System.err.println("readResolve is called!!!!!!!!!!!");
1483                me = readResolve(me, alias); //defaults to me, if no readResolve
1484
}
1485
1486            defaultCalledStore = localDefaultCalledStore; //pop
1487
//for defaultReadObject
1488
meStore = localMeStore; //pop
1489
meClassFrameStore = localMeClassFrameStore; //pop
1490
emptyTagStore = localEmptyTagStore; //pop
1491

1492            //for Objects (tags)
1493
objStoreIndex = localStackObjStoreIndex; //restore
1494
objStore = localStackObjStore;//restore objects for previous level
1495

1496            //for Primitives (attr)
1497
primStoreIndex = localStackPrimStoreIndex; //restore
1498
primStore = localStackPrimStore;//restore primitives for previous level
1499
}
1500
1501        if (superVersion) { //duplicate of above
1502
if (!subclass) {
1503                if (NOV_DEBUG) System.err.println("readResolve is called!!!!!!!!!!!");
1504                me = readResolve(me, alias); //defaults to me, if no readResolve
1505
}
1506        }
1507
1508
1509//PLUG: CUSTOM DATA->BUFFER; FIELD->PARENT
1510
//There is no real reason that this cannot be at the end. It is even an
1511
//appropriate place for it to be - just before it is returned, which is
1512
//the alternate mechanism.
1513
//--Note that this section uses variables that are set from above--
1514
//6 Aug 2001 - QUESTION: can we move both set and add to after readObject?
1515
//They both seem to only relate to the parent, which is invariant
1516
//across the readObject invocation - great if could confirm this.
1517
//DEPENDS ON: me, parent, nameMissing, nameValue, a nested objStore.
1518
//addTags is done by the call to defaultReadObject(), which the object's
1519
//readObject() needs to invoke to setup the custom data for reading.
1520
//Basically: the "parent" context (parent, objStore) is set up around
1521
//the readObject/defaultReadObject calls... ah! The naming shows that it is
1522
//the central thing - even when not explicitly called, it still is called.
1523
//Our confusion is mainly caused by addAttr, which is because we mix
1524
//info *about* object (name, class, etc) with *content* of the object
1525
//itself - ie primitives and strings. This is conceptually inconsistent.
1526
if (!subclass) { //ie if not ob-name="subclass"
1527
if (parent!=null) { //can't plug if no parent (vect, hash, array do sep)
1528
//(1). BUFFER (CUSTOM DATA)
1529
if (nameMissing) {//no name
1530
if (parent.getClass()!=Vector.class && //not vector
1531
parent.getClass()!=Hashtable.class && //not hashtable
1532
!parent.getClass().isArray()) { //not array
1533
objStore.add(me); //store custom *Object* in buffer
1534
if (INTERNAL_DEBUG)
1535                            System.err.println("custom obj["+objStoreIndex+"],"+objStore);
1536                    }
1537                }
1538                //(2). INTO CLASS AS FIELD
1539
else {
1540                    if (DEBUG_CUSTOM) {
1541                        System.err.println("getting field '"+name.value+"'");
1542                    }
1543                    //try {
1544
currentGetField.putObj(name.value, me); //prim/obj?
1545
Field f = getAllField(parent.getClass(), name.value);
1546                        magic.setFinal(f, parent, me);
1547                        //f.set(parent, me);
1548
// catch (NoSuchFieldException e)
1549
//
1550
}
1551            }
1552         } //subclass
1553

1554            //System.err.println("me being returned: "+me);
1555
//try { throw new Exception(); } catch (Exception e) {e.printStackTrace();}
1556

1557            return me;
1558        }
1559    }
1560
1561
1562    void f(Object JavaDoc me)
1563    throws IOException, ParserXML.ExceptionXML, ClassNotFoundException JavaDoc {
1564        //eatOpen(): finish eating the <F>, and setup for next token
1565
String JavaDoc openTagName = lookahead.tag.name; //save for close
1566
ParserXML.Attr[] addAttrTmp = {lookahead.attr, lookahead.name};
1567        addPrim(addAttrTmp, me, false); //glue - discard name
1568
lookahead.set(p.readTag(), addName()); //setup lookahead for <C>
1569

1570        if (lookahead.tag.start && lookahead.name.value.equals(XMLSerialize.SUPER_TOKEN))
1571            nestedSubclass(me);
1572        else
1573            throw new ParserXML.ExceptionXML("subclass openTag", lookahead.tag.name);
1574
1575        checkCloseTag(openTagName, lookahead.tag);
1576        //NB: we don't lookahead again - this is the current edge of our conversion
1577
}
1578
1579
1580/*
1581    void f(Object me) {
1582        ParserXML.Tag openSubclassTag = p.readTag();
1583        meClassFrame = Class.forName(ParseUtilities.descapeDollar(openSubclassTag.name));
1584        addAttrTmp = addName(); //should be subclass
1585        if (ADDATTR_NULL_DEBUG)
1586            System.err.println("addAttrTmp (in createObject)= "+addAttrTmp);
1587        nestedSubclass(addAttrTmp, meClassFrame, me); //a test!!!
1588        String expectedName = ParseUtilities.escapeDollar(me.getClass().getName());
1589        checkCloseTag(expectedName, lookahead.tag); //String, Tag
1590    }
1591*/

1592
1593/**-----------------------------------------------------------------------
1594    * Convenience methods for the common tasks of checking for open or close
1595    * tags. Note that these operate by throwing exceptions... in effect, we
1596    * have factored out the exception generating code... ;-)
1597    **/

1598
1599    //This is *great*! This is fantastic!
1600
private void eatClose(String JavaDoc expectedName) throws IOException, ParserXML.ExceptionXML {
1601        checkCloseTag(expectedName, lookahead.tag);
1602        advance();
1603    }
1604
1605  private void eatOpen(String JavaDoc tagName, String JavaDoc objName) throws IOException, ParserXML.ExceptionXML {
1606        if (tagName!=null) checkOpenTag(tagName, lookahead.tag); //null is wildcard
1607
if (objName!=null) checkObjName(objName, lookahead.name);
1608        advance();
1609    }
1610
1611  private void checkCloseTag(String JavaDoc expectedName, ParserXML.Tag tag) throws ParserXML.ExceptionXML {
1612        if (tag.start) //we want close - this is not my beautiful closet!
1613
throw new ParserXML.ExceptionXML("Wanted a close tag, got an open tag", "</"+expectedName+">","<"+tag.name+">");
1614        else if (!tag.name.equals(expectedName))
1615                throw new ParserXML.ExceptionXML("</"+expectedName+">","</"+tag.name+">");
1616    }
1617
1618  private void checkOpenTag(String JavaDoc expectedName, ParserXML.Tag tag) throws ParserXML.ExceptionXML {
1619        if (!tag.start)
1620            throw new ParserXML.ExceptionXML("Wanted an open tag, got a close tag", "</"+expectedName+">","<"+tag.name+">");
1621        else if (!tag.name.equals(expectedName))
1622                throw new ParserXML.ExceptionXML("</"+expectedName+">","</"+tag.name+">");
1623    }
1624
1625  private void checkObjName(String JavaDoc expectedName, ParserXML.Attr name) throws ParserXML.ExceptionXML {
1626        if (name==null)
1627            throw new ParserXML.ExceptionXML("Expected a name, but none found");
1628        else if (!name.value.equals(expectedName))
1629                throw new ParserXML.ExceptionXML("</"+expectedName+">","</"+name.value+">");
1630    }
1631
1632
1633/** XML observations:
1634    * -----------------
1635    * Note: should not use name attributes to define elements, because then
1636    * close tags cannot definitively close them - as they lack name attributes
1637    * This is also a good argument for global element names, and the
1638    * various DTD conventions. If there is more than one "X", how can we be
1639    * sure which one it closes? OTOH, this matching up might not really be so
1640    * important - just an incidental benefit.
1641    * Actually, this is all mistaken - there can be more than one element with
1642    * the same name in the same document, and so the risk of ambiguity exists
1643    * there in a DTD based XML document also...
1644
1645    * Note: We can simplify checking that close tags are correct, by pushing it
1646    * down to the lexical/scanning layer. Then, only the position that we
1647    * find a close tag is important - we can ignore its name.
1648    **/

1649
1650/** ---------------------------------------------------------------------------
1651    * nestedSubclass
1652    * --------------
1653    * F -> C
1654    * C -> F*[O][C]
1655    * O -> F*
1656
1657    * C -> r[C] //C - nestedSubclass()
1658    * r -> (d|s) //r - generalReadObject(me);
1659    * d -> defRO[O] //d - defaultReadObject()
1660    * defRO -> F*
1661    * s -> [defRO][O] //s - readObject().
1662    * O -> (p|q)* //this mixes the levels - we need to read defRO and O
1663    * p -> [defRO]
1664    * I'm very lost here - defer it. This is a hard bit...
1665
1666    * NOTATION: uppercase non-terminals are elements, which contain their RHS
1667  * FIRST(C) = { <C> }
1668            Therefore, the very first token is always an open tag, followed by:
1669  * FIRST(C) = { FIRST(r),FIRST(n),</C> }
1670  * FIRST(r) = { <F>,<O>,e }
1671  * FIRST(n) = { <C>,e }
1672
1673    * C() { //FIRST(C) = {<C>};
1674            eatOpen(<C>);
1675                r();
1676                if (<C>) C(); //n()
1677            eatClose(</C>);
1678        }
1679
1680    * Lookahead interaction with other routines:
1681    * (1). addAttrTmp - is this argument a kind of lookahead?
1682    * (2). tag and addAttrTmp - these are used as if they were "lookahead".
1683    * However, where are these symbols eaten? If checking them did advance
1684    * the input stream, then maybe we wouldn't need the explicit readTag()?
1685
1686    * "Refactoring is crafting" it to be beautiful, and really understanding it,
1687    * and bring light, wisdom and clarity to it.
1688
1689    **/

1690
1691    private void nestedSubclass(Object JavaDoc me)
1692            throws IOException, ClassNotFoundException JavaDoc {
1693        String JavaDoc frameName = lookahead.tag.name; //save a local copy ("push")
1694
generalReadObject(me); //now uses lookahead
1695
if (lookahead.tag.start &&
1696                lookahead.name.value.equals(XMLSerialize.SUPER_TOKEN)) {
1697            nestedSubclass(me);
1698        }
1699        eatClose(frameName);
1700    }
1701
1702
1703    final Lookahead lookahead = new Lookahead();
1704    static class Lookahead {
1705        ParserXML.Tag tag;
1706        ParserXML.Attr attr;
1707        ParserXML.Attr name;
1708        void set(ParserXML.Tag tag, ParserXML.Attr attr, ParserXML.Attr name) {
1709            this.tag = tag;
1710            this.attr = attr;
1711            this.name = name;
1712        }
1713        void set(ParserXML.Tag tag, ParserXML.Attr[] addAttrTmp) {
1714            this.tag = tag;
1715            if (addAttrTmp!=null) {
1716                this.attr = addAttrTmp[0];
1717                this.name = addAttrTmp[1];
1718            }
1719        }
1720    }
1721
1722
1723
1724/**
1725    * If it returns to here, means either: open sub-class, or a close tag.
1726    * Question: who handles the non-subclass close tags?
1727    *
1728    * (Consider: for convenience, we could provide both Class and String.)
1729    **/

1730/*
1731 //copy of nestedSubclass() containing all comments.
1732    private void nestedSubclass(ParserXML.Attr[] addAttrTmp, Class classFrame, Object me) throws IOException, ClassNotFoundException {
1733        //it should *only* be here if a classinstance
1734        if (NESTED_DEBUG)
1735            System.err.println(">>>> entering nested");
1736
1737            //meClassFrameStore = classFrame;
1738            if (ADDATTR_NULL_DEBUG)
1739                System.err.println("addAttrTmp = "+addAttrTmp);
1740        ParserXML.Tag tag = readObject(addAttrTmp, me, classFrame); //attr & tags...
1741        addAttrTmp = defaultReadObject_attrs_out; //pass it out
1742
1743        //DOWN (nest deeper)
1744        if (tag.start) { //open
1745            if (NESTED_DEBUG)
1746                System.err.println("nestedSubclass got an open subclass tag: "+tag);
1747                //assume "sub-class": the only open tag defaultReadObject returns on
1748            Class nextClass = Class.forName(ParseUtilities.descapeDollar(tag.name));
1749            if (ADDATTR_NULL_DEBUG)
1750                System.err.println("addAttrTmp (DOWN)= "+addAttrTmp);
1751            nestedSubclass(addAttrTmp, nextClass, me); //attr & tags...
1752            ParserXML.Tag endTag = p.readTag(); //when return, must be a close tag
1753            //it should be a close tag, matching the open tag
1754            if (endTag.start) //if not a close tag
1755                throw new ParserXML.ExceptionXML(
1756                    "</"+classFrame.getName()+">","<"+endTag.name+">");
1757            else
1758                checkCloseTag(ParseUtilities.escapeDollar(classFrame.getName()),endTag);
1759                //if (!classFrame.getName().equals(endTag.name)) //end, but wrong one
1760                    //throw new ParserXML.ExceptionXML(
1761                        //"</"+classFrame.getName()+">","</"+endTag.name+">");
1762            if (NESTED_DEBUG)
1763                System.err.println("tag when return from nestedSubclass: "+tag);
1764        } else {
1765        //UP (return)
1766            if (NESTED_DEBUG)
1767                System.err.println("nestedSubclass got a close tag: "+tag);
1768            String expectedTag = ParseUtilities.escapeDollar(classFrame.getName());
1769            if (!tag.name.equals(expectedTag))
1770                throw new ParserXML.ExceptionXML(
1771                    "</"+expectedTag+">","</"+tag.name+">");
1772        }
1773        if (NESTED_DEBUG)
1774            System.err.println("<<<< leaving nested, with tag: "+tag);
1775    }
1776*/

1777
1778
1779    static class ObjStack { //don't extend: forces errors on old call to Vector API
1780
Vector v = new Vector();
1781        void push(Object JavaDoc o) {
1782            v.add(o);
1783        }
1784        void set(Object JavaDoc o) {
1785            v.set(v.size()-1, o);
1786        }
1787        Object JavaDoc get() {
1788            return v.get(v.size()-1);
1789        }
1790        Object JavaDoc pop() {
1791            return v.remove(v.size()-1);
1792        }
1793    }
1794
1795    static class TagStack { //not extend, because narrowing return types
1796
ObjStack s = new ObjStack();
1797        void push(ParserXML.Tag tag) {
1798            //System.err.println("push("+tag+")");
1799
s.push(tag);
1800        }
1801        void set(ParserXML.Tag tag) {
1802            //System.err.println("\tset("+tag+")");
1803
s.set(tag);
1804        }
1805        ParserXML.Tag get() {
1806            return (ParserXML.Tag)s.get();
1807        }
1808        ParserXML.Tag pop() {
1809            //System.err.println("pop = "+get());
1810
return (ParserXML.Tag)s.pop();
1811        }
1812    }
1813
1814    static class BooleanStack { //not extend, because narrowing return types
1815
ObjStack s = new ObjStack();
1816        void push(boolean bool) {
1817            s.push(bool?Boolean.TRUE:Boolean.FALSE); //reuse, instead of creating
1818
//System.err.println("push("+bool+")");
1819
}
1820        void set(boolean bool) {
1821            s.set(bool?Boolean.TRUE:Boolean.FALSE);
1822        }
1823        boolean get() {
1824            return ((Boolean JavaDoc)s.get()).booleanValue();
1825        }
1826        boolean pop() {
1827            //System.err.println("pop() = "+get());
1828
return ((Boolean JavaDoc)s.pop()).booleanValue();
1829        }
1830    }
1831
1832    TagStack defROStack = new TagStack();
1833    BooleanStack defROCalledStack = new BooleanStack();
1834
1835    void generalReadObject(Object JavaDoc me)
1836            throws IOException, ClassNotFoundException JavaDoc {
1837        Class JavaDoc meClassFrame = Class.forName(ParseUtilities.descapeDollar(lookahead.tag.name));
1838        ParserXML.Attr[] addAttrTmp = {lookahead.attr, lookahead.name}; //glue
1839

1840        defaultReadObject_attrs_in = addAttrTmp;
1841        defaultReadObject_me = me;
1842        defROStack.push(null);
1843        defROCalledStack.push(false);
1844
1845        Method readObjectMethod = XMLSerialize.getDeclaredMethod(meClassFrame,
1846"readObject", OIS_ARGS, Modifier.PRIVATE, Modifier.STATIC);
1847        if (readObjectMethod==null) {
1848            //note: lookahead should be set correctly at this point
1849
d();
1850        } else {
1851            System.err.println(" ----- s() is called");
1852            s(me, readObjectMethod);
1853        }
1854        defROStack.pop();
1855        defROCalledStack.pop(); //not sure how nec this is
1856
}
1857
1858/**
1859    * This is d():
1860    * d -> defRO[O]
1861    * O -> e //assume empty (could be F*)
1862    *
1863    * defRO -> F*
1864    *
1865    **/

1866
1867    void d() throws ParserXML.ExceptionXML, IOException, ClassNotFoundException JavaDoc {
1868        defaultReadObject_ver2_from_d(); //sets defRO to true when called
1869

1870        if (lookahead.tag.start && lookahead.tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
1871            eatOpen(XMLSerialize.OPT_DATA_TOKEN, null); //null - no name
1872
//...assume empty, because no code to read optional data, anyway
1873
eatClose(XMLSerialize.OPT_DATA_TOKEN);
1874        }
1875    }
1876
1877
1878
1879/**
1880    * This is s():
1881    * s -> defRO[O]
1882    **/

1883    void s(Object JavaDoc me, Method readObjectMethod)
1884    throws ParserXML.ExceptionXML, IOException, ClassNotFoundException JavaDoc {
1885        invokeReadObject(me, readObjectMethod);
1886        if (!defROCalledStack.get()) { //no explicit call nor from readInt()
1887
System.err.println("calling defRO from s()");
1888            defaultReadObject_ver2_from_s(); //NB: any readInt() etc will have called defRO
1889
}
1890
1891        //If any <opt> found earlier (*only* place defROStack is used).
1892
//Problem:
1893
//It looks like the last tag seen in defaultReadObject_ver2...
1894

1895
1896/*
1897        if (!lookahead.tag.start && lookahead.tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
1898            System.err.println("reading <opt> in s()");
1899            ParserXML.Tag earlierOpt = defROStack.get();
1900            if (!earlierOpt.name.equals(lookahead.tag.name)) {
1901                System.err.println("earlier: "+earlierOpt);
1902                System.err.println("lookahead: "+lookahead.tag);
1903                throw new IOException("defRO stack not same as lookahead");
1904            }
1905            eatClose(XMLSerialize.OPT_DATA_TOKEN);
1906        }
1907*/

1908
1909        ParserXML.Tag earlierOpt = defROStack.get();
1910        if (earlierOpt.start && earlierOpt.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
1911            advance(); //read in, so we can check it
1912
if (!earlierOpt.name.equals(lookahead.tag.name)) {
1913                System.err.println("earlier: "+earlierOpt);
1914                System.err.println("lookahead: "+lookahead.tag);
1915                //throw new IOException("defRO stack not same as lookahead");
1916
System.exit(0);
1917            }
1918        System.err.println("reading <opt> in s()");
1919        eatClose(XMLSerialize.OPT_DATA_TOKEN);
1920    }
1921
1922/*
1923        ParserXML.Tag earlierOpt = defROStack.get();
1924        if (!earlierOpt.equals(lookahead.tag)) {
1925            System.err.println("earlier: "+earlierOpt);
1926            System.err.println("lookahead: "+lookahead.tag);
1927            //throw new IOException("defRO stack not same as lookahead");
1928            System.exit(0);
1929        }
1930        if (earlierOpt.start && earlierOpt.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
1931            System.err.println("reading <opt> in s()");
1932            advance();
1933            eatClose(XMLSerialize.OPT_DATA_TOKEN);
1934        }
1935*/

1936
1937    }
1938
1939
1940
1941    void invokeReadObject(Object JavaDoc me, Method readObjectMethod)
1942    throws ClassNotFoundException JavaDoc, IOException {
1943        if (NOV_DEBUG) System.err.println("using correct class frame now (I believe)");
1944        if (NESTEDREADOBJECT_DEBUG) System.err.print("\t--JSX deserialize--");
1945        try {
1946            readObjectMethod.invoke(me, readObjectArglist);
1947            if (NESTEDREADOBJECT_DEBUG)
1948                System.err.println("\tinvoke "+readObjectMethod+" on "+me+" of " +me.getClass());
1949        } catch (InvocationTargetException e) {
1950            if (DEBUG) {
1951                System.err.println("JSX InvocationTargetException:");
1952                System.err.println("Object which is a: " +me.getClass());
1953                e.printStackTrace(); //should we just rethrow this? YES!
1954
}
1955            //we just copied Java's ObjectInputStream handling of it!
1956
//It does all this in an "invokeObjectReader" method
1957
Throwable JavaDoc t = e.getTargetException();
1958            if (t instanceof ClassNotFoundException JavaDoc)
1959                throw (ClassNotFoundException JavaDoc) t;
1960            else if (t instanceof IOException)
1961                throw (IOException)t;
1962            else if (t instanceof RuntimeException JavaDoc)
1963                throw (RuntimeException JavaDoc) t;
1964            else if (t instanceof Error JavaDoc)
1965                throw (Error JavaDoc) t;
1966            else
1967                throw new Error JavaDoc("internal error");
1968        } catch (IllegalAccessException JavaDoc e) {
1969            System.err.println("IllegalAccessExcetion - please report this error");
1970            e.printStackTrace();
1971            //defaultReadObject(); //can this happen?
1972
}
1973        //POP: at end of RO, pop stack, to get the most recently invoked defRO
1974
//tag = defRO_tagStack.remove(defRO_tagStack.size()-1);
1975
}
1976
1977
1978
1979/* copy, retaining all comments:
1980//A convenience wrapper for argument passing.
1981        Vector defROStack = new Vector();
1982        Vector defROCalledStack = new Vector();
1983    ParserXML.Tag readObject(Object me)
1984            throws IOException, ClassNotFoundException {
1985        Class meClassFrame = Class.forName(ParseUtilities.descapeDollar(lookahead.tag.name));
1986        ParserXML.Attr[] addAttrTmp = {lookahead.attr, lookahead.name};//glue
1987        if (ADDATTR_NULL_DEBUG)
1988            System.err.println("addAttrTmp =" +addAttrTmp);
1989
1990        //(1). set up call to defaultReadObject, from whichever path.
1991            //Note that these are always direct, so don't need to stack.
1992        defaultReadObject_attrs_in = addAttrTmp; //pass arg in
1993        defaultReadObject_me = me; //pass arg in
1994        defROStack.add(null); //Make room on top of stack - in case no defRO call
1995        defROCalledStack.add(null); //null means "not called"
1996        // defaultReadObject_ver2(); //*always* called!
1997            //condition this call, so only call directly, or when no RO
1998        // ParserXML.Tag entryTag = defaultReadObject_tag;
1999        Class clazz = meClassFrame;
2000            //me.getClass();
2001
2002        Method readObjectMethod = XMLSerialize.getDeclaredMethod(clazz,
2003"readObject", OIS_ARGS, Modifier.PRIVATE, Modifier.STATIC);
2004        if (readObjectMethod==null) {
2005            defaultReadObject_ver2(); //called by default (if no readObject)
2006        } else {
2007            if (NOV_DEBUG) System.err.println("using correct class frame now (I believe)");
2008            if (NESTEDREADOBJECT_DEBUG) System.err.print("\t--JSX deserialize--");
2009            try {
2010                readObjectMethod.invoke(me, readObjectArglist);
2011                if (NESTEDREADOBJECT_DEBUG)
2012                    System.err.println("\tinvoke "+readObjectMethod+" on "+me+" of " +me.getClass());
2013            } catch (InvocationTargetException e) {
2014                if (DEBUG) {
2015                    System.err.println("JSX InvocationTargetException:");
2016                    System.err.println("Object which is a: " +me.getClass());
2017                    e.printStackTrace(); //should we just rethrow this? YES!
2018                }
2019                //we just copied Java's ObjectInputStream handling of it!
2020                //It does all this in an "invokeObjectReader" method
2021                Throwable t = e.getTargetException();
2022                if (t instanceof ClassNotFoundException)
2023                    throw (ClassNotFoundException) t;
2024                else if (t instanceof IOException)
2025                    throw (IOException)t;
2026                else if (t instanceof RuntimeException)
2027                    throw (RuntimeException) t;
2028                else if (t instanceof Error)
2029                    throw (Error) t;
2030                else
2031                    throw new Error("internal error");
2032            } catch (IllegalAccessException e) {
2033                System.err.println("IllegalAccessExcetion - please report this error");
2034                e.printStackTrace();
2035                //defaultReadObject(); //can this happen?
2036            }
2037            //POP: at end of RO, pop stack, to get the most recently invoked defRO
2038            //tag = defRO_tagStack.remove(defRO_tagStack.size()-1);
2039        }
2040        ParserXML.Tag entryTag;
2041        entryTag = (ParserXML.Tag) defROStack.get(defROStack.size()-1);
2042        //(1). If entryTag==null, it means that defRO was *not* called...
2043        //(2). If defRO was not called, it means that we need to eat the def data,
2044        // or just ignore it (in fact, there shouldn't be any def data)
2045        //(3). We call "eatDef()", which leaves us in the same state as
2046        //the real def. In fact, as a short-cut fake, we could just call defRO...
2047        //(4). What we *should* do is copy defaultReadObject_ver2, and
2048        //remove the forwarding bits... not sure how to do this... just set
2049        //"obj" to null?
2050        if (entryTag==null) {
2051            defaultReadObject_ver2(); //if not called. This can happen if:
2052                //(1). RO
2053                //(2). not called explicitly within RO
2054                //(3). no writeObject or writeInt etc within RO
2055                //-> we need to call it now, to set up the next part
2056                //set flag for writeObject (ie deserialize) and writePrim
2057            entryTag = (ParserXML.Tag) defROStack.get(defROStack.size()-1);
2058        }
2059
2060
2061
2062        if (NOV_DEBUG) System.err.println("------ entryTag just got from defROStack: "+entryTag);
2063
2064        //ParserXML.Tag entryTag = defaultReadObject_tag; //assuming that def
2065            //was called... if not, what is the value of this?
2066
2067        //eat any opt tags that appear (eg writeObject made them, but no readObject)
2068            if (KELLYSUPER_DEBUG)
2069                System.err.println("called readObject, entryTag is "+entryTag);
2070            if (entryTag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
2071                ParserXML.Tag tag = p.readTag();
2072                if (tag.start && tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {//eat <opt>
2073                    addAttr(null, false); //eat it
2074                    tag = p.readTag(); //read next one: in case <opt></opt>
2075                }
2076                checkCloseTag(XMLSerialize.OPT_DATA_TOKEN, tag); //String, Tag
2077                //defaultReadObject_tag = p.readTag();
2078                defROStack.set(defROStack.size()-1, p.readTag()); //replace the top null
2079                if (((ParserXML.Tag)defROStack.get(defROStack.size()-1)).start)
2080                    defaultReadObject_attrs_out = addName(); //to set up...
2081            }
2082        //What if there is a writeObject, but no readObject?
2083            if (KELLYSUPER_DEBUG)
2084                System.err.println("entryTag is "+entryTag);
2085
2086// if (readObjectMethod==null) { //separate, not "else", for clarity
2087// defaultReadObject_ver2();
2088// }
2089        //return defaultReadObject_tag; //get return value out
2090        return (ParserXML.Tag)defROStack.remove(defROStack.size()-1); //finally pop
2091    }
2092*/

2093
2094
2095
2096/** ------------------------------------------------------------------------
2097    * defaultReadObject
2098    * -----------------
2099    * For superVersion, will be called directly from object's readObject()...
2100    * Switch, according to which version, for back-compatibility.
2101    **/

2102    void defaultReadObject() throws IOException, ClassNotFoundException JavaDoc {
2103        if (superVersion) {
2104            ; //disable, so that always called.
2105
defaultReadObject_ver2(); //new, nesting one
2106
//re-enable this, so that the *time* of call is correct
2107
} else {
2108            defaultReadObject_ver1(); //old
2109
}
2110    }
2111
2112
2113
2114/**
2115    * defaultReadObject_ver2_from_d()
2116    * -------------------------------
2117    * Only called from d().
2118    *
2119    **/

2120    void defaultReadObject_ver2_from_d() throws IOException, ClassNotFoundException JavaDoc {
2121        defROCalledStack.set(true);
2122        Object JavaDoc me = defaultReadObject_me;
2123
2124        addPrim(new ParserXML.Attr[] {lookahead.attr, lookahead.name}, me, false);
2125        advance();
2126        while (openF(me)) {
2127            createObject(lookahead.tag, new ParserXML.Attr[]{lookahead.attr,lookahead.name}, me); //F();
2128
advance(); //ideal if F() set lookahead when eatClose()
2129
}
2130    }
2131
2132
2133/**
2134    * defaultReadObject_ver2_from_s()
2135    * ------------------------
2136    * Only called from s().
2137    **/

2138    void defaultReadObject_ver2_from_s() throws IOException, ClassNotFoundException JavaDoc {
2139        defROCalledStack.set(true);
2140        Object JavaDoc me = defaultReadObject_me;
2141            //(1). this argument is "passed" to us
2142
//(2). we stack a local copy, to save against recursion
2143
addPrim(defaultReadObject_attrs_in, me, false);
2144        advance(); //ideal: last match should have set lookahead
2145
while (openF(me)) { //uses lookahead
2146
createObject(lookahead.tag,
2147                                    new ParserXML.Attr[]{lookahead.attr,lookahead.name},
2148                                    me); //F();
2149
advance();//ideal: F() sets lookahead when eatClose()
2150
}
2151        if (lookahead.tag.start && lookahead.tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {// eatOpen(<O>)
2152
addPrim(new ParserXML.Attr[] {lookahead.attr,lookahead.name}, me, false);
2153        }
2154        defaultReadObject_attrs_out =
2155            new ParserXML.Attr[] {lookahead.attr,lookahead.name};
2156        defROStack.set(lookahead.tag); //interface to 2 x readObject()
2157
}
2158
2159
2160    Object JavaDoc defaultReadObject_me;
2161    ParserXML.Attr[] defaultReadObject_attrs_in;
2162    ParserXML.Attr[] defaultReadObject_attrs_out;
2163    void defaultReadObject_ver2() throws IOException, ClassNotFoundException JavaDoc {
2164        defROCalledStack.set(true);
2165        Object JavaDoc me = defaultReadObject_me;
2166            //(1). this argument is "passed" to us
2167
//(2). we stack a local copy, to save against recursion
2168
addPrim(defaultReadObject_attrs_in, me, false);
2169        advance(); //ideal: last match should have set lookahead
2170
while (openF(me)) { //uses lookahead
2171
createObject(lookahead.tag,
2172                                    new ParserXML.Attr[]{lookahead.attr,lookahead.name},
2173                                    me); //F();
2174
advance();//ideal: F() sets lookahead when eatClose()
2175
}
2176        if (lookahead.tag.start && lookahead.tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {// eatOpen(<O>)
2177
addPrim(new ParserXML.Attr[] {lookahead.attr,lookahead.name}, me, false);
2178        }
2179        defaultReadObject_attrs_out =
2180            new ParserXML.Attr[] {lookahead.attr,lookahead.name};
2181        defROStack.set(lookahead.tag); //interface to 2 x readObject()
2182
}
2183
2184
2185
2186/** -------------------------------------------------------------------------
2187    * read the next token (tag) into lookahead. Should be done for every match
2188    *
2189    */

2190    void advance() throws IOException {
2191        ParserXML.Tag tag = p.readTag();
2192        if (tag.start) lookahead.set(tag, addName());
2193        else lookahead.set(tag, null, null); //no attributes to read for close tag
2194
}
2195
2196/** Check lookahead to see if it is an <F> or not.
2197    * This is complex: <F> is not an actual tag, but can only be discerned by
2198    * it *not* being any of the other possible tags. That is, it is by
2199    * default.
2200    **/

2201
2202    boolean openF(Object JavaDoc me) throws IOException, ClassNotFoundException JavaDoc {
2203        if (lookahead.tag==null) {
2204            throw new EOFException("Premature EOF");
2205        }
2206        else if (lookahead.tag.start && lookahead.tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {//<O>
2207
return false; //<O>, not <F>
2208
}
2209        else if (lookahead.tag.start && lookahead.name!=null && lookahead.name.value.equals(XMLSerialize.SUPER_TOKEN)) {//<C>
2210
return false; //<C>, not <F>
2211
}
2212        else if (lookahead.tag.start) { //default is <F>
2213
return true;
2214        }
2215        else if (!lookahead.tag.start) {//</C> - validity checked later
2216
return false;
2217        }
2218        else {
2219            throw new Error JavaDoc("Internal error: Please send the stacktrace and XML "+
2220                "input file to bren@mail.csse.monash.edu.au to report this problem");
2221        }
2222    }
2223
2224
2225/* Store version before refactoring
2226    Object defaultReadObject_me;
2227    ParserXML.Attr[] defaultReadObject_attrs_in;
2228    ParserXML.Attr[] defaultReadObject_attrs_out;
2229    void defaultReadObject_ver2() throws IOException, ClassNotFoundException {
2230        defROCalledStack.set(true);
2231        Object me = defaultReadObject_me;
2232            //(1). this argument is "passed" to us
2233            //(2). we stack a local copy, to save against recursion
2234        addPrim(defaultReadObject_attrs_in, me, false);
2235
2236        while (true) {
2237            ParserXML.Tag tag = p.readTag();
2238            if (tag==null) throw new EOFException("Premature EOF");
2239            if (tag.start) {
2240                ParserXML.Attr[] addAttrTmp = addName();
2241                if (tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {//<O>
2242                    addPrim(addAttrTmp, me, false);
2243                    defROStack.set(tag);
2244                    return;
2245                } else {//<C>
2246                    ParserXML.Attr name = addAttrTmp[1];
2247                    if (name!=null && name.value.equals(XMLSerialize.SUPER_TOKEN)) {
2248                        defaultReadObject_attrs_out = addAttrTmp;
2249                        defROStack.set(tag);
2250                        return;
2251                    } else {//<F>
2252                        createObject(tag, addAttrTmp, me);
2253                    }
2254                }
2255            } else { //</?>
2256                defROStack.set(tag);
2257                return;
2258            }
2259        } //while
2260    }
2261*/

2262
2263
2264/* a confusion:
2265    tag.name - changes with the type
2266    attr.value - changes with the name
2267*/

2268
2269
2270/* Copy retaining comments:
2271
2272    * new Version of defaultCalledStore
2273    * Should it still have "defaultCalledStore" and "emptyTagStore"?
2274
2275    //boolean defaultCalledStore; //so only called once per object used??
2276    Object defaultReadObject_me; //arg in
2277    //Class defaultReadObject_classFrame; //arg in
2278    //ParserXML.Tag defaultReadObject_tag; //return value
2279    ParserXML.Attr[] defaultReadObject_attrs_in; //arg
2280    ParserXML.Attr[] defaultReadObject_attrs_out; //return value
2281    //boolean emptyTagStore; //?
2282//Called from ObjIn and also from here
2283    void defaultReadObject_ver2() throws IOException, ClassNotFoundException {
2284        defROCalledStack.set(true);
2285
2286            //just to say that this work has been done
2287        Object me = defaultReadObject_me; //"passing" these two arguments.
2288        ParserXML.Attr[] addAttrTmp = defaultReadObject_attrs_in;
2289        if (ADDATTR_NULL_DEBUG)
2290            System.err.println("addAttrTmp = "+addAttrTmp);
2291        addPrim(addAttrTmp, me, false);
2292
2293        while (true) {
2294            ParserXML.Tag tag = p.readTag();
2295            if (tag==null) throw new EOFException("Premature EOF");
2296            if (tag.start) {
2297                if (tag.name.equals(XMLSerialize.OPT_DATA_TOKEN)) {
2298                    if (NOV_DEBUG) System.err.println("got a opt data tag");
2299                    addPrim(addName(), me, false); //just clear end of opt tag
2300                        //add a "discard" flag arg?
2301                    //defaultReadObject_tag = tag; //needed!
2302                    defROStack.set(tag); //replace the top null
2303                            //PUSH tag for readObject
2304                    return;
2305                } else {
2306                    addAttrTmp = addName();//to detect "sub-class"...
2307                    ParserXML.Attr name = addAttrTmp[1];
2308                    if (name!=null && name.value.equals(XMLSerialize.SUPER_TOKEN)) {
2309                        if (NOV_DEBUG) System.err.println("defRO got an open subclass tag: "+tag);
2310                        defaultReadObject_attrs_out = addAttrTmp;
2311                        //defaultReadObject_tag = tag;
2312                        defROStack.set(tag); //replace the top null
2313                            //PUSH tag for readObject
2314                        return; //break loop
2315                    } else {
2316                        //or falls through to here:
2317                        if (NOV_DEBUG) System.err.println("defRO got an open non-subclass tag: "+tag);
2318                        createObject(tag, addAttrTmp, me); //need classFrame eventually
2319                        //add a flag arg to createObject - "discard", to eatDef
2320                    }
2321                }
2322            } else { //ie close tag ---- no addAttrTmp is appropriate!
2323                if (NOV_DEBUG) System.err.println("defRO got a close tag: "+tag);
2324                //defaultReadObject_tag = tag;
2325                defROStack.set(tag); //replace the top null
2326                            //PUSH tag for readObject
2327                            //defRO_tagStack.add(tag);
2328                return; //break loop
2329            }
2330        } //while
2331    //Never gets here! - infinite while, only exits by return or exception
2332    }
2333*/

2334
2335
2336    //these were static, and may have made JSX un-thread-safe for
2337
//Jean-Chrsitophe Salome, from Paris, 10 Oct 2001 (JSX's 1st birthday)
2338
boolean defaultCalledStore; //so only called once per object
2339
Object JavaDoc meStore; //pass arg to object's readObject()
2340
Class JavaDoc meClassFrameStore;
2341    boolean emptyTagStore;
2342//Called from ObjIn and also from here
2343
void defaultReadObject_ver1() throws
2344            IOException,
2345            //NoSuchFieldException,
2346
ClassNotFoundException JavaDoc {
2347        //NotActiveException
2348
if (!defaultCalledStore) { //only do it once, until reset
2349
defaultCalledStore = true;
2350            if (!emptyTagStore) {
2351                    //addTags(meStore, meClassFrameStore); //<--- before absorbed
2352
Object JavaDoc me = meStore; //"passing" these two arguments.
2353
Class JavaDoc meClassFrame = meClassFrameStore;
2354                ParserXML.Tag tag;
2355                while ( ((tag=p.readTag())!=null) && (tag.start)) {
2356                    createObject(tag, me);
2357                }
2358                if (tag==null) throw new EOFException("Premature EOF");
2359                else {
2360                    String JavaDoc className = meClassFrame.getName(); //set for class create
2361
String JavaDoc rightTag = ParseUtilities.escapeDollar(className);
2362                    String JavaDoc closeTag = tag.name;
2363                    if (cfg!=null && cfg.refactor!=null)
2364                        closeTag = cfg.refactor.mapClassname(closeTag); //map it, too
2365
if (!closeTag.equals(rightTag))
2366                        throw new ParserXML.ExceptionXML(
2367                            "</"+rightTag+">","</"+closeTag+">");
2368                }
2369            }
2370        }
2371    }
2372
2373
2374    /**--------------------------------------------------------------------------
2375        * addTags <------- absorbed into defaultReadObject() above: simpler
2376        * -------
2377        * Handles just tags (no attrs)
2378        * CONSIDER: is there an elegant way to reuse this code in Hashtable?
2379        * This is where the end tag is read
2380        * Perhaps pass in the open tag explicity, rather than (inefficiently)
2381        * recalling it from the object itself. Why? Because this doesn't work
2382        * for null objects.
2383        **/

2384/*
2385    private void addTags(Object me, Class meClassFrame) throws
2386            ClassNotFoundException,
2387            //NoSuchFieldException,
2388            IOException {
2389        ParserXML.Tag tag;
2390        while ( ((tag=p.readTag())!=null) && (tag.start))
2391            //createObject(tag.name, me); //add this new tag to me
2392            //createObject(tag, me, meClassFrame); //add this new tag to me //30Apr2001
2393            createObject(tag, me); //add this new tag to me //30Apr2001
2394//May10: NOTE!!! We throw away the returned value, because it is already
2395                //plugged in for us! (done like this, because parent not known here)
2396                //Returned value is only used by: array, vector, hashtable
2397        if (tag==null)
2398            throw new EOFException("Premature EOF");
2399// if (me==null) { //dangling else!
2400// if (!tag.name.equals(XMLSerialize.NULL_TOKEN)) //special case of null
2401// throw new ParserXML.ExceptionXML(
2402// "</"+XMLSerialize.NULL_TOKEN+">","</"+tag.name+">");
2403// }
2404        else {
2405            String className = meClassFrame.getName(); //set for class create
2406            String rightTag = ParseUtilities.escapeDollar(className);
2407            //if (!tag.name.equals(ParseUtilities.escapeDollar(me.getClass().getName())))
2408            if (!tag.name.equals(rightTag))
2409                throw new ParserXML.ExceptionXML(
2410                    "</"+rightTag+">","</"+tag.name+">");
2411        }
2412    }
2413*/

2414
2415
2416
2417/** ---------------------------------------------------------------------------
2418    * readResolve
2419    * -----------
2420    * Reflectively invoke the present objects's readResolve().
2421    * Return value can be: an object or an exception. We are using an
2422    * Exception to flag the result, instead of trying to encode it in the
2423    * return value itself (by wrapping it, for example - which is sort of how
2424    * Exceptions work, anyway.
2425    * QUESTION: If we checked for it separately, we can choose whether to cache
2426    * or not...
2427    * "parameterless" = no-arg
2428    **/

2429    Object JavaDoc readResolve(Object JavaDoc me, String JavaDoc alias) throws ObjectStreamException {
2430        if (READRESOLVE_DEBUG)
2431            System.err.println("entered readResolve, with "+me+" and alias="+alias);
2432        try {
2433            Method readResolveMethod = null;
2434            try {
2435                readResolveMethod = me.getClass().getDeclaredMethod("readResolve", new Class JavaDoc[]{});
2436            } catch (NoSuchMethodException JavaDoc e) {
2437                //Not an error - just means that it doesn't implement readResolve
2438
if (READRESOLVE_DEBUG)
2439                    System.err.println("No readResolve() found - checking super");
2440                //<ADD by="BGM"> code triggered by Kevin Day (Trumpet, Inc.)
2441
Class JavaDoc clazz = me.getClass(); //repeat above, to avoid endless loop
2442
while (clazz!=Object JavaDoc.class) { //iterate up the superclasses
2443
try {
2444                        clazz = clazz.getSuperclass();
2445                        readResolveMethod = clazz.getDeclaredMethod("readResolve", new Class JavaDoc[]{});
2446                        break; //a funny kind of Exception-continued loop (check JOS code)
2447
} catch (NoSuchMethodException JavaDoc continueLoop) {
2448                    }
2449                }
2450                if (readResolveMethod==null) //ie, if no readResolve() found...
2451
return me;
2452                if (!checkSuperMethodAccess(readResolveMethod, me.getClass()))
2453                    return me; //forget it - invoke nothing
2454
//</ADD>
2455

2456                //<DEL by="BGM">
2457
//try {
2458
// Class superClass = me.getClass().getSuperclass();
2459
// if (superClass!=null)
2460
// readResolveMethod = superClass.getDeclaredMethod("readResolve", new Class[]{});
2461
//get public readResolve() visible from any superclasses.
2462
//NOTE: technically, I think it should just be the first one found,
2463
//and for simulating inheritance, it should include all but private
2464
//(ie, not just public)
2465
//This improved hack provided by Kevin Day (Trumpet, Inc.)
2466
//readResolveMethod = me.getClass().getSuperclass().getMethod("readResolve", new Class[]{});
2467
//} catch (NoSuchMethodException e2) { //of super
2468
// return me; //forget it - invoke nothing
2469
//}
2470
//if (!checkSuperMethodAccess(readResolveMethod, me.getClass()))
2471
// return me; //forget it - invoke nothing
2472
//</DEL>
2473
}//end of super - method is now set correctly
2474
readResolveMethod.setAccessible(true);
2475            Object JavaDoc newme = readResolveMethod.invoke(me, new Object JavaDoc[]{}); //no arg
2476
updateAlias(newme, alias); //only need to update if it really is new.
2477
if (READRESOLVE_DEBUG)
2478                System.err.println("readResolve returned "+newme);
2479            return newme; //if no readResolve - the object is simply unchanged!
2480

2481        } catch (InvocationTargetException e) {
2482            System.err.println("JSX InvocationTargetException:");
2483            //e.printStackTrace(); //not sure how this should be handled...
2484
//is this the best way to handle it? What does Serialization do?
2485
//Probably best to throw whatever is thrown.
2486
try {
2487                throw (ObjectStreamException) e.getTargetException(); //uncork it
2488
//contract of readResolve guarantees that it only throws this class
2489
//I should check that it *does* throw only this kind...
2490
} catch (ClassCastException JavaDoc f) {
2491                throw new Error JavaDoc("readResolve() of "+me.getClass()+ " threw an '"+e.getTargetException()+"'. It should only be of type ObjectStreamException");
2492            }
2493        } catch (Exception JavaDoc e) {
2494            System.err.println("not an InvocationTargetException");
2495            e.printStackTrace();
2496        }
2497        return null; //to signal some error.
2498
}
2499
2500//Adapted from ObjectInputStream
2501
static private boolean checkSuperMethodAccess(Method scMethod, Class JavaDoc ofClass) {
2502        if (scMethod == null) {
2503            return false;
2504        }
2505        int supermods = scMethod.getModifiers();
2506        if (Modifier.isPublic(supermods) || Modifier.isProtected(supermods)) {
2507            return true;
2508        } else if (Modifier.isPrivate(supermods)) {
2509            return false;
2510        } else {
2511        // check package-private access.
2512
return isSameClassPackage(scMethod.getDeclaringClass(), ofClass);
2513            //NB: ofClass is the *runtime* (lowest, leaf) class, *not* the super
2514
//return false; //cheat for now - inherited default visible readResolve
2515
//won't work properly at the moment.
2516
}
2517    }
2518    /* Will not work for array classes. */
2519    static private boolean isSameClassPackage(Class JavaDoc cl1, Class JavaDoc cl2) {
2520        if (cl1.getClassLoader() != cl2.getClassLoader()) {
2521            return false;
2522        } else {
2523            String JavaDoc clName1 = cl1.getName();
2524            String JavaDoc clName2 = cl2.getName();
2525            int idx1 = clName1.lastIndexOf('.');
2526            int idx2 = clName2.lastIndexOf('.');
2527            if (idx1 == -1 || idx2 == -1) {
2528                /* One of the two doesn't have a package. Only return true
2529                * if the other one also does not have a package.
2530                */

2531                return idx1 == idx2;
2532            } else {
2533                return clName1.regionMatches(false, 0,
2534                clName2, 0, idx1 - 1);
2535            }
2536        }
2537    }
2538
2539
2540
2541
2542
2543
2544
2545    /**--------------------------------------------------------------------------
2546        * deserializeNull()
2547        * -----------------
2548        * Called from createObject(), to separate out the parsing code.
2549        **/

2550    //private NamedObject deserializeNull(Object parent) throws
2551
private NamedObject deserializeNull(ParserXML.Attr[] addAttrTmp, Object JavaDoc parent) throws
2552            ClassNotFoundException JavaDoc,
2553            //NoSuchFieldException,
2554
NotActiveException,
2555            IOException {
2556        String JavaDoc name = null;
2557        Object JavaDoc me = null;
2558        boolean named = false;
2559        //ParserXML.Attr firstAttr = addPrim(me, false); //gets NAME_TOKEN
2560
ParserXML.Attr firstAttr = addPrim(addAttrTmp, me, false); //gets NAME_TOKEN
2561
if (!firstAttr.nameMissing) { //see dup in createArray
2562
named = true;
2563            name = firstAttr.value; //NB: name of field is *value* of attr
2564
}
2565        if (!firstAttr.emptyTag) { //if not <null/>
2566
//then start & end: <null></null> - read the end one.
2567
//addTags(null);
2568
ParserXML.Tag tag = p.readTag();
2569            if (tag==null)
2570                throw new EOFException("Premature EOF");
2571            else if (tag.start) //if start tag...
2572
throw new ParserXML.ExceptionXML(
2573                        "</"+XMLSerialize.NULL_TOKEN+">","<"+tag.name+">");
2574            else //if an end tag... it needs to match the <null> start tag...
2575
if (!tag.name.equals(XMLSerialize.NULL_TOKEN)) { //special case of null
2576
throw new ParserXML.ExceptionXML(
2577                        "</"+XMLSerialize.NULL_TOKEN+">","</"+tag.name+">");
2578                }
2579            }
2580
2581    //again, code dup from createArray
2582
if (parent!=null) {
2583            if (named) {
2584                if (name==null) { //should never happen
2585
throw new IOException("name: No " +XMLSerialize.NAME_TOKEN+ " attribute found for "+parent.getClass());
2586                }
2587                //try {
2588
currentGetField.putObj(name, me); //prim/obj?
2589
Field f = getAllField(parent.getClass(), name);
2590                    magic.setFinal(f, parent, me);
2591                    //f.set(parent, me);
2592
//} catch (NoSuchFieldException e) {
2593
//}
2594
return new NamedObject(name, me, named);
2595            } else { //!named -> (internal: parent, but no name)
2596
return new NamedObject(name, me, named);
2597            }
2598        } else { //parent==null -> (root, array, hash, vect)
2599
return new NamedObject(name, me, named);
2600        }
2601    }
2602
2603
2604
2605
2606    /**--------------------------------------------------------------------------t
2607        * getAllField()
2608        * -----------
2609        * Named in accord with the JSX serialization method "getAllFields()".
2610        * Returns private and superclass fields.
2611
2612        * If NoSuchFieldException, we call NoSuchFieldHander... and re-try just
2613        * *once* again:
2614        * If NoSuchFieldException,
2615        * if handler: get the new field and try once more.
2616        * if another NoSuchFieldException, then fail noisily.
2617        * else (ie no-handler): fail silently.
2618        * Thus, adding the handler is switches on debug exceptions (ie non-silent)
2619        *
2620        * This method never returns null.
2621        *
2622        **/

2623    private Field getAllField(Class JavaDoc c, String JavaDoc fieldName)
2624        throws SecurityException JavaDoc {
2625        Field field = null;
2626        try {
2627            field = attemptGetAllField(c, fieldName); //try once...
2628
} catch (NoSuchFieldException JavaDoc firstE) {//if field not there, try handler...
2629
if (cfg.noSuchFieldHandler!=null) {//...provided there is one...
2630
if (CFG_DEBUG) System.err.println("cfg = "+cfg);
2631                String JavaDoc mappedFieldName = null;
2632                try {
2633                    mappedFieldName = cfg.noSuchFieldHandler.noSuchField(c, fieldName);
2634                } catch (NoSuchFieldException JavaDoc secondE) {
2635                    throw new IllegalArgumentException JavaDoc(secondE+""); //wrap
2636
} //end: lookup field
2637
try {
2638                    field = attemptGetAllField(c, mappedFieldName); //try twice...
2639
} catch (NoSuchFieldException JavaDoc secondE) {
2640                        //if handler cannot handle it: "Noisy fail":
2641
throw new IllegalArgumentException JavaDoc(
2642                        "Mapped '"+fieldName+"'" +
2643                        " to '"+mappedFieldName+"'" +
2644                        ", but neither found in "+c+": "+secondE); //poor man's exception wrapping
2645
} //end: third NoSuchFieldException
2646
}
2647            //if no handler, then: "Silent fail".
2648
} //end: first NoSuchFieldException
2649
return field;
2650    }
2651
2652
2653    /**--------------------------------------------------------------------------t
2654        * attemptGetAllField()
2655        * --------------------
2656        * Named in accord with the JSX serialization method "getAllFields()".
2657        * returns private and superclass fields.
2658        * Called by "getAllFields" above.
2659        *
2660        * DANGER: it could find a field from a superclass that customises its
2661        * own serialization. This shouldn't happen. It really should check for
2662        * the readObject() method, and if present, disregard that declared class.
2663        *
2664        * We need to parse any class-qualified fields, and we can get these
2665        * directly from the named class - some unexpected efficiency.
2666        *
2667        * NOTE: should not consider any fields that are static and/or transient...
2668        *
2669        * This method never returns null.
2670        *
2671        */

2672
2673/* code from XMLSerialize:
2674
2675                Map fmap = SerialPF.getFieldNameType(c);
2676                if (fmap.containsKey(f.getName()))...
2677*/

2678
2679    private Field attemptGetAllField(Class JavaDoc c, String JavaDoc fieldName) throws
2680            NoSuchFieldException JavaDoc,
2681            SecurityException JavaDoc {
2682        if (fieldName==null)
2683            throw new NoSuchFieldException JavaDoc("field name had value 'null'");
2684        Field f = null;
2685        if ((f=getShadowedField(fieldName))!=null) { //if class-qualified
2686
//if ((f.getModifiers() & XMLSerialize.NOT_SERIALIZED) == 0) { //don't skip
2687
if (SerialPF.isFieldSerializable(f)) { //don't skip
2688
f.setAccessible(true); //so can see private fields (Java 1.2)
2689
return f; //then just grab it
2690
}
2691            else throw new NoSuchFieldException JavaDoc(f +": this field evolved to non-serializeable, it seems.");
2692        }
2693            //else continue on
2694
Class JavaDoc initc = c; //store it for error
2695
boolean isObjSer = Serializable.class.isAssignableFrom(initc);
2696        do {
2697            if (isObjSer && !Serializable.class.isAssignableFrom(c))
2698                continue; // if !isObjSer, then JSX does all; else JOS skips non-Ser
2699
try {
2700                f = c.getDeclaredField(fieldName);
2701            } catch (NoSuchFieldException JavaDoc e) { //do nothing; it's OK
2702
} catch (Exception JavaDoc e) {
2703                System.err.println(e +": No such field '"+ fieldName +"' in "+ c +" (superclass of " +initc);
2704            }
2705        } while ( f==null && (c = c.getSuperclass()) != Object JavaDoc.class);
2706        if (f==null)
2707            throw new NoSuchFieldException JavaDoc("JSX can't find the field "
2708            +((fieldName==null)?"<null>":"'"+fieldName+"'")
2709            + ". Not in " +initc+ ", or private, or in superclasses\n"
2710            + "Could it be that the class lacks a readObject() method, or is a non-Serializable ancestor of a Serializable class? (see JOS rules on this)");
2711        //if ((f.getModifiers() & XMLSerialize.NOT_SERIALIZED) == 0) { //don't skip
2712
if (SerialPF.isFieldSerializable(f)) { //don't skip
2713
f.setAccessible(true); //so can see private fields (Java 1.2)
2714
return f; //then just grab it
2715
}
2716            else throw new NoSuchFieldException JavaDoc(f +": this field evolved to non-serializeable, it seems.");
2717    }
2718
2719
2720    // must be non-static to access oisSubclass
2721
Field getShadowedField(String JavaDoc s) throws NoSuchFieldException JavaDoc {
2722        int dotpos = s.lastIndexOf('.');
2723        if (dotpos==-1) return null; //signal: couldn't find (clarity?)
2724

2725        String JavaDoc classQual = s.substring(0, dotpos); //front
2726
String JavaDoc fieldName = s.substring(dotpos+1); //end (skip the dot)
2727
Class JavaDoc clazz = null;
2728
2729        String JavaDoc escapedName = ParseUtilities.descapeDollar(classQual);
2730        try {
2731            ObjectStreamClass osc = magic.getOsc(escapedName);
2732            clazz = oisSubclass.resolveClass(osc); //done!
2733
} catch (IOException e) {
2734            e.printStackTrace();
2735        } catch (ClassNotFoundException JavaDoc e) {
2736// try {
2737
// clazz = Class.forName(ParseUtilities.descapeDollar(classQual));
2738
// } catch (ClassNotFoundException e) {
2739
System.err.println("The class-qualification of this shadowed field was not found: "+ s);
2740            e.printStackTrace();
2741        }
2742        return clazz.getDeclaredField(fieldName);
2743    }
2744
2745
2746/**==========================================================================**/
2747/**======================= ARRAY SECTION ====================================**/
2748/**==========================================================================**/
2749
2750    /**--------------------------------------------------------------------------
2751        * createArray
2752        * -----------
2753        * Create one Array object.
2754        * Need to parse the whole tag name to know the component type,
2755        * eg: <ArrayOf-ArrayOf-int ..../>
2756        * that is: (ArrayOf-)*Type
2757        * (actually use a constant ARRAY_TOKEN = "ArrayOf-")
2758        * The mapping is:
2759        * (ArrayOf-)*Type -> Type([])*
2760
2761        * It counts the ARRAY_TOKENS, extracts the type, then:
2762        * Array.new_Instance(Class.getClass(baseType), new int[count])
2763        * NB: we avoid the complex disambiguation constraint on tok, by name choice
2764
2765        * Complexity/Issues:
2766        * - Array of objects (nested tags)
2767        * - Array of primitives (attrs)
2768        * - named: a field (parent is fields of a classInstance)
2769        * - parent: only if a field or internal
2770        * - unnamed: root, hash, vect, or array, internal
2771        * - wrinkle 1: if parent and unnamed, then internal
2772        * - wrinkle 2: if primitive, name attr can be before or after length attr
2773      * NB: Internals are read in before we *know* they are internal, and they
2774      * can *only* occur in classInstances, so they will have a parent.
2775
2776        * Refactor for loose-coupled symbolic bindings
2777        * --------
2778        * Move all returned values to explicitly named variables for clarity
2779        * - less efficient, but Knuth's first law of optimization is "don't".
2780        **/

2781    //private NamedObject createArray(String tagName, Object parent) throws
2782
private NamedObject createArray(ParserXML.Attr[] addAttrTmp, String JavaDoc tagName, Object JavaDoc parent) throws
2783            ClassNotFoundException JavaDoc,
2784            //NoSuchFieldException,
2785
IOException {
2786        //Loose-coupling: move returned values to named variables
2787
int length = -1; //how many components of array
2788
String JavaDoc name = null; //field name of array (if parent is a classInstance)
2789
//NB: Name may be in firstAttr or secondAttr, for arrays of primitives
2790
boolean named = false; //redundant clarity (named==(name!=null))
2791
//named==true: if a *field* (of a classInstance)
2792
//named==false: if *internal* info (of a classInstance),
2793
// --or-- if *root* or *element* (of array, hash or vector).
2794
boolean emptyTag = false; //true=="<tag/>", false=="<tag>...</tag>"
2795

2796        //ParserXML.Attr firstAttr = addAttr(null, true); //object=null, isArray=true
2797
ParserXML.Attr firstAttr = addPrim(addAttrTmp, null, true); //object=null, isArray=true
2798
length = firstAttr.length;
2799        if (!firstAttr.nameMissing) { //if present, get name
2800
named = true;
2801            name = firstAttr.value; //NB: name of field is *value* of attr
2802
}
2803        emptyTag = firstAttr.emptyTag;
2804
2805        //If an array of primitives, call addAttr twice:
2806
//(1). addAttr, to get length (and name, if present at start)
2807
//(3). create array
2808
//(2). addAttr, to get elements/attributes (and name, if present)
2809
// NB: need length to create array
2810
// NB: don't read elements til array created (could buffer in a Vector)
2811
// NB: store name til after elements (attributes) are read
2812

2813            //THUS: in addAttr (for array of primitives):
2814
// (1). addAttr to return after seeing "length"; and
2815
// (2). to call addAttr a second time, treating attr as indices.
2816

2817            //THUS: in addAttr (for array of objects):
2818
// (1). reads the whole tag, including the ">" at the end of the tag.
2819
int dim = 0;
2820        int index=0;
2821        String JavaDoc tok = XMLSerialize.ARRAY_TOKEN;
2822        int skip = tok.length();
2823        //(1). take off the leading "ArrayOf-"'s (by incrementing an index for eff)
2824
while (tagName.startsWith(tok, index)) { //could be > eff if assume tok qual
2825
dim++; //and just check first char...
2826
index+=skip;
2827        }
2828        if (DEBUG) System.err.println("dim= " +dim);
2829        String JavaDoc type = tagName.substring(index); //get the type
2830
if (DEBUG) System.err.println("type= " +type);
2831        Object JavaDoc me = null;
2832        //try {
2833
int[] dimsArray = new int[dim];
2834            dimsArray[0] = length; //NOTE use of length parsed from attributes
2835
Class JavaDoc comp;
2836            //shockingly inefficient! This is probably why they used the 'L'
2837
if (type.equals("int")) comp = int.class;
2838            else if (type.equals("double")) comp = double.class;
2839            else if (type.equals("boolean")) comp = boolean.class;
2840            else if (type.equals("char")) comp = char.class;
2841            else if (type.equals("byte")) comp = byte.class;
2842            else if (type.equals("short")) comp = short.class;
2843            else if (type.equals("long")) comp = long.class;
2844            else if (type.equals("float")) comp = float.class;
2845            else {
2846                if (MIKE_DEBUG) System.err.println("LOGGING: array object component, within the Mike Special");
2847                ObjectStreamClass osc = magic.getOsc(ParseUtilities.descapeDollar(type));
2848                comp = oisSubclass.resolveClass(osc); //done!
2849
//comp = Class.forName(ParseUtilities.descapeDollar(type));
2850
}
2851            me = Array.newInstance(comp, dimsArray);
2852            putAlias(me);
2853        //} catch (Exception e) {
2854
//System.err.println("Illegal type as Array base component, it seems; "+e);
2855
//}
2856
if (DEBUG) System.err.println("type= " +me.getClass().getName());
2857        if (DEBUG) System.err.println("len= " +Array.getLength(me));
2858
2859
2860        ParserXML.Tag tag;
2861        ParserXML.Attr secondAttr = null;
2862        //If array of primitives...
2863
if (dim==1 && me.getClass().getComponentType().isPrimitive()) {
2864            //secondAttr = addAttr(me, true); //object=me, isArray=true
2865
addAttrTmp = addName(); //for *second* call
2866
secondAttr = addPrim(addAttrTmp, me, true); //object=me, isArray=true
2867
//addAttr now reads primitive attrs into array
2868
length = -1; //shouldn't be used again (just did it in addAttr)
2869
if (!secondAttr.nameMissing) { //was name mixed in with attrs?
2870
if (named) throw new IOException("two names: "
2871                    +"'"+firstAttr.value+"' and '"+ secondAttr.value+"' "
2872                    +"in "+parent.getClass());
2873                named = true;
2874                name = secondAttr.value; //NB: name of field is *value* of attr
2875
}
2876            emptyTag = secondAttr.emptyTag;
2877            //(1). above allows "obj-name" to be before or after elements.
2878
//(2). "length" must always preceed the elements (alt: vector buffer)
2879
//(3). firstAttr/secondAttr seems only to be needed for emptyTag.
2880
if (DEBUG) System.err.println("after setting primitive array, saw: " + secondAttr);
2881        }
2882        //If array of objects...
2883
else {
2884            //do the contained objects (as does Vector) - it dispatches in a loop
2885
//NOTE the recursion runs through create object, which tests isArray,
2886
//so as to automatically bottom out. AND WHAT A HACK IT IS!
2887
ParserXML.Attr end = p.readAttr(); //slurp up the left-over '>' after length read.
2888
if (DEBUG) System.err.println("after starting reference array, saw: " + end);
2889                //NB: this implies that the name (if present) *must* precede length
2890
for (int i=0; i<length; i++) {
2891                //System.err.println("i = "+i);
2892
Object JavaDoc obj;
2893                tag=p.readTag();
2894                if (tag==null) throw new EOFException("Premature EOF");
2895                if (!tag.start)
2896                    throw new IOException("Expected "+length+" elements in array; read only"+i);
2897                if (tag.name.equals("java.lang.String") //bad: doubles up
2898
|| tag.name.equals("java.lang.Integer")
2899                        || tag.name.equals("java.lang.Double")
2900                        || tag.name.equals("java.lang.Boolean")
2901                        || tag.name.equals("java.lang.Character")
2902                        || tag.name.equals("java.lang.Byte")
2903                        || tag.name.equals("java.lang.Short")
2904                        || tag.name.equals("java.lang.Long")
2905                        || tag.name.equals("java.lang.Float")
2906                    )
2907                    obj = deserializeWrapper(tag, null).object;
2908                else
2909                    //obj = createObject(tag.name, null); //null=orphan object
2910
obj = createObject(tag, null); //null=orphan object //30Apr2001
2911
if (DEBUG) System.err.println("Adding {"+obj+"} to Array");
2912                Array.set(me, i, obj);
2913            } //for loop
2914
} //tags (for refernce base type - array or class)
2915

2916        if (!emptyTag) { //if not <..../>, check for close tag
2917
tag=p.readTag();
2918            if (tag==null) throw new EOFException("Premature EOF");
2919            if (tag.start)
2920                throw new IOException("End of array");
2921            if (!tag.name.equals(tagName)) //check for matching close tag
2922
throw new ParserXML.ExceptionXML(
2923                    "</"+tagName+">","</"+tag.name+">");
2924        }
2925//parent is null (this is an "orphan") for:
2926
//root, arrays, hash and vect
2927
//NB: Internals are read in before we *know* they are internal, and they
2928
//can *only* occur in classInstances, so they will have a parent.
2929

2930        if (parent!=null) {
2931            if (named) {
2932                if (name==null) { //should never happen
2933
throw new IOException("name: No " +XMLSerialize.NAME_TOKEN+ " attribute found for "+parent.getClass());
2934                }
2935                //if name -> (plug & return null); if no name (return me).
2936
//For primitives, call addAttr() twice - initialAttr is first one.
2937
//26May - now initialAttr is the initial value (whether called once
2938
//or twice.
2939
//try {
2940
currentGetField.putObj(name, me); //prim/obj?
2941
Field f = getAllField(parent.getClass(), name);
2942                    magic.setFinal(f, parent, me);
2943                    //f.set(parent, me);
2944
//} catch (NoSuchFieldException e) {
2945
//}
2946
//return null; //a signal: already been dealt with...
2947
//gets here if: has parent, and has attr.value, and has name
2948
return new NamedObject(name, me, named);
2949            } else { //!named -> (internal: parent, but no name)
2950
return new NamedObject(name, me, named);
2951            }
2952        } else { //parent==null -> (root, array, hash, vect)
2953
return new NamedObject(name, me, named);
2954        }
2955
2956    }
2957
2958
2959//Why is the following code still here?
2960
/**--------------------------------------------------------------------------
2961        * getArrayType: "ArrayOf-int" to "LI"
2962        * ------------
2963        * convert Array name string from:
2964        * [* //any number of '[' (including 0)
2965        * (B|C|D|F|I|J|S|Z) | //one of these represeting a primitive type
2966        * (L[^;]*;) //or the name of the class, in between 'L' and ';'
2967        **/

2968/*
2969    private static String getArrayType(Class clazz) {
2970        String a = ""; //will hold result
2971        String name = clazz.getName(); //NOTE: would need to escapeDollar
2972        if (DEBUG) System.err.println("Array type = \""+name+"\"");
2973        int i;
2974        for (i=0; name.charAt(i)=='['; i++)
2975            a += ARRAY_TOKEN;
2976        switch (name.charAt(i++)) {
2977            case 'B': a += "byte";
2978                break;
2979            case 'C': a += "char";
2980                break;
2981            case 'D': a += "double";
2982                break;
2983            case 'F': a += "float";
2984                break;
2985            case 'I': a += "int";
2986                break;
2987            case 'J': a += "long";
2988                break;
2989            case 'S': a += "short";
2990                break;
2991            case 'Z': a += "boolean";
2992                break;
2993            case 'L': a += name.substring(i, name.lastIndexOf(';'));
2994                        //could also be name.length()-1
2995            break;
2996        }
2997        // NB: in a multi-dimensional array, Class.getComponentType() returns
2998        // the next one further in (which may also be an array).
2999        return a;
3000    }
3001*/

3002
3003
3004
3005    /**--------------------------------------------------------------------------t
3006        * addAttr (map to primitives and String)
3007        * -------
3008        * Handles: byte, char, short, int, long, float, double and String.
3009        * As fields, arrays and custom data.
3010        * NOTE: String can never appear as custom data (can only be written as an
3011        * object).
3012        *
3013        * Consider: BIZARRE: Could do something odd for arrays: take the object
3014        * only as indicating the exact type of the array, and in here, when read
3015        * the length, actually create it. And yet it makes sense in a way.
3016        *
3017        * or, return a proper "attrMsg", which includes the length as info. This
3018        * is wha should be returned, anyway - not this hacked use of Attr.
3019        **/

3020    private ParserXML.Attr addAttr(Object JavaDoc o, boolean isArray) throws
3021            IOException,
3022            //NoSuchFieldException,
3023
ClassNotFoundException JavaDoc,
3024            ParserXML.ExceptionXML {
3025        ParserXML.Attr[] addAttrTmp = addName(); //split it up
3026
return addPrim(addAttrTmp, o, isArray);
3027    }
3028
3029    /**--------------------------------------------------------------------------t
3030        * addName
3031        * -------
3032        * Functionality factored out of addAttr, for the sake of super protocol.
3033        *
3034        **/

3035    private ParserXML.Attr[] addName() throws
3036            IOException,
3037            //NoSuchFieldException,
3038
//ClassNotFoundException,
3039
ParserXML.ExceptionXML {
3040        ParserXML.Attr attr; //it *will* be set
3041
ParserXML.Attr name = null; //it might not be set
3042
if ( !(attr=p.readAttr()).isEnd ) {
3043            if (DEBUG) System.err.println(attr);
3044            if (attr.name.equals(XMLSerialize.NAME_TOKEN)) { //if name of this class
3045
if (name!=null)
3046                    throw new ParserXML.ExceptionXML("just one name field");
3047                name = attr;
3048                //if (name.value.equals(XMLSerialize.SUPER_TOKEN)) //not field, an action
3049
// meClassFrame = Class.forName(ParseUtilities.descapeDollar(elementName));
3050
//continue; //next attr
3051
}
3052        }
3053        return new ParserXML.Attr[] {attr, name};
3054    }
3055
3056    private ParserXML.Attr addPrim(ParserXML.Attr[] addAttrTmp, Object JavaDoc o, boolean isArray) throws
3057            IOException,
3058            //NoSuchFieldException,
3059
ClassNotFoundException JavaDoc,
3060            ParserXML.ExceptionXML {
3061        //Class meClassFrame = o.getClass(); //if not superVersion
3062
int length = -1; //to flag if not set
3063
if (ADDATTR_NULL_DEBUG)
3064            System.err.println("addAttrTmp = "+addAttrTmp);
3065        ParserXML.Attr attr = addAttrTmp[0]; //now passed in above
3066

3067        ParserXML.Attr name = addAttrTmp[1]; //now passed in above
3068
if (!attr.isEnd) {
3069        //while (!(attr=p.readAttr()).isEnd) //cont from addName
3070
do {
3071            if (name==attr) continue; //only go on if not yet processed
3072
//ignore all fields of array, but for the name
3073
if (isArray) { //length or primitive attributes
3074
if (attr.name.equals(XMLSerialize.LENGTH_TOKEN)) { //if length
3075
if (INTERNAL_DEBUG) {
3076                        System.err.println("ARRAY LENGTH: " +attr);
3077                    }
3078                    length = Integer.parseInt(attr.value);
3079                    break; //break off part way through (will this work?)
3080
}
3081                if (INTERNAL_DEBUG) {
3082                    System.err.println("ARRAY PRIMITIVE ATTRIBUTES: " +attr);
3083                }
3084                //else, continues on, reading primitive values, and sticking them in.
3085
//assumed: .startsWith(XMLSerialize.ARRAY_PRIMITIVE_INDEX_TOKEN)
3086
String JavaDoc indexStr = attr.name.substring(XMLSerialize.ARRAY_PRIMITIVE_INDEX_TOKEN.length());
3087                int index = Integer.parseInt(indexStr); //we know it is an int
3088
if (DEBUG) System.err.println(attr);
3089                Object JavaDoc value = getValue(o.getClass().getComponentType(), attr);
3090                    //This also puts alias for String, because we can infer the type from
3091
//the field. Not so with custom data. I need to write this up. The
3092
//immediate result is that we serialize internal Strings like objects
3093
//(in tags), like primitives (in attributes).
3094
Array.set(o, index, value);
3095            }
3096            else { //if not array...
3097
if (attr.name.endsWith(XMLSerialize.ALIAS_STRING_TOKEN)) {//string alias
3098
if (INTERNAL_DEBUG) {
3099                        System.err.println("STRING ALIAS: " +attr);
3100                    }
3101                    //(1). extract the fieldName
3102
String JavaDoc fieldName = attr.name.substring(0, attr.name.indexOf(XMLSerialize.ALIAS_STRING_TOKEN));
3103                    //(2). get the value & use it to lookup the object
3104
Object JavaDoc strObj = aliasHash.get(attr.value); //get the object (String)
3105
//(4). assign that object to the field
3106
//try {
3107
currentGetField.putObj(fieldName, strObj); //prim/obj?
3108
Field f = getAllField(o.getClass(), fieldName);
3109                        //Field f = getAllField(meClassFrame, fieldName);
3110
if (f!=null) {
3111                          //Class fc = f.getType(); //type specified by java, not by xml
3112
//if (DEBUG) System.err.println("Made Field \""+fc.getName()+"\"");
3113
magic.setFinal(f, o, strObj);
3114                        }
3115                            //just pass discard into setFinal? That should catch everything!
3116
//} catch (NoSuchFieldException e) {
3117
//}
3118
//f.set(o, strObj); //note: referring to already XML decoded object
3119
}
3120                else { //if not string alias...
3121
if (attr.name.startsWith(XMLSerialize.INTERNAL_PRIMITIVE_TOKEN)) {
3122                        if (INTERNAL_DEBUG) {
3123                            System.err.println("INTERNAL: " +attr);
3124                        }
3125                        String JavaDoc primString = attr.name.substring(XMLSerialize.INTERNAL_PRIMITIVE_TOKEN.length()); //omit prefix (typically '_' or "_." or "someclass.")
3126
if (DEBUG) System.err.println(attr);
3127                            //non-XML: we rely on the ordering of attributes
3128
//need (perhaps) to detect non-ordering, and at that time
3129
//start allowing for it....
3130
//or, we could detect the size, and add in nulls to match it.
3131
//And signal an error later.
3132
//NB: I think that Strings will always be done as tags, even
3133
//though attributes might be neater.
3134
primStore.add(attr.value); //keep it in the universal form of "string"
3135
//IF we do readUTF() as a primitive, then we need to test the
3136
//type of it here, and put it into
3137
//PROBLEM!!! We cannot tell the type of attributes!!! Thus, we
3138
//cannot handle Strings differently! We *could* add type
3139
//information, in the same hack that we use for alias strings; but
3140
//this is extra work, and is ugly. We simply don't know until
3141
//the readObject() code is run, and the present buffer method
3142
//means that we cannot tell at all, unless we kept track of the
3143
//exact order of all custom data written... but this is lost
3144
//in communication, because of the split between attr and tags...
3145
//Thus, we must change the format to do this, either utterly (so
3146
//that the order tells us), or slightly, to include type information
3147
//for Strings that are custom data. Need to write a little white
3148
//paper on this!
3149
//Question: how are internal Strings stored?
3150
//It doesn't matter, except that they will be read with
3151
//readObject, and so should not be on the primStore...
3152
//There is also an alias issue...
3153
}
3154                    else { //if an ordinary field (not an array at all)
3155
if (INTERNAL_DEBUG) {
3156                            System.err.println("ordinary: "+attr);
3157                        }
3158                        //try {
3159
if (IBM_DEBUG) System.err.println("IBM: currentGetField = "+currentGetField);
3160                            if (IBM_DEBUG) System.err.println("IBM: attr = "+attr);
3161                            currentGetField.putObj(attr.name, attr.value); //prim/obj?
3162
if (IBM_DEBUG) System.err.println("IBM: o is "+
3163                                (o==null?"null":"not null") );
3164                            //Class wolfClazz = o.getClass();
3165
//String wolfName = attr.name; //sep lines, for debug
3166
//Field f = getAllField(wolfClazz, wolfName);
3167
Field f = getAllField(o.getClass(), attr.name); //subclass Field?
3168
//Field f = getAllField(meClassFrame, attr.name);
3169
if (f!=null) {
3170                                Class JavaDoc fc = f.getType(); //type specified by java, not by xml
3171
if (DEBUG) System.err.println("Made Field \""+fc.getName()+"\"");
3172                                magic.setFinal(f, o, getValue(fc, attr));
3173                            }
3174                            //f.set(o, getValue(fc, attr));
3175
//} catch (NoSuchFieldException e) {
3176
//}
3177
}
3178                }
3179            }
3180        } while (!(attr=p.readAttr()).isEnd);
3181   }
3182            //combine info from the name and final attr returned. Awkward.
3183
attr.length = length;
3184        if (name==null) {
3185                //attr.value = null; //some paths assume the name is here... ugly
3186
attr.nameMissing = true;
3187            return attr; //simple. No name to return
3188
}
3189            //else, combine info from both (name and last attr)
3190
name.isEnd = attr.isEnd;
3191        name.emptyTag = attr.emptyTag;
3192        name.length = attr.length;
3193        return name;
3194    }
3195
3196
3197    /**--------------------------------------------------------------------------
3198        * getValue
3199        * ------------------
3200        * In: the class of the primitive; the attr (name-value pair)
3201        * Out: * object representing the primitive
3202        * Class may come from the "TYPE" field, or from array component type.
3203        * Used by:
3204        * deserializeWrapper
3205        * addAttr - note: an intermediate wrapper object is created redundantly
3206        *
3207        * It may be more efficient to not reuse this code.
3208        * when used for an attribute, it creates an Object needlessly;
3209        * however, such an object is required for Hashtable
3210        **/

3211    private Object JavaDoc getValue(Class JavaDoc fc, ParserXML.Attr attr) throws IOException {
3212        if (DEBUG) System.err.println("getting value of " +fc);
3213        if (fc.isPrimitive()) { //PRIMITIVE
3214
if (fc==int.class) return new Integer JavaDoc(attr.value); //most likely first
3215
else if (fc==double.class)
3216                return new Double JavaDoc(ParseUtilities.parseDouble(attr.value)); //second pr
3217
else if (fc==boolean.class) {
3218                    return new Boolean JavaDoc(ParseUtilities.parseBoolean(attr.value));
3219            }
3220            else if (fc==byte.class) return new Byte JavaDoc(attr.value);
3221            else if (fc==char.class) {
3222                String JavaDoc a = ParseUtilities.decodeXML(attr.value); //many chars may go to one
3223
if (a.length()!=1) throw new IllegalArgumentException JavaDoc("Character data must be exactly one character long; instead we got: '" +a+ "'");
3224                return new Character JavaDoc(a.charAt(0));
3225            }
3226            else if (fc==short.class) return new Short JavaDoc(attr.value);
3227            //else if (fc==int.class) return new Integer(attr.value));
3228
else if (fc==long.class) return new Long JavaDoc(attr.value);
3229            else if (fc==float.class)
3230                return new Float JavaDoc(ParseUtilities.parseFloat(attr.value));
3231            else throw new ParserXML.ExceptionXML(
3232                "Unimplemented primitive: \"" +fc.getName()+ "\"");
3233        }
3234        else { //CLASS
3235
if (fc==String JavaDoc.class) {
3236                String JavaDoc s = ParseUtilities.decodeXML(attr.value);
3237                putAlias(s); //String is an object - not called for attr string
3238
return s;
3239            }
3240            else throw new ParserXML.ExceptionXML(
3241                "'"+attr.name+"' is not primitive or String");
3242        }
3243    }
3244
3245
3246
3247
3248    /**--------------------------------------------------------------------------
3249        * addVectorElements
3250        * -----------------
3251        * Note: this should now be possible automatically, since we can read
3252        * private fields (and any internal serialization)
3253        * Note: Vector object already created when we get here.
3254        **/

3255  private void addVectorElements(Vector v) throws
3256            ClassNotFoundException JavaDoc,
3257            //NoSuchFieldException,
3258
IOException {
3259        ParserXML.Tag tag;
3260        while (true) {
3261            Object JavaDoc obj;
3262            tag = p.readTag();
3263            if (tag==null) throw new EOFException("Premature EOF");
3264            if (!tag.start) return; //done! caller will check if a correct endtag
3265
if (tag.name.equals("java.lang.String") //bad: doubles up
3266
|| tag.name.equals("java.lang.Integer")
3267                    || tag.name.equals("java.lang.Double")
3268                    || tag.name.equals("java.lang.Boolean")
3269                    || tag.name.equals("java.lang.Character")
3270                    || tag.name.equals("java.lang.Byte")
3271                    || tag.name.equals("java.lang.Short")
3272                    || tag.name.equals("java.lang.Long")
3273                    || tag.name.equals("java.lang.Float")
3274                )
3275                obj = deserializeWrapper(tag, null).object;
3276            else
3277                //obj = createObject(tag.name, null); //null=orphan object
3278
obj = createObject(tag, null); //null=orphan object //30Apr2001
3279
if (DEBUG) System.err.println("Adding {"+obj+"} to Vector");
3280            v.add(obj);
3281        }
3282        //if (DEBUG) System.err.println("Vector: \n" +v);
3283
}
3284
3285    /**--------------------------------------------------------------------------
3286        * addHashtableElements
3287        * --------------------
3288        * An container object.
3289        * Handles just tags (no attrs)
3290        **/

3291    private void addHashtableElements(Hashtable h) throws
3292            ClassNotFoundException JavaDoc,
3293            //NoSuchFieldException,
3294
IOException {
3295        ParserXML.Tag tag;
3296        while (true) {
3297            Object JavaDoc[] objs = {null, null};
3298            for (int i=0; i<objs.length; i++) { //{key, value}
3299
tag = p.readTag();
3300                if (tag==null) throw new EOFException("Premature EOF");
3301                if (!tag.start)
3302                    if (i==0) return; //end of hashtable
3303
else throw new IOException("incomplete Hashtable entry");
3304                if (tag.name.equals("java.lang.String") //bad: doubles up
3305
|| tag.name.equals("java.lang.Integer")
3306                        || tag.name.equals("java.lang.Double")
3307                        || tag.name.equals("java.lang.Boolean")
3308                        || tag.name.equals("java.lang.Character")
3309                        || tag.name.equals("java.lang.Byte")
3310                        || tag.name.equals("java.lang.Short")
3311                        || tag.name.equals("java.lang.Long")
3312                        || tag.name.equals("java.lang.Float")
3313                    )
3314                    objs[i] = deserializeWrapper(tag, null).object;
3315                else
3316                    //objs[i] = createObject(tag.name, null); //null=orphan object
3317
objs[i] = createObject(tag, null); //null=orphan object//30Apr2001
3318
}
3319            if (DEBUG) System.err.println("Putting {"+objs[0]+", "+objs[1]+"}");
3320            h.put(objs[0], objs[1]);
3321        }
3322        //if (DEBUG) System.err.println("Hashtable: \n"+h);
3323
}
3324
3325    /**--------------------------------------------------------------------------
3326        * deserializeClass
3327        * ----------------
3328        * Handles the attrs.
3329        * Need to refactor the test for wrapper type
3330        * Reads the whole thing in, including <x></x> or <x/>
3331        **/

3332    private NamedObject deserializeClass(ParserXML.Attr[] addAttrTmp, ParserXML.Tag startTag, Object JavaDoc cheatParent) throws
3333            ClassNotFoundException JavaDoc,
3334            //NoSuchFieldException,
3335
IOException {
3336        ParserXML.Attr attr = addAttrTmp[0];
3337        ParserXML.Attr end, name = null;
3338        ParserXML.Tag tag;
3339        //attr = p.readAttr(); //p is the ParserXML.
3340
if (DEBUG) System.err.println("deserializeClass: "+ attr);
3341        if (attr.name.equals(XMLSerialize.NAME_TOKEN)) {
3342            //System.err.println("Got a name in a wrapper: "+startTag.name+" "+attr.value+";");
3343
name = attr;
3344            attr = p.readAttr();
3345        }
3346        if (!attr.name.equals(XMLSerialize.CLASSNAME_TOKEN))
3347            throw new ParserXML.ExceptionXML(XMLSerialize.CLASSNAME_TOKEN+ " expected");
3348        if (!(end = p.readAttr()).isEnd)
3349            throw new ParserXML.ExceptionXML("\">\" or \"/>\" expected");
3350        if (!end.emptyTag) {
3351            tag = p.readTag();
3352            if (!tag.name.equals(startTag.name) || tag.start)
3353                throw new ParserXML.ExceptionXML("</"+tag.name+"> expected");
3354        }
3355        //IDEA: could make up the expected tag, and use Tag.equals() to compare
3356
if (DEBUG) System.err.println("String read in: " + attr.value);
3357
3358        String JavaDoc className = ParseUtilities.descapeDollar(attr.value);
3359        Class JavaDoc clazz = null;
3360        try {
3361            // clazz = Class.forName(className); //won't find primitives
3362
// Stefano Pedon encountered this bug - Mark van der Kraan diagnosed
3363
// and together they tested it.
3364
clazz=Thread.currentThread().getContextClassLoader().loadClass(className); //this brilliant bug-fix courtesy Ing Thomas Margreiter, 13 June 2002
3365
//javadocs: "If name denotes a primitive type or void, an attempt will"
3366
//"be made to locate a user-defined class in the unnamed package whose"
3367
//"name is name. Therefore, this method cannot be used to obtain any"
3368
//"of the Class objects representing primitive types or void."
3369
//bug scratch patch submitted by: matthew sandoz, Nov 2, 2001
3370

3371        } catch (ClassNotFoundException JavaDoc e) {
3372            if (className.equals("int")) clazz = int.class;
3373            else if (className.equals("double")) clazz = double.class;
3374            else if (className.equals("boolean")) clazz = boolean.class;
3375            else if (className.equals("byte")) clazz = byte.class;
3376            else if (className.equals("char")) clazz = char.class;
3377            else if (className.equals("short")) clazz = short.class;
3378            else if (className.equals("long")) clazz = long.class;
3379            else if (className.equals("float")) clazz = float.class;
3380            else if (className.equals("void")) clazz = void.class;
3381            else
3382                throw new ClassNotFoundException JavaDoc("class '"+className+"' not found");
3383        }
3384
3385        putAlias(clazz); //String is an object
3386
if (name!=null) { //can only happen for Strings... (ugly hack!)
3387
//try {
3388
currentGetField.putObj(name.value, clazz); //prim/obj?
3389
Field f = getAllField(cheatParent.getClass(), name.value);
3390                magic.setFinal(f, cheatParent, clazz);
3391                //f.set(cheatParent, clazz); //Custom "Class" object code...
3392
//} catch (NoSuchFieldException e) {
3393
//evolution policy is to fail silently.
3394
//}
3395
return new NamedObject(name.value, clazz, true); //name!=null -> true
3396
}
3397        return new NamedObject(null, clazz, false); //name!=null -> false
3398
}
3399
3400
3401    /**--------------------------------------------------------------------------
3402        * deserializeBinary
3403        * ----------------
3404        * Handles the attrs.
3405        * Need to refactor the test for wrapper type
3406        * Reads the whole thing in, including <x></x> or <x/>
3407        **/

3408    private NamedObject deserializeBinary(ParserXML.Attr[] addAttrTmp, ParserXML.Tag startTag, Object JavaDoc cheatParent) throws
3409            ClassNotFoundException JavaDoc,
3410            //NoSuchFieldException,
3411
IOException {
3412        ParserXML.Attr attr = addAttrTmp[0];
3413        ParserXML.Attr end, name = null;
3414        ParserXML.Tag tag;
3415        //attr = p.readAttr(); //p is the ParserXML.
3416
if (DEBUG) System.err.println("deserializeBinary: "+ attr);
3417        if (attr.name.equals(XMLSerialize.NAME_TOKEN)) {
3418            //System.err.println("Got a name in a wrapper: "+startTag.name+" "+attr.value+";");
3419
name = attr;
3420            attr = p.readAttr();
3421        }
3422        if (!attr.name.equals(XMLSerialize.VALUE_TOKEN))
3423            throw new ParserXML.ExceptionXML(XMLSerialize.VALUE_TOKEN+ " expected");
3424        if (!(end = p.readAttr()).isEnd)
3425            throw new ParserXML.ExceptionXML("\">\" or \"/>\" expected");
3426        if (!end.emptyTag) {
3427            tag = p.readTag();
3428            if (!tag.name.equals(startTag.name) || tag.start)
3429                throw new ParserXML.ExceptionXML("</"+tag.name+"> expected");
3430        }
3431        //IDEA: could make up the expected tag, and use Tag.equals() to compare
3432
if (DEBUG) System.err.println("Binary data read in: " + attr.value);
3433
3434        byte[] binaryData = ParseUtilities.decodeHex(attr.value);
3435        putAlias(binaryData); //String is an object
3436
if (name!=null) { //can only happen for Strings... (ugly hack!)
3437
//try {
3438
currentGetField.putObj(name.value, binaryData); //prim/obj?
3439
Field f = getAllField(cheatParent.getClass(), name.value);
3440                magic.setFinal(f, cheatParent, binaryData);
3441                //f.set(cheatParent, binaryData); //
3442
//} catch (NoSuchFieldException e) {
3443
//evolution policy is to fail silently.
3444
//}
3445
return new NamedObject(name.value, binaryData, true); //name!=null -> true
3446
}
3447        return new NamedObject(null, binaryData, false); //name!=null -> false
3448
}
3449
3450
3451    /**--------------------------------------------------------------------------
3452        * deserializeWrapper
3453        * ------------------
3454        * Handles the attrs.
3455        * Need to refactor the test for wrapper type
3456        * Reads the whole thing in, including <x></x> or <x/>
3457        **/

3458    private NamedObject deserializeWrapper(ParserXML.Tag startTag, Object JavaDoc cheatParent) throws
3459            ClassNotFoundException JavaDoc,
3460            //NoSuchFieldException,
3461
IOException {
3462            return deserializeWrapper(addName(), startTag, cheatParent);
3463    }
3464    private NamedObject deserializeWrapper(ParserXML.Attr[] addAttrTmp, ParserXML.Tag startTag, Object JavaDoc cheatParent) throws
3465            ClassNotFoundException JavaDoc,
3466            //NoSuchFieldException,
3467
IOException {
3468        ParserXML.Attr attr = addAttrTmp[0];
3469        ParserXML.Attr end, name = null;
3470        ParserXML.Tag tag;
3471        //attr = p.readAttr(); //p is the ParserXML.
3472
if (DEBUG) System.err.println("deserializeWrapper: "+ attr);
3473        if (attr.name.equals(XMLSerialize.NAME_TOKEN)) {
3474            //System.err.println("Got a name in a wrapper: "+startTag.name+" "+attr.value+";");
3475
name = attr;
3476            attr = p.readAttr();
3477        }
3478        if (!attr.name.equals(XMLSerialize.VALUE_TOKEN))
3479            throw new ParserXML.ExceptionXML(XMLSerialize.VALUE_TOKEN+ " expected");
3480        if (!(end = p.readAttr()).isEnd)
3481            throw new ParserXML.ExceptionXML("\">\" or \"/>\" expected");
3482        if (!end.emptyTag) {
3483            tag = p.readTag();
3484            if (!tag.name.equals(startTag.name) || tag.start)
3485                throw new ParserXML.ExceptionXML("</"+tag.name+"> expected");
3486        }
3487        //IDEA: could make up the expected tag, and use Tag.equals() to compare
3488
if (DEBUG) System.err.println("String read in: " + attr.value);
3489        if (startTag.name.equals("java.lang.String")) {
3490            String JavaDoc s = ParseUtilities.decodeXML((String JavaDoc)attr.value); //not attr.name! Don't forget to decode!
3491
putAlias(s); //String is an object
3492
if (name!=null) { //can only happen for Strings... (ugly hack!)
3493
//try {
3494
currentGetField.putObj(name.value, s); //prim/obj?
3495
Field f = getAllField(cheatParent.getClass(), name.value);
3496                    magic.setFinal(f, cheatParent, s);
3497                    //f.set(cheatParent, s);
3498
//} catch (NoSuchFieldException e) {
3499
//}
3500
return new NamedObject(name.value, s, true); //name!=null -> true
3501
}
3502            return new NamedObject(null, s, false); //name!=null -> false
3503
}
3504        Class JavaDoc c = Class.forName(ParseUtilities.descapeDollar(startTag.name));
3505        try {
3506        //NOTE: We had best make this work directly, rather than via reflection,
3507
//because then we can use static type checking rather than messy
3508
//exceptions (which we'd rather not have to deal with!)
3509
//Convert from Integer.class (Wrapper type) to int.class (primitive type)
3510
Field f = c.getDeclaredField("TYPE"); //will work for wrappers
3511
//Field f = getAllField(c, "TYPE"); //a cheap way to get the type
3512
Object JavaDoc o = getValue((Class JavaDoc)f.get(c), attr);
3513            putAlias(o); //NOTE: *not* called if a String, so not inc twice
3514
return new NamedObject(null, o, false); //can never be named
3515
} catch (NoSuchFieldException JavaDoc e) {
3516            //Special case: Wrappers should have a field named "TYPE".
3517
System.err.println("'" +c+ "' is not a wrapper class: "+e);
3518        } catch (IllegalAccessException JavaDoc e) {
3519            System.err.println("'" +c+ "' is not a wrapper class: "+e);
3520        }
3521        throw new IOException("Shouldn't get here");
3522    }
3523
3524
3525
3526    /**--------------------------------------------------------------------------
3527        * Test class
3528        * ----------
3529        * Predates ObjIn and ObjOut!
3530        **/

3531    public static class Test {
3532        public static void main(String JavaDoc args[]) throws Exception JavaDoc {
3533
3534            XMLDeserialize d = new XMLDeserialize(); //defaults to System.in
3535
XMLSerialize s = new XMLSerialize(); //defaults to System.out
3536
Object JavaDoc o;
3537            while ( (o = d.deserialize()) != null) { //can loop for streamed XML
3538
/*
3539                System.err.println("Printing created object:");
3540                System.err.println();
3541                System.err.println(o);
3542                System.err.println();
3543                System.err.println("(Re)serializing created object:");
3544                System.err.println();
3545            */

3546                s.serialize(o);
3547            }
3548        }
3549    }
3550}
3551
Popular Tags