[Home] DMelt member access . Java source code: javolution37.javolution.xml.XmlFormat
/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2005 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution37.javolution.xml;
import java.io.IOException;
import java.lang.CharSequence;
import java.lang.IllegalStateException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javax.realtime.MemoryArea;
import java.lang.Appendable;
import javolution37.javolution.lang.ClassInitializer;
import javolution37.javolution.lang.Reflection;
import javolution37.javolution.lang.Text;
import javolution37.javolution.lang.TypeFormat;
import javolution37.javolution.util.FastCollection;
import javolution37.javolution.util.FastComparator;
import javolution37.javolution.util.FastMap;
import javolution37.javolution.util.FastTable;
import javolution37.javolution.util.LocalMap;
/**
* This class represents the format base class for XML serialization and
* deserialization.
*
* Application classes typically define a XML format for their instances
* using static {@link XmlFormat} class members.
* The format is inherited by sub-classes. For example:[code]
* public abstract class Graphic {
* private boolean _isVisible;
* private Paint _paint; // null if none.
* private Stroke _stroke; // null if none.
* private Transform _transform; // null if none.
*
* // XML format with positional associations (members identified by their position),
* // see xml package description for examples of name associations.
* protected static final XmlFormat GRAPHIC_XML = new XmlFormat(Graphic.class) {
* public void format(Graphic g, XmlElement xml) {
* xml.setAttribute("isVisible", g._isVisible);
* xml.add(g._paint); // First.
* xml.add(g._stroke); // Second.
* xml.add(g._transform); // Third.
* }
* public Graphic parse(XmlElement xml) {
* Graphic g = xml.object();
* g._isVisible = xml.getAttribute("isVisible", true);
* g._paint = xml.getNext();
* g._stroke = xml.getNext();
* g._transform = xml.getNext();
* return g;
* }
* };
* }[/code]
*
* Due to the sequential nature of xml serialization/deserialization,
* formatting/parsing of xml attributes should always be performed before
* formatting/parsing of the xml content.
*
* Xml formats can dynamically associated. For example:[code]
*
* // XML Conversion (different formats for reading and writing).
* XmlFormat readFormat = new XmlFormat() {...};
* XmlFormat writeFormat = new XmlFormat() {...};
* LocalContext.enter();
* try { // Local context to avoid impacting other threads.
* XmlFormat.setFormat(Foo.class, readFormat);
* Foo foo = new ObjectReader().read(in);
* XmlFormat.setFormat(Foo.class, writeFormat);
* new ObjectWriter().write(foo, out);
* } finally {
* LocalContext.exit();
* }
*
* // Temporary change of String format during formatting.
* public void format(List list, XmlElement xml) {
* LocalContext.enter();
* try { // Always safer to scope the change.
* XmlFormat.setFormat(String.class, MY_STRING_FORMAT);
* for (Object elem : list) {
* xml.add(elem);
* }
* } finally {
* LocalContext.exit();
* }
* }[/code]
*
* A default format is defined for null values
* (<null/>) and the following types:
* java.lang.Object (default)
* java.lang.Class
* java.lang.String
* javolution.lang.Text
* java.lang.Appendable
* java.util.Collection
* java.util.Map
* and all primitive types wrappers (e.g.
* Boolean, Integer ...)
*
*
* Here is an example of serialization/deserialization using predefined
* formats:[code]
* List list = new ArrayList();
* list.add("John Doe");
* list.add(null);
* Map map = new FastMap();
* map.put("ONE", new Integer(1));
* map.put("TWO", new Integer(2));
* list.add(map);
* ObjectWriter ow = new ObjectWriter();
* ow.write(list, new FileOutputStream("C:/list.xml"));[/code]
* Here is the output list.xml document produced:[code]
*
*
*
*
*
*
*
*
* [/code]
* The list can be read back with the following code:[code]
* ObjectReader or = new ObjectReader();
* List list = (List) or.read(new FileInputStream("C:/list.xml"));
* [/code]
*
* Finally, xml formats can be made impervious to obfuscation by
* setting local {@link #setAlias aliases} for the obfuscated classes.
*
* @author Jean-Marie Dautelle
* @version 3.6, October 13, 2005
*/
public abstract class XmlFormat {
private static final FastMap CLASS_TRANSFORMATIONS = new FastMap()
.setShared(true);
/**
* Holds the default formats.
*/
private static final FastMap DEFAULT_INSTANCES = new FastMap()
.setShared(true);
/**
* Holds the dynamic class-to-format override.
*/
private static final LocalMap DYNAMIC_INSTANCES = new LocalMap();
/**
* Holds the local look-up.
*/
private static final LocalMap CLASS_TO_FORMAT = new LocalMap();
/**
* Holds the class-to-alias mapping.
*/
private static final LocalMap CLASS_TO_ALIAS = new LocalMap();
/**
* Holds the class-name to class mapping.
*/
private static FastMap CLASS_NAME_TO_CLASS = new FastMap().setShared(true);
/**
* Holds the local-name/uri to class mapping (localName to uri/class pairs).
*/
private static FastMap LOCAL_NAME_TO_URI_CLASS = new FastMap()
.setShared(true);
/**
* Holds the alias-to-class mapping.
*/
private static final LocalMap ALIAS_TO_CLASS = new LocalMap();
/**
* Holds the default XML representation when a more specific format
* cannot be found. This representation consists of an empty XML element
* with no attribute. Objects are deserialized using the class public
* no-arg constructor (the class being identified by the element's name).
*/
public static final XmlFormat DEFAULT_XML = new XmlFormat() {
public void format(Object obj, XmlElement xml) {
// Do nothing.
}
public Object parse(XmlElement xml) {
return xml.object();
}
};
/**
* Holds the null object.
*/
static final Null NULL = new Null();
/**
* Holds the XML representation for null objects
* (<null/>).
*/
static final XmlFormat NULL_XML = new XmlFormat(NULL.getClass()) {
public String identifier(boolean isReference) {
return null; // Disables references.
}
public void format(Object obj, XmlElement xml) {
// Empty tag.
}
public Object parse(XmlElement xml) {
return null;
}
};
static {
CLASS_TO_ALIAS.putDefault(NULL.getClass(), "null");
ALIAS_TO_CLASS.putDefault("null", NULL.getClass());
}
/**
* Holds the default XML representation for java.lang.Class
* instances. This representation consists of a "name"
* attribute holding the class name.
*/
public static final XmlFormat CLASS_XML = new XmlFormat(
"java.lang.Class") {
public void format(Object obj, XmlElement xml) {
xml.setAttribute("name", ((Class) obj).getName());
}
public Object parse(XmlElement xml) {
Class cls = Reflection.getClass(xml.getAttribute("name", ""));
if (cls == null)
throw new XmlException("Class: " + cls + " not found");
return cls;
}
};
/**
* Holds the default XML representation for java.lang.String
* classes. This representation consists of a "value" attribute
* holding the string.
*/
public static final XmlFormat STRING_XML = new XmlFormat(
"java.lang.String") {
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", (String) obj);
}
public Object parse(XmlElement xml) {
return xml.getAttribute("value", "");
}
};
/**
* Holds the default XML representation for javolution.lang.Text
* classes. This representation consists of a "value" attribute
* holding the characters.
*/
public static final XmlFormat TEXT_XML = new XmlFormat(
"javolution37.javolution.lang.Text") {
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", (Text) obj);
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
return csq != null ? Text.valueOf(csq) : Text.EMPTY;
}
};
/**
* Holds the default XML representation for java.lang.Appendable
* classes. This representation consists of a "value" attribute
* holding the characters.
*/
public static final XmlFormat APPENDABLE_XML = new XmlFormat(
"java.lang.Appendable") {
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", Text.valueOf(obj));
}
public Object parse(XmlElement xml) {
Appendable appendable = (Appendable) xml.object();
CharSequence csq = xml.getAttribute("value");
try {
return csq != null ? appendable.append(csq) : appendable;
} catch (IOException e) {
throw new XmlException(e);
}
}
};
/**
* Holds the default XML representation for FastComparator.
*/
static final XmlFormat FAST_COMPARATOR_XML = new XmlFormat(
"javolution37.javolution.util.FastComparator") {
public Object allocate(XmlElement xml) {
if (xml.objectClass() == FastComparator.DEFAULT.getClass())
return FastComparator.DEFAULT;
if (xml.objectClass() == FastComparator.DIRECT.getClass())
return FastComparator.DIRECT;
if (xml.objectClass() == FastComparator.IDENTITY.getClass())
return FastComparator.IDENTITY;
if (xml.objectClass() == FastComparator.LEXICAL.getClass())
return FastComparator.LEXICAL;
if (xml.objectClass() == FastComparator.REHASH.getClass())
return FastComparator.REHASH;
return null;
}
public void format(Object obj, XmlElement xml) {
// Nothing to do.
}
public Object parse(XmlElement xml) {
return xml.object();
}
};
/**
* Holds the default XML representation for classes implementing
* the java.util.Collection interface.
* This representation consists of nested XML elements one for each
* element of the collection. The elements' order is defined by
* the collection iterator order. Collections are deserialized using their
* default constructor.
*/
public static final XmlFormat COLLECTION_XML = new XmlFormat(
"java.util.Collection") {
// Preallocates in case of circular references.
public Object allocate(XmlElement xml) {
return xml.object();
}
public void format(Object obj, XmlElement xml) {
final Collection collection = (Collection) obj;
// Special processing for FastCollection.
if (collection instanceof FastCollection) {
FastComparator valueComparator = ((FastCollection) collection)
.getValueComparator();
if (valueComparator != FastComparator.DEFAULT) {
xml.add(valueComparator, "ValueComparator");
}
}
for (Iterator i = collection.iterator(); i.hasNext();) {
xml.add(i.next());
}
}
public Object parse(XmlElement xml) {
final Collection collection = (Collection) xml.object();
// Special processing for FastCollection.
if (collection instanceof FastCollection) {
FastComparator valueComparator = (FastComparator) xml
.get("ValueComparator");
if (valueComparator != null) {
((FastCollection) collection)
.setValueComparator(valueComparator);
}
}
while (xml.hasNext()) {
collection.add(xml.getNext());
}
return collection;
}
};
/**
* Holds the default XML representation for classes implementing
* the java.util.Map interface.
* This representation consists of key/value pair as nested XML elements.
* For example:[code]
*
*
*
*
*
*
*
* [/code]
*
* The elements' order is defined by the map's entries iterator order.
* Maps are deserialized using their default constructor.
*/
public static final XmlFormat MAP_XML = new XmlFormat("java.util.Map") {
// Preallocates in case of circular references.
public Object allocate(XmlElement xml) {
return xml.object();
}
public void format(Object obj, XmlElement xml) {
final Map map = (Map) obj;
// Special processing for FastMap.
if (map instanceof FastMap) {
FastComparator keyComparator = ((FastMap) map)
.getKeyComparator();
if (keyComparator != FastComparator.DEFAULT) {
xml.add(keyComparator, "KeyComparator");
}
FastComparator valueComparator = ((FastMap) map)
.getValueComparator();
if (valueComparator != FastComparator.DEFAULT) {
xml.add(valueComparator, "ValueComparator");
}
}
for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
xml.add(entry.getKey(), "Key");
xml.add(entry.getValue(), "Value");
}
}
public Object parse(XmlElement xml) {
final Map map = (Map) xml.object();
// Special processing for FastMap.
if (map instanceof FastMap) {
FastComparator keyComparator = (FastComparator) xml
.get("KeyComparator");
if (keyComparator != null) {
((FastMap) map).setKeyComparator(keyComparator);
}
FastComparator valueComparator = (FastComparator) xml
.get("ValueComparator");
if (valueComparator != null) {
((FastMap) map).setValueComparator(valueComparator);
}
}
while (xml.hasNext()) {
Object key = xml.get("Key");
Object value = xml.get("Value");
map.put(key, value);
}
return map;
}
};
/**
* Holds the default XML representation for java.lang.Boolean.
*/
public static final XmlFormat BOOLEAN_XML = new XmlFormat(
"java.lang.Boolean") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Boolean) obj).booleanValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Boolean(TypeFormat.parseBoolean(csq));
}
};
/**
* Holds the default XML representation for java.lang.Byte.
*/
public static final XmlFormat BYTE_XML = new XmlFormat(
"java.lang.Byte") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Byte) obj).byteValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Byte(TypeFormat.parseByte(csq));
}
};
/**
* Holds the default XML representation for java.lang.Character.
*/
public static final XmlFormat CHARACTER_XML = new XmlFormat(
"java.lang.Character") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", Text.valueOf(((Character) obj)
.charValue()));
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if ((csq == null) || (csq.length() != 1))
throw new XmlException("Missing or invalid value attribute");
return new Character(csq.charAt(0));
}
};
/**
* Holds the default XML representation for java.lang.Integer.
*/
public static final XmlFormat INTEGER_XML = new XmlFormat(
"java.lang.Integer") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Integer) obj).intValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Integer(TypeFormat.parseInt(csq));
}
};
/**
* Holds the default XML representation for java.lang.Long.
*/
public static final XmlFormat LONG_XML = new XmlFormat(
"java.lang.Long") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Long) obj).longValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Long(TypeFormat.parseLong(csq));
}
};
/**
* Holds the default XML representation for java.lang.Short.
*/
public static final XmlFormat SHORT_XML = new XmlFormat(
"java.lang.Short") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Short) obj).shortValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Short(TypeFormat.parseShort(csq));
}
};
/**
* Holds the default XML representation for java.lang.Float.
/**/
public static final XmlFormat
/**/
/**/
FLOAT_XML = new XmlFormat("java.lang.Float") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Float) obj).floatValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Float(TypeFormat.parseFloat(csq));
}
};
/**/
/**
* Holds the default XML representation for java.lang.Double.
/**/
public static final XmlFormat
/**/
/**/
DOUBLE_XML = new XmlFormat("java.lang.Double") {
public String identifier(boolean isReference) {
return null; // Always by value.
}
public void format(Object obj, XmlElement xml) {
xml.setAttribute("value", ((Double) obj).doubleValue());
}
public Object parse(XmlElement xml) {
CharSequence csq = xml.getAttribute("value");
if (csq == null)
throw new XmlException("Missing value attribute");
return new Double(TypeFormat.parseDouble(csq));
}
};
/**/
/**
* Creates a dynamic XML format which can be
* {@link javolution37.javolution.realtime.LocalContext locally} mapped to any class
* (see {@link #setFormat}).
*/
protected XmlFormat() {
}
/**
* Creates a default XML format for instances of the specified
* class/interface.
*
* @param rootClass the root class/interface for this XML format.
* @throws java.lang.IllegalStateException if the specified class is
* already mapped to another format.
*/
protected XmlFormat(Class rootClass) {
if (DEFAULT_INSTANCES.put(rootClass, this) != null) {
throw new IllegalStateException("XmlFormat already mapped to "
+ rootClass);
}
}
/**
* Creates a default XML format for instances of the class/interface
* identified by the specified String.
*
* @param className the name of the target class/interface for this
* XML format.
* @throws java.lang.IllegalStateException if the specified class is
* already mapped to another format.
*/
protected XmlFormat(String className) {
this(classForName(className));
}
private static Class classForName(String className) {
Class cls = Reflection.getClass(className);
if (cls == null)
throw new XmlException("Class: " + cls + " not found");
return cls;
}
/**
* Specifies the format associated to the specified class
* ({@link LocalMap local association}).
*
* @param forClass the class/interface being locally mapped.
* @param that the format to be associated to the specified class.
*/
public static void setFormat(Class forClass, XmlFormat that) {
DYNAMIC_INSTANCES.put(forClass, that);
CLASS_TO_FORMAT.clear();
}
/**
* Returns the format for the specified class/interface.
* This method searches for the more specialized format (default or
* dynamic); if none is found {@link #DEFAULT_XML} format is returned.
*
* Note: This method forces the initialization of the specified
* class. This to ensure that the class static fields (which
* may hold the most specialized format) are initialized.
*
* @param forClass the class/interface for which the most specialized
* format is returned.
* @return the format to use for the specified class.
*/
public static XmlFormat getInstance(Class forClass) {
Object xmlFormat = CLASS_TO_FORMAT.get(forClass);
return (xmlFormat != null) ? (XmlFormat) xmlFormat
: (XmlFormat) searchInstanceFor(forClass, false);
}
/**
* Sets the {@link javolution37.javolution.realtime.LocalContext local} alias to be used
* instead of the class name for the specified class (element tag name or
* "j:class" attribute).
* This method is particularly useful in case of obfuscation to ensure
* proper/invariant xml formatting (you don't want to use the obfuscated
* class name in such case).
*
* @param forClass the class for which the specified alias should be used.
* @param alias the name to use for the specified class.
* @see LocalMap
*/
public static void setAlias(Class forClass, String alias) {
CLASS_TO_ALIAS.put(forClass, alias);
ALIAS_TO_CLASS.put(alias, forClass);
}
/**
* Returns the name to be used when objects associated to this
* format are added with no name specified (default null
* the element name is the object class name).
*
* @return the default element name for objects using this format.
*/
public String defaultName() {
return null;
}
/**
* Returns the names of the identifiers attributes when cross-reference
* is enabled. The default implementation returns
* isReference ? "j:ref" : "j:id".
* Format sub-classes may override this method to use different
* attribute names. This method may also return null for
* objects exclusively manipulated by value (e.g. immutable objects).
*
* @param isReference indicates if the attribute name returned is for
* a reference or an identifier.
* @return the name of the attribute identifier or null
* if references should not be used.
* @see ObjectWriter#setReferencesEnabled(boolean)
*/
public String identifier(boolean isReference) {
return isReference ? "j:ref" : "j:id";
}
/**
* Allocates a new object corresponding to this xml element.
* By default, the {@link XmlElement#object} method returns an object
* created using the deserialized class public no-arg constructor.
* Xml formats may perform custom allocations by overriding this method.
*
* @param xml the xml elements.
* @return the object corresponding to the specified xml element.
*/
public T allocate(XmlElement xml) {
return null;
}
/**
* Formats an object into the specified {@link XmlElement}.
*
* @param obj the object to format.
* @param xml the XmlElement destination.
*/
public abstract void format(T obj, XmlElement xml);
/**
* Parses the specified {@link XmlElement} to produce an object.
*
* @param xml the XmlElement to parse.
* @return an Object parsed from the specified
* XmlElement.
* @throws IllegalArgumentException if the character sequence contains
* an illegal syntax.
*/
public abstract T parse(XmlElement xml);
/**
* Searches the best matching format for the specified class.
*
* @param clazz the class for which the format is searched.
* @param forceInherited true if directly mapped format are
* excluded; false otherwise.
* @return the best matching format.
*/
private static XmlFormat searchInstanceFor(Class clazz,
boolean forceInherited) {
ClassInitializer.initialize(clazz); // Forces initialization.
XmlFormat bestMatchFormat = null;
Class bestMatchClass = null;
// Searches dynamic mapping.
FastCollection dynamicEntries = (FastCollection) DYNAMIC_INSTANCES
.entrySet();
for (FastCollection.Record r = dynamicEntries.head(), end = dynamicEntries
.tail(); (r = r.getNext()) != end;) {
Map.Entry e = (Map.Entry) r;
Class cls = (Class) e.getKey();
if ((cls == clazz) && forceInherited)
continue;
if (cls.isAssignableFrom(clazz)) { // Compatible.
if ((bestMatchClass == null)
|| (bestMatchClass.isAssignableFrom(cls))) {
bestMatchClass = cls;
bestMatchFormat = (XmlFormat) e.getValue();
}
}
}
// Searches default mapping.
for (FastMap.Entry e = DEFAULT_INSTANCES.head(), end = DEFAULT_INSTANCES
.tail(); (e = (FastMap.Entry) /*CAST UNNECESSARY WITH JDK1.5*/e
.getNext()) != end;) {
Class cl = (Class) e.getKey();
if ((cl == clazz) && forceInherited)
continue;
Class cls = (Class) e.getKey();
if ((cls == clazz) && forceInherited)
continue;
if (cls.isAssignableFrom(clazz)) { // Compatible.
if ((bestMatchClass == null)
|| ((bestMatchClass != cls) && bestMatchClass
.isAssignableFrom(cls))) {
bestMatchClass = cls;
bestMatchFormat = (XmlFormat) e.getValue();
}
}
}
// If none found, use default object format.
if (bestMatchFormat == null) {
bestMatchFormat = DEFAULT_XML;
}
// Updates local look-up (when not inherited format)
if (!forceInherited) {
CLASS_TO_FORMAT.put(clazz, bestMatchFormat);
}
return bestMatchFormat;
}
/**
* Returns the name or alias for the specified class.
*
* @param cls the class for which the name or alias is returned.
* @return the class name or alias.
*/
static String aliasFor(Class cls) {
return (String) CLASS_TO_ALIAS.get(cls);
}
/**
* Returns the class for the specified name or alias.
*
* @param name the class name or alias (CharSequenceImpl).
* @return the corresponding class.
*/
static Class classFor(CharSequence name) {
Class cls = (Class) CLASS_NAME_TO_CLASS.get(name);
return (cls != null) ? cls : searchClassFor(name);
}
private static Class searchClassFor(CharSequence name) {
String transformedName = CLASS_TRANSFORMATIONS.get(name);
if (transformedName != null)
name = transformedName;
Class cls = (Class) ALIAS_TO_CLASS.get(name);
if (cls != null)
return cls;
String className = name.toString();
cls = Reflection.getClass(className);
if (cls == null)
throw new XmlException("Class: " + cls + " not found");
// Class objects and interned Strings are in ImmortalMemory.
CLASS_NAME_TO_CLASS.put(intern(className), cls);
return cls;
}
/**
* Returns the class for the specified uri/local name.
*
* @param uri the namespace uri (CharSequenceImpl).
* @param localName the local name (CharSequenceImpl).
* @return the corresponding class.
*/
static Class classFor(CharSequence uri, CharSequence localName) {
FastTable uriClassTable = (FastTable) LOCAL_NAME_TO_URI_CLASS
.get(localName);
if (uriClassTable != null) {
for (int i = 0; i < uriClassTable.size(); i += 2) {
if (uri.equals(uriClassTable.get(i)))
return (Class) uriClassTable.get(i + 1);
}
}
return searchClassFor(uri, localName);
}
private static Class searchClassFor(CharSequence uri, CharSequence localName) {
String uriString = uri.toString();
final String localNameString = localName.toString();
if (!uriString.startsWith("java:"))
throw new XmlException("Invalid package uri (" + uriString
+ "), the package uri should start with \"java:\"");
String uriSubString = uriString.substring(5)
+ (uriString.length() > 5 ? "." : "");
String className = uriSubString + localName;
// CLASS_TRANSFORMATIONS has full names
String transformedName = CLASS_TRANSFORMATIONS.get(className);
if (transformedName != null)
className = transformedName;
Class cls = Reflection.getClass(className);
if (cls == null)
if (transformedName == null)
throw new XmlException("Class: " + cls + " not found");
else
throw new XmlException("Class: " + cls
+ " not found (after classname transformation)");
FastTable uriClassTable = (FastTable) LOCAL_NAME_TO_URI_CLASS
.get(localNameString);
if (uriClassTable == null) {
MemoryArea.getMemoryArea(LOCAL_NAME_TO_URI_CLASS).executeInArea(
new Runnable() {
public void run() {
LOCAL_NAME_TO_URI_CLASS.put(
intern(localNameString), new FastTable());
}
});
uriClassTable = (FastTable) LOCAL_NAME_TO_URI_CLASS
.get(localNameString);
}
// Class objects and interned Strings are in ImmortalMemory.
uriClassTable.add(intern(uriString));
uriClassTable.add(cls);
return cls;
}
private static String intern(String str) { // J2ME CLDC.
return STRING_INTERN != null ? (String) STRING_INTERN.invoke(str) : str;
}
private static Reflection.Method STRING_INTERN = Reflection
.getMethod("java.lang.String.intern()");
/**
* Holds the target class for null values.
*/
private static class Null {
};
/**
* Used in search for a class, transformations should be added after classes
* have been renamed and the renamed classes should be loaded from existing
* XML serializations. Useful to read in older versions.
*
* @param oldName
* @param newName
*/
public static void addClassTransformation(String oldName, String newName) {
CLASS_TRANSFORMATIONS.put(oldName, newName);
}
}
Ads help maintain this website.