-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 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.util;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.ObjectInputStream;\r
-import java.io.ObjectOutputStream;\r
-import java.io.OutputStream;\r
-import java.util.Random;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Files;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.adapter.RuntimeAdaptException;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.OptionalBinding;\r
-import org.simantics.databoard.binding.RecordBinding;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.error.RuntimeBindingException;\r
-import org.simantics.databoard.binding.util.RandomValue;\r
-import org.simantics.databoard.parser.repository.DataTypeSyntaxError;\r
-import org.simantics.databoard.parser.repository.DataValueRepository;\r
-import org.simantics.databoard.serialization.Serializer;\r
-import org.simantics.databoard.serialization.SerializerConstructionException;\r
-import org.simantics.databoard.type.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-\r
-/**\r
- * Record classes enable databoard features by sub-classing Bean.\r
- * \r
- * Instructions, the fields must be public, or have public get/setters.\r
- * Sub-class gains the following services: \r
- * \r
- * toString #toString()\r
- * string #print() / #parse()\r
- * Hash-Equals #hashCode() / #equals()\r
- * Comparable #compareTo()\r
- * Serialization #serialize()/#deserialize(), #readObject()/#writeObject(), #readFile()/#writeFile() \r
- * Cloning #clone() / #readFrom()\r
- * Initialization #init() / #setToDefault() / #setToRandom()\r
- * \r
- * The class must be compatible with databoard's type system. \r
- * \r
- * See BeanExample for example.\r
- * \r
- * The identify of this class is composed from all the fields. The identity\r
- * affects to the behavior how hash and equals are counted.\r
- * \r
- * If only some fields compose the hash-equals-compareTo identity, use {@link Bean.Id} instead. \r
- * In this case the identifying fields have @Identity annotation. \r
- * \r
- * Example:\r
- * \r
- * public class MyClass extends Bean.Id {\r
- * public @Identify String id;\r
- * ...\r
- * }\r
- * \r
- * @author toni.kalajainen\r
- */\r
-public class Bean implements Cloneable, /*Serializable, */Comparable<Bean> {\r
- \r
- transient protected RecordBinding binding;\r
- \r
- protected Bean() {\r
- this.binding = Bindings.getBindingUnchecked( getClass() );\r
- }\r
- \r
- protected Bean(Binding binding) {\r
- this.binding = (RecordBinding) binding;\r
- }\r
- \r
- /**\r
- * Return datatype binding to this class.\r
- * \r
- * @return record binding\r
- */\r
- public RecordBinding getBinding() {\r
- return binding;\r
- }\r
- \r
- /**\r
- * Read all field values from another object. All fields are deep-cloned, \r
- * except immutable values which are referenced.\r
- * \r
- * @param other\r
- */\r
- public void readFrom(Bean other) { \r
- binding.readFromUnchecked(other.binding, other, this);\r
- }\r
- \r
- public void readAvailableFields(Bean other) {\r
- if ( other.binding instanceof RecordBinding == false ) return;\r
- Component components[] = binding.type().getComponents(); \r
- for (int i=0; i<components.length; i++) {\r
- Component c = components[i];\r
- int ix = other.binding.getComponentIndex(c.name);\r
- if ( ix<0 ) continue;\r
- try {\r
- Object value = other.binding.getComponent(other, ix);\r
- Object value2 = Bindings.adapt(value, other.binding.getComponentBinding(ix), binding.getComponentBinding(i));\r
- binding.setComponent(this, i, value2);\r
- } catch (AdaptException e) {\r
- throw new RuntimeException(e);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- } \r
- }\r
- }\r
- \r
- /**\r
- * Set default value to any null field that is not optional. \r
- */\r
- public void init() {\r
- try {\r
- // Set value to uninitialized fields\r
- for (int i=0; i<binding.getComponentCount(); i++) {\r
- Object v = binding.getComponent(this, i);\r
- Binding cb = binding.componentBindings[i];\r
- if (v==null && cb instanceof OptionalBinding==false) {\r
- v = cb.createDefault();\r
- binding.setComponent(this, i, v);\r
- } \r
- }\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Sets all fields to default values. \r
- * Strings are set to "", arrays cleared or set to minimum count,\r
- * numbers are set to 0.\r
- */\r
- public void setToDefault() {\r
- for (int i=0; i<binding.componentBindings.length; i++)\r
- {\r
- Binding cb = binding.componentBindings[i]; \r
- try {\r
- binding.setComponent(this, i, cb.createDefault());\r
- } catch (BindingException e) {\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * Sets all fields with random values\r
- */\r
- public void setToRandom(Random random) {\r
- RandomValue rv = new RandomValue( random );\r
- for (int i=0; i<binding.componentBindings.length; i++)\r
- {\r
- Binding cb = binding.componentBindings[i]; \r
- try {\r
- binding.setComponent(this, i, cb.createRandom( rv ));\r
- } catch (BindingException e) {\r
- }\r
- }\r
- }\r
- \r
- @Override\r
- public int hashCode() {\r
- try {\r
- return binding.hashValue( this );\r
- } catch (BindingException e) {\r
- return -1;\r
- }\r
- }\r
- \r
- @Override\r
- public Bean clone() {\r
- try {\r
- return (Bean) binding.clone(this);\r
- } catch (AdaptException e) {\r
- throw new RuntimeAdaptException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Compare to another bean of same datatype. (Can be different binding)\r
- */\r
- @Override\r
- public boolean equals(Object obj) {\r
- if (obj==null) return false;\r
- if (obj==this) return true;\r
- if ( obj instanceof Bean == false ) return false;\r
- Bean other = (Bean) obj;\r
- if (other.binding==binding) {\r
- return binding.equals(this, obj);\r
- } else {\r
- try {\r
- return Bindings.equals(binding, this, other.binding, other);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- } \r
- }\r
- }\r
- \r
- public boolean equalContents(Object obj) {\r
- if (obj==null) return false;\r
- if (obj==this) return true;\r
- Bean other = (Bean) obj;\r
- if (other.binding==binding) {\r
- return binding.equals(this, obj);\r
- } else {\r
- try {\r
- return Bindings.equals(binding, this, other.binding, other);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- } \r
- }\r
- } \r
-\r
- @Override\r
- public int compareTo(Bean o) {\r
- if (o==null) return -1;\r
- return binding.compare(this, o);\r
- }\r
-\r
- /**\r
- * Print the object as string\r
- * @return toString()\r
- */\r
- @Override\r
- public String toString() {\r
- try {\r
- return binding.toString(this);\r
- } catch (BindingException e) {\r
- return e.getMessage();\r
- }\r
- }\r
- \r
- /**\r
- * Print the value in string format \r
- * \r
- * @param out\r
- * @throws IOException\r
- */\r
- public void print(Appendable out) throws IOException {\r
- try {\r
- DataValueRepository rep = new DataValueRepository();\r
- binding.printValue(this, out, rep, false);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Print the value to Databoard string format\r
- * \r
- * @return Bean in Databoard string format\r
- * @throws IOException\r
- */\r
- public String print() throws IOException {\r
- try {\r
- StringBuilder sb = new StringBuilder();\r
- DataValueRepository rep = new DataValueRepository();\r
- binding.printValue(this, sb, rep, false);\r
- return sb.toString();\r
- \r
- /* StringBuilder sb = new StringBuilder();\r
- DataValueRepository rep = new DataValueRepository();\r
- rep.setTypeRepository( Datatypes.datatypeRepository );\r
- DataValuePrinter vp = new DataValuePrinter(sb, rep);\r
- vp.setFormat( PrintFormat.MULTI_LINE );\r
- DataTypePrinter tp = new DataTypePrinter( sb );\r
- tp.setLinedeed( true );\r
- rep.put("value", binding, this);\r
-\r
- for (String name : rep.getValueNames()) {\r
- MutableVariant value = rep.get(name);\r
- Datatype type = value.type();\r
- tp.print(type);\r
- vp.print(value); \r
- } \r
- \r
- for (String name : rep.getValueNames()) {\r
- MutableVariant value = rep.get(name);\r
- Datatype type = value.type();\r
- sb.append( name+" : " );\r
- tp.print(type);\r
- sb.append( " = " );\r
- vp.print(value); \r
- sb.append("\n");\r
- }\r
- System.out.println(sb);\r
- return sb.toString();\r
-*/\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Print the value to Databoard string format\r
- * \r
- * @return string representation in a line\r
- * @throws IOException\r
- */\r
- public String printLine() throws IOException {\r
- try {\r
- StringBuilder sb = new StringBuilder();\r
- DataValueRepository rep = new DataValueRepository();\r
- binding.printValue(this, sb, rep, true);\r
- return sb.toString();\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Read the contents from Databoard String\r
- * @param str\r
- * @throws DataTypeSyntaxError\r
- */\r
- public void parse( String str ) throws DataTypeSyntaxError {\r
- try {\r
- DataValueRepository rep = new DataValueRepository();\r
- Object v = binding.parseValue( str, rep );\r
- init();\r
- binding.readFrom(binding, v, this); \r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException( e );\r
- }\r
- }\r
- \r
- public void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {\r
- try {\r
- Serializer s = Bindings.getSerializer(binding);\r
- s.deserialize((InputStream)in, this);\r
- } catch (SerializerConstructionException e) {\r
- throw new IOException(e);\r
- }\r
- }\r
-\r
- public void writeObject(ObjectOutputStream out) throws IOException {\r
- try {\r
- Serializer s = Bindings.getSerializer(binding);\r
- s.serialize((OutputStream) out, this);\r
- } catch (SerializerConstructionException e) {\r
- throw new IOException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Serialize the object to a byte array\r
- * \r
- * @return bean as serialized\r
- * @throws IOException\r
- */\r
- public byte[] serialize() throws IOException {\r
- try {\r
- Serializer s = Bindings.getSerializer(binding);\r
- return s.serialize( this );\r
- } catch (SerializerConstructionException e) {\r
- throw new IOException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Deserialize the object from a byte array\r
- * \r
- * @param data\r
- * @throws IOException\r
- */\r
- public void deserialize( byte[] data ) throws IOException {\r
- try {\r
- Serializer s = Bindings.getSerializer(binding);\r
- init(); \r
- s.deserialize(data, this);\r
- } catch (SerializerConstructionException e) {\r
- throw new IOException(e);\r
- } \r
- }\r
- \r
- public void readFile( File file ) throws IOException {\r
- init();\r
- Files.readFile(file, binding, this);\r
- }\r
- \r
- public void writeFile( File file ) throws IOException {\r
- Files.writeFile(file, binding, this);\r
- }\r
-\r
- public void assertIsValid() throws BindingException {\r
- binding.assertInstaceIsValid(this);\r
- }\r
- \r
- public void setField(String fieldName, Binding fieldBinding, Object field) throws BindingException {\r
- try {\r
- int index = binding.getComponentIndex(fieldName);\r
- if ( index<0 ) throw new BindingException("There is no field "+fieldName);\r
- Binding localFieldBinding = binding.getComponentBinding(index);\r
- if ( localFieldBinding instanceof OptionalBinding ) {\r
- OptionalBinding ob = (OptionalBinding) localFieldBinding;\r
- if ( field == null ) {\r
- binding.setComponent(this, index, ob.createNoValue());\r
- } else {\r
- Object newValue = Bindings.adapt(field, fieldBinding, ob.componentBinding);\r
- binding.setComponent(this, index, ob.createValue(newValue));\r
- }\r
- } else {\r
- Object newValue = Bindings.adapt(field, fieldBinding, localFieldBinding);\r
- binding.setComponent(this, index, newValue);\r
- }\r
- } catch (AdaptException e) {\r
- if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();\r
- throw new BindingException(e);\r
- }\r
- }\r
-\r
- public void setField(int fieldIndex, Binding fieldBinding, Object field) throws BindingException {\r
- try {\r
- if ( fieldIndex<0 ) throw new BindingException("There is no field #"+fieldIndex);\r
- Binding localFieldBinding = binding.getComponentBinding(fieldIndex);\r
- if ( localFieldBinding instanceof OptionalBinding ) {\r
- OptionalBinding ob = (OptionalBinding) localFieldBinding;\r
- if ( field == null ) {\r
- binding.setComponent(this, fieldIndex, ob.createNoValue());\r
- } else {\r
- Object newValue = Bindings.adapt(field, fieldBinding, ob.componentBinding);\r
- binding.setComponent(this, fieldIndex, ob.createValue(newValue));\r
- }\r
- } else {\r
- Object newValue = Bindings.adapt(field, fieldBinding, localFieldBinding);\r
- binding.setComponent(this, fieldIndex, newValue);\r
- }\r
- } catch (AdaptException e) {\r
- if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();\r
- throw new BindingException(e);\r
- }\r
- }\r
- \r
- public boolean hasField(String fieldName) throws BindingException {\r
- return binding.getComponentIndex(fieldName)>=0;\r
- }\r
- \r
- /**\r
- * Get binding of a field\r
- * \r
- * @param fieldName\r
- * @return binding or null of field does not exist\r
- * @throws BindingException\r
- */\r
- public Binding getFieldBinding(String fieldName) throws BindingException {\r
- int index = binding.getComponentIndex(fieldName);\r
- if ( index<0 ) return null;\r
- Binding r = binding.getComponentBinding(index);\r
- if ( r!=null && r instanceof OptionalBinding ) {\r
- r = ((OptionalBinding)r).componentBinding;\r
- }\r
- return r;\r
- }\r
- \r
- /**\r
- * Get value of a field\r
- * @param fieldName\r
- * @return value or null if field does not exist\r
- * @throws BindingException\r
- */\r
- public Object getField(String fieldName) throws BindingException {\r
- int index = binding.type().getComponentIndex2(fieldName);\r
- if (index<0) return null;\r
- return binding.getComponent(this, index);\r
- }\r
- \r
- /**\r
- * Get value of a field\r
- * @param fieldName\r
- * @return value or null if field does not exist\r
- * @throws BindingException\r
- */\r
- public Object getField(int fieldIndex) throws BindingException {\r
- return binding.getComponent(this, fieldIndex);\r
- }\r
- \r
- /**\r
- * Get value of a field\r
- * @param fieldName\r
- * @param binding requested binding\r
- * @return value or null if field does not exist\r
- * @throws BindingException\r
- */\r
- public Object getField(String fieldName, Binding binding) throws BindingException {\r
- int index = this.binding.type().getComponentIndex2(fieldName);\r
- if (index<0) return null; \r
- Object obj = this.binding.getComponent(this, index);\r
- if ( obj == null ) return null;\r
- Binding fieldBinding = this.binding.getComponentBinding(index);\r
- if ( fieldBinding instanceof OptionalBinding ) {\r
- fieldBinding = ((OptionalBinding)fieldBinding).componentBinding;\r
- }\r
- try {\r
- return Bindings.adapt(obj, fieldBinding, binding);\r
- } catch (AdaptException e) {\r
- if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();\r
- throw new BindingException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Get value of a field\r
- * @param fieldName\r
- * @return value or null if field does not exist\r
- * @throws RuntimeBindingException\r
- */\r
- public Object getFieldUnchecked(String fieldName) throws RuntimeBindingException {\r
- int index = binding.type().getComponentIndex2(fieldName);\r
- if (index<0) return null;\r
- try {\r
- return binding.getComponent(this, index);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Get value of a field\r
- * @param fieldName\r
- * @return value or null if field does not exist\r
- * @throws RuntimeBindingException\r
- */\r
- public Object getFieldUnchecked(int fieldIndex) throws RuntimeBindingException {\r
- try {\r
- return binding.getComponent(this, fieldIndex);\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Get identifier binding. Use @Identifier annotation to indicate which \r
- * fields compose the identifier of the record.\r
- * \r
- * @return idenfitier binding. \r
- * @throws BindingException there is no identifier\r
- */\r
- public Binding getIdentifierBinding() throws BindingException {\r
- Datatype idType = binding.type().getIdentifierType();\r
- if (idType == null) throw new BindingException("There is are no @Identifier fields in the bean");\r
- return Bindings.getBinding( idType );\r
- }\r
- \r
- /**\r
- * Get identifier of the object. Use @Identifier annotation to indicate which\r
- * fields compose the identifier of the record.\r
- * \r
- * @return identifier\r
- * @throws BindingException\r
- */\r
- public Object getIdentifier() throws BindingException \r
- {\r
- int ids[] = binding.type().getIdentifiers();\r
- if (ids.length == 0) throw new BindingException("There is are no @Identifier fields in the bean");\r
- if (ids.length == 1) return binding.getComponent(this, ids[0]);\r
- RecordBinding rb = (RecordBinding) getIdentifierBinding();\r
- Object result = rb.createPartial();\r
- int ix = 0;\r
- for (int i : ids) {\r
- rb.setComponent(result, ix++, binding.getComponent(this, i));\r
- }\r
- return result;\r
- }\r
- \r
- /**\r
- * In this version of the bean, the hash/equals compares to identifiers.\r
- * Identifier is a field with @Idenfitier annotation. \r
- */\r
- public static class Id extends Bean {\r
- protected Id() {}\r
- protected Id(Binding binding) {\r
- super(binding);\r
- }\r
-\r
- @Override\r
- public int hashCode() {\r
- int hash = 0;\r
- try {\r
- for (int index : binding.type().getIdentifiers())\r
- {\r
- Object c = binding.getComponent(this, index);\r
- Binding cb = binding.getComponentBinding(index);\r
- hash = 13*hash + cb.hashValue(c);\r
- }\r
- } catch (BindingException e) {\r
- }\r
- return hash;\r
- }\r
- \r
- /**\r
- * Compare to another bean of same datatype for equal identifier. (Can be different binding)\r
- */\r
- @Override\r
- public boolean equals(Object obj) {\r
- if (obj==null) return false;\r
- if (obj==this) return true;\r
- if ( obj instanceof Bean == false ) return false;\r
- Bean other = (Bean) obj;\r
- try {\r
- if (other.binding==binding) {\r
- \r
- for (int index : binding.type().getIdentifiers())\r
- {\r
- Object tc = binding.getComponent(this, index);\r
- Object oc = binding.getComponent(other, index);\r
- Binding cb = binding.getComponentBinding(index);\r
- if ( !cb.equals(tc, oc) ) return false;\r
- }\r
- \r
- } else {\r
-\r
- for (int index : binding.type().getIdentifiers())\r
- {\r
- Object tc = binding.getComponent(this, index);\r
- Object oc = binding.getComponent(other, index);\r
- Binding tcb = binding.getComponentBinding(index);\r
- Binding ocb = other.binding.getComponentBinding(index);\r
- if ( !Bindings.equals(tcb, tc, ocb, oc) ) return false;\r
- }\r
- }\r
- return true;\r
- } catch (BindingException e) {\r
- throw new RuntimeBindingException(e);\r
- } \r
- }\r
- \r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.Random;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Files;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.adapter.RuntimeAdaptException;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.OptionalBinding;
+import org.simantics.databoard.binding.RecordBinding;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingException;
+import org.simantics.databoard.binding.util.RandomValue;
+import org.simantics.databoard.parser.repository.DataTypeSyntaxError;
+import org.simantics.databoard.parser.repository.DataValueRepository;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.serialization.SerializerConstructionException;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+
+/**
+ * Record classes enable databoard features by sub-classing Bean.
+ *
+ * Instructions, the fields must be public, or have public get/setters.
+ * Sub-class gains the following services:
+ *
+ * toString #toString()
+ * string #print() / #parse()
+ * Hash-Equals #hashCode() / #equals()
+ * Comparable #compareTo()
+ * Serialization #serialize()/#deserialize(), #readObject()/#writeObject(), #readFile()/#writeFile()
+ * Cloning #clone() / #readFrom()
+ * Initialization #init() / #setToDefault() / #setToRandom()
+ *
+ * The class must be compatible with databoard's type system.
+ *
+ * See BeanExample for example.
+ *
+ * The identify of this class is composed from all the fields. The identity
+ * affects to the behavior how hash and equals are counted.
+ *
+ * If only some fields compose the hash-equals-compareTo identity, use {@link Bean.Id} instead.
+ * In this case the identifying fields have @Identity annotation.
+ *
+ * Example:
+ *
+ * public class MyClass extends Bean.Id {
+ * public @Identify String id;
+ * ...
+ * }
+ *
+ * @author toni.kalajainen
+ */
+public class Bean implements Cloneable, /*Serializable, */Comparable<Bean> {
+
+ transient protected RecordBinding binding;
+
+ protected Bean() {
+ this.binding = Bindings.getBindingUnchecked( getClass() );
+ }
+
+ protected Bean(Binding binding) {
+ this.binding = (RecordBinding) binding;
+ }
+
+ /**
+ * Return datatype binding to this class.
+ *
+ * @return record binding
+ */
+ public RecordBinding getBinding() {
+ return binding;
+ }
+
+ /**
+ * Read all field values from another object. All fields are deep-cloned,
+ * except immutable values which are referenced.
+ *
+ * @param other
+ */
+ public void readFrom(Bean other) {
+ binding.readFromUnchecked(other.binding, other, this);
+ }
+
+ public void readAvailableFields(Bean other) {
+ if ( other.binding instanceof RecordBinding == false ) return;
+ Component components[] = binding.type().getComponents();
+ for (int i=0; i<components.length; i++) {
+ Component c = components[i];
+ int ix = other.binding.getComponentIndex(c.name);
+ if ( ix<0 ) continue;
+ try {
+ Object value = other.binding.getComponent(other, ix);
+ Object value2 = Bindings.adapt(value, other.binding.getComponentBinding(ix), binding.getComponentBinding(i));
+ binding.setComponent(this, i, value2);
+ } catch (AdaptException e) {
+ throw new RuntimeException(e);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+ }
+
+ /**
+ * Set default value to any null field that is not optional.
+ */
+ public void init() {
+ try {
+ // Set value to uninitialized fields
+ for (int i=0; i<binding.getComponentCount(); i++) {
+ Object v = binding.getComponent(this, i);
+ Binding cb = binding.componentBindings[i];
+ if (v==null && cb instanceof OptionalBinding==false) {
+ v = cb.createDefault();
+ binding.setComponent(this, i, v);
+ }
+ }
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Sets all fields to default values.
+ * Strings are set to "", arrays cleared or set to minimum count,
+ * numbers are set to 0.
+ */
+ public void setToDefault() {
+ for (int i=0; i<binding.componentBindings.length; i++)
+ {
+ Binding cb = binding.componentBindings[i];
+ try {
+ binding.setComponent(this, i, cb.createDefault());
+ } catch (BindingException e) {
+ }
+ }
+ }
+
+ /**
+ * Sets all fields with random values
+ */
+ public void setToRandom(Random random) {
+ RandomValue rv = new RandomValue( random );
+ for (int i=0; i<binding.componentBindings.length; i++)
+ {
+ Binding cb = binding.componentBindings[i];
+ try {
+ binding.setComponent(this, i, cb.createRandom( rv ));
+ } catch (BindingException e) {
+ }
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ try {
+ return binding.hashValue( this );
+ } catch (BindingException e) {
+ return -1;
+ }
+ }
+
+ @Override
+ public Bean clone() {
+ try {
+ return (Bean) binding.clone(this);
+ } catch (AdaptException e) {
+ throw new RuntimeAdaptException(e);
+ }
+ }
+
+ /**
+ * Compare to another bean of same datatype. (Can be different binding)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj==null) return false;
+ if (obj==this) return true;
+ if ( obj instanceof Bean == false ) return false;
+ Bean other = (Bean) obj;
+ if (other.binding==binding) {
+ return binding.equals(this, obj);
+ } else {
+ try {
+ return Bindings.equals(binding, this, other.binding, other);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+ }
+
+ public boolean equalContents(Object obj) {
+ if (obj==null) return false;
+ if (obj==this) return true;
+ Bean other = (Bean) obj;
+ if (other.binding==binding) {
+ return binding.equals(this, obj);
+ } else {
+ try {
+ return Bindings.equals(binding, this, other.binding, other);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+ }
+
+ @Override
+ public int compareTo(Bean o) {
+ if (o==null) return -1;
+ return binding.compare(this, o);
+ }
+
+ /**
+ * Print the object as string
+ * @return toString()
+ */
+ @Override
+ public String toString() {
+ try {
+ return binding.toString(this);
+ } catch (BindingException e) {
+ return e.getMessage();
+ }
+ }
+
+ /**
+ * Print the value in string format
+ *
+ * @param out
+ * @throws IOException
+ */
+ public void print(Appendable out) throws IOException {
+ try {
+ DataValueRepository rep = new DataValueRepository();
+ binding.printValue(this, out, rep, false);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Print the value to Databoard string format
+ *
+ * @return Bean in Databoard string format
+ * @throws IOException
+ */
+ public String print() throws IOException {
+ try {
+ StringBuilder sb = new StringBuilder();
+ DataValueRepository rep = new DataValueRepository();
+ binding.printValue(this, sb, rep, false);
+ return sb.toString();
+
+ /* StringBuilder sb = new StringBuilder();
+ DataValueRepository rep = new DataValueRepository();
+ rep.setTypeRepository( Datatypes.datatypeRepository );
+ DataValuePrinter vp = new DataValuePrinter(sb, rep);
+ vp.setFormat( PrintFormat.MULTI_LINE );
+ DataTypePrinter tp = new DataTypePrinter( sb );
+ tp.setLinedeed( true );
+ rep.put("value", binding, this);
+
+ for (String name : rep.getValueNames()) {
+ MutableVariant value = rep.get(name);
+ Datatype type = value.type();
+ tp.print(type);
+ vp.print(value);
+ }
+
+ for (String name : rep.getValueNames()) {
+ MutableVariant value = rep.get(name);
+ Datatype type = value.type();
+ sb.append( name+" : " );
+ tp.print(type);
+ sb.append( " = " );
+ vp.print(value);
+ sb.append("\n");
+ }
+ System.out.println(sb);
+ return sb.toString();
+*/
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Print the value to Databoard string format
+ *
+ * @return string representation in a line
+ * @throws IOException
+ */
+ public String printLine() throws IOException {
+ try {
+ StringBuilder sb = new StringBuilder();
+ DataValueRepository rep = new DataValueRepository();
+ binding.printValue(this, sb, rep, true);
+ return sb.toString();
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Read the contents from Databoard String
+ * @param str
+ * @throws DataTypeSyntaxError
+ */
+ public void parse( String str ) throws DataTypeSyntaxError {
+ try {
+ DataValueRepository rep = new DataValueRepository();
+ Object v = binding.parseValue( str, rep );
+ init();
+ binding.readFrom(binding, v, this);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException( e );
+ }
+ }
+
+ public void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
+ try {
+ Serializer s = Bindings.getSerializer(binding);
+ s.deserialize((InputStream)in, this);
+ } catch (SerializerConstructionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public void writeObject(ObjectOutputStream out) throws IOException {
+ try {
+ Serializer s = Bindings.getSerializer(binding);
+ s.serialize((OutputStream) out, this);
+ } catch (SerializerConstructionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Serialize the object to a byte array
+ *
+ * @return bean as serialized
+ * @throws IOException
+ */
+ public byte[] serialize() throws IOException {
+ try {
+ Serializer s = Bindings.getSerializer(binding);
+ return s.serialize( this );
+ } catch (SerializerConstructionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Deserialize the object from a byte array
+ *
+ * @param data
+ * @throws IOException
+ */
+ public void deserialize( byte[] data ) throws IOException {
+ try {
+ Serializer s = Bindings.getSerializer(binding);
+ init();
+ s.deserialize(data, this);
+ } catch (SerializerConstructionException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public void readFile( File file ) throws IOException {
+ init();
+ Files.readFile(file, binding, this);
+ }
+
+ public void writeFile( File file ) throws IOException {
+ Files.writeFile(file, binding, this);
+ }
+
+ public void assertIsValid() throws BindingException {
+ binding.assertInstaceIsValid(this);
+ }
+
+ public void setField(String fieldName, Binding fieldBinding, Object field) throws BindingException {
+ try {
+ int index = binding.getComponentIndex(fieldName);
+ if ( index<0 ) throw new BindingException("There is no field "+fieldName);
+ Binding localFieldBinding = binding.getComponentBinding(index);
+ if ( localFieldBinding instanceof OptionalBinding ) {
+ OptionalBinding ob = (OptionalBinding) localFieldBinding;
+ if ( field == null ) {
+ binding.setComponent(this, index, ob.createNoValue());
+ } else {
+ Object newValue = Bindings.adapt(field, fieldBinding, ob.componentBinding);
+ binding.setComponent(this, index, ob.createValue(newValue));
+ }
+ } else {
+ Object newValue = Bindings.adapt(field, fieldBinding, localFieldBinding);
+ binding.setComponent(this, index, newValue);
+ }
+ } catch (AdaptException e) {
+ if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();
+ throw new BindingException(e);
+ }
+ }
+
+ public void setField(int fieldIndex, Binding fieldBinding, Object field) throws BindingException {
+ try {
+ if ( fieldIndex<0 ) throw new BindingException("There is no field #"+fieldIndex);
+ Binding localFieldBinding = binding.getComponentBinding(fieldIndex);
+ if ( localFieldBinding instanceof OptionalBinding ) {
+ OptionalBinding ob = (OptionalBinding) localFieldBinding;
+ if ( field == null ) {
+ binding.setComponent(this, fieldIndex, ob.createNoValue());
+ } else {
+ Object newValue = Bindings.adapt(field, fieldBinding, ob.componentBinding);
+ binding.setComponent(this, fieldIndex, ob.createValue(newValue));
+ }
+ } else {
+ Object newValue = Bindings.adapt(field, fieldBinding, localFieldBinding);
+ binding.setComponent(this, fieldIndex, newValue);
+ }
+ } catch (AdaptException e) {
+ if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();
+ throw new BindingException(e);
+ }
+ }
+
+ public boolean hasField(String fieldName) throws BindingException {
+ return binding.getComponentIndex(fieldName)>=0;
+ }
+
+ /**
+ * Get binding of a field
+ *
+ * @param fieldName
+ * @return binding or null of field does not exist
+ * @throws BindingException
+ */
+ public Binding getFieldBinding(String fieldName) throws BindingException {
+ int index = binding.getComponentIndex(fieldName);
+ if ( index<0 ) return null;
+ Binding r = binding.getComponentBinding(index);
+ if ( r!=null && r instanceof OptionalBinding ) {
+ r = ((OptionalBinding)r).componentBinding;
+ }
+ return r;
+ }
+
+ /**
+ * Get value of a field
+ * @param fieldName
+ * @return value or null if field does not exist
+ * @throws BindingException
+ */
+ public Object getField(String fieldName) throws BindingException {
+ int index = binding.type().getComponentIndex2(fieldName);
+ if (index<0) return null;
+ return binding.getComponent(this, index);
+ }
+
+ /**
+ * Get value of a field
+ * @param fieldName
+ * @return value or null if field does not exist
+ * @throws BindingException
+ */
+ public Object getField(int fieldIndex) throws BindingException {
+ return binding.getComponent(this, fieldIndex);
+ }
+
+ /**
+ * Get value of a field
+ * @param fieldName
+ * @param binding requested binding
+ * @return value or null if field does not exist
+ * @throws BindingException
+ */
+ public Object getField(String fieldName, Binding binding) throws BindingException {
+ int index = this.binding.type().getComponentIndex2(fieldName);
+ if (index<0) return null;
+ Object obj = this.binding.getComponent(this, index);
+ if ( obj == null ) return null;
+ Binding fieldBinding = this.binding.getComponentBinding(index);
+ if ( fieldBinding instanceof OptionalBinding ) {
+ fieldBinding = ((OptionalBinding)fieldBinding).componentBinding;
+ }
+ try {
+ return Bindings.adapt(obj, fieldBinding, binding);
+ } catch (AdaptException e) {
+ if ( e.getCause() !=null && e.getCause() instanceof BindingException ) throw (BindingException) e.getCause();
+ throw new BindingException(e);
+ }
+ }
+
+ /**
+ * Get value of a field
+ * @param fieldName
+ * @return value or null if field does not exist
+ * @throws RuntimeBindingException
+ */
+ public Object getFieldUnchecked(String fieldName) throws RuntimeBindingException {
+ int index = binding.type().getComponentIndex2(fieldName);
+ if (index<0) return null;
+ try {
+ return binding.getComponent(this, index);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Get value of a field
+ * @param fieldName
+ * @return value or null if field does not exist
+ * @throws RuntimeBindingException
+ */
+ public Object getFieldUnchecked(int fieldIndex) throws RuntimeBindingException {
+ try {
+ return binding.getComponent(this, fieldIndex);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Get identifier binding. Use @Identifier annotation to indicate which
+ * fields compose the identifier of the record.
+ *
+ * @return idenfitier binding.
+ * @throws BindingException there is no identifier
+ */
+ public Binding getIdentifierBinding() throws BindingException {
+ Datatype idType = binding.type().getIdentifierType();
+ if (idType == null) throw new BindingException("There is are no @Identifier fields in the bean");
+ return Bindings.getBinding( idType );
+ }
+
+ /**
+ * Get identifier of the object. Use @Identifier annotation to indicate which
+ * fields compose the identifier of the record.
+ *
+ * @return identifier
+ * @throws BindingException
+ */
+ public Object getIdentifier() throws BindingException
+ {
+ int ids[] = binding.type().getIdentifiers();
+ if (ids.length == 0) throw new BindingException("There is are no @Identifier fields in the bean");
+ if (ids.length == 1) return binding.getComponent(this, ids[0]);
+ RecordBinding rb = (RecordBinding) getIdentifierBinding();
+ Object result = rb.createPartial();
+ int ix = 0;
+ for (int i : ids) {
+ rb.setComponent(result, ix++, binding.getComponent(this, i));
+ }
+ return result;
+ }
+
+ /**
+ * In this version of the bean, the hash/equals compares to identifiers.
+ * Identifier is a field with @Idenfitier annotation.
+ */
+ public static class Id extends Bean {
+ protected Id() {}
+ protected Id(Binding binding) {
+ super(binding);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ try {
+ for (int index : binding.type().getIdentifiers())
+ {
+ Object c = binding.getComponent(this, index);
+ Binding cb = binding.getComponentBinding(index);
+ hash = 13*hash + cb.hashValue(c);
+ }
+ } catch (BindingException e) {
+ }
+ return hash;
+ }
+
+ /**
+ * Compare to another bean of same datatype for equal identifier. (Can be different binding)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj==null) return false;
+ if (obj==this) return true;
+ if ( obj instanceof Bean == false ) return false;
+ Bean other = (Bean) obj;
+ try {
+ if (other.binding==binding) {
+
+ for (int index : binding.type().getIdentifiers())
+ {
+ Object tc = binding.getComponent(this, index);
+ Object oc = binding.getComponent(other, index);
+ Binding cb = binding.getComponentBinding(index);
+ if ( !cb.equals(tc, oc) ) return false;
+ }
+
+ } else {
+
+ for (int index : binding.type().getIdentifiers())
+ {
+ Object tc = binding.getComponent(this, index);
+ Object oc = binding.getComponent(other, index);
+ Binding tcb = binding.getComponentBinding(index);
+ Binding ocb = other.binding.getComponentBinding(index);
+ if ( !Bindings.equals(tcb, tc, ocb, oc) ) return false;
+ }
+ }
+ return true;
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ }
+
+}