package com.skaringa.javaxml.serializers;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Hashtable;
import java.util.Map;
import org.xml.sax.Attributes;
import com.skaringa.javaxml.DeserializerException;
/**
* Registry that holds the serializers.
* It is implemented using the singleton pattern.
*/
public final class SerializerRegistry {
private static SerializerRegistry _theRegistry;
private Map _serializers = new Hashtable();
private Map _deserializers = new Hashtable();
static {
_theRegistry = new SerializerRegistry();
}
/**
* Get the one instance of the SerializerRegistry.
* @return The SerializerRegistry.
*/
public static SerializerRegistry getInstance() {
return _theRegistry;
}
/**
* Construct a SerializerRegistry.
* Bootstrap all known serializers.
*/
private SerializerRegistry() {
// primitive types
bootstrapPrimitiveTypes();
// wrappers
bootstrapWrappers();
// string and date
bootstrapStringAndDate();
// Set types
bootstrapSets();
// collection types
bootstrapCollections();
// map types
bootstrapMaps();
// java.math
addSerializer(
BigDecimal.class.getName(),
new PrimitiveTypeSerializer("xsd:decimal", BigDecimal.class));
addSerializer(
BigInteger.class.getName(),
new PrimitiveTypeSerializer("xsd:integer", BigInteger.class));
// java.util.Locale
addSerializer(
java.util.Locale.class.getName(),
new LocaleSerializer(java.util.Locale.class.getName()));
bootstrapOtherTypes();
}
/**
* These are types that Skaringa will not use during
* serialization (for the most part they're specialized
* versions and Skaringa will use the more general versions).
* however, we might see them during deserialization so we
* hook them into the deserializer.
*/
private void bootstrapOtherTypes() {
_deserializers.put(
"xsd:nonPositiveInteger",
new PrimitiveTypeSerializer("xsd:nonPositiveInteger", Integer.class));
_deserializers.put(
"xsd:nonNegativeInteger",
new PrimitiveTypeSerializer("xsd:nonNegativeInteger", Integer.class));
_deserializers.put(
"xsd:positiveInteger",
new PrimitiveTypeSerializer("xsd:positiveInteger", Integer.class));
_deserializers.put(
"xsd:negativeInteger",
new PrimitiveTypeSerializer("xsd:negativeInteger", Integer.class));
_deserializers.put(
"xsd:unsignedLong",
new PrimitiveTypeSerializer("xsd:unsignedLong", Long.class));
_deserializers.put(
"xsd:unsignedInt",
new PrimitiveTypeSerializer("xsd:unsignedInt", Integer.class));
_deserializers.put(
"xsd:unsignedShort",
new PrimitiveTypeSerializer("xsd:unsignedShort", Short.class));
_deserializers.put(
"xsd:unsignedByte",
new PrimitiveTypeSerializer("xsd:unsignedByte", Byte.class));
_deserializers.put("xsd:date", new DateSerializer("xsd:date"));
}
/**
* Map and subtypes.
*
*/
private void bootstrapMaps() {
addSerializer(
java.util.Map.class.getName(),
new MapSerializer(java.util.Map.class.getName()));
addSerializer(
java.util.SortedMap.class.getName(),
new MapSerializer(java.util.SortedMap.class.getName()));
addSerializer(
java.util.AbstractMap.class.getName(),
new MapSerializer(java.util.AbstractMap.class.getName()));
addSerializer(
java.util.HashMap.class.getName(),
new MapSerializer(java.util.HashMap.class.getName()));
addSerializer(
java.util.Hashtable.class.getName(),
new MapSerializer(java.util.Hashtable.class.getName()));
addSerializer(
java.util.WeakHashMap.class.getName(),
new MapSerializer(java.util.WeakHashMap.class.getName()));
addSerializer(
java.util.TreeMap.class.getName(),
new MapSerializer(java.util.TreeMap.class.getName()));
addSerializer(
java.util.Properties.class.getName(),
new MapSerializer(java.util.Properties.class.getName()));
// map entry
addSerializer(
java.util.Map.Entry.class.getName(),
new MapEntrySerializer());
}
/**
* Collection and subtypes without Sets.
*
*/
private void bootstrapCollections() {
addSerializer(
java.util.Collection.class.getName(),
new CollectionSerializer(java.util.Collection.class.getName()));
addSerializer(
java.util.List.class.getName(),
new CollectionSerializer(java.util.List.class.getName()));
addSerializer(
java.util.AbstractCollection.class.getName(),
new CollectionSerializer(java.util.AbstractCollection.class.getName()));
addSerializer(
java.util.AbstractList.class.getName(),
new CollectionSerializer(java.util.AbstractList.class.getName()));
addSerializer(
java.util.AbstractSequentialList.class.getName(),
new CollectionSerializer(
java.util.AbstractSequentialList.class.getName()));
addSerializer(
java.util.ArrayList.class.getName(),
new CollectionSerializer(java.util.ArrayList.class.getName()));
addSerializer(
java.util.LinkedList.class.getName(),
new CollectionSerializer(java.util.LinkedList.class.getName()));
addSerializer(
java.util.Stack.class.getName(),
new CollectionSerializer(java.util.Stack.class.getName()));
addSerializer(
java.util.Vector.class.getName(),
new CollectionSerializer(java.util.Vector.class.getName()));
}
/**
* Set and subtypes.
*
*/
private void bootstrapSets() {
addSerializer(
java.util.Set.class.getName(),
new SetSerializer(java.util.Set.class.getName()));
addSerializer(
java.util.SortedSet.class.getName(),
new SetSerializer(java.util.SortedSet.class.getName()));
addSerializer(
java.util.AbstractSet.class.getName(),
new SetSerializer(java.util.AbstractSet.class.getName()));
addSerializer(
java.util.BitSet.class.getName(),
new SetSerializer(java.util.BitSet.class.getName()));
addSerializer(
java.util.HashSet.class.getName(),
new SetSerializer(java.util.HashSet.class.getName()));
addSerializer(
java.util.TreeSet.class.getName(),
new SetSerializer(java.util.TreeSet.class.getName()));
}
/**
* String and Date
*
*/
private void bootstrapStringAndDate() {
addSerializer(
String.class.getName(),
new PrimitiveTypeSerializer("xsd:string", String.class));
addSerializer(java.util.Date.class.getName(), new DateSerializer());
}
/**
* Wrappers around primitive types.
*
*/
private void bootstrapWrappers() {
addSerializer(
Integer.class.getName(),
new PrimitiveTypeSerializer("xsd:int", Integer.class));
addSerializer(
Long.class.getName(),
new PrimitiveTypeSerializer("xsd:long", Long.class));
addSerializer(
Short.class.getName(),
new PrimitiveTypeSerializer("xsd:short", Short.class));
addSerializer(
Byte.class.getName(),
new PrimitiveTypeSerializer("xsd:byte", Byte.class));
addSerializer(
Float.class.getName(),
new PrimitiveTypeSerializer("xsd:float", Float.class));
addSerializer(
Double.class.getName(),
new PrimitiveTypeSerializer("xsd:double", Double.class));
addSerializer(
Character.class.getName(),
new PrimitiveTypeSerializer("char", Character.class));
addSerializer(
Boolean.class.getName(),
new PrimitiveTypeSerializer("xsd:boolean", Boolean.class));
}
/**
* Primitive types.
*
*/
private void bootstrapPrimitiveTypes() {
addSerializer(
int.class.getName(),
new PrimitiveTypeSerializer("xsd:int", Integer.class));
addSerializer(
long.class.getName(),
new PrimitiveTypeSerializer("xsd:long", Long.class));
addSerializer(
short.class.getName(),
new PrimitiveTypeSerializer("xsd:short", Short.class));
addSerializer(
byte.class.getName(),
new PrimitiveTypeSerializer("xsd:byte", Byte.class));
addSerializer(
float.class.getName(),
new PrimitiveTypeSerializer("xsd:float", Float.class));
addSerializer(
double.class.getName(),
new PrimitiveTypeSerializer("xsd:double", Double.class));
addSerializer(
char.class.getName(),
new PrimitiveTypeSerializer("char", Character.class));
addSerializer(
boolean.class.getName(),
new PrimitiveTypeSerializer("xsd:boolean", Boolean.class));
}
/**
* Add a serializer to the registry.
* @param className The name of the class which should be handled by the
* serializer.
* @param ser The serializer.
*/
public void addSerializer(String className, ComponentSerializer ser) {
_serializers.put(className, ser);
_deserializers.put(ser.getXMLTypeName(), ser);
}
/**
* Get a serializer for a type.
* @param type The type.
* @return The serializer.
*/
public ComponentSerializer getSerializer(Class type) {
String typeStr = type.getName();
ComponentSerializer ser = (ComponentSerializer) _serializers.get(typeStr);
if (ser == null) {
// no serializer found
// check for array
if (type.isArray()) {
ser = new ArraySerializer(type);
addSerializer(typeStr, ser);
}
else {
ser = getAndRegisterType(type, typeStr);
}
}
return ser;
}
/**
* Get and register serializers for types unknown by this registry.
* @param type The type of the object to (de)serialize.
* @param typeStr The XML type string to register the type with.
* @return The serializer.
*/
private ComponentSerializer getAndRegisterType(Class type, String typeStr) {
ComponentSerializer ser;
// check for subtypes of map, collection, and set
if (java.util.Map.class.isAssignableFrom(type)) {
ser = new MapSerializer(typeStr);
addSerializer(typeStr, ser);
}
else if (java.util.Set.class.isAssignableFrom(type)) {
ser = new SetSerializer(typeStr);
addSerializer(typeStr, ser);
}
else if (java.util.Collection.class.isAssignableFrom(type)) {
ser = new CollectionSerializer(typeStr);
addSerializer(typeStr, ser);
}
else {
// use and register new object serializer
ser = new ObjectSerializer(typeStr);
addSerializer(typeStr, ser);
}
return ser;
}
/**
* Get a deserializer for an xml element.
* This is done by evaluating the xsi:type attribute.
* @param elementName The name of the element.
* @param attrs The attributes of the element.
* @param classLoader The class loader used to load new classes.
* @return The deserializer.
* @throws DeserializerException If the deserializer can't be found.
*/
public ComponentSerializer getDeserializer(
String elementName,
Attributes attrs,
ClassLoader classLoader)
throws DeserializerException {
String xmlType = attrs.getValue("xsi:type");
if (xmlType == null) {
throw new DeserializerException(
"Element " + elementName + " missing xsi:type attribute");
}
ComponentSerializer ser = (ComponentSerializer) _deserializers.get(xmlType);
if (ser == null) {
// handle arrays
if (xmlType.startsWith(ArraySerializer.TYPENAME_PREFIX)) {
ser = new ArraySerializer(xmlType);
addSerializer(xmlType, ser);
}
else {
// no serializer found - use and register new serializer
try {
Class c =
Class.forName(
ObjectSerializer.fixJavaTypeNameForInnerClass(xmlType),
true,
classLoader);
ser = getAndRegisterType(c, xmlType);
}
catch (ClassNotFoundException e) {
throw new DeserializerException(
"class not found: "
+ ObjectSerializer.fixJavaTypeNameForInnerClass(xmlType));
}
}
}
return ser;
}
/**
* Try to find a deserializer if no xsi:type attribute is available.
* This is done by examining the class definition of the parent.
* @param parent The parent object.
* @param elementName The name of the element.
* @return The deserializer.
* @throws DeserializerException If the deserializer can't be found.
*/
public ComponentSerializer findDeserializer(
Object parent,
String elementName)
throws DeserializerException {
Class c = null;
if (parent != null) {
// parent available
if (parent instanceof ArrayHelper) {
// parent is array
c = ((ArrayHelper) parent).getComponentType();
}
else if (elementName != null){
// look if the parent has a field with name "elementName"
c = ObjectSerializer.getFieldType(parent, elementName);
}
}
if (c == null && elementName != null) {
// possibly a root element - then its name should be the classname
try {
c =
Class.forName(
ObjectSerializer.fixJavaTypeNameForInnerClass(elementName));
}
catch (ClassNotFoundException ex) {
// c remains null
}
}
if (c == null) {
throw new DeserializerException(
"can't determine java type for element: " + elementName);
}
return getSerializer(c);
// we must call getSerializer() here instead of getDeserializer(),
// because we have the JAVA type and not the XML type string!
}
/**
* Guess the deserializer for a number.
*
* @param s
* The number as String.
* @return The deserializer.
* @throws DeserializerException
* If the argument is not a number.
*/
public ComponentSerializer guessDeserializerForNumber(String s)
throws DeserializerException {
try {
Integer.parseInt(s);
return getSerializer(Integer.class);
} catch (NumberFormatException e) {
}
try {
Long.parseLong(s);
return getSerializer(Long.class);
} catch (NumberFormatException e) {
}
try {
Double.parseDouble(s);
return getSerializer(Double.class);
} catch (NumberFormatException e) {
}
try {
new BigInteger(s);
return getSerializer(BigInteger.class);
} catch (NumberFormatException e) {
}
try {
new BigDecimal(s);
return getSerializer(BigDecimal.class);
} catch (NumberFormatException e) {
}
throw new DeserializerException("Not a number: " + s);
}
}