/******************************************************************************* * 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 { 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=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); } } } }