package ecologylab.serialization.types; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import ecologylab.generic.Debug; import ecologylab.platformspecifics.FundamentalPlatformSpecifics; import ecologylab.serialization.XMLTools; import ecologylab.serialization.types.scalar.CompositeAsScalarType; /** * This class implements registries of instances of ScalarType and CollectionType. * <p/> * Thus, for example, the key for the type translated by IntType is "int", not "IntType". * (But for the type translated by IntegerType, it is Integer :-) * It must be * this way, because automatic translation is performed based on Field declarations, and the Field * declarations do not know about these Types, only about the underlying Java types. */ public class TypeRegistry<ST extends SimplType> extends Debug implements CrossLanguageTypeConstants { /** * This is now a doubleton class, like a singleton, but there are 2. * One instance for ScalarType, one instance for CollectionType. */ private static TypeRegistry<ScalarType> scalarRegistry; private static TypeRegistry<CollectionType> collectionRegistry; /** * These are by Java full name. */ private final HashMap<String, ST> typesByJavaName = new HashMap<String, ST>(); private final HashMap<String, ST> typesByCrossPlatformName = new HashMap<String, ST>(); private final HashMap<String, ST> typesBySimpleName = new HashMap<String, ST>(); private final HashMap<String, ST> typesByCSharpName = new HashMap<String, ST>(); private final HashMap<String, ST> typesByObjectiveCName = new HashMap<String, ST>(); private final HashMap<String, ST> typesByDbName = new HashMap<String, ST>(); private CollectionType defaultCollectionType, defaultMapType; static { init(); } private static boolean init; /** * */ public static void init() { if (!init) { init = true; new FundamentalTypes(); FundamentalPlatformSpecifics.get().initializePlatformSpecificTypes(); } } public TypeRegistry() { } private static TypeRegistry<ScalarType> scalarRegistry() { TypeRegistry<ScalarType> result = scalarRegistry; if (result == null) { synchronized (TypeRegistry.class) { result = scalarRegistry; if (result == null) { result = new TypeRegistry<ScalarType>(); scalarRegistry = result; } } } return result; } private static TypeRegistry<CollectionType> collectionRegistry() { TypeRegistry<CollectionType> result = collectionRegistry; if (result == null) { synchronized (TypeRegistry.class) { result = collectionRegistry; if (result == null) { result = new TypeRegistry<CollectionType>(); collectionRegistry = result; } } } return result; } static boolean registerSimplType(SimplType type) { TypeRegistry registry = CollectionType.class.isAssignableFrom(type.getClass()) ? collectionRegistry() : scalarRegistry(); return registry.registerType(type); } /** * Enter this type in the registry, which is a map in which the Type's Class object's fully * qualified named is used as a key. */ static boolean registerScalarType(ScalarType type) { return scalarRegistry().registerType(type); } private synchronized boolean registerTypeIfNew(ST type) { String javaTypeName = type.getJavaTypeName(); return typesByJavaName.containsKey(javaTypeName) ? false : registerType(type); } private synchronized boolean registerType(ST type) { String javaTypeName = type.getJavaTypeName(); typesByJavaName.put(javaTypeName, type); String crossPlatformName = type.getName(); typesByCrossPlatformName.put(crossPlatformName, type); String cSharpTypeName = type.getCSharpTypeName(); if (cSharpTypeName != null) typesByCSharpName.put(cSharpTypeName, type); String objectiveCTypeName = type.getObjectiveCTypeName(); if (objectiveCTypeName != null) typesByObjectiveCName.put(objectiveCTypeName, type); String dbTypeName = type.getDbTypeName(); if (dbTypeName != null) typesByDbName.put(dbTypeName, type); String simpleName = type.getSimpleName(); ST previous = typesBySimpleName.put(simpleName, type); boolean definingNewType = previous != null && !previous.equals(type); if (definingNewType) { warning("registerType(): Redefining type: " + simpleName); } return definingNewType; } /** * Get the Scalar Type corresponding to the Class, by using its name. * * @param thatClass * @return Type associated with thatClass */ public static <U> ScalarType<U> getScalarType(Class<U> thatClass) { if (XMLTools.isEnum(thatClass)) { return scalarRegistry().getTypeByClass(Enum.class); } else { ScalarType<U> result = scalarRegistry().getTypeByClass(thatClass); if (result == null && XMLTools.isComposite(thatClass)) result = scalarRegistry().getTypeByClass(CompositeAsScalarType.class); return result; } } /** * Check to see if we have a Type corresponding to the Class, by using its name. * * @param thatClass * @return true if thatClass is in this TypeRegistry */ public static boolean containsScalarType(Class thatClass) { return scalarRegistry().contains(thatClass); } public static ScalarType getScalarTypeByName(String name) { return scalarRegistry().getTypeByJavaName(name); } public static ScalarType getScalarTypeBySimpleName(String simpleName) { return scalarRegistry().getTypeBySimpleName(simpleName); } ST getTypeBySimpleName(String simpleName) { return typesBySimpleName.get(simpleName); } boolean contains(Class javaClass) { return containsByJavaName(javaClass.getName()); } boolean containsByJavaName(String javaName) { return typesByJavaName.containsKey(javaName); } ST getTypeByClass(Class<?> javaClass) { return getTypeByJavaName(javaClass.getName()); } ST getTypeByJavaName(String javaName) { return typesByJavaName.get(javaName); } ST getTypeByCSharpName(String cSharpName) { return typesByCSharpName.get(cSharpName); } ST getTypeByObjectiveCName(String objectiveCName) { return typesByObjectiveCName.get(objectiveCName); } ST getTypeByDbName(String dbName) { return typesByDbName.get(dbName); } /** * This method is only called by the constructor of CollectionType. * * @param collectionType */ static void registerCollectionType(CollectionType collectionType) { collectionRegistry().registerType(collectionType); // TypeRegistry registrySingleton = collectionRegistry(); // registrySingleton.typesByJavaName.put(collectionType.getName(), collectionType); // registrySingleton.typesBySimpleName.put(collectionType.getJavaTypeName(), collectionType); } /** * Get by unique, cross-platform name. * * @param crossPlatformName * @return */ public static CollectionType getCollectionTypeByCrossPlatformName(String crossPlatformName) { return collectionRegistry().typesByCrossPlatformName.get(crossPlatformName); } public static CollectionType getCollectionTypeByCSharpName(String cSharpName) { return collectionRegistry().typesByCSharpName.get(cSharpName); } public static CollectionType getCollectionTypeByObjectiveCName(String objectiveCName) { return collectionRegistry().typesByObjectiveCName.get(objectiveCName); } public static CollectionType getCollectionTypeBySimpleName(String simpleName) { return collectionRegistry().typesBySimpleName.get(simpleName); } /** * Lookup a collection type using the Java class or its full unqualifiedName. * * @param javaField Declaring class of this field is key for lookup * * @return */ public static CollectionType getCollectionType(Field javaField) { return getCollectionType(javaField.getType()); } /** * Lookup a collection type using the Java class or its full unqualifiedName. * If it does not exist, construct a new CollectionType, but with no capabilities for Cross-Language Code Generation. * * @param javaClass * @return */ public static CollectionType getCollectionType(Class javaClass) { String javaClassName = javaClass.getName(); CollectionType result = getCollectionTypeByJavaName(javaClassName); if (result == null) { if (javaClass.isInterface() || Modifier.isAbstract(javaClass.getModifiers())) { return Map.class.isAssignableFrom(javaClass) ? collectionRegistry().defaultMapType : collectionRegistry().defaultCollectionType; } else { String crossPlatformName = SimplType.deriveCrossPlatformName(javaClass, false); collectionRegistry().warning("No CollectionType was pre-defined for " + crossPlatformName + ", so constructing one on the fly.\nCross-language code for fields defined with this type cannot be generated."); result = new CollectionType(javaClass, null, null); } } return result; } /** * Lookup a collection type using the Java class or its full unqualifiedName. * * @param javaClassName * @return */ public static CollectionType getCollectionTypeByJavaName(String javaClassName) { return collectionRegistry().typesByJavaName.get(javaClassName); } public static TypeRegistry typeRegistry() { return scalarRegistry; } public static void setDefaultCollectionType(CollectionType ct) { collectionRegistry().defaultCollectionType = ct; } public static void setDefaultMapType(CollectionType ct) { collectionRegistry().defaultMapType = ct; } static CollectionType getDefaultCollectionType() { return collectionRegistry().defaultCollectionType; } static CollectionType getDefaultMapType() { return collectionRegistry().defaultMapType; } static CollectionType getDefaultCollectionOrMapType(boolean isMap) { return isMap ? getDefaultMapType() : getDefaultCollectionType(); } }