package com.revolsys.datatype; import java.awt.Color; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URI; import java.sql.Blob; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.measure.Measure; import javax.xml.namespace.QName; import org.apache.log4j.Logger; import com.revolsys.awt.WebColors; import com.revolsys.collection.map.Maps; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryCollection; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Lineal; import com.revolsys.geometry.model.LinearRing; import com.revolsys.geometry.model.MultiLineString; import com.revolsys.geometry.model.MultiPoint; import com.revolsys.geometry.model.MultiPolygon; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.geometry.model.Polygonal; import com.revolsys.geometry.model.Punctual; import com.revolsys.identifier.Identifier; import com.revolsys.io.FileUtil; import com.revolsys.io.PathName; import com.revolsys.record.RecordDataType; import com.revolsys.record.code.CodeDataType; import com.revolsys.util.Booleans; import com.revolsys.util.Dates; import com.revolsys.util.Measures; import com.revolsys.util.UrlUtil; import com.revolsys.util.number.BigDecimals; import com.revolsys.util.number.BigIntegers; import com.revolsys.util.number.Bytes; import com.revolsys.util.number.Doubles; import com.revolsys.util.number.Floats; import com.revolsys.util.number.Integers; import com.revolsys.util.number.Longs; import com.revolsys.util.number.Shorts; // TODO manage data types by classloader and allow unloading of registered classes. public final class DataTypes { private static final Map<String, DataType> CLASS_TYPE_MAP = new HashMap<>(); private static final Map<String, DataType> NAME_TYPE_MAP = new HashMap<>(); public static final DataType ANY_URI = new FunctionDataType("anyURI", URI.class, UrlUtil::toUri); public static final DataType BASE64_BINARY = new SimpleDataType("base64Binary", byte[].class); public static final DataType BLOB = new SimpleDataType("blob", Blob.class); public static final DataType BOOLEAN = new FunctionDataType("boolean", Boolean.class, Booleans::valueOf); public static final DataType BOUNDING_BOX = new FunctionDataType("boolean", BoundingBox.class, BoundingBox::newBoundingBox); public static final DataType BYTE = new Bytes(); public static final DataType COLOR = new FunctionDataType("color", Color.class, WebColors::toColor, WebColors::toString); public static final DataType CODE = new CodeDataType(); public static final DataType DATE = new FunctionDataType("date", java.util.Date.class, Dates::getDate, Dates::toDateTimeString, Dates::equalsNotNull); public static final DataType DATE_TIME = new FunctionDataType("dateTime", Timestamp.class, Dates::getTimestamp, Dates::toTimestampString, Dates::equalsNotNull); public static final DataType DECIMAL = new BigDecimals(); public static final DataType DOUBLE = new Doubles(); public static final DataType DURATION = new SimpleDataType("duration", Date.class); public static final DataType FILE = new FunctionDataType("File", File.class, FileUtil::newFile); public static final DataType FLOAT = new Floats(); public static final DataType GEOMETRY = FunctionDataType.newToObjectEquals("Geometry", Geometry.class, Geometry::newGeometry, Geometry::equalsExact); public static final DataType GEOMETRY_COLLECTION = FunctionDataType.newToObjectEquals( "GeometryCollection", GeometryCollection.class, GeometryCollection::newGeometryCollection, Geometry::equalsExact); public static final DataType IDENTIFIER = new FunctionDataType("identifier", Identifier.class, Identifier::newIdentifier); public static final DataType INT = new Integers(); public static final DataType INTEGER = new BigIntegers(); public static final DataType LINE_STRING = FunctionDataType.newToObjectEquals("LineString", LineString.class, LineString::newLineString, Geometry::equalsExact); public static final DataType LINEAR_RING = FunctionDataType.newToObjectEquals("LinearRing", LinearRing.class, LinearRing::newLinearRing, Geometry::equalsExact); private static final Logger LOG = Logger.getLogger(DataTypes.class); public static final DataType LONG = new Longs(); public static final DataType MEASURE = new FunctionDataType("measure", Measure.class, Measures::newMeasure, Measures::toString); public static final DataType MULTI_LINE_STRING = FunctionDataType.newToObjectEquals( "MultiLineString", MultiLineString.class, Lineal::newLineal, Geometry::equalsExact); public static final DataType MULTI_POINT = FunctionDataType.newToObjectEquals("MultiPoint", MultiPoint.class, Punctual::newPunctual, Geometry::equalsExact); public static final DataType MULTI_POLYGON = FunctionDataType.newToObjectEquals("MultiPolygon", MultiPolygon.class, Polygonal::newPolygonal, Geometry::equalsExact); public static final DataType OBJECT = new ObjectDataType(); public static final DataType POINT = FunctionDataType.newToObjectEquals("Point", Point.class, Point::newPoint, Geometry::equalsExact); public static final DataType POLYGON = FunctionDataType.newToObjectEquals("Polygon", Polygon.class, Polygon::newPolygon, Geometry::equalsExact); public static final DataType QNAME = new SimpleDataType("QName", QName.class); public static final DataType PATH_NAME = new FunctionDataType("pathName", PathName.class, PathName::newPathName); public static final DataType RECORD = new RecordDataType(); public static final DataType SHORT = new Shorts(); public static final DataType SQL_DATE = new FunctionDataType("date", java.sql.Date.class, Dates::getSqlDate, Dates::toSqlDateString, Dates::equalsNotNull); public static final DataType STRING = new FunctionDataType("string", String.class, Object::toString); public static final DataType XML = new FunctionDataType("xml", String.class, Object::toString); public static final DataType TIME = new SimpleDataType("time", Time.class); public static final DataType TIMESTAMP = new FunctionDataType("timestamp", Timestamp.class, Dates::getTimestamp, Dates::toTimestampString, Dates::equalsNotNull); public static final DataType URL = new FunctionDataType("url", java.net.URL.class, UrlUtil::toUrl); public static final DataType UUID = new SimpleDataType("uuid", UUID.class); public static final DataType COLLECTION = new CollectionDataType("Collection", Collection.class, OBJECT); public static final DataType LIST = new CollectionDataType("List", List.class, OBJECT); public static final DataType MAP = new FunctionDataType("Map", Map.class, (value) -> { if (value instanceof Map) { return (Map)value; } else { return value; } }, Maps::equalsNotNull, Maps::equalsNotNull); public static final DataType RELATION = new CollectionDataType("Relation", Collection.class, OBJECT); public static final DataType SET = new CollectionDataType("Set", Set.class, OBJECT); static { final Field[] fields = DataTypes.class.getDeclaredFields(); for (final Field field : fields) { if (Modifier.isStatic(field.getModifiers())) { if (DataType.class.isAssignableFrom(field.getType())) { try { final DataType type = (DataType)field.get(null); register(type); } catch (final Throwable e) { LOG.error("Error registering type " + field.getName(), e); } } } } register(Boolean.TYPE, BOOLEAN); register(Byte.TYPE, BYTE); register(Short.TYPE, SHORT); register(Integer.TYPE, INT); register(Long.TYPE, LONG); register(Float.TYPE, FLOAT); register(Double.TYPE, DOUBLE); } public static DataType getDataType(final Class<?> clazz) { if (clazz == null) { return OBJECT; } else { DataType dataType = CLASS_TYPE_MAP.get(clazz.getName()); if (dataType == null) { final Class<?>[] interfaces = clazz.getInterfaces(); if (interfaces != null) { for (final Class<?> inter : interfaces) { dataType = getDataType(inter); if (dataType != null && dataType != OBJECT) { return dataType; } } } return getDataType(clazz.getSuperclass()); } else { return dataType; } } } public static DataType getDataType(final Object object) { if (object == null) { return OBJECT; } else if (object instanceof DataTypeProxy) { final DataTypeProxy proxy = (DataTypeProxy)object; return proxy.getDataType(); } else if (object instanceof DataType) { final DataType type = (DataType)object; return type; } else { final Class<?> clazz = object.getClass(); return getDataType(clazz); } } public static DataType getDataType(final String name) { if (name == null) { return OBJECT; } else { final DataType type = NAME_TYPE_MAP.get(name.toLowerCase()); if (type == null) { return OBJECT; } else { return type; } } } public static DataType getDataType(final Type type) { if (type instanceof Class) { final Class<?> clazz = (Class<?>)type; return getDataType(clazz); } else { throw new IllegalArgumentException("Cannot get dataType for: " + type); } } public static void register(final Class<?> typeClass, final DataType type) { final String typeClassName = typeClass.getName(); CLASS_TYPE_MAP.put(typeClassName, type); } public static void register(final DataType type) { final String name = type.getName().toLowerCase(); if (!NAME_TYPE_MAP.containsKey(name)) { NAME_TYPE_MAP.put(name, type); } final Class<?> typeClass = type.getJavaClass(); register(typeClass, type); } public static void register(final String name, final Class<?> javaClass) { final DataType type = new SimpleDataType(name, javaClass); register(type); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static <V> V toObject(final Class<?> clazz, final Object value) { // TODO enum if (value == null) { return null; } else if (clazz.isAssignableFrom(value.getClass())) { return (V)value; } else { if (clazz.isEnum()) { try { return (V)Enum.valueOf((Class<Enum>)clazz, value.toString()); } catch (final Throwable e) { } } final DataType dataType = getDataType(clazz); if (dataType == null) { return (V)value; } else { return dataType.toObject(value); } } } public static String toString(final Object value) { if (value == null) { return null; } else if (value instanceof String) { return (String)value; } else { final Class<?> valueClass = value.getClass(); final DataType dataType = getDataType(valueClass); if (dataType == null) { return value.toString(); } else { return dataType.toString(value); } } } private DataTypes() { } }