KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javolution > xml > XmlFormat


1 /*
2  * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
3  * Copyright (C) 2005 - Javolution (http://javolution.org/)
4  * All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software is
7  * freely granted, provided that this notice is preserved.
8  */

9 package javolution.xml;
10
11 import java.io.IOException;
12
13 import j2me.lang.CharSequence;
14 import j2me.util.Collection;
15 import j2me.util.Iterator;
16 import j2me.util.Map;
17
18 import javolution.JavolutionError;
19 import javolution.lang.Appendable;
20 import javolution.lang.Reflection;
21 import javolution.lang.Text;
22 import javolution.lang.TypeFormat;
23 import javolution.util.FastList;
24 import javolution.util.FastMap;
25
26 /**
27  * <p> This class represents the format base class for XML serialization and
28  * deserialization.</p>
29  * <p> Application classes may provide a default XML format using static class
30  * members. For example:<pre>
31  * public abstract class Graphic {
32  * private boolean _isVisible;
33  * private Paint _paint; // null if none.
34  * private Stroke _stroke; // null if none.
35  * private Transform _transform; // null if none.
36  *
37  * // XML format with positional association (members identified by their position),
38  * // see {@link javolution.xml} for examples of name association.
39  * protected static final XmlFormat&lt;Graphic&gt; GRAPHIC_XML = new XmlFormat&lt;Graphic&gt;(Graphic.class) {
40  * public void format(Graphic g, XmlElement xml) {
41  * xml.setAttribute("isVisible", g._isVisible);
42  * xml.getContent().add(g._paint); // First.
43  * xml.getContent().add(g._stroke); // Second.
44  * xml.getContent().add(g._transform); // Third.
45  * }
46  * public Graphic parse(XmlElement xml) {
47  * Graphic g = xml.object();
48  * g._isVisible = xml.getAttribute("isVisible", true);
49  * g._paint = (Paint) xml.getContent().removeFirst();
50  * g._stroke = (Stroke) xml.getContent().removeFirst();
51  * g._transform = (Transform) xml.getContent().removeFirst();
52  * return g;
53  * }
54  * };
55  * }</pre>
56  * <p> A default format is defined for <code>null</code> values
57  * (<code>&lt;null/&gt;</code>) and the following types:<ul>
58  * <li><code>java.lang.Object</code> (empty tag)</li>
59  * <li><code>java.lang.Class</code></li>
60  * <li><code>java.lang.String</code></li>
61  * <li><code>javolution.lang.Text</code></li>
62  * <li><code>javolution.lang.Appendable</code></li>
63  * <li><code>j2me.util.Collection</code></li>
64  * <li><code>j2me.util.Map</code></li>
65  * <li>and all primitive types wrappers (e.g.
66  * <code>Boolean, Integer ...</code>)</li>
67  * </ul></p>
68  *
69  * <p>Here is an example of serialization/deserialization using predefined
70  * formats:<pre>
71  * ArrayList names = new ArrayList();
72  * names.add("John Doe");
73  * names.add(null);
74  * names.add("Jean Bon");
75  * ObjectWriter ow = new ObjectWriter();
76  * ow.setNamespace("", "java.lang"); // Default namespace for java.lang.* classes
77  * ow.write(names, new FileOutputStream("C:/names.xml"));</pre>
78  * Here is the output <code>names.xml</code> document produced:<pre>
79  * &lt;j:java.util.ArrayList xmlns:j="http://javolution.org" xmlns="java:java.lang">
80  * &lt;String value="John Doe"/>
81  * &lt;j:null/>
82  * &lt;String value="Jean Bon"/>
83  * &lt;/j:java.util.ArrayList></pre>
84  * The list can be read back with the following code:<pre>
85  * ObjectReader or = new ObjectReader();
86  * ArrayList names = (ArrayList) or.read(new FileInputStream("C:/names.xml"));
87  * </pre></p>
88  * <p> Formats can also be dynamically modified. For example:<pre>
89  * // Changes XML mapping for java.util.ArrayList
90  * XmlFormat xmlFormat = new XmlFormat() {
91  * public void format(Object obj, XmlElement xml) {
92  * ArrayList list = (ArrayList) obj;
93  * xml.getContent().addAll(list); // Adds list's elements as nested objects.
94  * }
95  * public Object parse(XmlElement xml) {
96  * // Avoids resizing of ArrayList instances.
97  * ArrayList list = (xml.objectClass() == ArrayList.class) ?
98  * new ArrayList(xml.getContent().size()) : (ArrayList) xml.object();
99  * list.addAll(xml.getContent());
100  * return list;
101  * }
102  * };
103  * XmlFormat.setInstance(xmlFormat, java.util.ArrayList.class);
104  * </pre></p>
105  * <p> Finally, xml formats can be made impervious to obfuscation by setting
106  * the {@link #setAlias aliases} of the obfuscated classes. Aliases can also
107  * be used when the application has no control over the xml representation
108  * (e.g. SOAP messages). Then, the aliases holds the mapping between
109  * the tag name and the associated class.</p>
110  *
111  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
112  * @version 3.3, March 18, 2005
113  */

114 public abstract class XmlFormat/*<T>*/{
115
116     /**
117      * Holds the base class to format look-up table (no removal allowed)
118      */

119     private static final FastMap BASE_CLASS_TO_FORMAT = new FastMap();
120
121     /**
122      * Hold the updated class to format look-up table (no removal allowed).
123      */

124     private static final FastMap CLASS_TO_FORMAT = new FastMap();
125
126     /**
127      * Holds class to tag name (CharSequence) look-up table
128      * (no removal allowed).
129      */

130     private static final FastMap CLASS_TO_NAME = new FastMap();
131
132     /**
133      * Holds the tag name (CharSequence) to class look-up table
134      * (no removal allowed).
135      */

136     private static final FastMap NAME_TO_CLASS = new FastMap();
137
138     /**
139      * Holds (uri, local name) to class look-up table (no removal allowed)
140      */

141     private static final FastMap URI_LOCAL_NAME_TO_CLASS = new FastMap();
142
143     /**
144      * Holds the object representing <code>null</code> values
145      * <p> Note: Applications may change the representation of <code>null</code>
146      * values by changing the class alias of this object. For example:<pre>
147      * XmlFormat.setAlias(XmlFormat.NULL.getClass(), "none");</pre></p>
148      */

149     protected static final Null NULL = new Null();
150
151     /**
152      * Holds the default XML representation when a more specific format
153      * cannot be found. This representation consists of an empty XML element
154      * with no attribute. Objects are deserialized using the class default
155      * constructor (the class being identified by the element's name).
156      */

157     protected static final XmlFormat OBJECT_XML = new XmlFormat() {
158         public void format(Object obj, XmlElement xml) {
159             // Do nothing.
160
}
161
162         public Object parse(XmlElement xml) {
163             return xml.object();
164         }
165     };
166
167     /**
168      * Holds the XML representation for <code>null</code> objects
169      * (<code>&lt;null/&gt;</code>).
170      */

171     private static final XmlFormat NULL_XML = new XmlFormat(NULL.getClass()) {
172         public void format(Object obj, XmlElement xml) {
173             // Empty tag.
174
}
175
176         public Object parse(XmlElement xml) {
177             return null;
178         }
179     };
180     static {
181         XmlFormat.setAlias(NULL.getClass(), "null");
182     }
183
184     /**
185      * Holds the default XML representation for <code>java.lang.Class</code>
186      * instances. This representation consists of a <code>"name"</code>
187      * attribute holding the class name.
188      */

189     protected static final XmlFormat CLASS_XML = new XmlFormat(
190             "java.lang.Class") {
191         public void format(Object obj, XmlElement xml) {
192             xml.setAttribute("value", ((Class) obj).getName());
193         }
194
195         public Object parse(XmlElement xml) {
196             try {
197                 return Reflection.getClass(xml.getAttribute("name", ""));
198             } catch (ClassNotFoundException e) {
199                 throw new XmlException(e);
200             }
201         }
202     };
203
204     /**
205      * Holds the default XML representation for <code>java.lang.String</code>
206      * classes. This representation consists of a <code>"value"</code> attribute
207      * holding the string.
208      */

209     protected static final XmlFormat STRING_XML = new XmlFormat(
210             "java.lang.String") {
211         public void format(Object obj, XmlElement xml) {
212             xml.setAttribute("value", (String) obj);
213         }
214
215         public Object parse(XmlElement xml) {
216             return xml.getAttribute("value", "");
217         }
218     };
219
220     /**
221      * Holds the default XML representation for <code>javolution.lang.Text</code>
222      * classes. This representation consists of a <code>"value"</code> attribute
223      * holding the characters.
224      */

225     protected static final XmlFormat TEXT_XML = new XmlFormat(
226             "javolution.lang.Text") {
227         public void format(Object obj, XmlElement xml) {
228             xml.setAttribute("value", (Text) obj);
229         }
230
231         public Object parse(XmlElement xml) {
232             CharSequence csq = xml.getAttribute("value");
233             return csq != null ? Text.valueOf(csq) : Text.EMPTY;
234         }
235     };
236
237     /**
238      * Holds the default XML representation for <code>javolution.lang.Appendable</code>
239      * classes. This representation consists of a <code>"value"</code> attribute
240      * holding the characters.
241      */

242     protected static final XmlFormat APPENDABLE_XML = new XmlFormat(
243             "javolution.lang.Appendable") {
244         public void format(Object obj, XmlElement xml) {
245             xml.setAttribute("value", Text.valueOf(obj));
246         }
247
248         public Object parse(XmlElement xml) {
249             Appendable appendable = (Appendable) xml.object();
250             CharSequence csq = xml.getAttribute("value");
251             try {
252                 return csq != null ? appendable.append(csq) : appendable;
253             } catch (IOException e) {
254                 throw new XmlException(e);
255             }
256         }
257     };
258
259     /**
260      * Holds the default XML representation for classes implementing
261      * the <code>j2me.util.Collection</code> interface.
262      * This representation consists of nested XML elements one for each
263      * element of the collection. The elements' order is defined by
264      * the collection iterator order. Collections are deserialized using their
265      * default constructor.
266      */

267     protected static final XmlFormat COLLECTION_XML = new XmlFormat(
268             "j2me.util.Collection") {
269         public void format(Object obj, XmlElement xml) {
270             xml.getContent().addAll((Collection) obj);
271         }
272
273         public Object parse(XmlElement xml) {
274             Collection collection = (Collection) xml.object();
275             collection.addAll(xml.getContent());
276             return collection;
277         }
278     };
279
280     /**
281      * Holds the default XML representation for classes implementing
282      * the <code>j2me.util.Map</code> interface.
283      * This representation consists of key/value pair as nested XML elements.
284      * The elements' order is defined by the map 's entries iterator order.
285      * Maps are deserialized using their default constructor.
286      */

287     protected static final XmlFormat MAP_XML = new XmlFormat("j2me.util.Map") {
288
289         public void format(Object obj, XmlElement xml) {
290             Map map = (Map) obj;
291             for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
292                 Map.Entry entry = (Map.Entry) it.next();
293                 xml.getContent().addLast(entry.getKey());
294                 xml.getContent().addLast(entry.getValue());
295             }
296         }
297
298         public Object parse(XmlElement xml) {
299             Map map = (Map) xml.object();
300             for (FastList.Node n = xml.getContent().headNode(), end = xml
301                     .getContent().tailNode(); (n = n.getNextNode()) != end;) {
302                 Object key = n.getValue();
303                 Object value = (n = n.getNextNode()).getValue();
304                 map.put(key, value);
305             }
306             return map;
307         }
308     };
309
310     /**
311      * Holds the default XML representation for <code>java.lang.Boolean</code>.
312      */

313     protected static final XmlFormat BOOLEAN_XML = new XmlFormat(
314             "java.lang.Boolean") {
315         public void format(Object obj, XmlElement xml) {
316             xml.setAttribute("value", ((Boolean) obj).booleanValue());
317         }
318
319         public Object parse(XmlElement xml) {
320             CharSequence csq = xml.getAttribute("value");
321             if (csq == null)
322                 throw new XmlException("Missing value attribute");
323             return new Boolean(TypeFormat.parseBoolean(csq));
324         }
325     };
326
327     /**
328      * Holds the default XML representation for <code>java.lang.Byte</code>.
329      */

330     protected static final XmlFormat BYTE_XML = new XmlFormat("java.lang.Byte") {
331         public void format(Object obj, XmlElement xml) {
332             xml.setAttribute("value", ((Byte) obj).byteValue());
333         }
334
335         public Object parse(XmlElement xml) {
336             CharSequence csq = xml.getAttribute("value");
337             if (csq == null)
338                 throw new XmlException("Missing value attribute");
339             return new Byte(TypeFormat.parseByte(csq));
340         }
341     };
342
343     /**
344      * Holds the default XML representation for <code>java.lang.Character</code>.
345      */

346     protected static final XmlFormat CHARACTER_XML = new XmlFormat(
347             "java.lang.Character") {
348         public void format(Object obj, XmlElement xml) {
349             xml.setAttribute("value", Text.valueOf(((Character) obj)
350                     .charValue()));
351         }
352
353         public Object parse(XmlElement xml) {
354             CharSequence csq = xml.getAttribute("value");
355             if ((csq == null) || (csq.length() != 1))
356                 throw new XmlException("Missing or invalid value attribute");
357             return new Character(csq.charAt(0));
358         }
359     };
360
361     /**
362      * Holds the default XML representation for <code>java.lang.Integer</code>.
363      */

364     protected static final XmlFormat INTEGER_XML = new XmlFormat(
365             "java.lang.Integer") {
366         public void format(Object obj, XmlElement xml) {
367             xml.setAttribute("value", ((Integer) obj).intValue());
368         }
369
370         public Object parse(XmlElement xml) {
371             CharSequence csq = xml.getAttribute("value");
372             if (csq == null)
373                 throw new XmlException("Missing value attribute");
374             return new Integer(TypeFormat.parseInt(csq));
375         }
376     };
377
378     /**
379      * Holds the default XML representation for <code>java.lang.Long</code>.
380      */

381     protected static final XmlFormat LONG_XML = new XmlFormat("java.lang.Long") {
382         public void format(Object obj, XmlElement xml) {
383             xml.setAttribute("value", ((Long) obj).longValue());
384         }
385
386         public Object parse(XmlElement xml) {
387             CharSequence csq = xml.getAttribute("value");
388             if (csq == null)
389                 throw new XmlException("Missing value attribute");
390             return new Long(TypeFormat.parseLong(csq));
391         }
392     };
393
394     /**
395      * Holds the default XML representation for <code>java.lang.Short</code>.
396      */

397     protected static final XmlFormat SHORT_XML = new XmlFormat("java.lang.Short") {
398         public void format(Object obj, XmlElement xml) {
399             xml.setAttribute("value", ((Short) obj).shortValue());
400         }
401
402         public Object parse(XmlElement xml) {
403             CharSequence csq = xml.getAttribute("value");
404             if (csq == null)
405                 throw new XmlException("Missing value attribute");
406             return new Short(TypeFormat.parseShort(csq));
407         }
408     };
409
410     /**
411      * Holds the default XML representation for <code>java.lang.Float</code>.
412      /*@FLOATING_POINT@
413      protected static final XmlFormat FLOAT_XML = new XmlFormat("java.lang.Float") {
414      public void format(Object obj, XmlElement xml) {
415      xml.setAttribute("value", ((Float)obj).floatValue());
416      }
417
418      public Object parse(XmlElement xml) {
419      CharSequence csq = xml.getAttribute("value");
420      if (csq == null)
421      throw new XmlException("Missing value attribute");
422      return new Float(TypeFormat.parseFloat(csq));
423      }
424      };
425      /**/

426
427     /**
428      * Holds the default XML representation for <code>java.lang.Double</code>.
429      /*@FLOATING_POINT@
430      protected static final XmlFormat DOUBLE_XML = new XmlFormat("java.lang.Double") {
431      public void format(Object obj, XmlElement xml) {
432      xml.setAttribute("value", ((Double)obj).doubleValue());
433      }
434
435      public Object parse(XmlElement xml) {
436      CharSequence csq = xml.getAttribute("value");
437      if (csq == null)
438      throw new XmlException("Missing value attribute");
439      return new Double(TypeFormat.parseDouble(csq));
440      }
441      };
442      /**/

443
444     /**
445      * The class/interface mapped to this format.
446      */

447     private Class _mappedClass;
448
449     /**
450      * Holds the xml format this format overrides.
451      */

452     private XmlFormat _super;
453
454     /**
455      * Holds the name identifier.
456      */

457     final Text _idName;
458
459     /**
460      * Holds the reference identifier.
461      */

462     final Text _idRef;
463
464     /**
465      * Default constructor (used for dynamic mapping).
466      */

467     protected XmlFormat() {
468         _idName = null;
469         _idRef = null;
470     }
471
472     /**
473      * Creates a default XML mapping for the specified class/interface;
474      * this mapping is inherited by sub-classes or implementing classes.
475      *
476      * @param clazz the class/interface for which this XML format can be used.
477      * @throws IllegalArgumentException if a default mapping already exists
478      * for the specified class.
479      */

480     protected XmlFormat(Class/*<T>*/ clazz) {
481         mapTo(clazz);
482         _idName = _super._idName;
483         _idRef = _super._idRef;
484     }
485
486     /**
487      * Creates a default XML mapping for the specified class/interface;
488      * this mapping is inherited by sub-classes or implementing classes.
489      *
490      * @param clazz the class/interface for which this XML format can be used.
491      * @param idName the qualified attribute identifier for non-references.
492      * @param idRef the qualified attribute identifier for references.
493      * @throws IllegalArgumentException if a default mapping already exists
494      * for the specified class.
495      */

496     protected XmlFormat(Class/*<T>*/ clazz, String idName, String idRef) {
497         mapTo(clazz);
498         _idName = (idName != null) ? Text.valueOf(idName) : null;
499         _idRef = (idRef != null) ? Text.valueOf(idRef) : null;
500     }
501
502     /**
503      * Creates a default XML mapping for the class/interface having the
504      * specified name.
505      *
506      * @param className the name of the class/interface for which this
507      * XML format can be used.
508      * @throws IllegalArgumentException if a default mapping already exists
509      * for the specified class.
510      */

511     protected XmlFormat(String className) {
512         try {
513             mapTo(Reflection.getClass(className));
514         } catch (ClassNotFoundException e) {
515             throw new JavolutionError(e);
516         }
517         _idName = _super._idName;
518         _idRef = _super._idRef;
519     }
520
521     private void mapTo(Class clazz) {
522         _mappedClass = clazz;
523         synchronized (BASE_CLASS_TO_FORMAT) {
524             if (BASE_CLASS_TO_FORMAT.containsKey(clazz)) {
525                 throw new IllegalArgumentException(
526                         "Mapping already exists for " + clazz);
527             }
528             _super = XmlFormat.getInstance(clazz);
529             BASE_CLASS_TO_FORMAT.put(clazz, this);
530             invalidateClassToFormatMapping();
531         }
532     }
533
534     /**
535      * Sets the formats for the specified class/interface.
536      *
537      * @param xmlFormat the XML format for the specified class/interface
538      * or <code>null</code> to unset the current mapping.
539      * @param forClass the associated class/interface.
540      */

541     public static void setInstance(XmlFormat xmlFormat, Class forClass) {
542         xmlFormat._mappedClass = forClass;
543         synchronized (BASE_CLASS_TO_FORMAT) {
544             xmlFormat._super = XmlFormat.getInstance(forClass);
545             BASE_CLASS_TO_FORMAT.put(forClass, xmlFormat);
546             invalidateClassToFormatMapping();
547         }
548     }
549
550     /**
551      * Returns the format for the specified class/interface.
552      * This method looks for the more specialized format; if none is found
553      * the default {@link Object} format is returned (empty element).
554      *
555      * <p> Note: This method forces the initialization of the specified
556      * class. This to ensure that the class static fields (which
557      * may hold the most specialized format) are initialized.</p>
558      *
559      * @param mappedClass the class/interface for which the most specialized
560      * format is returned.
561      * @return the format to use for the specified class.
562      */

563     public static /*<T>*/ XmlFormat/*<T>*/ getInstance(Class/*<T>*/ mappedClass) {
564
565         // Checks look-up.
566
Object obj = CLASS_TO_FORMAT.get(mappedClass);
567         if (obj != null)
568             return (XmlFormat/*<T>*/) obj;
569
570         // Ensures that the class is initialized.
571
try {
572             Reflection.getClass(mappedClass.getName());
573         } catch (ClassNotFoundException e) {
574             // Ignores (hopefully the class has already been initialized).
575
}
576         Class bestMatchClass = null;
577         XmlFormat bestMatchFormat = null;
578         // Searches best match.
579
for (FastMap.Entry e = BASE_CLASS_TO_FORMAT.headEntry(), end = BASE_CLASS_TO_FORMAT
580                 .tailEntry(); (e = e.getNextEntry()) != end;) {
581             Class clazz = (Class) e.getKey();
582             if (clazz.isAssignableFrom(mappedClass)) { // Compatible.
583
if ((bestMatchClass == null)
584                         || (bestMatchClass.isAssignableFrom(clazz))) {
585                     // clazz more specialized that bestMatchClass.
586
XmlFormat xmlFormat = (XmlFormat) BASE_CLASS_TO_FORMAT
587                             .get(clazz);
588                     if (xmlFormat != null) {
589                         bestMatchClass = clazz;
590                         bestMatchFormat = xmlFormat;
591                     }
592                 }
593             }
594         }
595
596         // If none found, use default object format.
597
if (bestMatchFormat == null) {
598             bestMatchFormat = OBJECT_XML;
599         }
600         // Updates look-up.
601
synchronized (CLASS_TO_FORMAT) {
602             CLASS_TO_FORMAT.put(mappedClass, bestMatchFormat);
603         }
604         return (XmlFormat/*<T>*/) bestMatchFormat;
605
606     }
607
608     /**
609      * Sets the element name (tag name) to be used when serializing instances
610      * of the specified class. This method is particularly useful
611      * in case of obfuscation to ensure proper/invariant xml formatting
612      * (you don't want to use the obfuscated class name in such case).
613      * It is also useful if you have no control over the xml format (e.g.
614      * SOAP messages) in order to manually control the mapping between the
615      * element name and the corresponding class (as you cannot rely
616      * upon the <code>"j:class"</code> attribute to be set).
617      *
618      * @param forClass the class for which the specified alias should be used.
619      * @param alias the tag name to use for the specified class.
620      */

621     public static void setAlias(Class forClass, String alias) {
622         synchronized (CLASS_TO_NAME) {
623             CLASS_TO_NAME.put(forClass, alias);
624             NAME_TO_CLASS.put(alias, forClass);
625         }
626     }
627
628     /**
629      * Returns the name to be used for the serialization of the specified
630      * class. If no alias has been set this method returns the full class name.
631      *
632      * @param clazz the class for which the name to use is returned.
633      * @return the name of the element identifying instances of the
634      * specified class during serialization.
635      */

636     static String nameFor(Class clazz) {
637         String name = (String) CLASS_TO_NAME.get(clazz);
638         if (name == null) {
639             name = clazz.getName();
640             synchronized (CLASS_TO_NAME) {
641                 CLASS_TO_NAME.put(clazz, name);
642                 NAME_TO_CLASS.put(name, clazz);
643             }
644         }
645         return name;
646     }
647
648     /**
649      * Returns the class for the specified identifier.
650      *
651      * @param uri class uri namespace (CharSequenceImpl or String).
652      * @param localName the local name (CharSequenceImpl).
653      * @throws XmlException if there is no class matching the specified classId.
654      */

655     static Class classFor(Object uri, CharSequence localName) {
656         // Searches current mapping.
657
FastList list = (FastList) URI_LOCAL_NAME_TO_CLASS.get(localName);
658         if (list != null) {
659             for (int i=1; i < list.size(); i += 2) {
660                 if (uri.equals(list.get(i))) {
661                     return (Class) list.get(i-1);
662                 }
663             }
664         }
665         // Extracts the class name (or alias).
666
String uriAsString = uri.toString();
667         String localNameAsString = localName.toString();
668         String className;
669         if ((uriAsString.length() == 0) || uriAsString.equals("http://javolution.org")) {
670             className = localNameAsString; // Class name is the local name.
671
} else { // Use package prefix.
672
if (uriAsString.startsWith("java:")) {
673                 className = (uriAsString.length() > 5) ? uriAsString.substring(5) + "."
674                         + localNameAsString : localNameAsString;
675             } else {
676                 throw new XmlException("Invalid URI (must use a java scheme)");
677             }
678         }
679
680         // Finds the class object.
681
Class clazz = (Class) NAME_TO_CLASS.get(className);
682         if (clazz == null) {
683             try {
684                 clazz = Reflection.getClass(className);
685             } catch (ClassNotFoundException e) {
686                 throw new XmlException(e);
687             }
688             synchronized (CLASS_TO_NAME) {
689                 CLASS_TO_NAME.put(clazz, className);
690                 NAME_TO_CLASS.put(className, clazz);
691             }
692         }
693
694         // Adds new id-class mapping.
695
synchronized (URI_LOCAL_NAME_TO_CLASS) {
696             list = (FastList) URI_LOCAL_NAME_TO_CLASS.get(localNameAsString);
697             if (list == null) {
698                 list = (FastList) FastList.newInstance().moveHeap();
699                 URI_LOCAL_NAME_TO_CLASS.put(localNameAsString, list);
700             }
701             list.addLast(clazz);
702             list.addLast(uriAsString);
703         }
704         return clazz;
705     }
706
707     /**
708      * Invalidates the current class to format mapping.
709      * Look-up table cannot be cleared (to avoid synchronized read),
710      * each entry value is set to null.
711      */

712     private static void invalidateClassToFormatMapping() {
713         synchronized (CLASS_TO_FORMAT) {
714             for (FastMap.Entry e = CLASS_TO_FORMAT.headEntry(), end = CLASS_TO_FORMAT
715                     .tailEntry(); (e = e.getNextEntry()) != end;) {
716                 e.setValue(null);
717             }
718         }
719     }
720
721     /**
722      * Returns the format being overriden by this format.
723      *
724      * @return the format being overriden.
725      */

726     public final /*<T>*/ XmlFormat/*<T>*/ getSuper() {
727         return (XmlFormat/*<T>*/) _super;
728     }
729
730     /**
731      * Preallocates the object corresponding to this xml element in order to
732      * support circular references.
733      *
734      * @param xml the xml elements holding this objects's attribute but
735      * not its content yet.
736      * @return <code>getSuper().preallocate(xml)</code>
737      * @see XmlElement#object()
738      */

739     public Object/*T*/preallocate(XmlElement xml) {
740         return (_super != null) ? (Object/*T*/)_super.preallocate(xml) : null;
741     }
742
743
744     /**
745      * <p> Returns the class associated to the specified element name.</p>
746      * <p> By default when name association is employed, a <code>"j:class"
747      * </code>attribute holding the class name (or alias) is created.</p>
748      * <p> Applications may avoid this extra attribute when the name-to-class
749      * mapping is known at compile time. For example:<pre>
750      * public class Graphic {
751      * private java.awt.Color _color; // Color class is final.
752      * static final XmlFormat&lt;Graphic> GRAPHIC_XML = new XmlFormat&lt;Graphic>(Graphic.class) {
753      * public void format(Graphic g, XmlElement xml) {
754      * xml.add("Color", g._color);
755      * }
756      * public Graphic parse(XmlElement xml) {
757      * Graphic g = xml.object();
758      * g._color = xml.get("Color");
759      * return g;
760      * }
761      * public Class classFor(CharSequence elementName) {
762      * if (name.equals("Color")) return java.awt.Color;
763      * return getSuper().classFor(name);
764      * }
765      * };
766      * }</pre>
767      *
768      * @param elementName the element name or tag name for which the associated
769      * class is known and returned.
770      * @return <code>getSuper().classFor(name)</code>
771      */

772     public Class classFor(CharSequence elementName) {
773         return (_super != null) ? _super.classFor(elementName) : null;
774     }
775
776     /**
777      * Formats an object into the specified {@link XmlElement}.
778      *
779      * @param obj the object to format.
780      * @param xml the <code>XmlElement</code> destination.
781      */

782     public abstract void format(Object/*T*/obj, XmlElement xml);
783
784     /**
785      * Parses the specified {@link XmlElement} to produce an object.
786      *
787      * @param xml the <code>XmlElement</code> to parse.
788      * @return an <code>Object</code> parsed from the specified
789      * <code>XmlElement</code>.
790      * @throws IllegalArgumentException if the character sequence contains
791      * an illegal syntax.
792      */

793     public abstract Object/*T*/parse(XmlElement xml);
794
795     /**
796      * Holds the class for <code>null</code> values.
797      */

798     private static class Null {
799     };
800 }
Popular Tags