/******************************************************************************* * 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 java.io.IOException; import java.lang.ref.SoftReference; import java.util.concurrent.Executor; import org.simantics.databoard.Bindings; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.OptionalAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.error.ReferenceException; import org.simantics.databoard.accessor.event.Event; import org.simantics.databoard.accessor.event.OptionalValueAssigned; import org.simantics.databoard.accessor.event.OptionalValueRemoved; import org.simantics.databoard.accessor.event.ValueAssigned; import org.simantics.databoard.accessor.file.FileOptionalAccessor; 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.OptionalInterestSet; 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.OptionalBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.SerializerConstructionException; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.util.binary.Blob; public class BinaryOptional extends BinaryObject implements OptionalAccessor, FileOptionalAccessor { /** Accessor to component */ SoftReference component; public BinaryOptional(BinaryObject parent, Blob blob, OptionalType type, AccessorParams params) throws AccessorConstructionException { super(parent, blob, type, params); } @Override public OptionalType type() { return (OptionalType) type; } @Override public void setValueNoflush(Binding binding, Object newValue) throws AccessorException { assert b.isOpen(); writeLock(); try { OptionalBinding ob = (OptionalBinding) binding; Object ov = newValue; boolean hasValue = ob.hasValue(newValue); if (hasValue) { Binding cb = ob.getComponentBinding(); Object cv = ob.getValue(ov); setComponentValueNoflush(cb, cv); } else { setNoValueNoflush(); } } catch (BindingException e) { throw new AccessorException(e); } finally { writeUnlock(); } } public void setComponentValueNoflush(Binding cb, Object cv) throws AccessorException { assert b.isOpen(); writeLock(); try { BinaryObject sa = getExistingAccessor(); if (sa!=null) { sa.setValue(cb, cv); return; } // Write Serializer cs = params.serializerScheme.getSerializer( cb ); int size = cs.getSize(cv, null)+1; b.setLength(size); b.position(0L); b.write((byte)1); cs.serialize(b, null, cv); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { OptionalInterestSet is = le.getInterestSet(); if (is.inNotifications()) { MutableVariant newComponentValue = null; if (is.inValues()) newComponentValue = new MutableVariant(cb, cb.isImmutable() ? cv : cb.clone(cv)); OptionalValueAssigned e = new OptionalValueAssigned(newComponentValue); emitEvent(le, e); } // Attach component interest // if (is.getComponentInterest()!=null && getComponentAccessor()!=null) { // sa = getComponentAccessor(); // } 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(); } } public void setNoValueNoflush() throws AccessorException { assert b.isOpen(); writeLock(); try { // Write b.position(0L); b.setLength(1); b.write((byte)0); // Disconnect sub-accessor BinaryObject sa = getExistingAccessor(); component = null; // Notify about disconnection of sub-accessor if (sa!=null) sa.invalidatedNotification(); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { OptionalInterestSet is = le.getInterestSet(); if (is.inNotifications()) { OptionalValueRemoved e = new OptionalValueRemoved(); emitEvent(le, e); } le = le.next; } } catch (IOException e) { throw new AccessorException(e); } finally { writeUnlock(); } } @Override public void setNoValue() throws AccessorException { assert b.isOpen(); writeLock(); try { setNoValueNoflush(); b.flush(); } catch (IOException e) { throw new AccessorException( e ); } finally { writeUnlock(); } } @Override public void setComponentValue(Binding cb, Object cv) throws AccessorException { assert b.isOpen(); writeLock(); try { setComponentValueNoflush(cb, cv); b.flush(); } catch (IOException e) { throw new AccessorException( e ); } finally { writeUnlock(); } } @SuppressWarnings("unchecked") @Override public T getComponentAccessor() throws AccessorConstructionException { assert b.isOpen(); readLock(); try { // Get existing or create new BinaryObject sa = getExistingAccessor(); if (sa!=null) return (T) sa; // Instantiate new accessor Datatype ct = type().getComponentType(); sa = createSubAccessor(ct, 1, b.length()-1, params); component = new SoftReference(sa); // Add listener to component, if it is in our interest set ListenerEntry le = listeners; while (le!=null) { OptionalInterestSet 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); } } // Attach component interest if (is.getComponentInterest()!=null) { sa = getComponentAccessor(); sa.addListener(le.listener, is.getComponentInterest(), null, le.executor); } le = le.next; } return (T) sa; } catch (IOException e) { throw new AccessorConstructionException( e ); } catch (AccessorException e) { throw new AccessorConstructionException( e ); } finally { readUnlock(); } } /** * Get existing sub accessor * * @return sub-accessor or null */ BinaryObject getExistingAccessor() { // Get existing or create new SoftReference ref = component; BinaryObject accessor = (ref!=null)?(BinaryObject)ref.get():null; return accessor; } @Override public Object getComponentValue(Binding cb) throws AccessorException { assert b.isOpen(); readLock(); try { b.position(1L); Serializer cs = params.serializerScheme.getSerializer( cb ); return cs.deserialize(b, null); } catch (IOException e) { throw new AccessorException( e ); } catch (SerializerConstructionException e) { throw new AccessorException( e ); } finally { readUnlock(); } } @Override public boolean hasValue() throws AccessorException { assert b.isOpen(); readLock(); try { b.position(0L); byte v = b.readByte(); if (v==0) return false; if (v==1) return true; throw new AccessorException("Unexpected value "+v+", expected 0/1"); } catch (IOException 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("o")) { Accessor result = getComponentAccessor(); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } } if (reference instanceof ComponentReference) { Accessor result = getComponentAccessor(); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType"); } @Override public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException { super.addListener(listener, interestSet, path, executor); OptionalInterestSet is = (OptionalInterestSet) interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null) return; BinaryObject sa = getExistingAccessor(); if (sa==null) return; ChildReference childPath = ChildReference.concatenate( path, new ComponentReference() ); sa.addListener(listener, cis, childPath, executor); } @Override public void removeListener(Listener listener) throws AccessorException { ListenerEntry e = detachListener(listener); if (e==null) return; OptionalInterestSet is = (OptionalInterestSet) e.interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null) return; BinaryObject sa = getExistingAccessor(); if (sa==null) return; sa.removeListener(listener); } @Override Event applyLocal(Event e, boolean makeRollback) throws AccessorException { Event rollback = null; if (makeRollback) { if (hasValue()) { OptionalBinding cb = (OptionalBinding) Bindings .getMutableBinding(type().getComponentType()); Object cv = getComponentValue(cb); rollback = new OptionalValueAssigned(cb, cv); } else { rollback = new OptionalValueRemoved(); } } if (e instanceof ValueAssigned) { ValueAssigned va = (ValueAssigned) e; setValueNoflush(va.newValue.getBinding(), va.newValue.getValue()); return rollback; } else if (e instanceof OptionalValueAssigned) { OptionalValueAssigned oa = (OptionalValueAssigned) e; if (oa.newValue == null) throw new AccessorException("Cannot apply field assignment event, the value is missing"); setComponentValueNoflush(oa.newValue.getBinding(), oa.newValue.getValue()); } else if (e instanceof OptionalValueRemoved) { setNoValueNoflush(); } else { throw new AccessorException("Unexpected event type "+ e.getClass().getName() + " for an Optional Type"); } return rollback; } }