KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JSX > XMLSerialize


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 /** ===========================================================================
12   * XMLSerialize (JSX0.8.21).
13     * ============
14     *
15     * NB: This file best viewed with tabs set to 2.
16     * For vim (vi clone):
17 :set ts=2
18     * For emacs:
19 M-x edit-tab-stops
20     * and then put a ":" in every second position (use space to overwrite).
21     * (M is "Meta key"; usually just Alt)
22     *
23     * DESCRIPTION
24     * Pass in an object, and it writes out a serialized version, in XML format.
25     * It mimics Java's own serialization, and so can handle almost all classes,
26     * including those with:
27     * - Private fields
28     * - A lack of a no-argument constructor
29     * - Their own internal serialization (except for Externalizable)
30     * It handles:
31     * - Circular references
32     * - Multi-dimensional arrays (ie arrays of arrays)
33     *
34     * For comments of any kind
35     * (eg bug reports, bug fixes, suggestions, patches, criticism, comments,
36     * insights, ideas, frustrations, irritations, or alternative software in the
37     * same space):
38                 bren@csse.monash.edu.au <G. Brendan Macmillan>
39     **/

40
41 /** BUGS.
42     * Wrapper classes only serialized correctly when within container types
43     * - Bug fixed or not?
44     * - would it now work automatically?
45     * String as first object?
46     **/

47
48 /** The mapping of Java to XML.
49     * Primitives map to attributes.
50     * References map to elements:
51     * - The element name is the *runtime class*. eg <java.util.Hashtable>
52     * - A NAME_TOKEN (eg "obj-name") attribute is the name of the field, if it
53     * has a name.
54     * - NB: NAME_TOKEN is illegal as a Java field (because of '-'), so no
55     * namespace conflict.
56     **/

57
58 /** Logic flow.
59     * For each class: (serializeObject)
60     * write all fields as attributes - if primitive (serializeFields)
61     * write all fields as elements - if reference (serializeReferences)
62     * -> recurse
63     **/

64
65 /** Notes.
66     * NB: Newlines within strings are outputted *exactly* as they are stored
67     * in the string. This might not be what you want, when moving across DOS
68     * (/n/r) and UNIX (/n).
69     **/

70
71 /** To do.
72     * - nice, informative error messages when the field is not found.
73     **/

74
75 /** Circular References.
76     * Increment a count whenever an object is serialized - the aliasSerialNumber.
77     *
78     * This number becomes the alias (or alternate name) for that object.
79     * It is the *very* simplest implementation; not very human readable, but
80     * will reveal implementation difficulties without complicating them.
81     * NB:
82     * - A null value is not an object (so doesn't increment the count)
83     * - "String ambivalence": String are objects, but encoded as attributes
84     * Could just say <fieldname>-alias="<aliasname>"
85     * Similarly, could say <fieldname>-null="" for null (!= to empty string!)
86     * Difficulty is extra parsing time to detect the "-". OK if can do it
87     * efficiently, which would be good, anyway.
88     * - A normal object is a an object.
89     * - A wrapper object is an object.
90     *
91     * In present implementation, aliases are needed in
92     * - serializeObject,
93     * - serializeWrapper,
94     * - serializeFields - for strings (strings in containers are in wrappers)
95     * NB: it is pre-inc. Begins at -1, and is PRE-INC just before each use.
96     **/

97
98 /** Handling internal serialization (ObjOut.java ObjIn.java).
99     * (1). When a class specifies its own serialization, it only does it for
100     * itself, not its ancestors (superclasses). Therefore, the test for a
101     * writeObject() method must be in getAllFields(), which walks up the
102     * inheritance tree.
103     * (2). This is presently called twice for each object: once for primitives,
104     * and once for objects (references). This is because JSX encodes these
105     * very differently in XML (attributes vs. tags), though this is not the
106     * only implementation. We now pass in a flag so that the internal
107     * serialization logic is only executed once (in the first call, which comes
108     * when serializing primitives (as attributes).
109     * (3). A class's internal serialization invokes the whole process again
110     * for objects (references), using writeObject() (in ObjOut.java),
111     * with no parameters to indicate that it is not a fresh level serialization.
112     * This is important to know, for reseting the alias table, for correct
113     * tabbing in formatting, and for whether to save up the Objects until
114     * after the primitives are done, instead of doing them right away. These
115     * two cases of fresh vs. internal are distinguised by:
116     * (a). serialize invocation (not serializeObject, which nested tags invoke)
117     * (b). invocationCount != 0
118     * The primitive writes, such as writeInt() are easily distinguished, as that
119     * is the only way they may legitimately be invoked (for JSX, anyway).
120     * (4). Thus, the internally serialized objects and primitives are gathered
121     * and collected in a bucket.
122     * (5). For primitives, it would be nice for this bucket to subclass Field,
123     * so they could remain invisible to the primitive-handling method
124     * serializeFields(). However, Fields are "final", so instead we pass back
125     * an array of type Object, and serializeFields() must distinguish between
126     * them with the instanceof operator, a name, value String pair (String[]?)
127     * Or class x (String name; String value;}? putField() is no good.
128     * (6). NB: No recursion occurs from this internal serialization - primitives
129     * don't recurse, and the object references are merely noted, not written
130     * at this point. Thus, the invoked writeObject() is guaranteed to return
131     * immediately.
132     * (7). Thus, we can use the stored primitives and objects immediately (like
133     * a one-level stack). The primitives are written as above; and the
134     * object references are serialized just before the call to
135     * serializeReferences.
136     * (8). NB: This call to serializeReferences() will invoke getAllFields() a
137     * second time, and we don't wish to invoke the internal serialization again,
138     * or it will be awkward to distinguish the first and second lot of stored
139     * objects. However, [NB] when we implement defaultWriteObject(), we will
140     * still need to detect (but not invoke), so we don't serialize twice.
141     * (9). We will need to start fresh for each invocation of an internal
142     * serialization, so mix up the storage of primitives and object references
143     * from different classes. Note that this can be true even within the one
144     * object, [NB] when we implement internal serialization of ancestor
145     * superclasses (which will be needed we when use Hashtable's internal
146     * serialization instead of the present ad hoc approach, and we want to allow
147     * users to subclass it - as per Michael Oswall's request.
148     **/

149
150 package JSX;
151 import java.lang.reflect.*;
152 import java.io.*;
153 import java.util.*;
154 import java.util.Hashtable JavaDoc; //for Hashtable
155
import java.util.LinkedHashMap JavaDoc; //for Hashtable
156
import java.util.Map JavaDoc;
157
158 public class XMLSerialize {
159     static final boolean defWO_DIRECT = false;
160     //static final boolean defWO_DIRECT = true;
161

162     static final boolean DEBUG = false;
163     //static final boolean DEBUG = true;
164

165     static final boolean DEBUG_HASH = false; //Hashtable uses .equals(), not ==
166
//static final boolean DEBUG_HASH = true;
167

168     static final boolean TESTCIRC = false;
169     //static final boolean TESTCIRC = true; //circular references
170

171     static final boolean INTERNAL_DEBUG = false;
172     //static final boolean INTERNAL_DEBUG = true; //internal serialization
173

174     static final boolean CACHE_DEBUG = false;
175     //static final boolean CACHE_DEBUG = true;//messy multi invoke of getAllFields
176

177     static final boolean REENTRY_DEBUG = false;
178     //static final boolean REENTRY_DEBUG = true; //when serializeObject() called
179

180     static final boolean PUT_FIELD_DEBUG = false;
181     //static final boolean PUT_FIELD_DEBUG = true;
182

183     static final boolean OPT_DEBUG = false;
184     //static final boolean OPT_DEBUG = true;
185

186
187     static final int ALIAS_INIT = -1;//so pre-increment in serializeObject
188
private int aliasSerialNumber; //simplest circular reference names
189

190     static final String JavaDoc ALIAS_TAG_TOKEN = "alias-ref";
191     static final String JavaDoc ALIAS_ATTR_TOKEN = "alias"; //'-' not needed, b/c alone
192
static final String JavaDoc ALIAS_STRING_TOKEN = "-alias";
193
194     static final String JavaDoc VERSION = "Version: JSX0.8.13 (maybe out of date - sorry! See the name of the jar.)"; //MUST remember to update this!
195

196     /**
197         * "-" avoids namespace conflicts between serialization flags and
198         * actual class or field names.
199         * "-" legal XML attr; illegal java field; illegal java class
200         **/

201     //static final String NAME = "is-the"; //"-" legal attr; illegal field
202
//static final String NAME = "class-of"; //opposite of "instanceof"
203
//static final String NAME = "run-type-of"; //opposite of "instanceof"
204
//there is an issue with compatibility here! I keep changing it, and old
205
//serializations are no longer compatible with it.
206
static final String JavaDoc NAME_TOKEN = "obj-name";
207
208     static final String JavaDoc CLASSNAME_TOKEN = "className";
209     //static final String ARRAY_TOKEN = "A-";
210
//static final String ARRAY_TOKEN = "[]"; //not allowed as XML names!
211
static final String JavaDoc ARRAY_TOKEN = "ArrayOf-";
212     //static final String ARRAY_PRIMITIVE_INDEX_TOKEN = "a-";
213
//NB: prefixes needed - attr names can't start with a numeral
214
static final String JavaDoc ARRAY_PRIMITIVE_INDEX_TOKEN = "a"; //'-' not needed
215
static final String JavaDoc INTERNAL_PRIMITIVE_TOKEN = "_."; //'-' not needed
216
//NB: '_' is perfect to convey the sense of an internal name!
217
//This may also be appropriate for arrays; but 'a' for array, and no
218
// other attributes to confuse (except name and length)
219
//Problem: a valid java fieldname. Although _0 _1 _2 (ie with numerals)
220
// are unlikely to be used as fieldnames, the simplist parsing is
221
// confused by fieldnames such as "_name". Using the prefix "_."
222
// should fix this. This bug was discovered by Thomas Hiller, who
223
// thus became the third successful JSX Challenger, on:
224
// Tue, 16 Jan 2001 17:58:24 +0100
225

226     static final String JavaDoc LENGTH_TOKEN = "length";
227     static final String JavaDoc VALUE_TOKEN = "valueOf";
228     static final String JavaDoc BINARY_DATA_TOKEN = "binary-data";
229     static final String JavaDoc NULL_TOKEN = "null";
230
231     //static final String SUPER_TOKEN = "super"; //a reserved keyword
232
//static final String SUPER_TOKEN = "subclass"; //**reversed** order
233
static final String JavaDoc SUPER_TOKEN = "sub-class"; //**reversed** order
234
//static final String SUPER_TOKEN = "extended-by"; //reversed order
235
static final String JavaDoc OPT_DATA_TOKEN = "opt-data";
236     static final String JavaDoc OPT_PRIM_DATA_TOKEN = "opt-prim-data";
237
238     static final String JavaDoc ALIAS_ID_TOKEN = "alias-ID";
239
240
241     static final String JavaDoc SPACER = " "; //\t
242

243     static final String JavaDoc JSX_HEADER_TARGET = "jsx"; //mv to constants interface
244
static final String JavaDoc XML_HEADER_TARGET = "xml";
245
246     static final int NOT_SERIALIZED =
247         Modifier.STATIC | Modifier.TRANSIENT;
248 // | Modifier.FINAL; //todo 26June2001: be able to set final fields
249

250
251     public static void main(String JavaDoc ars[]) {
252         System.err.println(VERSION);
253     }
254     /**--------------------------------------------------------------------------
255         * API
256         * ===
257         * CONSTRUCTORS
258         * ============
259         **/

260     //ALIAS ID related:
261
private boolean stringAsElement = true; //for alias-id
262
//private boolean stringAsElement = false; //for alias-id
263

264     //should be ganged to the above
265
private boolean includeAliasID = true; //for alias-id
266
//private boolean includeAliasID = false; //for alias-id
267

268
269     private boolean superVersion = false; //regression test
270
//config can override!!!
271
//private boolean superVersion = true;
272

273     private boolean optVersion = superVersion; //gang for convenience
274
//private boolean optVersion = false;
275
//private boolean optVersion = true;
276
private boolean primOrderVersion = optVersion; //gang for convenience
277
//private boolean primOrderVersion = false;
278
//private boolean primOrderVersion = true;
279

280     private PrintWriter out;
281     public XMLSerialize(PrintWriter out) throws IOException {
282         this(out, new Config()); // so Config becomes the default
283
// this.out = out;
284
}
285     Config cfg = new Config(); //so never null, and Config controls defaults
286
//we cut and pasted constructors - better to call the other
287
//if none call, same as cfg=null (is it? Does it matter?)
288
public XMLSerialize(PrintWriter out, Config cfg) throws IOException {
289         if (cfg==null) throw new IOException("Config object must not be null");
290         this.cfg = cfg;
291         setFormatted(cfg.formatted);
292         superVersion = cfg.superVersion;
293         optVersion = superVersion; //gang for convenience
294
primOrderVersion = optVersion; //gang for convenience
295
stringAsElement = cfg.aliasID; //alias ID does several things
296
includeAliasID = cfg.aliasID; //alias ID does several things
297
this.out = out;
298     }
299     public XMLSerialize(Config cfg) throws IOException { //defaults to System.out
300
this(new PrintWriter(new OutputStreamWriter(System.out), true), cfg);
301     }
302     public XMLSerialize() throws IOException { //defaults to System.out
303
this(new Config()); // so Config becomes the default
304
// *and* we run through the code that sets it
305
// this(new PrintWriter(new OutputStreamWriter(System.out), true));
306
}
307
308
309     //legacy method
310
public void serialize(Object JavaDoc o, PrintWriter newOut) throws IOException {
311         out = newOut; //can change output destination between invocations
312
commonSer(o);
313     }
314     public void serialize(Object JavaDoc o) throws IOException{
315         commonSer(o);
316     }
317     /** ------------------------------------------------------------------------
318         * commonSer()
319         * -----
320         * Heart.
321         * Consider: should this be static?
322         **/

323          /* Mechanism for internal serialization to pass out objects.
324             * Consider: reimplementing with faster ArrayList (not synch).
325             * Danger: incremental capacity increases.
326             * Consider: doubling concept used in OOS.
327             */

328     int invocationCount = 0; //don't ++ in nest, so max value is 1...
329
private void commonSer(Object JavaDoc o) throws IOException{
330         //nested invocation - custom serialization of a class's writeObject()
331
if (invocationCount!=0) { //Exception if no objStore exists.
332
if (superVersion)
333                 serializeObject(o, null, ""); //indent unknown - need a stack!
334
else
335                 objStore.add(o); //for internal serialization. NB: doesn't recurse.
336
return;
337         //first invocation (not nested) [ED: should this be first?]
338
} else {
339             reset();
340             invocationCount++;
341             String JavaDoc jsxVersion;
342             if (superVersion)
343                 jsxVersion = "2";
344             else
345                 jsxVersion = "1";
346             toOutPrintln("<?"+JSX_HEADER_TARGET+" version=\""+jsxVersion+"\"?>");
347
348             serializeObject(o, null, "");
349             toOutPrintln();
350             if (TESTCIRC) {
351                 System.err.println("Count was: " + (aliasSerialNumber-ALIAS_INIT));
352                 System.err.println();
353             }
354             invocationCount--;
355             if (cfg.dtdGenerator!=null) cfg.dtdGenerator.generate(); //hit it, man.
356
}
357     }
358
359
360
361     /** ------------------------------------------------------------------------
362         * METHODS
363         * =======
364         **/

365     /** ------------------------------------------------------------------------
366         * reset()
367         * -----
368         **/

369
370
371     static class MapProtect extends java.util.Stack JavaDoc {
372         void protect(Map JavaDoc[] cache) {
373             push(cache);
374         }
375         Map JavaDoc[] retrieve() {
376             return (Map JavaDoc[])pop();
377         }
378         Map JavaDoc[] value() {
379             return (Map JavaDoc[])peek();
380         }
381     }
382
383     static class MapCatcher extends java.util.Stack JavaDoc {
384         //static final boolean DEBUG = true;
385
static final boolean DEBUG = false;
386
387         void newCatcher() { // new frame or context
388
push(null); // begin with a default value
389
if (DEBUG) System.err.println("newCatcher: "+this);
390         }
391         void set(Map JavaDoc map) { // replace top with new value
392
pop(); // note: if Boolean was mutable, we would just change it
393
push(map);
394             if (DEBUG) System.err.println("set(): "+this);
395         }
396         Map JavaDoc freeCatcher() {
397             if (DEBUG) System.err.println("freeCatcher: "+this);
398             return (Map JavaDoc)pop(); // discard catcher
399
}
400         public String JavaDoc toString() {
401             if (size()==0) return "0";
402             else return size()+", "+peek().toString();
403         }
404     }
405
406
407
408 /** (Conventional) use of a stack to save state (normalprotection: "pull"
409  * communication: the push does not put information on the stack, but a holder
410  * into which information can be caught. Once sent, the information is
411  * retrieved with "pop" (or "is"). The metaphor is a hand extended (push) to
412  * "catch" the information, and then to bring it back (pop).
413     **/

414     static class BooleanProtect extends java.util.Stack JavaDoc {
415         void protect(boolean[] cache) {
416             push(cache);
417         }
418         boolean[] retrieve() {
419             return (boolean[])pop();
420         }
421         boolean[] value() {
422             return (boolean[])peek();
423         }
424     }
425
426
427 /** (Unconventional) use of a stack for communication: the "push" extends a a
428  * holder in which information can be caught. The "pull" brings it back. The
429  * metaphor is a hand extended to "catch" and then bring back the information.
430     **/

431     static class BooleanCatcher extends java.util.Stack JavaDoc {
432         //static final boolean DEBUG = true;
433
static final boolean DEBUG = false;
434
435         void newCatcher() { // new frame or context
436
push(Boolean.FALSE); // begin with a default value
437
if (DEBUG) System.err.println("newCatcher: "+this);
438         }
439         void set(boolean input) { // replace top with new value
440
Boolean JavaDoc b = new Boolean JavaDoc(input);
441             pop(); // note: if Boolean was mutable, we would just change it
442
push(b);
443             if (DEBUG) System.err.println("set(): "+this);
444         }
445         boolean is() { // read top (not a pop)
446
if (DEBUG) System.err.println("is(): "+this);
447             return ((Boolean JavaDoc)peek()).booleanValue(); // if True, then true; else false
448
}
449         boolean freeCatcher() {
450             if (DEBUG) System.err.println("freeCatcher: "+this);
451             return ((Boolean JavaDoc)pop()).booleanValue(); // discard catcher
452
}
453         public String JavaDoc toString() {
454             if (size()==0) return "0";
455             else return size()+", "+peek().toString();
456         }
457     }
458
459     public void reset() { //can reset - important part of API!
460
if (invocationCount!=0)
461             throw new Error JavaDoc("Serialize: Attempt to reset alias table mid-way.");
462         defaultCatcher.clear();
463         putFieldObjectsCatcher.clear();
464         primStore = new Vector();
465         initAlias();
466         putFieldClassStack.clear();
467     }
468     /** ------------------------------------------------------------------------
469         * defaultWriteObject()
470         * ------------------
471         * Forwarded from ObjOut.
472         * NB: no object specified. It is merely returning a flag.
473         **/

474
475     private BooleanCatcher defaultCatcher = new BooleanCatcher();
476     private BooleanProtect defaultProtect = new BooleanProtect();
477
478     private MapCatcher putFieldObjectsCatcher = new MapCatcher();
479     private MapProtect putFieldObjectsProtect = new MapProtect();
480     private Vector primStore; // = new Vector();
481
Vector objStore; //NB: a new one created for each
482
public void defaultWriteObject() throws IOException {
483         if (DEBUG)
484             System.err.println("defaultWriteObject() in XMLSerialize: should store it");
485         defaultCatcher.set(true);
486         if (defWO_DIRECT) {
487             System.err.println("Object "+thisObjectStack.get(thisObjectStack.size()-1));
488             System.err.println("\tof class "+clazzStack.get(clazzStack.size()-1));
489             System.err.println("\twith indent '"+indentStack.get(indentStack.size()-1)+"'");
490             System.err.println("\tJust called defaultWriteObject");
491             System.err.println();
492             serializeReferences(
493                 thisObjectStack.get(thisObjectStack.size()-1),
494                 (Class JavaDoc)clazzStack.get(clazzStack.size()-1),
495                 (String JavaDoc)indentStack.get(indentStack.size()-1) );
496         }
497
498     }
499     public void flush() { out.flush(); }
500     public void close() { out.flush(); out.close(); }
501
502     /** ------------------------------------------------------------------------
503         * writePrim(str)
504         * ------------------
505         * Forwarded from ObjOut.
506         * Caller (ObjOut) must convert each of the primitive types to String
507         * first. This is trivial eg: (x+"")
508         **/

509     public void writePrim(String JavaDoc str) {
510         if (DEBUG)
511             System.err.println("writeInt(i) in XMLSerialize: should store it");
512         if (primOrderVersion) {
513             //direct write-through - ordering is then perfectly correct
514
printOpenTag(OPT_PRIM_DATA_TOKEN, "", false); //indentation - not object
515
printAttr(VALUE_TOKEN, ParseUtilities.encodeXML(str));
516                 //could be string - better to take care of that case earlier...
517
toOutPrintln("/>"); //only prim fields
518
} else{
519             primStore.add(str);
520         }
521     }
522
523
524     /**===========================================================================
525         * alias nascent-object
526         * --------------------
527         * collect init into one routine... suggests it should be a class.
528         * change to public, so ObjOut can access it
529     * ASIDE: could do the alias "nascent object" as an inner class - and
530     * implement it by subclassing (instead of wrapping), and use HashMap
531     * (instead of Hashtable)
532         **/

533     private Hashtable JavaDoc aliasHash = new Hashtable JavaDoc(); //reuse the one
534
private void initAlias() {
535         aliasSerialNumber = ALIAS_INIT;
536         aliasHash.clear();
537     }
538     /**--------------------------------------------------------------------------
539         * put Alias
540         * --------
541         * inline = !serialized;
542         * if (inline)
543         * //add to hash
544         * else
545         * //write as alias
546         * return (inline); //to tell caller to serialize it inline!
547         * //else, caller leaves the ref writing up to this routine
548         **/

549     //simplest way to circumnavigate the ".equals" instead of identity test
550
//of Hashtable (in Java 1.2)
551
class IdObject {
552         Object JavaDoc o;
553         public int hashCode() {
554             return System.identityHashCode(o);
555         }
556         public boolean equals(Object JavaDoc x) { //essense
557
return ((IdObject)x).o == this.o; //assumption that used by Hashtable,
558
//and that the tested part (key) will always be an IdObject
559
}
560         public IdObject(Object JavaDoc o) {
561             this.o = o;
562         }
563     }
564     boolean areAliasesUsed=true; //default "on"; set in constructor
565
//this name feels confusing to me...
566
//Return value: false = I didn't add it (already there); don't inline it.
567
//true = created new alias' you need to serialize it inline.
568
private boolean putAlias(Object JavaDoc o, String JavaDoc fieldName, boolean isStringAttr, String JavaDoc indent) {
569         //if (!areAliasesUsed)
570
// return true;
571
if (DEBUG_HASH) System.err.println(" entering putAlias with "+o);
572
573         if (o==null) return true; //carry on
574
Object JavaDoc alias; //will be String, but save the conversion
575
if (DEBUG_HASH) {
576                 for (Enumeration keys=aliasHash.keys(); keys.hasMoreElements();) {
577                     Object JavaDoc a = keys.nextElement();
578                     System.err.println(a + " -- " + a.getClass() + " -- " +a.hashCode());
579                 }
580                 System.err.println("looking for: "+o+" -- "+o.getClass() + " -- " +System.identityHashCode(o));
581                 System.err.println("Got: "+myget(o));
582                 System.err.println("Same object? 1 " + (myget(o)!=null) );
583                 System.err.println("Same object? 2 " + (mycontainsKey(o)) );
584             }
585         if ( (alias = myget(o))==null) {
586             ++aliasSerialNumber; //pre-inc (use this syntax to highlight it)
587
myput(o, aliasSerialNumber+""); //add (convert to String)
588
if (DEBUG_HASH) System.err.println(" recording new alias: "+indent +"inline " +aliasSerialNumber+ ":");
589             //Need to:
590
// indicate "inline" somehow.
591
// write as inline (same as now).
592
return true; //yes, should serialize the object inline
593
}
594         else
595         {
596             if (isStringAttr) {
597                 printAttr(fieldName + ALIAS_STRING_TOKEN, alias+"");
598             }
599             else {
600                     if (DEBUG_HASH) System.err.println(" repeating old alias: "+indent+ "alias " +alias+ ":");
601                 printOpenTag(ALIAS_TAG_TOKEN, indent, false); //not a *fresh* object
602
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName); //nice to reuse
603
printAttr(ALIAS_ATTR_TOKEN, alias+"");
604                 toOutPrintln("/>"); //empty tag - under control of caller
605
}
606             return false; //no, don't serialize inline; we (or you?) write the alias
607
}
608     }
609     private void removeAlias(Object JavaDoc key) {
610         if (areAliasesUsed) { //if aliases are used, we don't mess with them
611
} else { //else, we remove them, so only the path remains.
612
try {
613                 Object JavaDoc oo = aliasHash.remove(new IdObject(key));
614                 if (oo==null)
615                     throw new Exception JavaDoc("Try to remove an object not in aliasHash: " +key);
616             } catch (Exception JavaDoc e) {
617                 e.printStackTrace();
618             }
619         }
620     }
621     public Object JavaDoc myput(Object JavaDoc key, Object JavaDoc value) {
622         if (cfg.dtdGenerator!=null) cfg.dtdGenerator.populate(key); //the object
623
return aliasHash.put(new IdObject(key), value); //just a dumb wrapper
624
}
625     public Object JavaDoc myget(Object JavaDoc key) {
626         return aliasHash.get(new IdObject(key)); //just a dumb wrapper
627
}
628     public boolean mycontainsKey(Object JavaDoc key) {
629         return aliasHash.containsKey(new IdObject(key)); //just a dumb wrapper
630
}
631
632
633
634     /**--------------------------------------------------------------------------
635         * Object (map to an Element)
636         * ------
637         * tag (name of element) is always the runtime class (type) of the object.
638         * SUGGEST: write open and close tags separately; but problem of attributes
639         * check: Wrapper classes here (String, primitive wrappers)
640         * check: container classes (like Hashtable)
641         *
642         * NB: aliasSerialNumber++ three times, before of nature of sub-arrays.
643         **/

644
645     Class JavaDoc[] OOS_ARGS = {ObjectOutputStream.class}; //works fine!
646
//just mispelling? it was: "ObjectOutpuStream.class"
647
Object JavaDoc[] writeObjectArglist; //set by caller, using setArg below
648
ObjectOutputStream thisOut;
649     /**--------------------------------------------------------------------------
650         * setArg()
651         * -------
652         * Consider: putting this in constructor; or caller in same package or same
653         * class.
654         * THIS IS TRICKY!!! MUST BE CHANGED!!!
655         **/

656     public void setArg(Object JavaDoc[] argList) {
657         writeObjectArglist = argList;
658         thisOut = (ObjectOutputStream)argList[0];
659     }
660
661
662 /** ================================================== =========================
663     * PutFieldImpl - inner class
664     * ------------
665     * Need not buffer, because we don't worry about the order (but JOS does).
666     *
667     * Need to package-qualify PutField, because not directly subclassing.
668
669     * (1). Treat them as full objects, invoking their writeObject() etc
670     * (2). Buffering fields? Or is this class itself the buffer?
671     *
672     * DEFER:
673     * Strings... aliasing them?
674     * For now, do as objects.
675     * -> Machine readable, but less humam readable.
676
677     * Indentation
678     * (the positional stuff doesn't actually recurse, but stores it (memory
679     * overhead is only the path to the current node).
680     * Recursing requires some communication, somewhere
681     * -> Machine readable, but much less humam readable.
682
683     * Shadowed names
684     * (1). superclasses fields shadowed by us,
685     * (2). and those in subclasses that shadow us ourselves.
686     * -> Not machine readable, or human readable; but rare.
687
688     * Empty tag
689     * <tag/> for empty tag (currently always does <tag> </tag>
690     * -> Machine readable, and humam readable. Mere finesse.
691
692     * Nesting
693     * my theory is that stacking it (same as for positional) is the
694     * easiest short-term solution, because it fits in with the rest of the
695     * code most easily.
696     * -> necessary for correctness
697     **/

698     boolean putFieldDiditAll; //should stack - or call through an object...
699

700     //static final class PutFieldImpl extends java.io.ObjectOutputStream.PutField
701
final class PutFieldImpl extends java.io.ObjectOutputStream.PutField {
702         Map JavaDoc objMap = null;
703         Map JavaDoc primMap = null; // store primitives as Strings
704
Class JavaDoc holdingClass;
705         Map JavaDoc serialMap;
706
707         public PutFieldImpl() { //"public" - but not needed, despite javadocs
708
if (PUT_FIELD_DEBUG) System.err.println("PutFieldImpl.<init>");
709             holdingClass = (Class JavaDoc) putFieldClassStack.peek();
710             serialMap = SerialPF.getFieldNameType(holdingClass);
711 /* NOTE: superVersion does *not* work with this!
712     * The opt-tag is confused:
713                 <opt-data>
714  lowestSetBit="-2" firstNonzeroByteNum="-2" signum="1" bitLength="-1" bitCount="
715 -1">
716 */

717             if (PUT_FIELD_DEBUG) {
718                 System.err.println("Present class is: "+holdingClass);
719                 System.err.println("Valid fields are: "+serialMap);
720                 try {
721                     SerialPF.main(new String JavaDoc[]{holdingClass.getName()}); //show it
722
} catch (Exception JavaDoc e ) {
723                     e.printStackTrace();
724                 }
725             }
726
727             // there seems to be no real reason to use a hash map, as we just
728
// iterate, not search. However, it does no harm, and we do want pairs.
729
// NB: linkedlist map maintains the order!
730
try { // LinkedHashMap only available in 1.4
731
objMap = (Map JavaDoc)Class.forName("java.util.LinkedHashMap").newInstance();
732                 primMap = (Map JavaDoc)Class.forName("java.util.LinkedHashMap").newInstance();
733             } catch (ClassNotFoundException JavaDoc e) {
734             } catch (InstantiationException JavaDoc e) {
735             } catch (IllegalAccessException JavaDoc e) {
736             }
737             if (objMap==null || primMap==null) { // neater than repeating in catch
738
objMap = new HashMap JavaDoc();
739                 primMap = new HashMap JavaDoc();
740             }
741
742             // populate objMap & primMap with default values
743
// NB: in JOS, if no put(), still serialized with default value
744
for(Iterator i = serialMap.entrySet().iterator(); i.hasNext();) {
745                 Map.Entry JavaDoc pair = (Map.Entry JavaDoc) i.next();
746                 Class JavaDoc type = (Class JavaDoc) pair.getValue();
747                 String JavaDoc fieldName = (String JavaDoc) pair.getKey();
748                 if (type.isPrimitive()) {
749                     if (type==boolean.class)
750                         primMap.put(fieldName, "false");
751                     else
752                         primMap.put(fieldName, "0");
753                 } else { // must be a reference type (object or array)
754
objMap.put(fieldName, null);
755                 }
756             }
757         }
758
759
760     /** Write fields.
761                 <tag primPutField="primValue" _.primCustomData= "primValue">
762                     <objCustomData/>
763                     <objPutField obj-name="fieldName"/>
764                 </tag>
765         **/

766     /**
767         * Primitives -> attributes.
768         * - *no* actual fields
769         * - custom data
770         * - PutField fields
771         **/

772     /**
773         * Objects -> elements. [I keep wanting to say "objectives"]
774         * - *no* actual fields
775         * - custom data
776         * - PutField fields
777         **/

778         public void write(ObjectOutput objectOutput) throws IOException {
779         //(1). Access to current state
780
//----------------------------
781
XMLSerialize s = ((JSX.ObjOut) objectOutput).s;
782             String JavaDoc indent = " "; //IDEA: ++ on openTag entry; -- on closeTag exit?
783

784         //(2). Attributes <- primitives
785
//-----------------------------
786
//(a). *NO* actual fields
787

788             //(b). virtual fields (ie PutField)
789
for(Iterator i = primMap.entrySet().iterator(); i.hasNext();) {
790                 Map.Entry JavaDoc pair = (Map.Entry JavaDoc) i.next();
791                 s.printAttr(pair.getKey()+"", ParseUtilities.encodeXML(pair.getValue()+""));
792                     //Downside is inefficiency. Ideally, only encodeXML if a char - but
793
//how to know what the type is?
794
}
795
796             //(c). custom fields (primitives)
797
//(cut and paste from serializeFields (out-factor it from here & there?)
798
int i=0;
799             for (Enumeration keys=s.primStore.elements(); keys.hasMoreElements();i++){
800                 s.toOutPrintln(); //Attribute indenting
801
s.toOutPrint(indent+s.spacer); //indent each attribute
802
s.printAttr(INTERNAL_PRIMITIVE_TOKEN+i, ParseUtilities.encodeXML((String JavaDoc)keys.nextElement()));
803                 //NOTE: will never be String, because it is serialized using object.
804
}
805             s.primStore.clear(); //NB: reset when consumed
806
//so isn't written out again by serializeFields
807

808                 // We comment out below, because we now leave the tag open, so other
809
// primitive values can be appended.
810
//s.toOutPrintln(">"); //we don't know the tag name. Easier to leave to
811
//serializeObject, and *always* do </endTag>... we could communicate
812
//more information through the same mechanism, of course (since we
813
//already need to communicate if PutField handled it or not.
814
// ie: in putFieldDiditAll
815

816         //(3). Elements <- objects
817
//(a). *NO* actual fields
818

819             //(b). virtual Object fields (ie PutField)
820
// --> move next to serializeReferences
821
putFieldObjectsCatcher.set(objMap);
822 /*
823             for (Iterator iObj = objMap.entrySet().iterator(); iObj.hasNext();) {
824                 Map.Entry pair = (Map.Entry) iObj.next();
825                 s.serializeObject(pair.getValue(), pair.getKey()+"", indent);
826                     //NB: [name-value] swapped
827             }
828 */

829
830             //(c). custom fields (primitives) <----- not yet implemented
831
//(cut and paste from serializeObject (out-factor it from here & there?)
832
/*
833             Vector localObjStore = objStore;//local->stack (recursion)
834             Vector localDefaultCache = defaultCache;//local->stack (recursion)
835             for (Enumeration keys=localObjStore.elements(); keys.hasMoreElements();) {
836                 Object a = keys.nextElement();
837                 if (INTERNAL_DEBUG && a!=null) System.err.println("in objStore: "+a+"\n"+a.getClass());
838                 serializeObject(a, a.getClass(), null, indent+spacer+spacer); //debug print out
839             }
840                 //objStore.clear(); //NB: reset when consumed
841                 //try: No need - now a new one each time
842             if (INTERNAL_DEBUG) {
843                 System.err.println("should be cleared: "+localObjStore.size());
844                     //do internal first, so parser knows end automatically.
845                 System.err.println("references:");
846                         }
847             defaultCache = localDefaultCache; //restore! (count is OK)
848 */

849
850             if (PUT_FIELD_DEBUG) {
851                 System.err.println("PutFieldImpl.write(out)");
852                 System.err.println("objMap:");
853                 System.err.println(objMap);
854                 System.err.println("primMap:");
855                 System.err.println(primMap);
856             }
857 // s.putFieldDiditAll = true; //this should really be a "push"!!
858
//hyp: have a theory that this is not necessary...
859
}
860
861         private void check(String JavaDoc name, Class JavaDoc clazz) {
862             //System.err.println(">>>>>>>>>> CHECKING "+name+"; sfp/SF are: "+serialMap);
863
if (PUT_FIELD_DEBUG) {
864                 System.err.println("\texpected "+clazz+", got " +serialMap.get(name));
865             }
866             if (!serialMap.containsKey(name))
867                 throw new IllegalArgumentException JavaDoc("no such field - " +name +" is not a valid 'Serializable Field' of "+holdingClass);
868             // Can the field be assigned to from the value?
869
// ie. is field a superclass of the value?
870
// For primitives, is it the same?
871
if ( !((Class JavaDoc)serialMap.get(name)).isAssignableFrom(clazz) )
872                 throw new IllegalArgumentException JavaDoc("no such field - "+name +" expects a "+serialMap.get(name)+" and cannot be assigned to from '"+ clazz+ "'");
873         }
874
875     /** put() for all 8 primitives, and 1 object */
876         public void put(String JavaDoc name, boolean value) {
877             check(name, boolean.class);
878             primMap.put(name, value+"");
879         }
880         public void put(String JavaDoc name, char value) {
881             check(name, char.class);
882             primMap.put(name, value+"");
883         }
884         public void put(String JavaDoc name, byte value) {
885             check(name, byte.class);
886             primMap.put(name, value+"");
887         }
888         public void put(String JavaDoc name, short value) {
889             check(name, short.class);
890             primMap.put(name, value+"");
891         }
892         public void put(String JavaDoc name, int value) {
893             check(name, int.class);
894             primMap.put(name, value+"");
895         }
896         public void put(String JavaDoc name, long value) {
897             check(name, long.class);
898             primMap.put(name, value+"");
899         }
900         public void put(String JavaDoc name, float value) {
901             check(name, float.class);
902             primMap.put(name, value+"");
903         }
904         public void put(String JavaDoc name, double value) {
905             check(name, double.class);
906             primMap.put(name, value+"");
907         }
908
909     /** put() for objects */
910         public void put(String JavaDoc name, Object JavaDoc value) {
911             check(name, value.getClass());
912             //BUG: what to do with null values? Need to be able to record them...
913
//JOS just uses an array (I wonder if this could be a problem with my
914
//other stacks? They are all Vectors, I think, so OK)
915

916             /* JOS spec says object must be same type as field - not subtype
917                 we duplicate that functionality here, under protest */

918             objMap.put(name, value); //what could be easier?
919
}
920
921 /*
922         // route above calls through here
923         private void _primPut(String name, String value) {
924             // (1). how know which "clazz" we are in?
925                 // Map serialMap = SerialPF.getFieldNameType(clazz);
926             // (2). if contains.
927             primMap.put(name, value);
928         }
929 */

930
931     }
932
933
934 // private void writeObject(ObjectOutputStream oos) {
935
// }
936
/**
937     * SerializeObject(obj, frameClass, fieldName, indent)
938     * ---------------
939     * Recurses both on object and class. Object is for aggregate objects
940     * (contained by reference); Class is a frame to inspect a different part
941     * of the superclass stack. Typical (object) recursion:
942             serializeObject(o, o.getClass(), fieldName, indent+spacer+spacer);
943  */

944     private void serializeObject(Object JavaDoc o, String JavaDoc fieldName, String JavaDoc indent)
945             throws IOException {
946         if (o==null) { //because of the o.getClass() below
947
serializeObject(true, o, null, -1, fieldName, indent); //start recursion
948
//serializeObject(o, null, fieldName, indent); //start recursion
949
} else {
950             Class JavaDoc[] superClasses = null;
951             //serializeObject(o, o.getClass(), fieldName, indent);
952
if (superVersion) {
953                 if (o.getClass()==Object JavaDoc.class || //special cases: ignore subclasses
954
o.getClass()==Vector.class ||
955                         o.getClass()==Hashtable JavaDoc.class ||
956                         o.getClass()==Class JavaDoc.class ||
957                         o.getClass()==String JavaDoc.class
958                         ) { //special case
959
superClasses = new Class JavaDoc[] { o.getClass() };
960                 } else {
961                     superClasses = getReversedSuperClasses(o.getClass());//leaf; no Object
962
// o.getClass() is the reason for the o==null
963
}
964             }
965             else {
966                 superClasses = new Class JavaDoc[] { o.getClass() };
967             }
968             serializeObject(true, o, superClasses, 0, fieldName, indent);
969                 //can't loop, need to recurse, because nested.
970
}
971     }
972
973     private void serializeObject(boolean superHeader, Object JavaDoc o, Class JavaDoc[] superClasses, int superI, String JavaDoc fieldName, String JavaDoc indent) throws IOException {
974     //private void serializeObject(Object o, Class clazz, String fieldName, String indent) throws IOException
975

976         Class JavaDoc clazz = null;
977         if (o!=null)
978             clazz = superClasses[superI];
979
980         if (REENTRY_DEBUG) {
981             System.err.println("-----------------------");
982             if (o!=null) {
983                 System.err.println(o);
984                 System.err.println(o.getClass());
985                 System.err.println(clazz);
986             }
987         }
988         //NOTE: null does not get an alias
989
if (o==null) { //an array object can also be null...
990
//in which case we lose the type; but null is only of type "null"
991
printOpenTag(NULL_TOKEN, indent, false); //null - not an object
992
//no alias ID! So no call to printAliasID();
993
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName); //nice to reuse
994
toOutPrintln("/>"); //good to have control of the newline here
995
return; //a null object can have no children
996
}
997     /*
998         try {
999             if (!(o instanceof Serializable)) //mark such fields "transient"
1000                throw new NotSerializableException(o.getClass().getName());
1001        } catch (Exception e) { System.err.println(e); return; }
1002    */

1003        String JavaDoc runTimeClass = ParseUtilities.escapeDollar(clazz.getName());//need to: $ -> ..
1004

1005//FACTOR this out into a separate method, so we can recurse nicely.
1006
if (clazz.isArray()) {
1007            serializeArrayReferences(o, fieldName, indent);
1008            //array is dealt with internally
1009
}
1010
1011        else if (clazz==Class JavaDoc.class) {
1012            serializeClass((Class JavaDoc) o, fieldName, indent);
1013            //each time a string is encoded, it becomes a new object?
1014
}
1015
1016        else if (clazz==String JavaDoc.class) { //TODO: deserializer needs to use this too.
1017
serializeWrapper(o, fieldName, indent);
1018            //each time a string is encoded, it becomes a new object?
1019
}
1020
1021        else if (clazz==Vector.class) {
1022            //don't check for alias, if not the leaf - but always write in full
1023
if ((clazz!=superClasses[0]) || putAlias(o, fieldName, false, indent)) {
1024            //if ((clazz!=o.getClass()) || putAlias(o, fieldName, false, indent))
1025
printOpenTag(runTimeClass, indent, true); //putAlias, therefore object
1026
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName); //nice to reuse
1027
if (((Vector)o).size() > 0) {
1028                    toOutPrintln(">"); //move into ParserXML?
1029
serializeVectorReferences((Vector)o, indent); //could poly
1030
printCloseTag(runTimeClass, indent);
1031                } else {
1032                    toOutPrintln("/>"); //empty tag
1033
}
1034                if (clazz==o.getClass()) //only do for leaf (or root) classes
1035
//if (clazz==o.getClass()) //only do for leaf (or root) classes
1036
removeAlias(o); //only acts when in "min aliases" mode
1037
}//putAlias
1038
}
1039        else if (clazz==Hashtable JavaDoc.class) {
1040            if ((clazz!=superClasses[0]) || putAlias(o, fieldName, false, indent)) {
1041            //if ((clazz!=o.getClass()) || putAlias(o, fieldName, false, indent))
1042
printOpenTag(runTimeClass, indent, true); //putAlias, therefore object
1043
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName);
1044                if (((Hashtable JavaDoc)o).size() > 0) { //same as Vector
1045
toOutPrintln(">"); //move into ParserXML?
1046
serializeHashtableReferences((Hashtable JavaDoc)o, indent); //poly out; nicer
1047
printCloseTag(runTimeClass, indent);
1048                } else {
1049                    toOutPrintln("/>"); //empty tag
1050
}
1051                if (clazz==superClasses[0]) //only do for leaf classes
1052
//if (clazz==o.getClass()) //only do for leaf classes
1053
removeAlias(o); //only acts when in "min aliases" mode
1054
}//putAlias
1055
}
1056
1057
1058
1059        //a Class Instance
1060
//What if root object *is* a wrapper class?
1061
else {
1062            //8Aug2001 - we need to do writeReplace before checking the alias.
1063
//The replacement object is decided dynamically - we can't predict it
1064
//from the instance which is replaced. From the point of view of the
1065
//XML stream, it is as if it the replacing object was the only one
1066
//that ever existed.
1067
//8Aug2001 - therefore, we call writeReplace now. It's simple:
1068
//if (!superVersion || (superVersion && (clazz==o.getClass())))
1069
if (!superVersion || (superVersion && (clazz==superClasses[0]))) {
1070                o = writeReplace(o); //in case change, update info:
1071

1072                if (superVersion) {
1073                    if (o.getClass()==Object JavaDoc.class) { //special case
1074
superClasses = new Class JavaDoc[] { o.getClass() };
1075                    } else {
1076                        superClasses = getReversedSuperClasses(o.getClass());//leaf;no Object
1077
//o.getClass() is the reason for the o==null
1078
}
1079                } else {
1080                    superClasses = new Class JavaDoc[] { o.getClass() };
1081                }
1082                superI = 0;
1083                clazz = superClasses[superI];
1084
1085                //clazz = o.getClass();
1086
//the redundant "if" is clearer, I think
1087
runTimeClass = ParseUtilities.escapeDollar(clazz.getName());
1088            }
1089
1090            /* NB: we do internal first, so parser knowes when finished
1091                * internal serialization
1092                * ASSUME:
1093                * (0). We ignore Hashtable and Vector (use old ad hoc methods)
1094                * (1). defaultWriteObject() does nothing (or we get double header)
1095                */

1096            boolean writeFull = false;
1097            if (superVersion) {
1098              if (superHeader) //most derived class takes role of highest superclass
1099
writeFull = putAlias(o, fieldName, false, indent); //ignore result
1100
else
1101                    writeFull = true; //superclasses *always* written in full
1102
} else {
1103                writeFull = putAlias(o, fieldName, false, indent);
1104            }
1105            if (writeFull) {
1106            //if ((clazz!=o.getClass()) || putAlias(o, fieldName, false, indent))
1107
//version=2
1108
if (superVersion && superI==0 && superHeader) { //first call
1109
String JavaDoc leafClassName = ParseUtilities.escapeDollar(
1110                        superClasses[superClasses.length-1].getName()
1111                    );
1112                    printOpenTag(leafClassName, indent, true); //
1113
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName); //nice to reuse
1114
toOutPrintln(">"); //*always* has children
1115
//superI always==0?
1116
serializeObject(false, o, superClasses, 0, SUPER_TOKEN, indent+spacer);
1117                    //NB: same super, not header any more, no name, indented
1118
printCloseTag(leafClassName, indent); //the last one
1119
} else { //version=1
1120
printOpenTag(runTimeClass, indent, true); //<openTag...
1121
if (fieldName!=null) //...[obj-name="name"]...
1122
printAttr(NAME_TOKEN, fieldName);
1123    //test two together, to see if any dumb errors
1124
if (optVersion) { //only gather here...
1125
serializeFields(o, clazz, indent); //starts a new objStore
1126
//disable this, only do when defaultWriteObject() called?
1127
} else { //just reunite the bifurcated halves.
1128
serializeFields(o, clazz, indent); //starts a new objStore
1129
// internalSer(clazz, o); //done in serialize Fields
1130
serializeOptAttr(o, clazz, indent);
1131                    }
1132                
1133                        //...(field="value")*...
1134
//serializeFields invokes writeObject() - if any
1135
//firstCall is not state - just which routine calls. Can recurse OK.
1136
boolean hasChildren = false; //init
1137
Class JavaDoc superClass = null;
1138                    if (superVersion) { //recursion for super classes
1139
//superClass = clazz.getSuperclass();
1140
if (superI+1<superClasses.length)
1141                        //if (superClass!=Object.class) //when it bottoms out
1142
hasChildren = true; //only for superVersion
1143
}
1144                        
1145                    //This doesn't make sense for superVersion - it is in fact a bug
1146
if (!putFieldDiditAll) { //switch on whether putFields or regular
1147
try {
1148                            hasChildren = hasChildren || hasChildren(o, clazz);
1149                            //in case superVersion
1150
} catch (IllegalAccessException JavaDoc ex) {
1151                            throw new WriteAbortedException("", ex);//sub-IOException wrapper
1152
}
1153                        if (optVersion) hasChildren = true; //opt might alone have children
1154
if (hasChildren) {
1155                            toOutPrintln(">"); //move into ParserXML?
1156
//<fields> ----------
1157
if (optVersion) //fields before opt
1158
serializeReferences(o, clazz, indent);//works here? Stacking?
1159
//disable this, until defaultWriteObject() called?
1160
//(1). connect defaultWriteObject() to call serializeReferences
1161
//(2). need to stack the clazz and object and indent.
1162
// it does not work! Format has field-opt order wrong!
1163

1164                            if (INTERNAL_DEBUG) System.err.println("internal objects:");
1165                            Vector localObjStore = objStore;//local->stack (recursion)
1166
Vector localDefaultCache = defaultCache;//local->stack (recursion)
1167

1168                            //<opt-data> ----------
1169
//if (localObjStore.size()>0) { //make it optional (graceful degrade)
1170
//this was so silly!! the objStore had not yet been filled
1171
boolean possibleOptData = false; //cache
1172
if (optVersion) {
1173                                    //objStore = new Vector(); //but see getAllFields()...
1174
//defaultCache = new Vector();
1175
possibleOptData = doesOwnSer(clazz);
1176                                    if (possibleOptData) {
1177                                        printOpenTag(OPT_DATA_TOKEN, indent+spacer, false);//no obj
1178
toOutPrintln(">"); //always assume children
1179

1180                                        if (defWO_DIRECT) {
1181                                            clazzStack.add(clazz); //push for defWO
1182
thisObjectStack.add(o);
1183                                            indentStack.add(indent);
1184                                        }
1185                                        internalSer(clazz, o); //output: obj dir + caches prim
1186
//push... but format requires def to be already written!
1187

1188                                        if (defWO_DIRECT) {
1189                                            indentStack.remove(indentStack.size()-1); //pop
1190
thisObjectStack.remove(thisObjectStack.size()-1);
1191                                            clazzStack.remove(clazzStack.size()-1);
1192                                        }
1193
1194                                        if (primStore.size()>0) {
1195                                            printOpenTag(OPT_PRIM_DATA_TOKEN, indent+spacer+spacer, false); //no obj
1196
serializeOptAttr(o, clazz, indent);
1197                                            //does the invoke (via internalSer), through false flag
1198
toOutPrintln("/>"); //only prim fields
1199
}
1200                                    }
1201                                }
1202                                //System.err.println("objStore==localObjStore "+(objStore==localObjStore));
1203
//System.err.println(localObjStore);
1204
if (!superVersion) { //temp try: super to serialize direct
1205
for (Enumeration keys=localObjStore.elements(); keys.hasMoreElements();) {
1206                                    Object JavaDoc a = keys.nextElement();
1207                                    if (INTERNAL_DEBUG && a!=null) System.err.println("in objStore: "+a+"\n"+a.getClass());
1208                                    serializeObject(a, null, indent+spacer+spacer); //debug print out
1209
}
1210                             }
1211                                if (optVersion && possibleOptData) {
1212                                    printCloseTag(OPT_DATA_TOKEN, indent+spacer);
1213                                }
1214                            //}
1215

1216                                //objStore.clear(); //NB: reset when consumed
1217
//try: No need - now a new one each time
1218
if (INTERNAL_DEBUG) {
1219                                System.err.println("should be cleared: "+localObjStore.size());
1220                                    //do internal first, so parser knows end automatically.
1221
System.err.println("references:");
1222                            }
1223                            defaultCache = localDefaultCache; //restore! (count is OK)
1224
if (superVersion && superI+1<superClasses.length) {//treated like a child
1225
serializeObject(false, o, superClasses, superI+1, SUPER_TOKEN, indent+spacer); //nice!!
1226
}
1227                            //if (superVersion && superClass!=Object.class) //treated like a child
1228
//serializeObject(o, superClass, SUPER_TOKEN, indent+spacer); //nice!!
1229
if (!optVersion) { //opt before fields
1230
// serialize out objects from PutField now.
1231
// each declared class, each object within each declared class
1232
for (int j=0; j<putFieldObjectsProtect.value().length; j++) {
1233                                    Map JavaDoc putFieldObjMap = putFieldObjectsProtect.value()[j];
1234                                    if (putFieldObjMap!=null) {
1235                                        for (Iterator iObj = putFieldObjMap.entrySet().iterator(); iObj.hasNext();) {
1236                                            Map.Entry JavaDoc pair = (Map.Entry JavaDoc) iObj.next();
1237                                            serializeObject(pair.getValue(), pair.getKey().toString(), indent+spacer);
1238                                                //NB: [name-value] swapped
1239
}
1240                                    }
1241                                }
1242
1243                                serializeReferences(o, clazz, indent);
1244                                finalizeGetAllFields();
1245                            }
1246                            printCloseTag(runTimeClass, indent);
1247                        } else {
1248                            finalizeGetAllFields();
1249                                                                                    //just a test, so far...
1250
toOutPrintln("/>"); //empty tag
1251
}
1252                    } else { //"serialPersistentFields" handled by PutField
1253
printCloseTag(runTimeClass, indent);//always <tag></tag>; never <tag/>
1254
putFieldDiditAll = false; //out fake "pop"!
1255
}
1256                }
1257                //8Aug2001 - the object has now been completely written.
1258
//It is too late to call writeReplace.
1259
if (clazz==superClasses[0]) //only do for leaf classes
1260
//if (clazz==o.getClass()) //only do for leaf classes
1261
removeAlias(o); //only acts when in "min aliases" mode
1262
}//putAlias
1263
}
1264    }
1265//NOTE: may be mixed from different objects - that's OK.
1266
//for defWO_DIRECT (need to leave in for commented out code to compile.
1267
Vector clazzStack = new Vector();
1268    Vector thisObjectStack = new Vector();
1269    Vector indentStack = new Vector();
1270
1271    /**--------------------------------------------------------------------------
1272        * I wonder if these tag factorings should be in ParserXML? It's not
1273        * parsing - quite the opposite - but it is at the same level as the parser.
1274        * In the parlace of Compilers, it is "code generation", I guess
1275        **/

1276    private void printOpenTag(String JavaDoc tag, String JavaDoc indent, boolean isObject) {
1277        out.print(indent + "<" +tag);
1278        if (isObject && includeAliasID)
1279            printAttr(ALIAS_ID_TOKEN, aliasSerialNumber+"");
1280    }
1281    private void printCloseTag(String JavaDoc tag, String JavaDoc indent) {
1282        toOutPrintln(indent + "</" +tag+ ">");
1283        //toOutPrintln(); //nicer formatting hmmm... also used by Wrapper...
1284
}
1285
1286/** ---------------------------------------------------------------------------
1287    * writeReplace
1288    * ------------
1289    * Reflectively invoke the present objects's writeReplace().
1290    * From the perspective of the XML stream, the replacement object (the one
1291    * that this routine returns) is the only one that ever existed.
1292    * "parameterless" = no-arg
1293    * This routine has a pattern that seems *very* similar to readResolve().
1294    * It may be worth factoring something out. Defer until another
1295    * reflexive invocation is required.
1296    **/

1297    Object JavaDoc writeReplace(Object JavaDoc me) throws ObjectStreamException {
1298        try {
1299            Class JavaDoc replacedClass = me.getClass();
1300            Method writeReplaceMethod = null; //changed, for try
1301
try {
1302                writeReplaceMethod = replacedClass.getDeclaredMethod("writeReplace", new Class JavaDoc[]{});
1303            } catch (NoSuchMethodException JavaDoc checkSuperClasses) { //added
1304
//Check for method in superclass (simulating "inheritance"):
1305
writeReplaceMethod = replacedClass.getMethod("writeReplace", new Class JavaDoc[]{});
1306                //To Do: iterate up the superclasses here, not just public
1307
}
1308
1309            writeReplaceMethod.setAccessible(true);
1310            Object JavaDoc newme = writeReplaceMethod.invoke(me, new Object JavaDoc[]{}); //no arg
1311
//The following check removed, because Kevin Day (Trumpet, Inc.) 12 March 2002
1312
//showed that non-compatible replacement *is* permitted by JOS spec.
1313
/*
1314            if ( !(me.getClass().isAssignableFrom(newme.getClass())) ) //ie go back
1315                throw new ClassCastException("The writeReplace() method of class '"+
1316                    replacedClass.getName() +"' returned an object of type '"+
1317                    newme.getClass().getName() +"'.");
1318                    //would it be more efficient to initialize all these exceptions at
1319                    //load time? Or better to create JIT, only as needed?
1320*/

1321            return newme;
1322        } catch (NoSuchMethodException JavaDoc e) {
1323            //Not an error - just means that it doesn't implement readResolve
1324
return me; //if no writeReplace - the object is simply unchanged!
1325
} catch (IllegalAccessException JavaDoc e) {
1326            System.err.println("IllegalAccessException - this shouldn't happen, since we did setAccessible(). Perhaps security on the JVM precludes it?");
1327            e.printStackTrace();
1328            return me; //just continue on - what would be a better thing to do?
1329
} catch (InvocationTargetException e) {
1330            System.err.println("JSX InvocationTargetException:");
1331            //e.printStackTrace(); //not sure how this should be handled...
1332
//is this the best way to handle it? What does Serialization do?
1333
//Probably best to throw whatever is thrown.
1334
try {
1335                throw (ObjectStreamException) e.getTargetException(); //uncork it
1336
//contract of writeReplace guarantees that it only throws this class
1337
//However, neither the compiler nor ObjectOutputClass checks this.
1338
//Since this is a compile-time kind of thing, it is reasonable that
1339
//ObjectOutputClass does not check it, but only runtime problems...
1340
//Is the situation different for things like readObject() being private?
1341
} catch (ClassCastException JavaDoc f) {
1342                throw new Error JavaDoc("writeReplace() of "+me.getClass()+ " threw an '"+e.getTargetException()+"'. It should only be of type ObjectStreamException");
1343            }
1344            //unreachable - can't to get here.
1345
}
1346        //let all other Exceptions through - esp ClassCastException! - for caller
1347
//to handle.
1348
}
1349
1350
1351    /**--------------------------------------------------------------------------
1352        * serializeArrayReferences
1353        * ------------------------
1354        * Can contain primitives (no wrapper) or objects
1355        * If called on non-array, it serializes as an Object (needed for recursion)
1356        **/

1357    private void serializeArrayReferences(Object JavaDoc o, String JavaDoc fieldName, String JavaDoc indent) throws IOException{
1358        Class JavaDoc clazz;
1359        if (o==null || !( (clazz=o.getClass()).isArray() )) {
1360    // System.err.println("Just about do output the base type element of an array");
1361
serializeWrapperOrObject(o, indent);
1362            return; //bottoms out.
1363
}
1364            //superVersion is no issue for arrays.
1365
if (!putAlias(o, fieldName, false, indent))
1366            return; //don't inline if already done
1367
String JavaDoc runTimeClass = ParseUtilities.escapeDollar(o.getClass().getName()); //redundancy here (see above)//need to: $ -> ..
1368
//HUH? The above is immediately overwritten...
1369
//toOutPrintln(indent + "ARRAYS NOT YET IMPLEMENTED");
1370
//Switch for byte[] goes here!
1371
if (clazz==byte[].class) { //if a binary array, treat differently
1372
printOpenTag(BINARY_DATA_TOKEN, indent, true); //<binary-data -- an obj
1373
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName);
1374            printAttr(VALUE_TOKEN, ParseUtilities.encodeHex((byte[])o, formatted));
1375            toOutPrintln("/>"); //nice if this and the '"' were indented
1376
// printCloseTag(BINARY_DATA_TOKEN, indent); //not needed!!!
1377
} else {
1378            runTimeClass = getArrayType(clazz);
1379            printOpenTag(runTimeClass, indent, true); //map "[" to "ArrayOf-"; obj
1380
if (fieldName!=null) printAttr(NAME_TOKEN, fieldName);
1381            int length = Array.getLength(o);
1382            printAttr(LENGTH_TOKEN, length+"");
1383/*
1384    //we comment out this section, because zero-length arrays in the emptyTag
1385    //format are are not being deserialized correctly. Disabling the emptyTag
1386    //option is the simplest hack to fix it, as some users need it urgently.
1387    //Many thanks to
1388    // Raimund 'Raimi' Jacob - Mon, 29 Jan 2001 00:47:58 +0100 (MET)
1389    // Gary Lawrence Murphy - Thu Feb 1 12:37:44 2001
1390    // for identifying this bug and help testing ways to solve it.
1391        if (length==0) //if o.length==0
1392            toOutPrintln("/>"); //empty tag
1393        else
1394*/

1395            { //<- this brace remains from the commented-out if-else above.
1396
//if primitive, put it here?
1397
//if *last* array (ie array of non-array), and also of a primitive.
1398
//well, to get here, must be an array...
1399
//...so check check to see if it as array of a primitive
1400
if ( clazz.getComponentType().isPrimitive() ) {
1401                    for (int i=0; i<length; i++) {
1402                        if (clazz.getComponentType() == byte.class) { //if byte[]
1403
//binary data special case. //Thanks to Mark Collette.
1404
//3Oct2001 - binary now a 100% drastically special case.
1405
} else {
1406                            toOutPrintln();
1407                            out.print(indent);
1408                        }
1409                        if (clazz.getComponentType()==char.class) //primitive, not wrapper
1410
printAttr(ARRAY_PRIMITIVE_INDEX_TOKEN+i, ParseUtilities.encodeXML(Array.get(o,i)+""));
1411                        else
1412                            printAttr(ARRAY_PRIMITIVE_INDEX_TOKEN+i, Array.get(o,i)+"");
1413                    }
1414                    toOutPrintln("/>"); //*cannot* contain other tags. (into ParserXML?)
1415
}
1416                else { //ie component is a reference type (another array or an object)
1417
toOutPrintln(">"); //move into ParserXML?
1418
for (int i=0; i<length; i++) { //actually a recursion
1419
serializeArrayReferences(Array.get(o, i), null, indent+spacer);
1420                    }
1421                        //closeTag is inside
1422
printCloseTag(runTimeClass, indent);
1423                }
1424            }
1425        }
1426        removeAlias(o);
1427    }
1428
1429    /** convert Array name string from:
1430        * [* //any number of '[' (including 0)
1431        * (B|C|D|F|I|J|S|Z) | //one of these represeting a primitive type
1432        * (L[^;]*;) //or the name of the class, in between 'L' and ';'
1433        **/

1434    static String JavaDoc getArrayType(Class JavaDoc clazz) { //utility
1435
String JavaDoc a = ""; //will hold result - if empty string, reference problem
1436
String JavaDoc name = ParseUtilities.escapeDollar(clazz.getName());//need to: $ -> ..
1437
if (DEBUG) System.err.println("Array type = \""+name+"\"");
1438        int i;
1439        for (i=0; name.charAt(i)=='['; i++)
1440            a += ARRAY_TOKEN;
1441        switch (name.charAt(i++)) {
1442            case 'B': a += "byte"; break;
1443            case 'C': a += "char"; break;
1444            case 'D': a += "double"; break;
1445            case 'F': a += "float"; break;
1446            case 'I': a += "int"; break;
1447            case 'J': a += "long"; break;
1448            case 'S': a += "short"; break;
1449            case 'Z': a += "boolean"; break;
1450            case 'L': a += name.substring(i, name.lastIndexOf(';')); break;
1451                        //could also be name.length()-1
1452
}
1453        // NB: in a multi-dimensional array, Class.getComponentType() returns
1454
// the next one further in (which may also be an array).
1455
return a;
1456    }
1457
1458    /**--------------------------------------------------------------------------
1459        * hasChildren
1460        * -----------
1461        * An inefficient but obvious prototype: better to factor out field
1462        * handlers (ie ref, primintive and this one), to farm out into appropriate
1463        * arrays for each.
1464        * "Children" def: existing (ie non-null) classes (ie non-primitive).
1465        **/

1466    private boolean hasChildren(Object JavaDoc o, Class JavaDoc frameClass) throws
1467            IllegalAccessException JavaDoc, InvalidClassException {
1468        //System.err.println(o+" hasChildren--------------------------");
1469
IllegalAccessException JavaDoc ex = null; //collect as we go along
1470
if (objStore.size()>0) //consider: return in fields - neat return
1471
return true;
1472        for (int j=0; j<putFieldObjectsProtect.value().length; j++) {
1473            Map JavaDoc putFieldObjMap = putFieldObjectsProtect.value()[j];
1474                if (putFieldObjMap!=null) {
1475                    if (putFieldObjMap.size()>0)
1476                        return true;
1477            }
1478        }
1479        //try {
1480
Field[] fields = getAllFields(o, frameClass, false); //later call!
1481
//null indicates that we query all superclasses
1482
//Field[] fields = o.getClass().getFields();
1483
for (int i=0; i<fields.length; i++) {
1484                Field f = fields[i];
1485                if (Modifier.isStatic(f.getModifiers())) // transient done in SerialPF
1486
// & NOT_SERIALIZED) != 0)
1487
continue;
1488                if (!f.getType().isPrimitive()) {
1489                    if (stringAsElement || f.getType()!=String JavaDoc.class) {
1490                        //try {
1491
//if (f.get(o)!=null)
1492
return true; // Now: we show, even if all nulls
1493
//} catch (IllegalAccessException e) {
1494
// ex = e; //we want to keep checking, even if an exception
1495
//}
1496
}
1497                }
1498            }
1499            //if no child found but exception raised, re-raise (rethrow) it.
1500
//(can't say for sire whether it has children or not. from Philip Gust
1501
//Q: What is the best feedback for the caller about this?
1502
if (ex != null) {
1503                throw ex;
1504            }
1505            return false; //found no non-primitive (includes String),
1506
//non-null objects within it
1507
//} catch (Exception e) {System.err.println(e); System.exit(0);}
1508
//get rid of this - exit() is bad in production code
1509
}
1510
1511
1512    /**--------------------------------------------------------------------------
1513        * serializeFields - attr/primitives + Strings
1514        * ---------------
1515        * SUGGEST: abstracting a "printAttr(String name, String value)" method.
1516        *
1517        * header==true for first time
1518        * <Class obj-name="..." ... >
1519        * header==false for second call (for opt-data)
1520        * <opt-data ...>
1521        * Note that we have *completely* bifurcated it... (mutually exclusive)
1522        **/

1523    private void serializeFields(Object JavaDoc o, Class JavaDoc frameClass, String JavaDoc indent)
1524        throws InvalidClassException {
1525        //System.out.println("entering serializeFields, "+o+", "+frameClass);
1526
//System.err.println(o+" serializeFields--------------------------");
1527
if (o==null) return;
1528        Field[] fields = getAllFields(o, frameClass, true); //primitive: 1st call
1529
//superVersion: this needs to become based on the current frame
1530
//the fields should now be all from the particular frame class...
1531
//getAllFields invokes writeObject() - if any.
1532
//Field[] fields = o.getClass().getFields();
1533
//Field[] fields = o.getClass().getDeclaredFields();
1534
//getDeclaredFeilds doesn't return inherited fields.
1535
//NOTE!!! getFields() returns ONLY public fields
1536
//getDeclaredFields() returns everything - except inherited fields
1537
try {
1538            for (int i=0; i<fields.length; i++) {
1539                Field f = fields[i];
1540                //shadowEncode not needed if (superVersion)...
1541
String JavaDoc fieldName = shadowEncode(f, o.getClass()); //added for shadowing
1542
//System.err.println("Field "+i+": "+f);
1543
if (DEBUG) System.err.println("Field "+i+": "+f);
1544                // if ((f.getModifiers() & NOT_SERIALIZED) != 0)
1545
if (Modifier.isStatic(f.getModifiers())) // transient done in SerialPF
1546
continue; //skip transient and static fields
1547
Class JavaDoc type = f.getType();
1548                //System.err.println("Type "+type);
1549
//how bad would it be to treat these two following cases the same?
1550
//ie. and *always* do encodeXML() automatically, in the ParserXML
1551
if (DEBUG) System.err.println("here1");
1552                if (type.isPrimitive()) {
1553                    toOutPrintln(); //Attribute indenting
1554
out.print(indent); //indent each attribute
1555
String JavaDoc outStr = f.get(o).toString();
1556                    if (type==char.class)
1557                        outStr = ParseUtilities.encodeXML(outStr);
1558                    printAttr(fieldName, outStr);
1559                }
1560//Stop String being written as an attribute
1561
else if (!stringAsElement && f.get(o)!=null && type==String JavaDoc.class){ //.getName().equals("java.lang.String"))
1562
toOutPrintln(); //Attribute indenting
1563
out.print(indent); //indent each attribute
1564
if (DEBUG) System.err.println("here2");
1565                    //fieldName;
1566
if (DEBUG) System.err.println("here3");
1567                    //f.get(o);
1568
if (DEBUG) System.err.println("here4");
1569                    //superVersion is no issue for Strings
1570
if (putAlias(f.get(o), shadowEncode(f, o.getClass()), true, indent)) {
1571                            //only inline if not done already
1572
//problem for attributes in String
1573
printAttr(fieldName, ParseUtilities.encodeXML(f.get(o)+""));
1574                        removeAlias(f.get(o));
1575                    }
1576                }
1577                    //NB: if neither String nor primitive, it just falls right through
1578
}
1579/*
1580        } catch (InvocationTargetException e) { // simple propagation
1581            try {
1582                throw (RuntimeException) e.getTargetException(); // getCause in 1.4, but need backcompat
1583            } catch (Exception nestedE) { // jeez, nesting
1584                nestedE.printStackTrace(); // if not a runtime
1585            }
1586*/

1587        } catch (Exception JavaDoc e) {
1588            //System.out.println("Field: "+e);
1589
e.printStackTrace();
1590            //System.exit(1);
1591
}
1592    }
1593
1594
1595    private void serializeOptAttr(Object JavaDoc o, Class JavaDoc frameClass, String JavaDoc indent) {
1596                //hopefully the buffer stacking survives this intermediate step...
1597
//internalSer(c, o);
1598
//System.err.println("object:"+o+", "+frameClass+" about to be called");
1599
//if (OPT_DEBUG) System.err.println("object:"+o+", "+frameClass+" about to be called");
1600
//(3). process returned values.
1601
int i=0;
1602        for (Enumeration keys=primStore.elements(); keys.hasMoreElements(); i++) {
1603            //toOutPrintln(); //Attribute indenting
1604
//out.print(indent+spacer); //indent each attribute
1605
printAttr(INTERNAL_PRIMITIVE_TOKEN+i, ParseUtilities.encodeXML((String JavaDoc)keys.nextElement()));
1606            //NOTE: will never be String, because it is serialized using object.
1607
}
1608        //try { throw new Exception(); }
1609
//catch (Exception e) { e.printStackTrace(); }
1610

1611        primStore.clear(); //NB: reset when consumed
1612
//consider: can reuse, because never recurse. Different from objStore
1613
//NB: need to explicitly cast Object to String
1614
//Consider: if an array, could specify a String base type
1615
//NB: lazy for now: always encode as XML, even tho only String needs it
1616
}
1617
1618
1619
1620/**--------------------------------------------------------------------------
1621    * isShadowed
1622    * ----------
1623    * Inherited fields with the same name as present fields are "shadowed",
1624    * and JSX needs some way to distinguish them. Needed for both primitives
1625    * and objects (references).
1626    * We check to see if there is a field of the same name in a younger (sub)
1627    * class. The order we scan the descendents does not matter - thus we can
1628    * use superclass() (no getSubclass() is avail!), and start at the bottom,
1629    * and work our way up until we are one class away from the class declaring
1630    * the field.
1631    * A
1632    * |
1633    * B <-- field declared here: B.class.getDeclaredField(fieldName);>
1634    * |
1635    * C
1636    * |
1637    * D <-- the class of the object we have: me.getClass();
1638    * |
1639    * E
1640    *
1641    * Attribution: based on scratch code by Wes Biggs, 20 March 2001, which
1642    * did the opposite - if a given field shadowed another. */

1643    boolean isShadowed(Field f, Class JavaDoc c) {
1644        Class JavaDoc stopc = f.getDeclaringClass();
1645        for (; c!=stopc; c=c.getSuperclass()) {
1646        try {
1647            Field f2 = c.getDeclaredField(f.getName());
1648                //System.toOutPrintln("Given field '" +qualField(f)+ "'");
1649
//System.toOutPrintln("is SHADOWED BY '" +qualField(f2)+ "'");
1650
return true;
1651        } catch (NoSuchFieldException JavaDoc e) {
1652            // expect this in most cases
1653
}
1654        }
1655        //System.toOutPrintln("not shadowed");
1656
return false;
1657    }
1658/**--------------------------------------------------------------------------
1659    * qualField
1660    * ---------
1661    * This creates the class-qualified field name. Arguably, this and the
1662    * XMLDeserialize "parseQualField" could go into the ParseUtilities class.
1663    **/

1664    String JavaDoc qualField(Field f) {
1665        return f.getDeclaringClass().getName()
1666                +"."+f.getName();
1667            //for field: $ -> ..
1668
}
1669
1670/**--------------------------------------------------------------------------
1671    * shadowEncode
1672    * ------------
1673    * Returns the qualified field name, if needed; else just the plain name.
1674    * NOTE: ParseUtilities.escapeDollar is done when attr names are printed -
1675    * this is the only time it is necessary. Note that primitive field names
1676    * are attributes; but reference field names are attribute *values*, and so
1677    * may contain "$".
1678    *
1679    * Could change this to also take into account the serialPersistentFields
1680    * somehow. (1). If it was enabled; (2). the same for each of the super
1681    * classes.
1682    * I think there is a timing issue, to do with them only be set at runtime
1683    * (no static serialPersistentFields to declare them): one solution is to
1684    * just qualify them when needed to distinguish them (which might be at the
1685    * level of the present class).
1686    *
1687    * The proper solution is (again) to separate the fields into those of each
1688    * inherited class (would this solve the runtime issue?)
1689    *
1690    **/

1691    String JavaDoc shadowEncode(Field f, Class JavaDoc c) {
1692        if (isShadowed(f, c))
1693            return qualField(f);
1694        else
1695            return f.getName();
1696            //for field: $ -> ..
1697
//arguable, could do all the dollar escaping here, but not as natural..
1698
}
1699
1700
1701    /** This should likewise be in ParserXML.
1702        * static!
1703        */

1704    private void printAttr(String JavaDoc attrName, String JavaDoc value) {
1705        //out.print(spacer +attrName+ " = \"" +value+ "\"");
1706
out.print(" "+ ParseUtilities.escapeDollar(attrName)+ "=\"" +value+ "\"");
1707    }
1708
1709
1710    /**--------------------------------------------------------------------------
1711        * serializeReferences(o," ") - tags/objects
1712        * -------------------
1713        * Needs take a Class object for framing.
1714        **/

1715    //should factor out the two iterations over fields (attr and ref)
1716
private void serializeReferences(Object JavaDoc o, Class JavaDoc frameClass, String JavaDoc indent) throws IOException {
1717        //System.err.println(o+" serializeReferences--------------------++++++++++++++");
1718
if (o==null) return;
1719        Field[] f = getAllFields(o, frameClass, false); //ref: 2nd call (last call)
1720
//superVersion: this needs to become based on the current frame
1721
//Field[] f = o.getClass().getFields();
1722
//Field[] f = o.getClass().getDeclaredFields(); //skips inherited fields
1723
for (int i=0; i<f.length; i++) {
1724            //bug found by Romain - thanks!
1725
// if ((f[i].getModifiers() & NOT_SERIALIZED) != 0)//left this out before!
1726
if (Modifier.isStatic(f[i].getModifiers())) // transient done in SerialPF
1727
continue;
1728            Class JavaDoc type = f[i].getType();
1729            try {
1730                if (!(type.isPrimitive() || (!stringAsElement && type==String JavaDoc.class))) //.getName().equals("java.lang.String")))
1731
serializeObject(f[i].get(o), shadowEncode(f[i], o.getClass()), indent+spacer);
1732            } catch (Exception JavaDoc e) {
1733                System.err.println("References: " +e);
1734                System.err.println("i = " +i);
1735                System.err.println("f.length = " +f.length);
1736                e.printStackTrace();
1737            }
1738        }
1739    }
1740
1741
1742    /**--------------------------------------------------------------------------
1743        * getAllFields(o, frame, true)
1744        * ------------
1745        * Walks up the class hierarchy, getting:
1746        * - non-public (private etc)
1747        * - inherited fields;
1748        * NB: getDeclaredFields() does only the former, getFields() only the latter.
1749        * Alt implementation:
1750        * 2 pass: build a vector of arrays, then arraycopy to the correct size
1751        * firstCall: called for both primitives and references. But should
1752        * invoke internal serialization only once (on firstCall). NB always need
1753        * to check for writeObject(), to know whether to skip (ie default) or not.
1754        **/

1755        Vector defaultCache; //still is recursion
1756
private void finalizeGetAllFields() {
1757        defaultProtect.retrieve(); // pop - clear the stack
1758
putFieldObjectsProtect.retrieve(); // pop - clear the stack
1759
}
1760    //private Field[] getAllFields(Object o, boolean firstCall) {
1761
private Field[] getAllFields(Object JavaDoc o, Class JavaDoc frameClass, boolean firstCall)
1762        throws InvalidClassException {
1763                //internalSer invokes writeObject() - if any.
1764
Vector v = new Vector();
1765        int cacheIndex = 0;
1766        if (CACHE_DEBUG)
1767            System.err.println("Entering " +firstCall);
1768        //if (!superVersion && firstCall) {
1769
if (firstCall) {
1770            objStore = new Vector(); //NB: once for all superclasses: collect all
1771
defaultCache = new Vector();
1772        }
1773
1774    //Externalizable - alternative must go here...
1775
//NB: superVersion has no effect here
1776
if (o instanceof Externalizable) {
1777            if (firstCall) { //only call once
1778
try {
1779                    ((Externalizable)o).writeExternal(thisOut);
1780                } catch (IOException e) {
1781                    //should propagate, but awkward in present architecture
1782
}
1783            }
1784            return new Field[0];
1785        }
1786
1787        //TEST: do just for leaf class. (later will do for all)
1788
//Need to go in reverse order (superclass -> subclass), because that is
1789
//the order that Serialization does it, and which classes which customise
1790
//their own serialization expect. Thanks Mark Collette (Simulutions.com)!
1791
//SOLN: create an array, then step it backwards.
1792

1793        //for superVersion, we ignore the whole reversed classes thing
1794
Class JavaDoc[] superClasses = null;
1795        if (superVersion)//with a "super" field, to nest superclasses
1796
superClasses = new Class JavaDoc[] {frameClass};
1797        else
1798            superClasses = getReversedSuperClasses(o.getClass()); //actually includes leaf...
1799
if (firstCall) {
1800            //defaultCache2 = new boolean[superClasses.length]; // create the cache
1801
defaultProtect.protect(new boolean[superClasses.length]); // create the cache
1802
putFieldObjectsProtect.protect(new Map JavaDoc[superClasses.length]); // create the cache
1803
}
1804        //System.err.println("boolean["+defaultProtect.value().length+"]");
1805
for (int j=0; j<superClasses.length; j++) { //traverse root to leaf
1806
//c = superClasses[(superClasses.length-1)-j]; //... from root (but for Object) to leaf
1807
Class JavaDoc c = superClasses[j]; //... from root (but for Object) to leaf
1808
if (CACHE_DEBUG)
1809                System.err.print(o.getClass() +"CACHE INDEX: " +cacheIndex+"\t\t");
1810            if (firstCall) { //do for each superclass
1811
if (!optVersion) { //Just stop the *actual* call here
1812
defaultCatcher.newCatcher();
1813                    putFieldObjectsCatcher.newCatcher();
1814                        internalSer(c, o); //get returned values in caller (confusing?)
1815
putFieldObjectsProtect.value()[j] = putFieldObjectsCatcher.freeCatcher();
1816
1817                    boolean defaultResult = defaultCatcher.freeCatcher(); // catch and...
1818
defaultProtect.value()[j] = defaultResult; // ...cache
1819
}
1820                if (CACHE_DEBUG)
1821                    System.err.println("firstcall: CACHE: " +defaultCache);
1822            } else {
1823                if (CACHE_DEBUG)
1824                    System.err.println("not firstcall CACHE: " +defaultCache);
1825                        //retrieve from cache for later calls
1826
}
1827
1828            boolean doesOwn = doesOwnSer(c);
1829            //(1). if it doesn't do internal serialization, then serialize normally
1830
//(2). if it does internal serialization, only serialize if default called
1831
//GREAT! Default fields omitted if defaultWriteObject() not called.
1832
//we just completely ignore the default issue for now!
1833
if (optVersion ||
1834                    (!doesOwn || (doesOwn && defaultProtect.value()[j])) ) { //verbose is clearer
1835
//(!doesOwn || (doesOwn && defaultCache2[j])) ) { //verbose is clearer
1836
Field[] f = c.getDeclaredFields();
1837                Map JavaDoc fmap = SerialPF.getFieldNameType(c);
1838                // very ineff, as we are dupicating same work
1839
boolean allOK = SerialPF.areSerializableFieldsValidFor_defWO(fmap, c);
1840                if (!allOK) throw new InvalidClassException("unmatched serializable field(s) declared");
1841                for (int i=0; i<f.length; i++) {
1842                    if (fmap.containsKey(f[i].getName())) // only if a serializable field
1843
v.add(f[i]);
1844                }
1845            }
1846        } //old: while ( (c = c.getSuperclass()) != Object.class); //not 'C'lass
1847

1848    // The commented out, "lastCall" below:
1849
// we would dearly love to detect the lastcall here, and clean up the cache
1850
// stack here, too, but we can't tell which will be last, from here. It could
1851
// be from hasChildren, or from SerializeReferences. We don't know.
1852
// Therefore, we delegate this clean up aspect to the code that decides
1853
// whether hasChildren or SerializeReferences is last. Search on
1854
// "defaultProtect" to find it.
1855
/*
1856        if (lastCall) {
1857            defaultProtect.retrieve(); // pop - clear the stack
1858        }
1859*/

1860    // System.err.println("Fields for "+o.getClass()+": "+v);
1861
Field[] f = new Field[v.size()]; //scope of other f finished
1862
v.copyInto(f); //obviate casting; and only way avail in 1.1
1863
//Java 1.2 security MAGIC!
1864
Field.setAccessible(f, true); //new in 1.2 - need to do version checks
1865
return f;
1866    }
1867
1868/**--------------------------------------------------------------------------
1869    * getReversedSuperClasses
1870    * -----------------------
1871    * A utility class, common to both serialization and deserialization.
1872    * used by both XMLSerialize and XMLDeserialize
1873    **/

1874    static public Class JavaDoc[] getReversedSuperClasses(Class JavaDoc c) {
1875        if (c==Object JavaDoc.class)
1876            return new Class JavaDoc[0]; //Object has no superclasses
1877
Class JavaDoc storeC = c;
1878        boolean isObjSer = Serializable.class.isAssignableFrom(storeC);
1879
1880        //int classDepth = 0; //count for array length (faster than using Vector)
1881
int numSerClasses = 0; //JOS style
1882
do {
1883            //classDepth++;
1884
if (!isObjSer)
1885                numSerClasses++; //non-Ser, JSX style (does them all)
1886
else if (Serializable.class.isAssignableFrom(c))
1887                numSerClasses++; //Ser, JOS style (don't count non-Ser)
1888
/* // shorter code, but less clear:
1889            if (isObjSer && !Serializable.class.isAssignableFrom(c))
1890                continue; // skip *only* in this case
1891            numSerClasses++; //JOS style
1892        */

1893        } while ( (c = c.getSuperclass()) != Object JavaDoc.class); // don't count Object
1894
// ALT: can stop when reach first non-Ser (would inherit Ser from any
1895
// Ser ancestor). Note this works for Object too (it is non-Ser).
1896

1897        //if (DEBUG) System.err.println("classDepth = " + classDepth);
1898
//Class[] superClasses = new Class[classDepth]; //JSX style
1899
Class JavaDoc[] superClasses = new Class JavaDoc[numSerClasses]; //JOS style
1900
c = storeC; //restore
1901
int iSuper=superClasses.length-1; //create it in reverse
1902
do {
1903            if (!isObjSer)
1904                superClasses[iSuper--] = c; // non-Ser: JSX does all
1905
else if (Serializable.class.isAssignableFrom(c))
1906                superClasses[iSuper--] = c; // non-Ser: JOS skips
1907
//if (Serializable.class.isAssignableFrom(c))
1908
// superClasses[iSuper--] = c;
1909
} while ( (c = c.getSuperclass()) != Object JavaDoc.class);
1910            //hmmm... nicer to start at "length", and use "--i"? Then ends on "0"
1911
//in order from root to leaf:
1912
//[0] is the last ancestor before Object; (ie the root but for Object)
1913
//[last] is the leaf - the actual class of the object
1914
if (DEBUG) {
1915            for (int j=0; j<superClasses.length; j++)
1916                System.err.println("superClasses["+j+"] = " + superClasses[j]);
1917        }
1918        return superClasses;
1919    }
1920
1921
1922    /**-------------------------------------------------------------------------
1923        * internalSer
1924        * -----------
1925        * returned values are stored in:
1926        * - defaultFlag
1927        * - objStore
1928        * - primStore
1929        * - returns: whether o implements its own serialization
1930        * NB: With new approach to serialization, this invocation will
1931        * have the effect of depositing the written objects to the objStore,
1932        * where they will be later retrieved: a crude, one-level stack for
1933        * return values.
1934        *
1935        * AH! This is why class and object are both needed: the class forms
1936        * view onto the object. Different slices are possible, for each
1937        * superclass...
1938        *
1939        * PutField - if writeFields was called, then don't do normal
1940        * defaultWriteObject(). Actually, this *is* the standard behaviour - if
1941        * a class has a writeObject(), that does not explicitly call
1942        * defaultWriteObject, then those fields are not written out.
1943        * But I don't think that JSX is actually behaving in that way at the
1944        * moment.
1945        **/

1946    Stack putFieldClassStack = new Stack();
1947        // or LinkedList: push=addLast(), pop=removeLast(), peek=getLast(), clear()
1948
private boolean internalSer(Class JavaDoc clazz, Object JavaDoc o) {
1949                //Class clazz = o.getClass();
1950
defaultCatcher.set(false);
1951        Method m;
1952            if ((m=getDeclaredMethod(clazz, "writeObject", OOS_ARGS,
1953                    Modifier.PRIVATE, Modifier.STATIC)) != null) {
1954            if (INTERNAL_DEBUG)
1955                System.err.print("--JSX serialize--");
1956            //invokeObjectWriter(m, o);
1957
//NB: return values are cleared when read (consumed)
1958
//Consider: reset when they are not generated?
1959
try {
1960    //(2). gather (catch) returned values (in a bucket).
1961
if (INTERNAL_DEBUG)
1962                    System.err.println("invoking: " +m);
1963                //KELLY DEBUG
1964
/*
1965                System.err.println("CHECK: before writeObject: "+clazz);
1966                if (o.getClass()== javax.swing.JInternalFrame.JDesktopIcon.class && clazz == javax.swing.JComponent.class) {
1967                        System.err.println("Encountered "+clazz+" in "+o.getClass()+", skipping writeObject");
1968                }
1969                else
1970*/

1971                // System.err.println(">>> about to call writeObject");
1972
putFieldClassStack.push(clazz); // pushing communication
1973
m.invoke(o, writeObjectArglist); //might as well do them all!
1974
putFieldClassStack.pop();
1975                // System.err.println("<<< just returned from call to writeObject");
1976
//objStore are in effect returned in objStore (returnedObjs?) */
1977
//System.err.println("invoking: " +m);
1978
} catch (InvocationTargetException e) {
1979                try {
1980                    //System.err.println(">>> about to rethrow "+e.getTargetException());
1981
RuntimeException JavaDoc r = (RuntimeException JavaDoc) e.getTargetException();
1982                    //System.err.println(">>>>>>> RuntimeException is "+r);
1983
throw r;
1984                } catch (ClassCastException JavaDoc nestedE) { // not "Exception" just throw!!
1985
//System.err.println(">>>> can't rethrow "+e.getTargetException());
1986
nestedE.printStackTrace();
1987                }
1988            } catch (Exception JavaDoc e) {
1989                System.err.println(e);
1990                e.printStackTrace();
1991            }
1992            //NB: Only process if they exist... hmmm, auto, if size = 0
1993
if (INTERNAL_DEBUG) {
1994            }
1995            if (INTERNAL_DEBUG)
1996                System.err.println("Object Store: "+objStore); //check
1997
if (INTERNAL_DEBUG)
1998                System.err.println("Primitive Store: "+primStore); //check
1999
/* now, we write them all out individually (not the Vector!) */
2000            return true; //"it implements writeObject :-)"
2001
}
2002        else {
2003            if (INTERNAL_DEBUG)
2004                System.err.println("No writeObject() found");
2005            defaultCatcher.set(true);
2006            return false; //"it doesn't implement writeObject :-("
2007
}
2008    }
2009
2010    /**-------------------------------------------------------------------------
2011        * doesOwnSer
2012        * ----------
2013        * Need to check the same superclass three times (refs, fields, hasChild).
2014        * If called internalSer, would collect three lots of information!
2015        * We can't cache it easily, because there are typically several
2016        * superclasses for each object.
2017        * The only downside of this approach is the inefficiency. However, the
2018        * way to counteract this is to refactoring the three calls into one.
2019        **/

2020    private boolean doesOwnSer(Class JavaDoc clazz) {
2021        return ((getDeclaredMethod(clazz, "writeObject", OOS_ARGS,
2022            Modifier.PRIVATE, Modifier.STATIC)) != null);
2023    }
2024
2025
2026
2027
2028    /**--------------------------------------------------------------------------
2029        * serializeVectorReferences (container type)
2030        * -------------------------
2031        * Contains objects only (primitives written as Wrappers inc String)
2032        * spc used instead of indent - is there any reason for this?
2033        *
2034        * Note: Does not provide extensibility by allowing subclasses to override
2035        * this method of Vector serialization (and Hashtable, array, etc).
2036        * Static methods cannot be overridden. To provide this, a instance
2037        * would need to be created at some stage - perhaps lazy instantiation
2038        * when first called. Then, all such method should be made object methods
2039        * (ie not static), so that they can be overridden.
2040        **/

2041    private void serializeVectorReferences (Vector o, String JavaDoc indent) throws IOException{
2042        final String JavaDoc spc = indent + spacer;
2043        if (o==null) throw new RuntimeException JavaDoc("impossible; redundant safety");
2044        for (Enumeration keys=o.elements(); keys.hasMoreElements(); ) {
2045            Object JavaDoc obj = keys.nextElement();
2046            serializeWrapperOrObject(obj, spc);
2047            //if (keys.hasMoreElements()) toOutPrintln(); //separating blank line
2048
}
2049    }; //what's the idea with the trailing ";"?
2050

2051
2052
2053    /**--------------------------------------------------------------------------
2054        * Wrapper OR ObjectVector
2055        * -----------------------
2056        * A helper method (used for Vector and Array only so far), where the
2057        * object is assumed to have no "field name".
2058        **/

2059    private void serializeWrapperOrObject (Object JavaDoc obj, String JavaDoc spc) throws IOException{
2060        if (obj==null) {
2061            printOpenTag(NULL_TOKEN, spc, false); //null is not an object
2062
toOutPrintln("/>"); //good to have control of the newline here
2063
return;
2064        }
2065        Class JavaDoc type = obj.getClass();
2066        if (type==Boolean JavaDoc.class || //is there a cleverer way to do this?
2067
type==Byte JavaDoc.class || //we could loop, yes, but...
2068
type==Short JavaDoc.class ||
2069                type==Integer JavaDoc.class ||
2070                type==Long JavaDoc.class ||
2071                type==Float JavaDoc.class ||
2072                type==Double JavaDoc.class)
2073            serializeWrapper(obj, null, spc);
2074        else if (type==String JavaDoc.class ||
2075                type==Character JavaDoc.class)
2076            serializeWrapper(obj, null, spc); //will cast do Character?
2077
else
2078            serializeObject(obj, null, spc); //null== no name
2079
}
2080
2081    /**--------------------------------------------------------------------------
2082        * serializeHashtableReferences (container type)
2083        * ----------------------------
2084        * Each key and value is an object.
2085        * distinguished by position alone.
2086        * primitives type written as special "wrapper" objects (String, too).
2087        **/

2088    private void serializeHashtableReferences (
2089            Hashtable JavaDoc o, String JavaDoc indent) throws IOException{
2090        if (o==null) throw new RuntimeException JavaDoc("impossible; redundant safety");
2091        for (Enumeration keys=o.keys(); keys.hasMoreElements(); ) {
2092            Object JavaDoc key = keys.nextElement();
2093            Object JavaDoc value = o.get(key);
2094            Object JavaDoc[] objs = {key, value};
2095            String JavaDoc[] spc = {indent+spacer, indent+spacer}; //so can stagger, if we want
2096
for (int i=0; i<objs.length; i++) {
2097                Class JavaDoc type = objs[i].getClass();
2098                if (type==Boolean JavaDoc.class || //is there a cleverer way to do this?
2099
type==Byte JavaDoc.class || //we could loop, yes, but...
2100
type==Short JavaDoc.class ||
2101                        type==Integer JavaDoc.class ||
2102                        type==Long JavaDoc.class ||
2103                        type==Float JavaDoc.class ||
2104                        type==Double JavaDoc.class)
2105                    serializeWrapper(objs[i], null, spc[i]);
2106                else if (type==String JavaDoc.class ||
2107                        type==Character JavaDoc.class)
2108                    serializeWrapper(objs[i], null, spc[i]);
2109                else
2110                    serializeObject(objs[i], null, spc[i]); //null== no name
2111
}
2112            if (keys.hasMoreElements()) toOutPrintln(); //separating blank line
2113
}
2114    }
2115
2116    /** -------------------------------------------------------------------------
2117        * serializeWrapper()
2118        * ------------------
2119        * Note: must check aliasing, *before* encodeXML creates a new object...!
2120        *
2121        * Is this where we should put the name, for case of root wrapper?
2122        * We know it must be a String in this case; but how about a more general
2123        * wrapper?
2124        *
2125        **/

2126    private void serializeWrapper(Object JavaDoc value, String JavaDoc name, String JavaDoc indent) {
2127        if (DEBUG_HASH) System.err.println("----enter serializeWrapper with "+name+"="+value);
2128        String JavaDoc tag = ParseUtilities.escapeDollar(value.getClass().getName()); //"java.lang.String";//need to: $ -> .. //Should *never* need this here!
2129
//superVersion is no issues for Wrappers.
2130
if (putAlias(value, name, false, indent)) { //any reason for null?
2131
//true returned: (first time seen - an alias was put)
2132
if (DEBUG_HASH) System.err.println("found NO alias, so do inline (in serializeWrapper)");
2133            printOpenTag(tag, indent, true); //putAlias: therefore, an object
2134
if (name!=null)
2135                printAttr(NAME_TOKEN, name);
2136            if (value.getClass()==String JavaDoc.class) //instanceof OK, as String is final
2137
printAttr(VALUE_TOKEN, ParseUtilities.encodeXML((String JavaDoc)value));
2138            else if (value.getClass()==Character JavaDoc.class) {
2139                printAttr(VALUE_TOKEN, ParseUtilities.encodeXML(value+""));
2140            }
2141            else {
2142                printAttr(VALUE_TOKEN, value+"");
2143            }
2144            out.print("/>"); //good to have control of the newline here
2145
//move into ParserXML?
2146
//printCloseTag(tag, "");
2147
//NOTE: a wrapper *always* is an empty tag, because it can't have children
2148
toOutPrintln(); //instead of printCloseTag()
2149
removeAlias(value);
2150        }
2151        else {
2152                //false returned
2153
if (DEBUG_HASH) System.err.println("found object aliased already (in serializeWrapper)");
2154            //putAlias() takes care of this already
2155
}
2156    }
2157        
2158    private void serializeClass(Class JavaDoc clazz, String JavaDoc name, String JavaDoc indent) {
2159        String JavaDoc tag = "java.lang.Class";
2160            //superVersion is no issue for Class objects.
2161
if (putAlias(clazz, name, false, indent)) {
2162            //true returned: (first time seen - an alias was put)
2163
printOpenTag(tag, indent, true); //putAlias, therefore an object
2164
if (name!=null)
2165                printAttr(NAME_TOKEN, name);
2166            //if (putAlias(clazz.getName(), CLASSNAME_TOKEN, true, indent))
2167
//comment out, because this invented field shouldn't be aliases
2168
printAttr(CLASSNAME_TOKEN, clazz.getName());
2169            out.print("/>"); //good to have control of the newline here
2170
toOutPrintln(); //instead of printCloseTag()
2171
removeAlias(clazz);
2172        }
2173    }
2174
2175
2176/** ===========================================================================
2177    * ===========================================================================
2178    * helper code for writeObject()
2179    * ===========================
2180    **/

2181/**--------------------------------------------------------------------------
2182    * getDeclaredMethod
2183    * -----------------
2184    * A utility class, common to both serialization and deserialization
2185    * - need to factor out into a utility class.
2186    *
2187    * CHANGED: "static" - we make it static for now
2188    * Taken directly from ObjectOutputStream... too general, but a good start.
2189    **/

2190    //NB: default "package" accessibility, so XMLDeserialize can access it
2191
static Method getDeclaredMethod(Class JavaDoc clazz,
2192            String JavaDoc methodName, Class JavaDoc[] args,
2193            int requiredModifierMask,
2194            int disallowedModifierMask) {
2195        Method method = null;
2196        try {
2197            method =
2198                clazz.getDeclaredMethod(methodName, args);
2199            if (method != null) {
2200                int mods = method.getModifiers();
2201                if ((mods & disallowedModifierMask) != 0 ||
2202                    (mods & requiredModifierMask) != requiredModifierMask) {
2203                        method = null;
2204                } else {
2205                    method.setAccessible(true); //needed, because prviate
2206
}
2207            }
2208        } catch (NoSuchMethodException JavaDoc e) {
2209        // Since it is alright if methodName does not exist,
2210
// no need to do anything special here.
2211
}
2212        return method;
2213    }
2214
2215    /*
2216    try {
2217        if ((m=testClazz.getDeclaredMethod("writeObject", new Class[] {})) != null)
2218            System.err.println("JSX1 "+m+ "Class " +testClazz
2219                + " has a writeObject()");
2220        else
2221            System.err.println("JSX1. Class " +testClazz+ " has no writeObject()");
2222    } catch (Exception e) {
2223            System.err.println(e);
2224    }
2225
2226    */

2227
2228
2229/**--------------------------------------------------------------------------
2230    * invokeObjectWriter
2231    * ------------------
2232    * CHANGED: "static" - we make it static for now
2233    * CHANGED: send in Method m!
2234    * Taken directly from ObjectOutputStream... good for exceptions!
2235    **/

2236    private void invokeObjectWriter(Method m, Object JavaDoc obj) throws IOException {
2237        try {
2238            m.invoke(obj, writeObjectArglist);
2239        } catch (InvocationTargetException e) {
2240            Throwable JavaDoc t = e.getTargetException();
2241            if (t instanceof IOException)
2242                throw (IOException)t;
2243            else if (t instanceof RuntimeException JavaDoc)
2244                throw (RuntimeException JavaDoc) t;
2245            else if (t instanceof Error JavaDoc)
2246                throw (Error JavaDoc) t;
2247            else
2248                throw new Error JavaDoc("internal error");
2249        } catch (IllegalAccessException JavaDoc e) {
2250        /* Should not happen, unless there is a problem with the VM. This
2251         * is because the writeObject method is obtained via
2252         * ObjectStreamClass.getDeclaredMethod, which calls setAccessible
2253         * on it, in privileged mode.
2254         */

2255            throw new InternalError JavaDoc("Unable to access writeObject method");
2256        }
2257    }
2258
2259/**--------------------------------------------------------------------------
2260  * Formatted flag
2261  * true if the output is to be formatted
2262  */

2263  private boolean formatted = true; //defaults to formatted
2264
//private boolean formatted = false; //easy test
2265

2266/**--------------------------------------------------------------------------
2267  * Spacer String
2268  * Set according to the formatted value
2269  */

2270  private String JavaDoc spacer = SPACER; //defaults to formatted
2271
//private String spacer = ""; //NB: need to execute code to set this;
2272
//or change all references to it.
2273

2274/**--------------------------------------------------------------------------
2275  * setFormatted(boolean)
2276  * Turns XML output formatting on or off...
2277  **/

2278  public void setFormatted(boolean formatted) {
2279      this.formatted = formatted;
2280      spacer = (formatted?SPACER:"");
2281  }
2282  
2283/**--------------------------------------------------------------------------
2284  * toOutPrint(String s)
2285  **/

2286  private void toOutPrint(String JavaDoc s) {
2287    out.print(s);
2288  }
2289/**--------------------------------------------------------------------------
2290  * toOutPrintln()
2291  **/

2292  private void toOutPrintln() {
2293    if (formatted)
2294      out.println();
2295  }
2296/**--------------------------------------------------------------------------
2297  * toOutPrintln(String s)
2298  **/

2299  private void toOutPrintln(String JavaDoc s) {
2300    if (formatted)
2301      out.println(s);
2302    else
2303      out.print(s);
2304  }
2305}
2306
Popular Tags