/******************************************************************************* * Copyright (c) 2010 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.accessor.binary; import gnu.trove.map.hash.TObjectIntHashMap; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.VariantAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.event.Event; import org.simantics.databoard.accessor.event.ValueAssigned; import org.simantics.databoard.accessor.file.FileVariantAccessor; import org.simantics.databoard.accessor.impl.AccessorParams; import org.simantics.databoard.accessor.impl.ListenerEntry; import org.simantics.databoard.accessor.interestset.InterestSet; import org.simantics.databoard.accessor.interestset.VariantInterestSet; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.ComponentReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.error.BindingConstructionException; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.serialization.RuntimeSerializerConstructionException; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.SerializerConstructionException; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.VariantType; import org.simantics.databoard.util.binary.Blob; public class BinaryVariant extends BinaryObject implements VariantAccessor, FileVariantAccessor { static Datatype type_type = Datatypes.getDatatypeUnchecked( Datatype.class ); static Binding type_binding = Bindings.getBindingUnchecked( Datatype.class ); static Serializer type_serializer = Bindings.getSerializerUnchecked( type_binding ); SoftReference child; public BinaryVariant(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) { super(parent, blob, type, params); } public VariantType type() { return (VariantType) type; } @SuppressWarnings("unchecked") @Override public T getContentAccessor() throws AccessorConstructionException { assert b.isOpen(); readLock(); try { BinaryObject sa = getExistingAccessor(); if (sa==null) { // Create new child b.position(0L); List ids = new ArrayList(0); Datatype type = (Datatype) type_serializer.deserialize(b, ids); sa = createSubAccessor(type, b.position(), b.length()-b.position(), params); child = new SoftReference(sa); // Attach listeners of interest set to the new accessor (of the value) ListenerEntry le = listeners; while (le!=null) { VariantInterestSet is = le.getInterestSet(); InterestSet cis = is.getComponentInterest(); if (cis != null) { try { ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() ); sa.addListener(le.listener, cis, childPath, le.executor); } catch (AccessorException e) { throw new AccessorConstructionException(e); } } if (is.inCompleteComponent()) { cis = InterestSet.newInterestSet(getContentType(), true, true, true); try { ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() ); sa.addListener(le.listener, cis, childPath, le.executor); } catch (AccessorException e) { throw new AccessorConstructionException(e); } } le = le.next; } } return (T) sa; } catch (IOException e) { throw new AccessorConstructionException(e); } catch (AccessorException e) { throw new AccessorConstructionException(e); } finally { readUnlock(); } } protected BinaryObject getExistingAccessor() { SoftReference r = child; if (r==null) return null; BinaryObject sa = r.get(); return sa; } @Override public void setContentValueNoflush(Binding valueBinding, Object value) throws AccessorException { assert b.isOpen(); writeLock(); try { // reuse sub-accessor, if type matches, and sub-accessor exists BinaryObject sa = getExistingAccessor(); if (sa!=null && valueBinding.type().equals(sa.type())) { sa.setValue(valueBinding, value); return; } { // Replace the old value with a new value if (sa!=null) { sa.invalidatedNotification(); child = null; sa = null; } // Write Binding cb = valueBinding; Serializer cs = params.serializerScheme.getSerializer( cb ); Datatype ct = valueBinding.type(); Object cv = value; TObjectIntHashMap ids = new TObjectIntHashMap(0); int len = type_serializer.getSize(ct, ids); len += cs.getSize(cv, null); b.setLength(len); b.position(0L); ids.clear(); type_serializer.serialize(b, ids, ct); cs.serialize(b, null, cv); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { VariantInterestSet is = le.getInterestSet(); if (is.inNotifications()) { MutableVariant v = null; if (is.inValues()) v = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value)); ValueAssigned e = new ValueAssigned( Bindings.MUTABLE_VARIANT, v ); emitEvent(le, e); } // Attach component listener // boolean hadSa = getExistingAccessor()!=null; // if (!hadSa) { // InterestSet cis = is.getComponentInterest(); // if (cis!=null) { // sa = getValueAccessor(); // } // } le = le.next; } } } catch (IOException e) { throw new AccessorException( e ); } catch (AdaptException e) { throw new AccessorException( e ); } catch (SerializerConstructionException e) { throw new AccessorException( e ); } finally { writeUnlock(); } } @Override public void setContentValue(Binding valueBinding, Object value) throws AccessorException { assert b.isOpen(); writeLock(); try { setContentValueNoflush(valueBinding, value); flush(); } finally { writeUnlock(); } } @Override public Datatype getContentType() throws AccessorException { assert b.isOpen(); readLock(); try { b.position(0L); List ids = new ArrayList(0); Datatype type = (Datatype) type_serializer.deserialize(b, ids); return type; } catch (IOException e) { throw new AccessorException( e ); } finally { readUnlock(); } } @Override public Object getContentValue(Binding contentBinding) throws AccessorException { assert b.isOpen(); readLock(); try { b.position(0L); List ids = new ArrayList(); Datatype type = (Datatype) type_serializer.deserialize(b, ids); ids.clear(); if (!contentBinding.type().equals(type)) { throw new AccessorException("Arg error, wrong type"); } Serializer value_serializer = params.serializerScheme.getSerializer( contentBinding ); return value_serializer.deserialize(b, ids); // The requested binding is a super type binding // Read the to the actual type and adapt // Binding lcb = params.bindingFactory.getBinding(type); // Binding rcb = contentBinding; // Object lv = lcb.serializer().deserialize(b, ids); // // return adapt(lv, lcb, rcb); } catch (IOException e) { throw new AccessorException( e ); } catch (SerializerConstructionException e) { throw new AccessorException( e ); } finally { readUnlock(); } } @SuppressWarnings("unchecked") @Override public T getComponent(ChildReference reference) throws AccessorConstructionException { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; if (lr.label.equals("v")) { Accessor sa = getContentAccessor(); if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } } if (reference instanceof ComponentReference) { Accessor sa = getContentAccessor(); if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } throw new AccessorConstructionException("Variant value reference expected, got "+reference.getClass().getName()); } @Override public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException { super.addListener(listener, interestSet, path, executor); VariantInterestSet is = (VariantInterestSet) interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null && !is.inCompleteComponent()) return; BinaryObject sa = getExistingAccessor(); if (sa==null) return; if (cis!=null) { ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() ); sa.addListener(listener, cis, childPath, executor); } if (is.inCompleteComponent()) { ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() ); cis = InterestSet.newInterestSet(getContentType(), true, true, true); sa.addListener(listener, cis, childPath, executor); } } @Override public void removeListener(Listener listener) throws AccessorException { ListenerEntry e = detachListener(listener); if (e==null) return; VariantInterestSet is = (VariantInterestSet) e.interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null && !is.inCompleteComponent()) return; BinaryObject sa = getExistingAccessor(); if (sa==null) return; if (cis!=null) { sa.removeListener(listener); } // Remove another? if (is.inCompleteComponent()) { sa.removeListener(listener); } } @Override Event applyLocal(Event e, boolean makeRollback) throws AccessorException { try { if (e instanceof ValueAssigned ) { Event rollback = null; ValueAssigned va = (ValueAssigned) e; if (va.newValue==null) throw new AccessorException("Cannot apply variant assignment event, the value is missing"); if (makeRollback) { b.position(0L); List ids = new ArrayList(); Datatype type = (Datatype) type_serializer.deserialize(b, ids); Binding cb = params.bindingScheme.getBinding(type); Serializer value_serializer = params.serializerScheme.getSerializer( cb ); Object lv = value_serializer.deserialize(b, ids); rollback = new ValueAssigned( Bindings.MUTABLE_VARIANT, new MutableVariant( cb, lv )); } setValueNoflush(va.newValue.getBinding(), va.newValue.getValue()); // setContentValueNoflush(va.newValue.getBinding(), va.newValue.getValue()); return rollback; } else { throw new AccessorException("Invalid event "+e.getClass().getName()); } } catch (IOException ioe) { throw new AccessorException(ioe); } catch (RuntimeSerializerConstructionException rsce) { throw new AccessorException(rsce); } catch (SerializerConstructionException e2) { throw new AccessorException(e2); } catch (BindingConstructionException e2) { throw new AccessorException(e2); } } @Override public void setValueNoflush(Binding variantBinding, Object variantValue) throws AccessorException { assert b.isOpen(); writeLock(); try { VariantBinding vb = (VariantBinding) variantBinding; Object vv = variantValue; Binding cb = vb.getContentBinding(vv); Object cv = vb.getContent(vv, cb); setContentValueNoflush(cb, cv); } catch (BindingException e) { throw new AccessorException( e ); } finally { writeUnlock(); } } }