package org.geotools.data.efeature;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.query.conditions.IDataTypeAdapter;
import org.eclipse.emf.query.conditions.booleans.BooleanAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter.ByteAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter.DoubleAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter.FloatAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter.LongAdapter;
import org.eclipse.emf.query.conditions.numbers.NumberAdapter.ShortAdapter;
import org.eclipse.emf.query.conditions.strings.StringAdapter;
import org.geotools.data.efeature.adapters.IntegerAdapter;
import org.geotools.data.efeature.adapters.CharacterAdapter;
import org.geotools.data.efeature.adapters.DateAdapter;
import org.geotools.data.efeature.adapters.GeometryAdapter;
import org.geotools.data.efeature.adapters.ObjectAdapter;
import org.geotools.data.efeature.adapters.WKBAdapter;
import org.geotools.data.efeature.adapters.WKTAdapter;
import org.opengis.filter.expression.Literal;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
public class DataTypes
{
// -----------------------------------------------------
// Private static fields
// -----------------------------------------------------
//private final static Logger LOGGER = Logging.getLogger(DataTypes.class);
private final static Map<String, Class<?>> typeMap = new HashMap<String, Class<?>>();
private final static Map<Class<?>, String> nameMap = new HashMap<Class<?>, String>();
private final static Map<Object, Class<?>> uniqueMap = new HashMap<Object, Class<?>>();
private final static Map<String, IDataTypeAdapter<?>> adapterMap = new HashMap<String, IDataTypeAdapter<?>>();
// -----------------------------------------------------
// Public static fields
// -----------------------------------------------------
public static final String WKT = "WellKnownText";
public static final String WKT_GEOMETRYCOLLECTION_EMPTY = "GEOMETRYCOLLECTION EMPTY";
public static final String WKT_MULTIPOLYGON_EMPTY = "MULTIPOLYGON EMPTY";
public static final String WKT_MULTILINESTRING_EMPTY = "MULTILINESTRING EMPTY";
public static final String WKT_MULTIPOINT_EMPTY = "MULTIPOINT EMPTY";
public static final String WKT_POLYGON_EMPTY = "POLYGON EMPTY";
public static final String WKT_LINESTRING_EMPTY = "LINESTRING EMPTY";
public static final String WKT_POINT_EMPTY = "POINT EMPTY";
public static final String WKB = "WellKnownBinary";
public static final byte[] WKB_GEOMETRYCOLLECTION_EMPTY = new byte[]{0, 0, 0, 0, 7, 0, 0, 0, 0};
public static final byte[] WKB_MULTIPOLYGON_EMPTY = new byte[]{0, 0, 0, 0, 6, 0, 0, 0, 0};
public static final byte[] WKB_MULTILINESTRING_EMPTY = new byte[]{0, 0, 0, 0, 5, 0, 0, 0, 0};
public static final byte[] WKB_MULTIPOINT_EMPTY = new byte[]{0, 0, 0, 0, 4, 0, 0, 0, 0};
public static final byte[] WKB_POLYGON_EMPTY = new byte[]{0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0};
public static final byte[] WKB_LINESTRING_EMPTY = new byte[]{0, 0, 0, 0, 2, 0, 0, 0, 0};
public static final byte[] WKB_POINT_EMPTY = new byte[]{}; // Apparently, there is no WKB convention for "POINT EMPTY"
/**
* Maximum length of empty WKB definition byte array.
*/
public static final int WKB_MAX_EMPTY_COUNT = Math.max(WKB_GEOMETRYCOLLECTION_EMPTY.length,
Math.max(WKB_MULTIPOLYGON_EMPTY.length,Math.max(WKB_MULTILINESTRING_EMPTY.length,
Math.max(WKB_MULTIPOINT_EMPTY.length,Math.max(WKB_LINESTRING_EMPTY.length,
WKB_POINT_EMPTY.length)))));
// -----------------------------------------------------
// Static constructor
// -----------------------------------------------------
static {
// ----------------------------------------------------------
// Add support for all primitives and their wrapper counterparts
// ----------------------------------------------------------
support(int[].class, Integer[].class, IntegerAdapter.DEFAULT,"0",0);
support(double[].class, Double[].class, DoubleAdapter.DEFAULT,"0.0",0.0);
support(float[].class, Float[].class, FloatAdapter.DEFAULT,"0.0f",0.0f);
support(byte[].class, Byte[].class, ByteAdapter.DEFAULT);
support(short[].class, Short[].class, ShortAdapter.DEFAULT);
support(long[].class, Long[].class, LongAdapter.DEFAULT,"OL",0L);
support(char[].class, Character[].class, CharacterAdapter.DEFAULT,Character.toString('\u0000'));
// ----------------------------------------------------------
// Add support for other often used data types
// ----------------------------------------------------------
support(Date[].class, DateAdapter.DEFAULT);
support(String[].class, StringAdapter.DEFAULT,"\"\"","");
support(boolean[].class, Boolean[].class,BooleanAdapter.DEFAULT,
"true","false","TRUE","FALSE",Boolean.TRUE,Boolean.FALSE);
// ----------------------------------------------------------
// JTS Geometry data types
// ----------------------------------------------------------
support(Geometry[].class, GeometryAdapter.DEFAULT);
support(Point[].class, GeometryAdapter.DEFAULT,WKT_POINT_EMPTY);
support(LineString[].class, GeometryAdapter.DEFAULT, WKT_LINESTRING_EMPTY);
support(Polygon[].class, GeometryAdapter.DEFAULT, WKT_POLYGON_EMPTY);
support(MultiPoint[].class, GeometryAdapter.DEFAULT, WKT_MULTIPOINT_EMPTY);
support(MultiLineString[].class, GeometryAdapter.DEFAULT, WKT_MULTILINESTRING_EMPTY);
support(MultiPolygon[].class, GeometryAdapter.DEFAULT,WKT_MULTIPOLYGON_EMPTY);
support(GeometryCollection[].class, GeometryAdapter.DEFAULT, WKT_GEOMETRYCOLLECTION_EMPTY);
// -----------------------------------------------------
// Special data types
// -----------------------------------------------------
support(WKT,String.class,WKTAdapter.DEFAULT,
WKT_POINT_EMPTY, WKT_LINESTRING_EMPTY, WKT_POLYGON_EMPTY, WKT_MULTIPOINT_EMPTY,
WKT_MULTILINESTRING_EMPTY, WKT_MULTIPOLYGON_EMPTY, WKT_GEOMETRYCOLLECTION_EMPTY);
support(WKB,byte[].class,WKBAdapter.DEFAULT,
WKB_POINT_EMPTY, WKB_LINESTRING_EMPTY, WKB_POLYGON_EMPTY, WKB_MULTIPOINT_EMPTY,
WKB_MULTILINESTRING_EMPTY, WKB_MULTIPOLYGON_EMPTY, WKB_GEOMETRYCOLLECTION_EMPTY);
// -----------------------------------------------------
// Generic data types
// -----------------------------------------------------
//
// Map Object to all supported data types.
//
support(Object.class,ObjectAdapter.DEFAULT);
//
// Map Object to supported data types. Note that the declaration of this default
// as type Integer actually doesn't matter at all, because the "cast" to (N), in this
// case (Integer), doesn't actually exist (is "erasure" is Number)
//
support(Number.class,NumberAdapter.<Integer>getDefault());
}
// -----------------------------------------------------
// Public methods
// -----------------------------------------------------
public static int getCount() {
return nameMap.size();
}
public static int getCount(Class<?> type, boolean subtype) {
int count = 0;
if(subtype) {
for(Class<?> it : typeMap.values()) {
if(it.isAssignableFrom(type))
count++;
}
} else {
for(Class<?> it : typeMap.values()) {
if(type.isAssignableFrom(it))
count++;
}
}
return count;
}
@SuppressWarnings("unchecked")
public static <T> List<Class<T>> getSubTypes(Class<T> type) {
List<Class<T>> types = new ArrayList<Class<T>>();
for(Class<?> it : typeMap.values()) {
if(!type.equals(it) && type.isAssignableFrom(it))
types.add((Class<T>)it);
}
return types;
}
public static String getName(Class<?> type) {
return nameMap.get(type);
}
public static Class<?> getType(String name) {
return typeMap.get(name);
}
public static boolean supports(String name)
{
return typeMap.containsKey(name);
}
public static boolean supports(Class<?> type)
{
if(!nameMap.containsKey(type)) {
return getSubTypes(type).size()>0;
}
return true;
}
public static IDataTypeAdapter<?> getAdapter(String name) {
return adapterMap.get(name);
}
@SuppressWarnings("unchecked")
public static <T> IDataTypeAdapter<T> getAdapter(Class<T> type) {
if(!nameMap.containsKey(type)) {
List<Class<T>> types = getSubTypes(type);
if(types.size()==0) {
return null;
}
type = types.get(0);
}
return (IDataTypeAdapter<T>) getAdapter(type.getName());
}
public static <N extends Number> N getMinValue(Class<N> type)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = type.getField("MIN_VALUE");
return type.cast(field.get(null));
}
public static <N extends Number> N getMaxValue(Class<N> type)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = type.getField("MAX_VALUE");
return type.cast(field.get(null));
}
public static boolean isArray(Literal value) {
return isArray(value.getValue());
}
public static boolean isArray(Object value) {
if(value != null) {
return value.getClass().isArray();
}
return false;
}
public static Class<?> toType(Literal value) {
return toType(value.getValue());
}
public static Class<?> toType(Object value) {
if(value !=null) {
Class<?> type = value.getClass();
return type.isArray() ? type.getComponentType() : type;
}
return null;
}
public static boolean isNumeric(Literal value) {
return isNumeric(value.getValue());
}
public static boolean isNumeric(Object value) {
return (value instanceof Number);
}
public static boolean isDate(Literal value) {
return isDate(value.getValue());
}
public static boolean isDate(Object value) {
return (value instanceof Date);
}
public static boolean isBoolean(Literal value, boolean parse) {
return isBoolean(value.getValue(),parse);
}
public static boolean isBoolean(Object value, boolean parse) {
if(value instanceof Boolean) {
return true;
}
else if(parse && (value instanceof String) ) {
String s =(String)value;
return s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false");
}
return false;
}
public static boolean isCharacter(Literal value) {
return isString(value.getValue());
}
public static boolean isCharacter(Object value) {
return (value instanceof Character);
}
public static boolean isString(Literal value) {
return isString(value.getValue());
}
public static boolean isString(Object value) {
return (value instanceof String);
}
public static boolean isGeometry(Literal value) {
return isGeometry(value.getValue());
}
public static boolean isGeometry(Object value) {
if(value instanceof Geometry) {
return true;
}
else if(value instanceof Class) {
Class<?> cls = (Class<?>)value;
if(Geometry.class.isAssignableFrom(cls)) {
return true;
}
}
return false;
}
// -----------------------------------------------------
// Private helper methods
// -----------------------------------------------------
private static void support(String name, Class<?> type, IDataTypeAdapter<?> adapter, Object... unique)
{
typeMap.put(name,type);
nameMap.put(type,name);
adapterMap.put(name, adapter);
Class<?> component = type.getComponentType();
if(component!=null)
{
support(component,adapter,unique);
}
else if(!(unique == null || unique.length==0))
{
// Only set nil values for non-array types
for(Object it : unique)
{
uniqueMap.put(it, type);
}
}
}
private static void support(Class<?> type, IDataTypeAdapter<?> adapter, Object... unique)
{
String name = type.getName();
support(name, type, adapter, unique);
}
private static void support(Class<?> primitive, Class<?> wrapper, IDataTypeAdapter<?> adapter, Object... unique)
{
support(primitive,adapter,unique);
support(wrapper,adapter,unique);
}
}