1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.accessor.binary;
14 import gnu.trove.map.hash.TObjectIntHashMap;
16 import java.io.IOException;
17 import java.lang.ref.SoftReference;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Executor;
22 import org.simantics.databoard.accessor.Accessor;
23 import org.simantics.databoard.accessor.UnionAccessor;
24 import org.simantics.databoard.accessor.error.AccessorConstructionException;
25 import org.simantics.databoard.accessor.error.AccessorException;
26 import org.simantics.databoard.accessor.error.ReferenceException;
27 import org.simantics.databoard.accessor.event.Event;
28 import org.simantics.databoard.accessor.event.UnionValueAssigned;
29 import org.simantics.databoard.accessor.event.ValueAssigned;
30 import org.simantics.databoard.accessor.file.FileUnionAccessor;
31 import org.simantics.databoard.accessor.impl.AccessorParams;
32 import org.simantics.databoard.accessor.impl.ListenerEntry;
33 import org.simantics.databoard.accessor.interestset.InterestSet;
34 import org.simantics.databoard.accessor.interestset.UnionInterestSet;
35 import org.simantics.databoard.accessor.reference.ChildReference;
36 import org.simantics.databoard.accessor.reference.ComponentReference;
37 import org.simantics.databoard.accessor.reference.IndexReference;
38 import org.simantics.databoard.accessor.reference.LabelReference;
39 import org.simantics.databoard.accessor.reference.NameReference;
40 import org.simantics.databoard.adapter.AdaptException;
41 import org.simantics.databoard.binding.Binding;
42 import org.simantics.databoard.binding.UnionBinding;
43 import org.simantics.databoard.binding.error.BindingConstructionException;
44 import org.simantics.databoard.binding.error.BindingException;
45 import org.simantics.databoard.binding.mutable.MutableVariant;
46 import org.simantics.databoard.serialization.Serializer;
47 import org.simantics.databoard.serialization.SerializerConstructionException;
48 import org.simantics.databoard.type.Datatype;
49 import org.simantics.databoard.type.UnionType;
50 import org.simantics.databoard.util.binary.Blob;
51 import org.simantics.databoard.util.binary.Endian;
53 public class BinaryUnion extends BinaryObject implements UnionAccessor, FileUnionAccessor {
55 /** Accessor to childr */
56 SoftReference<BinaryObject> component;
58 public BinaryUnion(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
59 super(parent, blob, type, params);
62 public UnionType type() {
63 return (UnionType) type;
68 return type().getComponentCount();
72 public void setValueNoflush(Binding binding, Object newValue)
73 throws AccessorException {
77 UnionBinding ub = (UnionBinding) binding;
78 int tag = ub.getTag(newValue);
79 Binding cb = ub.getComponentBinding(tag);
80 Object cv = ub.getValue(newValue);
81 setComponentValueNoflush(tag, cb, cv);
82 } catch (BindingException e) {
83 throw new AccessorException(e);
89 @SuppressWarnings("unchecked")
91 public <T extends Accessor> T getComponentAccessor()
92 throws AccessorConstructionException {
96 // Get existing or create new
97 BinaryObject sa = getExistingAccessor();
102 int tag = Endian.getUInt(b, count()-1);
103 int tagLen = (int) b.position();
104 Datatype ct = type().getComponent(tag).type;
106 // Instantiate sub accessor.
107 sa = createSubAccessor(ct, tagLen, b.length()-tagLen, params);
108 component = new SoftReference<BinaryObject>(sa);
110 // Add listener to component, if it is in our interest set
111 ListenerEntry le = listeners;
113 UnionInterestSet is = le.getInterestSet();
114 InterestSet cis = is.getComponentInterest(tag);
117 ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() );
118 sa.addListener(le.listener, cis, childPath, le.executor);
119 } catch (AccessorException e) {
120 throw new AccessorConstructionException(e);
129 } catch (IOException e) {
130 throw new AccessorConstructionException( e );
137 * Get existing sub-accessor
139 * @return sub-accessor or <code>null</code>
141 BinaryObject getExistingAccessor() {
142 SoftReference<BinaryObject> c = component;
143 if (c==null) return null;
148 public Object getComponentValue(Binding componentBinding)
149 throws AccessorException {
154 int tag = Endian.getUInt(b, count()-1);
155 Datatype ct = type().getComponent(tag).type;
156 if ( !ct.equals(componentBinding.type()) ) {
157 throw new AccessorException("Binding of "+ct+" expected.");
160 List<Object> ids = new ArrayList<Object>(0);
161 Serializer s = params.serializerScheme.getSerializer( componentBinding );
162 return s.deserialize(b, ids);
163 } catch (IOException e) {
164 throw new AccessorException(e);
165 } catch (SerializerConstructionException e) {
166 throw new AccessorException(e);
173 public int getTag() throws AccessorException {
178 return Endian.getUInt(b, count()-1);
179 } catch (IOException e) {
180 throw new AccessorException(e);
187 public void setComponentValue(int tag, Binding componentBinding,
188 Object componentValue) throws AccessorException {
192 setComponentValueNoflush(tag, componentBinding, componentValue);
199 @SuppressWarnings("unchecked")
201 public <T extends Accessor> T getComponent(ChildReference reference)
202 throws AccessorConstructionException {
206 if (reference==null) return (T) this;
208 if (reference instanceof LabelReference) {
209 LabelReference lr = (LabelReference) reference;
210 Integer tag = type().getComponentIndex( lr.label );
212 if (tag==null && lr.label.equals("uv")) {
213 Accessor result = getComponentAccessor();
214 if (reference.getChildReference() != null)
215 result = result.getComponent(reference.getChildReference());
217 } else if (tag==null) {
218 throw new ReferenceException("Tag \""+lr.label+"\" not found");
221 if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");
222 Accessor result = getComponentAccessor();
223 if (reference.getChildReference() != null)
224 result = result.getComponent(reference.getChildReference());
228 if (reference instanceof ComponentReference) {
229 Accessor result = getComponentAccessor();
230 if (reference.getChildReference() != null)
231 result = result.getComponent(reference.getChildReference());
235 if (reference instanceof IndexReference) {
236 IndexReference ir = (IndexReference) reference;
237 if (ir.index<0 || ir.index>=type().getComponentCount()) throw new ReferenceException("Tag index out of bounds");
238 if (ir.index != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(ir.index).name+")");
239 Accessor result = getComponentAccessor();
240 if (reference.getChildReference() != null)
241 result = result.getComponent(reference.getChildReference());
245 if (reference instanceof NameReference) {
246 NameReference nr = (NameReference) reference;
247 Integer tag = type().getComponentIndex( nr.name );
248 if (tag==null) throw new ReferenceException("Tag \""+nr.name+"\" not found");
249 if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");
250 Accessor result = getComponentAccessor();
251 if (reference.getChildReference() != null)
252 result = result.getComponent(reference.getChildReference());
256 throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType");
257 } catch (AccessorException ae) {
258 throw new AccessorConstructionException(ae);
265 public void setComponentValueNoflush(int tag, Binding cb,
266 Object cv) throws AccessorException {
270 int oldTag = getTag();
272 boolean hadSameTag = oldTag == newTag;
274 // Write to component
275 BinaryObject sa = getExistingAccessor();
277 // Tag type changes, invalidate old accessor
278 if (sa!=null && !hadSameTag) {
280 sa.invalidatedNotification();
283 // Tag type remains the same
284 if (sa!=null && hadSameTag) {
290 UnionType ut = type();
291 Datatype ct = ut.getComponent(tag).type;
292 if (!ct.equals(cb.type())) {
293 throw new AccessorException("Binding of "+ct+" expected.");
295 TObjectIntHashMap<Object> ids = new TObjectIntHashMap<Object>(0);
296 int len = Endian.getUIntLength(count()-1);
298 Serializer s = params.serializerScheme.getSerializer( cb );
299 len += s.getSize(cv, ids);
304 Endian.putUInt(b, tag, count()-1);
305 s.serialize(b, ids, cv);
308 ListenerEntry le = listeners;
310 UnionInterestSet is = le.getInterestSet();
311 if (is.inNotificationsOf(tag)) {
312 MutableVariant newComponentValue = null;
313 if (is.inValuesOf(tag)) newComponentValue = new MutableVariant(cb, cb.isImmutable() ? cv : cb.clone(cv));
314 UnionValueAssigned e = new UnionValueAssigned(newTag, newComponentValue);
318 // Attach component listener
319 // InterestSet cis = is.getComponentInterest(newTag);
320 // if (cis!=null && getExistingAccessor()==null) {
321 // sa = getComponentAccessor();
327 } catch (IOException e) {
328 throw new AccessorException(e);
329 } catch (AdaptException e) {
330 throw new AccessorException(e);
331 } catch (SerializerConstructionException e) {
332 throw new AccessorException(e);
340 public void addListener(Listener listener, InterestSet interestSet,
341 ChildReference path, Executor executor) throws AccessorException {
342 super.addListener(listener, interestSet, path, executor);
343 UnionInterestSet is = (UnionInterestSet) interestSet;
344 if (is.componentInterests!=null) {
346 InterestSet cis = is.componentInterests[tag];
347 if (cis==null) return;
348 BinaryObject sa = getExistingAccessor();
349 if (sa==null) return;
350 ChildReference childPath = ChildReference.concatenate(path, new IndexReference(tag) );
351 sa.addListener(listener, cis, childPath, executor);
356 public void removeListener(Listener listener) throws AccessorException {
357 ListenerEntry e = detachListener(listener);
359 UnionInterestSet is = (UnionInterestSet) e.interestSet;
361 if (is.componentInterests!=null) {
362 for (int i=0; i<is.componentInterests.length; i++) {
363 InterestSet cis = is.componentInterests[i];
364 if (cis==null) continue;
365 BinaryObject sa = getExistingAccessor();
366 if (sa==null) return;
367 sa.removeListener(listener);
373 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
374 Event rollback = null;
377 UnionType ut = type();
379 Datatype ct = ut.getComponent(tag).type;
380 Binding cb = params.bindingScheme.getBinding(ct);
381 Object cv = getComponentValue(cb);
382 MutableVariant v = new MutableVariant(cb, cv);
383 rollback = new UnionValueAssigned(tag, v);
384 } catch (BindingConstructionException e2) {
385 throw new AccessorException( e2 );
389 if (e instanceof ValueAssigned) {
390 ValueAssigned va = (ValueAssigned) e;
391 setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
394 if (e instanceof UnionValueAssigned) {
395 UnionValueAssigned ua = (UnionValueAssigned) e;
396 if (ua.tag<0 || ua.tag>=type().getComponentCount()) throw new AccessorException("Tag index ("+ua.tag+") out of bounds.");
397 if (!ua.newValue.type().equals( type().getComponent(ua.tag).type ) )
398 throw new AccessorException("Cannot assign "+ua.newValue.type()+" to "+type().getComponent(ua.tag).type);
400 setComponentValueNoflush(ua.tag, ua.newValue.getBinding(), ua.newValue.getValue());
403 throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type");