-/*******************************************************************************\r
- * Copyright (c) 2007, 2012 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.databoard;\r
-\r
-import java.io.IOException;\r
-import java.lang.reflect.Constructor;\r
-import java.lang.reflect.InvocationTargetException;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.adapter.Adapter;\r
-import org.simantics.databoard.adapter.AdapterConstructionException;\r
-import org.simantics.databoard.adapter.AdapterFactory;\r
-import org.simantics.databoard.adapter.RuntimeAdaptException;\r
-import org.simantics.databoard.adapter.RuntimeAdapterConstructionException;\r
-import org.simantics.databoard.annotations.ArgumentImpl;\r
-import org.simantics.databoard.annotations.Arguments;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.VariantBinding;\r
-import org.simantics.databoard.binding.classfactory.ImmutableClassesFactory;\r
-import org.simantics.databoard.binding.classfactory.TypeClassFactory;\r
-import org.simantics.databoard.binding.classfactory.TypeClassSubFactory;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
-import org.simantics.databoard.binding.factory.BindingRepository;\r
-import org.simantics.databoard.binding.factory.DefaultBindingFactory;\r
-import org.simantics.databoard.binding.factory.JavaUtilBindingsProvider;\r
-import org.simantics.databoard.binding.factory.MutableBindingFactory;\r
-import org.simantics.databoard.binding.factory.TroveBindingsProvider;\r
-import org.simantics.databoard.binding.factory.TypeBindingFactory;\r
-import org.simantics.databoard.binding.impl.BeanBinding;\r
-import org.simantics.databoard.binding.impl.ObjectVariantBinding;\r
-import org.simantics.databoard.binding.impl.StringVariantBinding;\r
-import org.simantics.databoard.binding.mutable.ImmutableVariantBinding;\r
-import org.simantics.databoard.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.binding.reflection.BindingProvider;\r
-import org.simantics.databoard.binding.reflection.BindingRequest;\r
-import org.simantics.databoard.binding.reflection.ClassBindingFactory;\r
-import org.simantics.databoard.binding.reflection.VoidBinding;\r
-import org.simantics.databoard.serialization.DefaultSerializerFactory;\r
-import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;\r
-import org.simantics.databoard.serialization.Serializer;\r
-import org.simantics.databoard.serialization.SerializerConstructionException;\r
-import org.simantics.databoard.serialization.SerializerFactory;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.util.Bean;\r
-import org.simantics.databoard.util.DataValueUtil;\r
-\r
-public class Databoard {\r
-\r
- // Repositories\r
-\r
- /** Repository of mutable bindings */\r
- public final Map<Datatype, Binding> mutableBindingRepository = Collections.synchronizedMap( new HashMap<Datatype, Binding>() );\r
-\r
- /** Repository of default bindings */\r
- public final Map<Datatype, Binding> defaultBindingRepository = Collections.synchronizedMap( new HashMap<Datatype, Binding>() );\r
-\r
- /** Repository of class Bindings */\r
- public final BindingRepository bindingRepository = new BindingRepository();\r
-\r
- /** Repository of serializers */\r
- public final Map<Binding, Serializer> serializerRepository = Collections.synchronizedMap( new HashMap<Binding, Serializer>() ); \r
- \r
- \r
- // Factories\r
-\r
- /** Mutable Bindings Factory */\r
- public final TypeBindingFactory mutableBindingFactory = new MutableBindingFactory( mutableBindingRepository );\r
-\r
- /** Default Bindings Factory */\r
- public final TypeBindingFactory defaultBindingFactory = new DefaultBindingFactory( defaultBindingRepository );\r
-\r
- /** Reflection based Binding Factory, create binding to class */\r
- public final ClassBindingFactory classBindingFactory = new ClassBindingFactory( bindingRepository, defaultBindingFactory );\r
-\r
- /** Serializer Factory */\r
- public final SerializerFactory serializationFactory = new DefaultSerializerFactory( serializerRepository );\r
-\r
- /** Adapter Factory */\r
- public final AdapterFactory adapterFactory = new AdapterFactory();\r
- \r
- /** Class Factory */ \r
- public final TypeClassFactory typeClassFactory = new TypeClassFactory();\r
- \r
- \r
- public final VariantBinding VARIANT; // Variant \r
- public final VariantBinding OBJECT; // java.lang.Object ( as variant ) \r
- public final VariantBinding STR_VARIANT; // java.lang.String ( as variant )\r
- public final Binding BEAN; // Bean ( as variant ) \r
-\r
- public Databoard() {\r
- classBindingFactory.addFactory( new TroveBindingsProvider() );\r
- classBindingFactory.addFactory( new JavaUtilBindingsProvider() );\r
- \r
- addDefaultBinding( Datatypes.STRING, Bindings.STRING );\r
- addDefaultBinding( Datatypes.INTEGER, Bindings.INTEGER );\r
- addDefaultBinding( Datatypes.BOOLEAN, Bindings.BOOLEAN );\r
- addDefaultBinding( Datatypes.BYTE, Bindings.BYTE );\r
- addDefaultBinding( Datatypes.LONG, Bindings.LONG );\r
- addDefaultBinding( Datatypes.DOUBLE, Bindings.DOUBLE ); \r
- addDefaultBinding( Datatypes.FLOAT, Bindings.FLOAT ); \r
- addDefaultBinding( Datatypes.VOID, Bindings.VOID );\r
- addDefaultBinding( Datatypes.BOOLEAN_ARRAY, Bindings.BOOLEAN_ARRAY );\r
- addDefaultBinding( Datatypes.BYTE_ARRAY, Bindings.BYTE_ARRAY );\r
- addDefaultBinding( Datatypes.INTEGER_ARRAY, Bindings.INT_ARRAY );\r
- addDefaultBinding( Datatypes.LONG_ARRAY, Bindings.LONG_ARRAY );\r
- addDefaultBinding( Datatypes.FLOAT_ARRAY, Bindings.FLOAT_ARRAY );\r
- addDefaultBinding( Datatypes.DOUBLE_ARRAY, Bindings.DOUBLE_ARRAY );\r
- addDefaultBinding( Datatypes.STRING_ARRAY, Bindings.STRING_ARRAY );\r
- \r
- VARIANT = new ImmutableVariantBinding( classBindingFactory, adapterFactory );\r
- OBJECT = new ObjectVariantBinding( classBindingFactory, adapterFactory );\r
- BEAN = new BeanBinding( classBindingFactory, typeClassFactory, adapterFactory );\r
- STR_VARIANT = new StringVariantBinding( serializationFactory, VARIANT ); \r
-\r
- // Add sub-factory that creates binding for Bean\r
- classBindingFactory.addFactory( new DataboardBindings() );\r
- \r
- // Add class factory that constructs basic types\r
- typeClassFactory.addFactory( new ImmutableClassesFactory() );\r
- \r
- // Bindings.databoard cannot initialize itself\r
- if (Bindings.databoard != null) {\r
- initialize();\r
- }\r
- }\r
- \r
- void initialize() {\r
- // Add run-time class factory, if objectweb.asm-library is available.\r
- try {\r
- // Check ASM Exists\r
- Class.forName("org.objectweb.asm.ClassWriter");\r
- Class<?> y = Class.forName("org.simantics.databoard.binding.classfactory.AsmTypeClassFactory");\r
- Constructor<?> c = y.getConstructor( TypeClassFactory.class );\r
- TypeClassSubFactory f = (TypeClassSubFactory) c.newInstance(typeClassFactory);\r
- typeClassFactory.addFactory( f );\r
- \r
- BindingRequest br = new BindingRequest( Datatype.class ); \r
- Binding datatypeBinding = getBinding( br );\r
- typeClassFactory.getRepository().put(datatypeBinding.type(), br);\r
- } catch (ClassNotFoundException e) {\r
- } catch (InstantiationException e) {\r
- } catch (IllegalAccessException e) {\r
- } catch (IllegalArgumentException e) {\r
- } catch (InvocationTargetException e) {\r
- } catch (SecurityException e) {\r
- } catch (NoSuchMethodException e) {\r
- } catch (BindingConstructionException e) {\r
- e.printStackTrace();\r
- }\r
- }\r
- \r
- public void clear() {\r
- mutableBindingRepository.clear();\r
- defaultBindingRepository.clear();\r
- bindingRepository.clear();\r
- serializerRepository.clear(); \r
- }\r
- \r
- /**\r
- * Get or create a binding that is completely mutable java class. \r
- * \r
- * DataType | Class of the bound instance\r
- * ===================|==================\r
- * UnionType | GenericBinding.TaggedObject.class\r
- * OptionType | ValueContainer.class\r
- * RecordType | Object[].class\r
- * BooleanType | MutableBoolean.class\r
- * DoubleType | MutableDouble.class\r
- * FloatType | MutableFloat.class\r
- * ByteType | MutableByte.class\r
- * IntegerType | MutableInt.class\r
- * LongType | MutableLong.class\r
- * StringType | ValueContainer.class\r
- * ArrayType | ArrayList.class\r
- * MapType | TreeMap.class\r
- * VariantType | MutableVariant.class\r
- * \r
- * Note, requesting a binding with this convenience method stores the \r
- * binding and the type with strong reference, thus preventing garbage \r
- * collection. To allow garbage collection, please use another instance of \r
- * GenericBindingFactory and binding repository (Map<Datatype, Binding>). <p>\r
- *\r
- * @param type the type to create binding to\r
- * @return binding binding to a mutable class \r
- */ \r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getMutableBinding(Datatype type) {\r
- try {\r
- Binding binding = mutableBindingRepository.get(type);\r
- if (binding!=null) return (T) binding;\r
- synchronized(mutableBindingRepository) {\r
- return (T) mutableBindingFactory.getBinding(type);\r
- }\r
- } catch (BindingConstructionException e) {\r
- // Unexpected - if error is thrown there is fault in GenericBindingScheme\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Get or create a binding based on default java classes, such as \r
- * Integer.class, or byte[].class. The result is often binding for an \r
- * immutable classs. These bindings are more efficient than mutable bindings (above).\r
- * \r
- * DataType | Class of the bound instance\r
- * ===================|==================\r
- * BooleanType | Boolean.class\r
- * ByteType | Byte.class\r
- * FloatType | Float.class\r
- * DoubleType | Double.class\r
- * IntegerType | Int.class\r
- * LongType | Long.class\r
- * StringType | String.class\r
- * UnionType | TaggedObject.class\r
- * OptionType | ValueContainer.class\r
- * RecordType | Object[].class\r
- * MapType | TreeMap.class\r
- * VariantType | Variant.class\r
- * ArrayType(Boolean) | boolean[].class\r
- * ArrayType(Byte) | byte[].class\r
- * ArrayType(Integer) | int[].class\r
- * ArrayType(Long) | long[].class\r
- * ArrayType(Float) | float[].class\r
- * ArrayType(Double) | double[].class\r
- * ArrayType(Byte) | byte[].class\r
- * ArrayType( T ) | Object[].class\r
- * \r
- * Note, requesting a binding with this convenience method stores the \r
- * binding and the type with strong reference, thus preventing garbage \r
- * collection. To allow garbage collection, please use another instance of \r
- * DefaultBindingFactory and binding repository (Map<Datatype, Binding>). <p>\r
- *\r
- * @param type the type to create binding to\r
- * @return binding binding to a mutable class \r
- */ \r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBinding(Datatype type) {\r
- try {\r
- Binding binding = defaultBindingRepository.get(type);\r
- if (binding!=null) return (T) binding;\r
- synchronized(defaultBindingRepository) {\r
- return (T) defaultBindingFactory.getBinding(type);\r
- }\r
- } catch (BindingConstructionException e) {\r
- // Unexpected - if error is thrown there is fault in DefaultBindingScheme\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- } \r
- \r
- /**\r
- * Get a binding to a Java Class. Details can be added by placing annotations\r
- * to the java classes. See more in package org.simantics.databoard.annotations. \r
- * <p>\r
- * \r
- * Whether the result is a completely mutable or not depends on the \r
- * requested class. For instance, fields such as Boolean, Integer, Long\r
- * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.\r
- * The length of Object[] is not mutable, but length of List<Object> is. <p>\r
- * \r
- * Note, requesting a binding with this convenience method stores the \r
- * binding and the class with strong reference, thus preventing garbage \r
- * collection. To allow garbage collection, please use another instance of \r
- * BindingFactory and binding repository (Map<BindingRequest, Binding>). <p> \r
- * \r
- * @see ClassBindingFactory \r
- * @param clazz \r
- * @return binding\r
- * @throws BindingConstructionException\r
- */\r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBinding(Class<?> clazz)\r
- throws BindingConstructionException\r
- {\r
- Binding binding = bindingRepository.get( clazz );\r
- if (binding != null) {\r
- return (T) binding;\r
- }\r
- \r
- BindingRequest request = new BindingRequest( clazz );\r
- synchronized(classBindingFactory) {\r
- binding = classBindingFactory.construct(request);\r
- }\r
- return (T) binding;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBinding(BindingRequest request)\r
- throws BindingConstructionException\r
- {\r
- synchronized(classBindingFactory) {\r
- return (T) classBindingFactory.construct(request);\r
- }\r
- }\r
- \r
- /**\r
- * Get a binding for a Java Class. Use this method to acquire class \r
- * parameters for a generics class. <p>\r
- * \r
- * Example 1: \r
- * \r
- * Binding binding = getBinding(Map.class, String.class, Integer.class);\r
- * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();\r
- * \r
- * Example 2:\r
- * \r
- * Binding d = getBinding(List.class, Integer.class);\r
- * List<Integer> list = (List<Integer>) d.createRandom(5);\r
- * \r
- * Example 3:\r
- * \r
- * Binding d = getBinding(List.class, List.class, Integer.class);\r
- * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);\r
- * \r
- * @see ClassBindingFactory \r
- * @param clazz\r
- * @return binding\r
- * @throws BindingConstructionException\r
- */\r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)\r
- throws BindingConstructionException\r
- {\r
- BindingRequest request = new BindingRequest( clazz, parameters );\r
- synchronized(classBindingFactory) {\r
- return (T) classBindingFactory.construct(request);\r
- }\r
- } \r
- \r
- /**\r
- * Read binding and type from a class. DataType details and parameters\r
- * are read as annotations placed in the class. \r
- * (See org.simantics.databoard.annotations)\r
- * <p>\r
- * As an exception, in the subclasses of {@link Throwable}, the fields of\r
- * Throwable are omited.\r
- * <p>\r
- * This method is used for well-known classes where the caller is 100% sure\r
- * that a binding is construable without exception. <p> \r
- * \r
- * @param clazz\r
- * @return binding\r
- * @throws RuntimeBindingConstructionException\r
- */\r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBindingUnchecked(Class<?> clazz)\r
- throws RuntimeBindingConstructionException\r
- {\r
- try {\r
- return (T) getBinding(clazz);\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- } \r
-\r
- /**\r
- * Get a binding for a Java Class. Use this method to acquire class \r
- * parameters for a generics class. <p>\r
- * \r
- * Example 1: \r
- * \r
- * Binding binding = getBinding(Map.class, String.class, Integer.class);\r
- * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();\r
- * \r
- * Example 2:\r
- * \r
- * Binding d = getBinding(List.class, Integer.class);\r
- * List<Integer> list = (List<Integer>) d.createRandom(5);\r
- * \r
- * Example 3:\r
- * \r
- * Binding d = getBinding(List.class, List.class, Integer.class);\r
- * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);\r
- * \r
- * @see ClassBindingFactory \r
- * @param clazz\r
- * @return binding\r
- * @throws BindingConstructionException\r
- */\r
- @SuppressWarnings("unchecked")\r
- public <T extends Binding> T getBindingUnchecked(Class<?> clazz, Class<?>...parameters)\r
- throws RuntimeBindingConstructionException\r
- {\r
- try {\r
- Arguments args = new ArgumentImpl(parameters);\r
- BindingRequest request = new BindingRequest( clazz, args );\r
- Binding binding = bindingRepository.get( request );\r
- if (binding!=null);\r
- synchronized(classBindingFactory) {\r
- binding = classBindingFactory.construct(request);\r
- }\r
- return (T) binding;\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- } \r
- \r
- /**\r
- * Add a simple binding to reflection binding factory.\r
- * \r
- * @param binding\r
- * @param clazz\r
- * @param parameters parameter classes\r
- */\r
- public void addBinding( Binding binding, Class<?> clazz, Class<?>...parameters )\r
- {\r
- ArgumentImpl args = new ArgumentImpl( parameters );\r
- BindingRequest request = new BindingRequest( clazz, args );\r
- bindingRepository.put( request, binding );\r
- }\r
- \r
- /**\r
- * Add binding factory for compositive bindings \r
- * \r
- * @param factory\r
- */\r
- public void addBindingFactory( BindingProvider factory )\r
- {\r
- classBindingFactory.addFactory( factory );\r
- }\r
- \r
- /**\r
- * Creates a bean class \r
- * @param type\r
- * @return class\r
- */\r
- public BindingRequest getBeanBindingRequest( Datatype type ) throws RuntimeBindingConstructionException {\r
- try {\r
- return typeClassFactory.getClass(type);\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Creates a bean class \r
- * @param type\r
- * @return class\r
- */\r
- public Class<?> getBeanClass( Datatype type ) throws BindingConstructionException {\r
- BindingRequest br = typeClassFactory.getClass(type);\r
- return br.getClazz();\r
- }\r
-\r
- /**\r
- * Create binding from datatype that instantiates java classes. \r
- * RecordTypes are Beans, UnionTypes are Classes with @Union annotation, \r
- * ArrayTypes are []. \r
- * \r
- * @param type\r
- * @return class\r
- */\r
- public Binding getBeanBinding( Datatype type ) throws RuntimeBindingConstructionException {\r
- try {\r
- BindingRequest br = typeClassFactory.getClass(type);\r
- return getBinding( br );\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeBindingConstructionException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Get serializer that follows Databoard serialization spec.\r
- * \r
- * @param binding\r
- * @return serializer\r
- * @throws SerializerConstructionException\r
- */\r
- public Serializer getSerializer(Binding binding) throws SerializerConstructionException {\r
- return serializationFactory.construct(binding);\r
- }\r
-\r
- /**\r
- * Get serializer that follows Databoard serialization spec.\r
- * \r
- * @param binding\r
- * @return serializer\r
- * @throws RuntimeSerializerConstructionException\r
- */\r
- public Serializer getSerializerUnchecked(Binding binding) throws RuntimeSerializerConstructionException {\r
- try {\r
- return serializationFactory.construct(binding);\r
- } catch (SerializerConstructionException e) {\r
- throw new RuntimeSerializerConstructionException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Get serializer that follows Databoard serialization spec.\r
- * \r
- * @param clazz\r
- * @return serializer\r
- * @throws SerializerConstructionException\r
- */\r
- public Serializer getSerializer(Class<?> clazz) throws SerializerConstructionException {\r
- try {\r
- Binding binding = getBinding(clazz);\r
- return serializationFactory.construct(binding);\r
- } catch (BindingConstructionException e) {\r
- throw new SerializerConstructionException( e );\r
- }\r
- }\r
-\r
- /**\r
- * Get serializer that follows Databoard serialization spec.\r
- * \r
- * @param clazz\r
- * @return serializer serializer\r
- * @throws RuntimeSerializerConstructionException\r
- */\r
- public Serializer getSerializerUnchecked(Class<?> clazz) throws RuntimeSerializerConstructionException {\r
- try {\r
- Binding binding = getBinding(clazz);\r
- return serializationFactory.construct(binding);\r
- } catch (SerializerConstructionException e) {\r
- throw new RuntimeSerializerConstructionException(e);\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeSerializerConstructionException( new SerializerConstructionException(e) );\r
- }\r
- } \r
- /**\r
- * Create an adapter that adapts two bindings of the same\r
- * data type.\r
- * \r
- * @param domain binding of the source instance\r
- * @param range binding of the result instance\r
- * @return result adapter\r
- * @throws AdapterConstructionException \r
- */\r
- public Adapter getAdapter(Binding domain, Binding range)\r
- throws AdapterConstructionException\r
- {\r
- return adapterFactory.getAdapter(domain, range, false, false);\r
- }\r
- \r
- /**\r
- * Create an adapter that adapts between two bindings of the same\r
- * data type.\r
- * \r
- * @param domain binding of the source instance\r
- * @param range binding of the result instance\r
- * @return result adapter\r
- * @throws AdapterConstructionException \r
- */\r
- public Adapter getAdapterUnchecked(Binding domain, Binding range)\r
- throws RuntimeAdapterConstructionException\r
- {\r
- try {\r
- return adapterFactory.getAdapter(domain, range, false, false);\r
- } catch (AdapterConstructionException e) {\r
- throw new RuntimeAdapterConstructionException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Create a type adapter that adapts instances from one Datatype to \r
- * another. Type Adapter does the following conversions: \r
- * \r
- * o Number Types, e.g. long -> double\r
- * o Unit Types, e.g. mph -> km/h\r
- * o Record Types, for each field of the range, there must be equal in domain \r
- * o Union Types, for each tag type of the domain, there must be equal in range\r
- * \r
- * {@link AdaptException} is thrown at runtime, if number conversion is not \r
- * posible, e.g. converting value 500 from Integer to Byte.\r
- * Note, there is also a possibility of precision loss, in many conversions\r
- * e.g. from double to int.\r
- * \r
- * @param domain binding of the source instance\r
- * @param range binding of the result instance\r
- * @return adapter\r
- * @throws AdapterConstructionException \r
- */\r
- public Adapter getTypeAdapter(Binding domain, Binding range)\r
- throws AdapterConstructionException\r
- {\r
- return adapterFactory.getAdapter(domain, range, true, false);\r
- }\r
-\r
- /**\r
- * Create a type adapter that adapts instances from one DataType to \r
- * another. Type Adapter does the following conversions: \r
- * \r
- * o Number Types, e.g. long -> double\r
- * o Unit Types, e.g. mph -> km/h\r
- * o Record Types, for each field of the range, there must be equal in domain \r
- * o Union Types, for each tag type of the domain, there must be equal in range\r
- * \r
- * {@link AdaptException} is thrown at runtime, if number values are \r
- * not compatible, e.g. converting value 500 from Long to Byte.\r
- * Note, there is also a possibility of precision loss, e.g. when \r
- * converting from double to int.\r
- * \r
- * @param domain binding of the source instance\r
- * @param range binding of the result instance\r
- * @return result adapter\r
- * @throws AdapterConstructionException \r
- */\r
- public Adapter getTypeAdapterUnchecked(Binding domain, Binding range)\r
- {\r
- try {\r
- return adapterFactory.getAdapter(domain, range, true, false);\r
- } catch (AdapterConstructionException e) {\r
- throw new RuntimeAdapterConstructionException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Adapt a value of one type to another. \r
- * \r
- * @param value\r
- * @param domain\r
- * @param range\r
- * @return adapted value\r
- * @throws AdapterConstructionException\r
- * @throws AdaptException\r
- */\r
- public Object adapt(Object value, Binding domain, Binding range)\r
- throws AdaptException\r
- {\r
- try {\r
- if (domain==range) {\r
- return value;\r
- }\r
- return adapterFactory.getAdapter(domain, range, true, false).adapt(value);\r
- } catch (AdapterConstructionException e) {\r
- throw new AdaptException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Adapt a value of one type to another. Exceptions are run-time. Use this\r
- * if it safe to assume the conversion will be successful.\r
- * \r
- * @param value\r
- * @param domain\r
- * @param range\r
- * @return adapted value\r
- * @throws AdapterConstructionException\r
- * @throws AdaptException\r
- */\r
- public Object adaptUnchecked(Object value, Binding domain, Binding range)\r
- throws RuntimeAdapterConstructionException, RuntimeAdaptException\r
- {\r
- try {\r
- if (domain==range) {\r
- return value;\r
- }\r
- return adapterFactory.getAdapter(domain, range, true, false).adaptUnchecked(value);\r
- } catch (RuntimeAdapterConstructionException e) {\r
- throw new RuntimeAdaptException(new AdaptException(e.getCause()));\r
- } catch (AdapterConstructionException e) {\r
- throw new RuntimeAdaptException(new AdaptException(e));\r
- }\r
- } \r
- \r
- /**\r
- * Adapt and clone a value instance to another type. Immutable \r
- * bindings may return the argument as is, others return a cloned copy. \r
- * \r
- * @param value\r
- * @param domain\r
- * @param range\r
- * @return adapted value\r
- * @throws AdapterConstructionException\r
- * @throws AdaptException\r
- */\r
- public Object clone(Object value, Binding domain, Binding range)\r
- throws AdaptException\r
- {\r
- try {\r
- if (domain==range) {\r
- if (domain.isImmutable()) {\r
- return value;\r
- } else { \r
- return domain.clone(value);\r
- }\r
- }\r
- return adapterFactory.getAdapter(domain, range, true, true).adapt(value);\r
- } catch (AdapterConstructionException e) {\r
- throw new AdaptException(e);\r
- }\r
- }\r
- \r
-\r
- /**\r
- * Clone a value of one binding to another. Bindings that handle immutable values\r
- * may return the same instance, others will guarantee a complete copy.\r
- * \r
- * This method throws only runtime exceptions. Use this if it is safe to assume\r
- * that the conversion will be successful.\r
- * \r
- * @param value\r
- * @param domain\r
- * @param range\r
- * @return adapted value\r
- * @throws AdapterConstructionException\r
- * @throws AdaptException\r
- */\r
- public Object cloneUnchecked(Object value, Binding domain, Binding range)\r
- throws RuntimeAdapterConstructionException, RuntimeAdaptException\r
- {\r
- try {\r
- return adapterFactory.getAdapter(domain, range, true, true).adapt(value);\r
- } catch (AdaptException e) {\r
- throw new RuntimeAdaptException(e);\r
- } catch (RuntimeAdapterConstructionException e) {\r
- throw new RuntimeAdaptException(new AdaptException(e.getCause()));\r
- } catch (AdapterConstructionException e) {\r
- throw new RuntimeAdaptException(new AdaptException(e));\r
- }\r
- }\r
- \r
- /**\r
- * Compares two data values for order. Returns a negative integer,\r
- * zero, or a positive integer if, the first argument precedes/lesser than \r
- * the second, is equal to, or successor/greater than the second.<p>\r
- * \r
- * DataTypes of b1 and b2 are not equal, then data types are compared. <p>\r
- *\r
- * The comparison function is defined at \r
- * http://dev.simantics.org/index.php/Org.simantics.datatype_Manual#CompareTo_and_Equals <p>\r
- * \r
- * Note, comparing 2 different number types will not result a value comparison.\r
- * Instead values have the following type precedence ByteType, IntegerType, LongType,\r
- * FloatType, and the highest DoubleType. <p>\r
- *\r
- * @param b1 Binding of o1 \r
- * @param o1 the first object to be compared.\r
- * @param b2 Binding of o2\r
- * @param o2 the second object to be compared.\r
- * @return a negative integer, zero, or a positive integer as the\r
- * first argument is less than, equal to, or greater than the\r
- * second.\r
- * @throws BindingException if object cannot be handled by a binding\r
- */\r
- public int compare(Binding b1, Object o1, Binding b2, Object o2) \r
- throws BindingException\r
- {\r
- return DataValueUtil.compare(b1, o1, b2, o2);\r
- }\r
- \r
- /**\r
- * Compare two data values for equality. <p>\r
- * \r
- * Note, comparing 2 different number types will not result a value comparison.\r
- * Instead values have the following type precedence ByteType, IntegerType, LongType,\r
- * FloatType, and the highest DoubleType. <p>\r
- * \r
- * @param b1\r
- * @param o1\r
- * @param b2\r
- * @param o2\r
- * @return true if equal\r
- * @throws BindingException\r
- */\r
- public boolean equals(Binding b1, Object o1, Binding b2, Object o2)\r
- throws BindingException\r
- {\r
- return DataValueUtil.equals(b1, o1, b2, o2);\r
- }\r
- \r
- public Comparator<Object> createComparator(final Binding b1, final Binding b2)\r
- {\r
- return DataValueUtil.createComparator(b1, b2);\r
- }\r
- \r
- /**\r
- * Print the content of an object as a structure.\r
- * Utility function for debug purposes.\r
- * \r
- * @param o\r
- * @return content\r
- */\r
- public String toString(Object o) {\r
- try {\r
- Binding b = getBinding( o.getClass() );\r
- return b.printValueDefinition(o, true);\r
- } catch (BindingConstructionException e) {\r
- return "<error "+e.getClass().getName()+" "+e.getMessage()+">";\r
- } catch (IOException e) {\r
- return "<error "+e.getClass().getName()+" "+e.getMessage()+">";\r
- } catch (BindingException e) {\r
- return "<error "+e.getClass().getName()+" "+e.getMessage()+">";\r
- }\r
- }\r
- \r
- class DataboardBindings implements BindingProvider {\r
- @Override\r
- public Binding provideBinding(ClassBindingFactory mainFactory, BindingRequest request) throws BindingConstructionException {\r
- // Variant Type\r
- if (request.getClazz().equals( Object.class )) return OBJECT;\r
- if (request.getClazz().equals( Void.class ) || request.getClazz().equals(void.class) ) return VoidBinding.VOID_BINDING;\r
- if (request.getClazz().equals( Variant.class) ) return Bindings.VARIANT;\r
- if (request.getClazz().equals( MutableVariant.class) ) return Bindings.MUTABLE_VARIANT;\r
- if (request.getClazz().equals( Bean.class ) ) return BEAN;\r
- \r
- return null;\r
- }\r
- } \r
- \r
- private void addDefaultBinding(Datatype datatype, Binding binding ) {\r
- defaultBindingRepository.put(datatype, binding);\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.databoard;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.adapter.Adapter;
+import org.simantics.databoard.adapter.AdapterConstructionException;
+import org.simantics.databoard.adapter.AdapterFactory;
+import org.simantics.databoard.adapter.RuntimeAdaptException;
+import org.simantics.databoard.adapter.RuntimeAdapterConstructionException;
+import org.simantics.databoard.annotations.ArgumentImpl;
+import org.simantics.databoard.annotations.Arguments;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.VariantBinding;
+import org.simantics.databoard.binding.classfactory.ImmutableClassesFactory;
+import org.simantics.databoard.binding.classfactory.TypeClassFactory;
+import org.simantics.databoard.binding.classfactory.TypeClassSubFactory;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
+import org.simantics.databoard.binding.factory.BindingRepository;
+import org.simantics.databoard.binding.factory.DefaultBindingFactory;
+import org.simantics.databoard.binding.factory.JavaUtilBindingsProvider;
+import org.simantics.databoard.binding.factory.MutableBindingFactory;
+import org.simantics.databoard.binding.factory.TroveBindingsProvider;
+import org.simantics.databoard.binding.factory.TypeBindingFactory;
+import org.simantics.databoard.binding.impl.BeanBinding;
+import org.simantics.databoard.binding.impl.ObjectVariantBinding;
+import org.simantics.databoard.binding.impl.StringVariantBinding;
+import org.simantics.databoard.binding.mutable.ImmutableVariantBinding;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.binding.reflection.BindingProvider;
+import org.simantics.databoard.binding.reflection.BindingRequest;
+import org.simantics.databoard.binding.reflection.ClassBindingFactory;
+import org.simantics.databoard.binding.reflection.VoidBinding;
+import org.simantics.databoard.serialization.DefaultConcurrentSerializerFactory;
+import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.serialization.SerializerConstructionException;
+import org.simantics.databoard.serialization.SerializerScheme;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.Bean;
+import org.simantics.databoard.util.DataValueUtil;
+
+public class Databoard {
+
+ // Repositories
+
+ /** Repository of mutable bindings */
+ public final Map<Datatype, Binding> mutableBindingRepository = Collections.synchronizedMap( new HashMap<Datatype, Binding>() );
+
+ /** Repository of default bindings */
+ public final Map<Datatype, Binding> defaultBindingRepository = Collections.synchronizedMap( new HashMap<Datatype, Binding>() );
+
+ /** Repository of class Bindings */
+ public final BindingRepository bindingRepository = new BindingRepository();
+
+ /** Repository of serializers */
+ public final Map<Binding, Serializer> serializerRepository = Collections.synchronizedMap( new HashMap<Binding, Serializer>() );
+
+
+ // Factories
+
+ /** Mutable Bindings Factory */
+ public final TypeBindingFactory mutableBindingFactory = new MutableBindingFactory( mutableBindingRepository );
+
+ /** Default Bindings Factory */
+ public final TypeBindingFactory defaultBindingFactory = new DefaultBindingFactory( defaultBindingRepository );
+
+ /** Reflection based Binding Factory, create binding to class */
+ public final ClassBindingFactory classBindingFactory = new ClassBindingFactory( bindingRepository, defaultBindingFactory );
+
+ /** Serializer Factory */
+ //public final SerializerFactory serializationFactory = new DefaultSerializerFactory( serializerRepository );
+ public final SerializerScheme serializationFactory = new DefaultConcurrentSerializerFactory();
+
+ /** Adapter Factory */
+ public final AdapterFactory adapterFactory = new AdapterFactory();
+
+ /** Class Factory */
+ public final TypeClassFactory typeClassFactory = new TypeClassFactory();
+
+
+ public final VariantBinding VARIANT; // Variant
+ public final VariantBinding OBJECT; // java.lang.Object ( as variant )
+ public final VariantBinding STR_VARIANT; // java.lang.String ( as variant )
+ public final Binding BEAN; // Bean ( as variant )
+
+ public Databoard() {
+ classBindingFactory.addFactory( new TroveBindingsProvider() );
+ classBindingFactory.addFactory( new JavaUtilBindingsProvider() );
+
+ addDefaultBinding( Datatypes.STRING, Bindings.STRING );
+ addDefaultBinding( Datatypes.INTEGER, Bindings.INTEGER );
+ addDefaultBinding( Datatypes.BOOLEAN, Bindings.BOOLEAN );
+ addDefaultBinding( Datatypes.BYTE, Bindings.BYTE );
+ addDefaultBinding( Datatypes.LONG, Bindings.LONG );
+ addDefaultBinding( Datatypes.DOUBLE, Bindings.DOUBLE );
+ addDefaultBinding( Datatypes.FLOAT, Bindings.FLOAT );
+ addDefaultBinding( Datatypes.VOID, Bindings.VOID );
+ addDefaultBinding( Datatypes.BOOLEAN_ARRAY, Bindings.BOOLEAN_ARRAY );
+ addDefaultBinding( Datatypes.BYTE_ARRAY, Bindings.BYTE_ARRAY );
+ addDefaultBinding( Datatypes.INTEGER_ARRAY, Bindings.INT_ARRAY );
+ addDefaultBinding( Datatypes.LONG_ARRAY, Bindings.LONG_ARRAY );
+ addDefaultBinding( Datatypes.FLOAT_ARRAY, Bindings.FLOAT_ARRAY );
+ addDefaultBinding( Datatypes.DOUBLE_ARRAY, Bindings.DOUBLE_ARRAY );
+ addDefaultBinding( Datatypes.STRING_ARRAY, Bindings.STRING_ARRAY );
+
+ VARIANT = new ImmutableVariantBinding( classBindingFactory, adapterFactory );
+ OBJECT = new ObjectVariantBinding( classBindingFactory, adapterFactory );
+ BEAN = new BeanBinding( classBindingFactory, typeClassFactory, adapterFactory );
+ STR_VARIANT = new StringVariantBinding( serializationFactory, VARIANT );
+
+ // Add sub-factory that creates binding for Bean
+ classBindingFactory.addFactory( new DataboardBindings() );
+
+ // Add class factory that constructs basic types
+ typeClassFactory.addFactory( new ImmutableClassesFactory() );
+
+ // Bindings.databoard cannot initialize itself
+ if (Bindings.databoard != null) {
+ initialize();
+ }
+ }
+
+ void initialize() {
+ // Add run-time class factory, if objectweb.asm-library is available.
+ try {
+ // Check ASM Exists
+ Class.forName("org.objectweb.asm.ClassWriter");
+ Class<?> y = Class.forName("org.simantics.databoard.binding.classfactory.AsmTypeClassFactory");
+ Constructor<?> c = y.getConstructor( TypeClassFactory.class );
+ TypeClassSubFactory f = (TypeClassSubFactory) c.newInstance(typeClassFactory);
+ typeClassFactory.addFactory( f );
+
+ BindingRequest br = new BindingRequest( Datatype.class );
+ Binding datatypeBinding = getBinding( br );
+ typeClassFactory.getRepository().put(datatypeBinding.type(), br);
+ bindingRepository.put(br, datatypeBinding);
+ } catch (ClassNotFoundException e) {
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (InvocationTargetException e) {
+ } catch (SecurityException e) {
+ } catch (NoSuchMethodException e) {
+ } catch (BindingConstructionException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void clear() {
+ mutableBindingRepository.clear();
+ defaultBindingRepository.clear();
+ bindingRepository.clear();
+ serializerRepository.clear();
+ }
+
+ /**
+ * Get or create a binding that is completely mutable java class.
+ *
+ * DataType | Class of the bound instance
+ * ===================|==================
+ * UnionType | GenericBinding.TaggedObject.class
+ * OptionType | ValueContainer.class
+ * RecordType | Object[].class
+ * BooleanType | MutableBoolean.class
+ * DoubleType | MutableDouble.class
+ * FloatType | MutableFloat.class
+ * ByteType | MutableByte.class
+ * IntegerType | MutableInt.class
+ * LongType | MutableLong.class
+ * StringType | ValueContainer.class
+ * ArrayType | ArrayList.class
+ * MapType | TreeMap.class
+ * VariantType | MutableVariant.class
+ *
+ * Note, requesting a binding with this convenience method stores the
+ * binding and the type with strong reference, thus preventing garbage
+ * collection. To allow garbage collection, please use another instance of
+ * GenericBindingFactory and binding repository (Map<Datatype, Binding>). <p>
+ *
+ * @param type the type to create binding to
+ * @return binding binding to a mutable class
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getMutableBinding(Datatype type) {
+ try {
+ Binding binding = mutableBindingRepository.get(type);
+ if (binding!=null) return (T) binding;
+ synchronized(mutableBindingRepository) {
+ return (T) mutableBindingFactory.getBinding(type);
+ }
+ } catch (BindingConstructionException e) {
+ // Unexpected - if error is thrown there is fault in GenericBindingScheme
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Get or create a binding based on default java classes, such as
+ * Integer.class, or byte[].class. The result is often binding for an
+ * immutable classs. These bindings are more efficient than mutable bindings (above).
+ *
+ * DataType | Class of the bound instance
+ * ===================|==================
+ * BooleanType | Boolean.class
+ * ByteType | Byte.class
+ * FloatType | Float.class
+ * DoubleType | Double.class
+ * IntegerType | Int.class
+ * LongType | Long.class
+ * StringType | String.class
+ * UnionType | TaggedObject.class
+ * OptionType | ValueContainer.class
+ * RecordType | Object[].class
+ * MapType | TreeMap.class
+ * VariantType | Variant.class
+ * ArrayType(Boolean) | boolean[].class
+ * ArrayType(Byte) | byte[].class
+ * ArrayType(Integer) | int[].class
+ * ArrayType(Long) | long[].class
+ * ArrayType(Float) | float[].class
+ * ArrayType(Double) | double[].class
+ * ArrayType(Byte) | byte[].class
+ * ArrayType( T ) | Object[].class
+ *
+ * Note, requesting a binding with this convenience method stores the
+ * binding and the type with strong reference, thus preventing garbage
+ * collection. To allow garbage collection, please use another instance of
+ * DefaultBindingFactory and binding repository (Map<Datatype, Binding>). <p>
+ *
+ * @param type the type to create binding to
+ * @return binding binding to a mutable class
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBinding(Datatype type) {
+ try {
+ Binding binding = defaultBindingRepository.get(type);
+ if (binding!=null) return (T) binding;
+ synchronized(defaultBindingRepository) {
+ return (T) defaultBindingFactory.getBinding(type);
+ }
+ } catch (BindingConstructionException e) {
+ // Unexpected - if error is thrown there is fault in DefaultBindingScheme
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Get a binding to a Java Class. Details can be added by placing annotations
+ * to the java classes. See more in package org.simantics.databoard.annotations.
+ * <p>
+ *
+ * Whether the result is a completely mutable or not depends on the
+ * requested class. For instance, fields such as Boolean, Integer, Long
+ * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
+ * The length of Object[] is not mutable, but length of List<Object> is. <p>
+ *
+ * Note, requesting a binding with this convenience method stores the
+ * binding and the class with strong reference, thus preventing garbage
+ * collection. To allow garbage collection, please use another instance of
+ * BindingFactory and binding repository (Map<BindingRequest, Binding>). <p>
+ *
+ * @see ClassBindingFactory
+ * @param clazz
+ * @return binding
+ * @throws BindingConstructionException
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBinding(Class<?> clazz)
+ throws BindingConstructionException
+ {
+ Binding binding = bindingRepository.get( clazz );
+ if (binding != null) {
+ return (T) binding;
+ }
+
+ BindingRequest request = new BindingRequest( clazz );
+ synchronized(classBindingFactory) {
+ binding = classBindingFactory.construct(request);
+ }
+ return (T) binding;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBinding(BindingRequest request)
+ throws BindingConstructionException
+ {
+ synchronized(classBindingFactory) {
+ return (T) classBindingFactory.construct(request);
+ }
+ }
+
+ /**
+ * Get a binding for a Java Class. Use this method to acquire class
+ * parameters for a generics class. <p>
+ *
+ * Example 1:
+ *
+ * Binding binding = getBinding(Map.class, String.class, Integer.class);
+ * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
+ *
+ * Example 2:
+ *
+ * Binding d = getBinding(List.class, Integer.class);
+ * List<Integer> list = (List<Integer>) d.createRandom(5);
+ *
+ * Example 3:
+ *
+ * Binding d = getBinding(List.class, List.class, Integer.class);
+ * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
+ *
+ * @see ClassBindingFactory
+ * @param clazz
+ * @return binding
+ * @throws BindingConstructionException
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
+ throws BindingConstructionException
+ {
+ BindingRequest request = new BindingRequest( clazz, parameters );
+ synchronized(classBindingFactory) {
+ return (T) classBindingFactory.construct(request);
+ }
+ }
+
+ /**
+ * Read binding and type from a class. DataType details and parameters
+ * are read as annotations placed in the class.
+ * (See org.simantics.databoard.annotations)
+ * <p>
+ * As an exception, in the subclasses of {@link Throwable}, the fields of
+ * Throwable are omited.
+ * <p>
+ * This method is used for well-known classes where the caller is 100% sure
+ * that a binding is construable without exception. <p>
+ *
+ * @param clazz
+ * @return binding
+ * @throws RuntimeBindingConstructionException
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBindingUnchecked(Class<?> clazz)
+ throws RuntimeBindingConstructionException
+ {
+ try {
+ return (T) getBinding(clazz);
+ } catch (BindingConstructionException e) {
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Get a binding for a Java Class. Use this method to acquire class
+ * parameters for a generics class. <p>
+ *
+ * Example 1:
+ *
+ * Binding binding = getBinding(Map.class, String.class, Integer.class);
+ * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
+ *
+ * Example 2:
+ *
+ * Binding d = getBinding(List.class, Integer.class);
+ * List<Integer> list = (List<Integer>) d.createRandom(5);
+ *
+ * Example 3:
+ *
+ * Binding d = getBinding(List.class, List.class, Integer.class);
+ * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
+ *
+ * @see ClassBindingFactory
+ * @param clazz
+ * @return binding
+ * @throws BindingConstructionException
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Binding> T getBindingUnchecked(Class<?> clazz, Class<?>...parameters)
+ throws RuntimeBindingConstructionException
+ {
+ try {
+ Arguments args = new ArgumentImpl(parameters);
+ BindingRequest request = new BindingRequest( clazz, args );
+ Binding binding = bindingRepository.get( request );
+ if (binding!=null);
+ synchronized(classBindingFactory) {
+ binding = classBindingFactory.construct(request);
+ }
+ return (T) binding;
+ } catch (BindingConstructionException e) {
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Add a simple binding to reflection binding factory.
+ *
+ * @param binding
+ * @param clazz
+ * @param parameters parameter classes
+ */
+ public void addBinding( Binding binding, Class<?> clazz, Class<?>...parameters )
+ {
+ ArgumentImpl args = new ArgumentImpl( parameters );
+ BindingRequest request = new BindingRequest( clazz, args );
+ bindingRepository.put( request, binding );
+ }
+
+ /**
+ * Add binding factory for compositive bindings
+ *
+ * @param factory
+ */
+ public void addBindingFactory( BindingProvider factory )
+ {
+ classBindingFactory.addFactory( factory );
+ }
+
+ /**
+ * Creates a bean class
+ * @param type
+ * @return class
+ */
+ public BindingRequest getBeanBindingRequest( Datatype type ) throws RuntimeBindingConstructionException {
+ try {
+ return typeClassFactory.getClass(type);
+ } catch (BindingConstructionException e) {
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Creates a bean class
+ * @param type
+ * @return class
+ */
+ public Class<?> getBeanClass( Datatype type ) throws BindingConstructionException {
+ BindingRequest br = typeClassFactory.getClass(type);
+ return br.getClazz();
+ }
+
+ /**
+ * Create binding from datatype that instantiates java classes.
+ * RecordTypes are Beans, UnionTypes are Classes with @Union annotation,
+ * ArrayTypes are [].
+ *
+ * @param type
+ * @return class
+ */
+ public Binding getBeanBinding( Datatype type ) throws RuntimeBindingConstructionException {
+ try {
+ BindingRequest br = typeClassFactory.getClass(type);
+ return getBinding( br );
+ } catch (BindingConstructionException e) {
+ throw new RuntimeBindingConstructionException(e);
+ }
+ }
+
+ /**
+ * Get serializer that follows Databoard serialization spec.
+ *
+ * @param binding
+ * @return serializer
+ * @throws SerializerConstructionException
+ */
+ public Serializer getSerializer(Binding binding) throws SerializerConstructionException {
+ return serializationFactory.getSerializer(binding);
+ }
+
+ /**
+ * Get serializer that follows Databoard serialization spec.
+ *
+ * @param binding
+ * @return serializer
+ * @throws RuntimeSerializerConstructionException
+ */
+ public Serializer getSerializerUnchecked(Binding binding) throws RuntimeSerializerConstructionException {
+ return serializationFactory.getSerializerUnchecked(binding);
+ }
+
+ /**
+ * Get serializer that follows Databoard serialization spec.
+ *
+ * @param clazz
+ * @return serializer
+ * @throws SerializerConstructionException
+ */
+ public Serializer getSerializer(Class<?> clazz) throws SerializerConstructionException {
+ try {
+ Binding binding = getBinding(clazz);
+ return serializationFactory.getSerializer(binding);
+ } catch (BindingConstructionException e) {
+ throw new SerializerConstructionException( e );
+ }
+ }
+
+ /**
+ * Get serializer that follows Databoard serialization spec.
+ *
+ * @param clazz
+ * @return serializer serializer
+ * @throws RuntimeSerializerConstructionException
+ */
+ public Serializer getSerializerUnchecked(Class<?> clazz) throws RuntimeSerializerConstructionException {
+ try {
+ Binding binding = getBinding(clazz);
+ return serializationFactory.getSerializerUnchecked(binding);
+ } catch (BindingConstructionException e) {
+ throw new RuntimeSerializerConstructionException( new SerializerConstructionException(e) );
+ }
+ }
+ /**
+ * Create an adapter that adapts two bindings of the same
+ * data type.
+ *
+ * @param domain binding of the source instance
+ * @param range binding of the result instance
+ * @return result adapter
+ * @throws AdapterConstructionException
+ */
+ public Adapter getAdapter(Binding domain, Binding range)
+ throws AdapterConstructionException
+ {
+ return adapterFactory.getAdapter(domain, range, false, false);
+ }
+
+ /**
+ * Create an adapter that adapts between two bindings of the same
+ * data type.
+ *
+ * @param domain binding of the source instance
+ * @param range binding of the result instance
+ * @return result adapter
+ * @throws AdapterConstructionException
+ */
+ public Adapter getAdapterUnchecked(Binding domain, Binding range)
+ throws RuntimeAdapterConstructionException
+ {
+ try {
+ return adapterFactory.getAdapter(domain, range, false, false);
+ } catch (AdapterConstructionException e) {
+ throw new RuntimeAdapterConstructionException(e);
+ }
+ }
+
+ /**
+ * Create a type adapter that adapts instances from one Datatype to
+ * another. Type Adapter does the following conversions:
+ *
+ * o Number Types, e.g. long -> double
+ * o Unit Types, e.g. mph -> km/h
+ * o Record Types, for each field of the range, there must be equal in domain
+ * o Union Types, for each tag type of the domain, there must be equal in range
+ *
+ * {@link AdaptException} is thrown at runtime, if number conversion is not
+ * posible, e.g. converting value 500 from Integer to Byte.
+ * Note, there is also a possibility of precision loss, in many conversions
+ * e.g. from double to int.
+ *
+ * @param domain binding of the source instance
+ * @param range binding of the result instance
+ * @return adapter
+ * @throws AdapterConstructionException
+ */
+ public Adapter getTypeAdapter(Binding domain, Binding range)
+ throws AdapterConstructionException
+ {
+ return adapterFactory.getAdapter(domain, range, true, false);
+ }
+
+ /**
+ * Create a type adapter that adapts instances from one DataType to
+ * another. Type Adapter does the following conversions:
+ *
+ * o Number Types, e.g. long -> double
+ * o Unit Types, e.g. mph -> km/h
+ * o Record Types, for each field of the range, there must be equal in domain
+ * o Union Types, for each tag type of the domain, there must be equal in range
+ *
+ * {@link AdaptException} is thrown at runtime, if number values are
+ * not compatible, e.g. converting value 500 from Long to Byte.
+ * Note, there is also a possibility of precision loss, e.g. when
+ * converting from double to int.
+ *
+ * @param domain binding of the source instance
+ * @param range binding of the result instance
+ * @return result adapter
+ * @throws AdapterConstructionException
+ */
+ public Adapter getTypeAdapterUnchecked(Binding domain, Binding range)
+ {
+ try {
+ return adapterFactory.getAdapter(domain, range, true, false);
+ } catch (AdapterConstructionException e) {
+ throw new RuntimeAdapterConstructionException(e);
+ }
+ }
+
+ /**
+ * Adapt a value of one type to another.
+ *
+ * @param value
+ * @param domain
+ * @param range
+ * @return adapted value
+ * @throws AdapterConstructionException
+ * @throws AdaptException
+ */
+ public Object adapt(Object value, Binding domain, Binding range)
+ throws AdaptException
+ {
+ try {
+ if (domain==range) {
+ return value;
+ }
+ return adapterFactory.getAdapter(domain, range, true, false).adapt(value);
+ } catch (AdapterConstructionException e) {
+ throw new AdaptException(e);
+ }
+ }
+
+ /**
+ * Adapt a value of one type to another. Exceptions are run-time. Use this
+ * if it safe to assume the conversion will be successful.
+ *
+ * @param value
+ * @param domain
+ * @param range
+ * @return adapted value
+ * @throws AdapterConstructionException
+ * @throws AdaptException
+ */
+ public Object adaptUnchecked(Object value, Binding domain, Binding range)
+ throws RuntimeAdapterConstructionException, RuntimeAdaptException
+ {
+ try {
+ if (domain==range) {
+ return value;
+ }
+ return adapterFactory.getAdapter(domain, range, true, false).adaptUnchecked(value);
+ } catch (RuntimeAdapterConstructionException e) {
+ throw new RuntimeAdaptException(new AdaptException(e.getCause()));
+ } catch (AdapterConstructionException e) {
+ throw new RuntimeAdaptException(new AdaptException(e));
+ }
+ }
+
+ /**
+ * Adapt and clone a value instance to another type. Immutable
+ * bindings may return the argument as is, others return a cloned copy.
+ *
+ * @param value
+ * @param domain
+ * @param range
+ * @return adapted value
+ * @throws AdapterConstructionException
+ * @throws AdaptException
+ */
+ public Object clone(Object value, Binding domain, Binding range)
+ throws AdaptException
+ {
+ try {
+ if (domain==range) {
+ if (domain.isImmutable()) {
+ return value;
+ } else {
+ return domain.clone(value);
+ }
+ }
+ return adapterFactory.getAdapter(domain, range, true, true).adapt(value);
+ } catch (AdapterConstructionException e) {
+ throw new AdaptException(e);
+ }
+ }
+
+
+ /**
+ * Clone a value of one binding to another. Bindings that handle immutable values
+ * may return the same instance, others will guarantee a complete copy.
+ *
+ * This method throws only runtime exceptions. Use this if it is safe to assume
+ * that the conversion will be successful.
+ *
+ * @param value
+ * @param domain
+ * @param range
+ * @return adapted value
+ * @throws AdapterConstructionException
+ * @throws AdaptException
+ */
+ public Object cloneUnchecked(Object value, Binding domain, Binding range)
+ throws RuntimeAdapterConstructionException, RuntimeAdaptException
+ {
+ try {
+ return adapterFactory.getAdapter(domain, range, true, true).adapt(value);
+ } catch (AdaptException e) {
+ throw new RuntimeAdaptException(e);
+ } catch (RuntimeAdapterConstructionException e) {
+ throw new RuntimeAdaptException(new AdaptException(e.getCause()));
+ } catch (AdapterConstructionException e) {
+ throw new RuntimeAdaptException(new AdaptException(e));
+ }
+ }
+
+ /**
+ * Compares two data values for order. Returns a negative integer,
+ * zero, or a positive integer if, the first argument precedes/lesser than
+ * the second, is equal to, or successor/greater than the second.<p>
+ *
+ * DataTypes of b1 and b2 are not equal, then data types are compared. <p>
+ *
+ * The comparison function is defined at
+ * http://dev.simantics.org/index.php/Org.simantics.datatype_Manual#CompareTo_and_Equals <p>
+ *
+ * Note, comparing 2 different number types will not result a value comparison.
+ * Instead values have the following type precedence ByteType, IntegerType, LongType,
+ * FloatType, and the highest DoubleType. <p>
+ *
+ * @param b1 Binding of o1
+ * @param o1 the first object to be compared.
+ * @param b2 Binding of o2
+ * @param o2 the second object to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is less than, equal to, or greater than the
+ * second.
+ * @throws BindingException if object cannot be handled by a binding
+ */
+ public int compare(Binding b1, Object o1, Binding b2, Object o2)
+ throws BindingException
+ {
+ return DataValueUtil.compare(b1, o1, b2, o2);
+ }
+
+ /**
+ * Compare two data values for equality. <p>
+ *
+ * Note, comparing 2 different number types will not result a value comparison.
+ * Instead values have the following type precedence ByteType, IntegerType, LongType,
+ * FloatType, and the highest DoubleType. <p>
+ *
+ * @param b1
+ * @param o1
+ * @param b2
+ * @param o2
+ * @return true if equal
+ * @throws BindingException
+ */
+ public boolean equals(Binding b1, Object o1, Binding b2, Object o2)
+ throws BindingException
+ {
+ return DataValueUtil.equals(b1, o1, b2, o2);
+ }
+
+ public Comparator<Object> createComparator(final Binding b1, final Binding b2)
+ {
+ return DataValueUtil.createComparator(b1, b2);
+ }
+
+ /**
+ * Print the content of an object as a structure.
+ * Utility function for debug purposes.
+ *
+ * @param o
+ * @return content
+ */
+ public String toString(Object o) {
+ try {
+ Binding b = getBinding( o.getClass() );
+ return b.printValueDefinition(o, true);
+ } catch (BindingConstructionException e) {
+ return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
+ } catch (IOException e) {
+ return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
+ } catch (BindingException e) {
+ return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
+ }
+ }
+
+ class DataboardBindings implements BindingProvider {
+ @Override
+ public Binding provideBinding(ClassBindingFactory mainFactory, BindingRequest request) throws BindingConstructionException {
+ // Variant Type
+ if (request.getClazz().equals( Object.class )) return OBJECT;
+ if (request.getClazz().equals( Void.class ) || request.getClazz().equals(void.class) ) return VoidBinding.VOID_BINDING;
+ if (request.getClazz().equals( Variant.class) ) return Bindings.VARIANT;
+ if (request.getClazz().equals( MutableVariant.class) ) return Bindings.MUTABLE_VARIANT;
+ if (request.getClazz().equals( Bean.class ) ) return BEAN;
+
+ return null;
+ }
+ }
+
+ private void addDefaultBinding(Datatype datatype, Binding binding ) {
+ defaultBindingRepository.put(datatype, binding);
+ }
+
+}