]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryVariant.java
Fixing several binding-related bugs
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryVariant.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
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
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.accessor.binary;
13
14 import gnu.trove.map.hash.TObjectIntHashMap;
15
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;
21
22 import org.simantics.databoard.Bindings;
23 import org.simantics.databoard.Datatypes;
24 import org.simantics.databoard.accessor.Accessor;
25 import org.simantics.databoard.accessor.VariantAccessor;
26 import org.simantics.databoard.accessor.error.AccessorConstructionException;
27 import org.simantics.databoard.accessor.error.AccessorException;
28 import org.simantics.databoard.accessor.event.Event;
29 import org.simantics.databoard.accessor.event.ValueAssigned;
30 import org.simantics.databoard.accessor.file.FileVariantAccessor;
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.VariantInterestSet;
35 import org.simantics.databoard.accessor.reference.ChildReference;
36 import org.simantics.databoard.accessor.reference.ComponentReference;
37 import org.simantics.databoard.accessor.reference.LabelReference;
38 import org.simantics.databoard.adapter.AdaptException;
39 import org.simantics.databoard.binding.Binding;
40 import org.simantics.databoard.binding.VariantBinding;
41 import org.simantics.databoard.binding.error.BindingConstructionException;
42 import org.simantics.databoard.binding.error.BindingException;
43 import org.simantics.databoard.binding.mutable.MutableVariant;
44 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
45 import org.simantics.databoard.serialization.Serializer;
46 import org.simantics.databoard.serialization.SerializerConstructionException;
47 import org.simantics.databoard.type.Datatype;
48 import org.simantics.databoard.type.VariantType;
49 import org.simantics.databoard.util.binary.Blob;
50
51 public class BinaryVariant extends BinaryObject implements VariantAccessor, FileVariantAccessor {
52
53         static Datatype type_type = Datatypes.getDatatypeUnchecked( Datatype.class ); 
54         static Binding type_binding = Bindings.getBindingUnchecked( Datatype.class );
55         static Serializer type_serializer = Bindings.getSerializerUnchecked( type_binding );
56         
57         SoftReference<BinaryObject> child;      
58         
59         public BinaryVariant(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
60                 super(parent, blob, type, params);
61         }
62         
63         public VariantType type() {
64                 return (VariantType) type;
65         }
66
67         @SuppressWarnings("unchecked")
68         @Override
69         public <T extends Accessor> T getContentAccessor()
70                         throws AccessorConstructionException {
71                 assert b.isOpen();
72                 readLock();
73                 try {
74                         BinaryObject sa = getExistingAccessor();
75                         if (sa==null) {
76                                 // Create new child                             
77                                 b.position(0L);
78                                 List<Object> ids = new ArrayList<Object>(0);
79                                 Datatype type = (Datatype) type_serializer.deserialize(b, ids);                         
80                                 sa = createSubAccessor(type, b.position(), b.length()-b.position(), params);                            
81                                 child = new SoftReference<BinaryObject>(sa);
82                                 
83                                 // Attach listeners of interest set to the new accessor (of the value)
84                                 ListenerEntry le = listeners;
85                                 while (le!=null) {                              
86                                         VariantInterestSet is = le.getInterestSet();
87                                         InterestSet cis = is.getComponentInterest();
88                                         if (cis != null) {
89                                                 try {
90                                                         ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() );
91                                                         sa.addListener(le.listener, cis, childPath, le.executor);
92                                                 } catch (AccessorException e) {
93                                                         throw new AccessorConstructionException(e);
94                                                 }
95                                         }
96                                         if (is.inCompleteComponent()) {
97                                                 cis = InterestSet.newInterestSet(getContentType(), true, true, true);
98                                                 try {
99                                                         ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() );
100                                                         sa.addListener(le.listener, cis, childPath, le.executor);
101                                                 } catch (AccessorException e) {
102                                                         throw new AccessorConstructionException(e);
103                                                 }
104                                         }
105                                         le = le.next;
106                                 }                                                               
107                         }
108                         return (T) sa;
109                 } catch (IOException e) {
110                         throw new AccessorConstructionException(e);
111                 } catch (AccessorException e) {
112                         throw new AccessorConstructionException(e);
113                 } finally {
114                         readUnlock();
115                 }
116         }
117                 
118         protected BinaryObject getExistingAccessor() {
119                 SoftReference<BinaryObject> r = child;
120                 if (r==null) return null;
121                 BinaryObject sa = r.get();
122                 return sa;
123         }               
124
125         @Override
126         public void setContentValueNoflush(Binding valueBinding, Object value)
127                         throws AccessorException {
128                 assert b.isOpen();
129                 writeLock();
130                 try {
131                         // reuse sub-accessor, if type matches, and sub-accessor exists
132                         BinaryObject sa = getExistingAccessor();
133                         if (sa!=null && valueBinding.type().equals(sa.type())) {
134                                 sa.setValue(valueBinding, value);
135                                 return;
136                         }
137                         
138                         {
139                                 // Replace the old value with a new value
140                                 if (sa!=null) {
141                                         sa.invalidatedNotification();
142                                         child = null;
143                                         sa = null;
144                                 }
145                                 
146                                 // Write                                
147                                 Binding cb = valueBinding;
148                                 Serializer cs = params.serializerScheme.getSerializer( cb );
149                                 Datatype ct = valueBinding.type();
150                                 Object cv = value;
151
152                                 TObjectIntHashMap<Object> ids = new TObjectIntHashMap<Object>(0);
153                                 int len = type_serializer.getSize(ct, ids);
154                                 len += cs.getSize(cv, null);
155                                 
156                                 b.setLength(len);
157                                 b.position(0L);
158                                 ids.clear();
159                                 type_serializer.serialize(b, ids, ct);
160                                 cs.serialize(b, null, cv);
161                                 
162                                 // Notify Listeners
163                                 ListenerEntry le = listeners;
164                                 while (le!=null) {                              
165                                         VariantInterestSet is = le.getInterestSet();
166                                         if (is.inNotifications()) {
167                                                 MutableVariant v = null;
168                                                 if (is.inValues()) v = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));
169                                                 ValueAssigned e = new ValueAssigned( Bindings.MUTABLE_VARIANT, v );
170                                                 emitEvent(le, e);
171                                         }
172                                         
173                                         // Attach component listener
174 //                                      boolean hadSa = getExistingAccessor()!=null;
175 //                                      if (!hadSa) {
176 //                                              InterestSet cis = is.getComponentInterest();
177 //                                              if (cis!=null) {
178 //                                                      sa = getValueAccessor();
179 //                                              }
180 //                                      }
181                                         
182                                         le = le.next;
183                                 }
184                         }
185                         
186                 } catch (IOException e) {
187                         throw new AccessorException( e );
188                 } catch (AdaptException e) {
189                         throw new AccessorException( e );
190                 } catch (SerializerConstructionException e) {
191                         throw new AccessorException( e );
192                 } finally {
193                         writeUnlock();
194                 }
195         }
196         
197         @Override
198         public void setContentValue(Binding valueBinding, Object value)
199                         throws AccessorException {      
200                 assert b.isOpen();
201                 writeLock();
202                 try {
203                         setContentValueNoflush(valueBinding, value);
204                         flush();
205                 } finally {
206                         writeUnlock();
207                 }
208         }
209         
210         @Override
211         public Datatype getContentType() throws AccessorException {
212                 assert b.isOpen();
213                 readLock();
214                 try {
215                         b.position(0L);
216                         List<Object> ids = new ArrayList<Object>(0);                    
217                         Datatype type = (Datatype) type_serializer.deserialize(b, ids);
218                         return type;
219                 } catch (IOException e) {
220                         throw new AccessorException( e );
221                 } finally {
222                         readUnlock();
223                 }
224         }
225         
226         @Override
227         public Object getContentValue(Binding contentBinding)
228                         throws AccessorException {
229                 assert b.isOpen();
230                 readLock();
231                 try {
232                         b.position(0L);
233                         List<Object> ids = new ArrayList<Object>();
234                         Datatype type = (Datatype) type_serializer.deserialize(b, ids);
235                         ids.clear();
236                         
237                         if (!contentBinding.type().equals(type)) {
238                                 throw new AccessorException("Arg error, wrong type");
239                         }
240                         Serializer value_serializer = params.serializerScheme.getSerializer( contentBinding );
241                         return value_serializer.deserialize(b, ids);
242                         
243                         // The requested binding is a super type binding
244                         // Read the to the actual type and adapt
245                                                 
246 //                      Binding lcb = params.bindingFactory.getBinding(type);
247 //                      Binding rcb = contentBinding;
248 //                      Object lv = lcb.serializer().deserialize(b, ids);                       
249 //                      
250 //                      return adapt(lv, lcb, rcb);
251                 } catch (IOException e) {
252                         throw new AccessorException( e );
253                 } catch (SerializerConstructionException e) {
254                         throw new AccessorException( e );
255                 } finally {
256                         readUnlock();
257                 }
258         }
259
260         @SuppressWarnings("unchecked")
261         @Override
262         public <T extends Accessor> T getComponent(ChildReference reference)
263                         throws AccessorConstructionException {
264                 if (reference==null) return (T) this;
265                 
266                 if (reference instanceof LabelReference) {
267                         LabelReference lr = (LabelReference) reference;
268                         if (lr.label.equals("v")) {
269                                 Accessor sa = getContentAccessor();
270                                 if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference());
271                                 return (T) sa;
272                         }                       
273                 }               
274                 
275                 if (reference instanceof ComponentReference) {
276                         Accessor sa = getContentAccessor();
277                         if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference());
278                         return (T) sa;
279                 }
280                 
281                 throw new AccessorConstructionException("Variant value reference expected, got "+reference.getClass().getName());
282         }
283
284         @Override
285         public void addListener(Listener listener, InterestSet interestSet,
286                         ChildReference path, Executor executor) throws AccessorException {
287                 super.addListener(listener, interestSet, path, executor);
288                 VariantInterestSet is = (VariantInterestSet) interestSet;
289                 InterestSet cis = is.getComponentInterest();
290                 if (cis==null && !is.inCompleteComponent()) return;
291                 BinaryObject sa = getExistingAccessor();
292                 if (sa==null) return;
293                 if (cis!=null) {
294                         ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() );
295                         sa.addListener(listener, cis, childPath, executor);
296                 }
297                 if (is.inCompleteComponent()) {
298                         ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() );
299                         cis = InterestSet.newInterestSet(getContentType(), true, true, true);
300                         sa.addListener(listener, cis, childPath, executor);
301                 }
302         }
303         
304         @Override
305         public void removeListener(Listener listener) throws AccessorException {
306                 ListenerEntry e = detachListener(listener);
307                 if (e==null) return;
308                 VariantInterestSet is = (VariantInterestSet) e.interestSet;
309                 
310                 InterestSet cis = is.getComponentInterest();
311                 if (cis==null && !is.inCompleteComponent()) return;
312                 BinaryObject sa = getExistingAccessor();
313                 if (sa==null) return;
314                 if (cis!=null) {
315                         sa.removeListener(listener);
316                 }
317                 // Remove another?
318                 if (is.inCompleteComponent()) {
319                         sa.removeListener(listener);
320                 }               
321         }
322         
323         @Override
324         Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
325                 try {
326                         if (e instanceof ValueAssigned ) {
327                                 Event rollback = null;
328                                 ValueAssigned va = (ValueAssigned) e; 
329                                 if (va.newValue==null) throw new AccessorException("Cannot apply variant assignment event, the value is missing");
330                                 
331                                 if (makeRollback) {
332                                         b.position(0L);
333                                         List<Object> ids = new ArrayList<Object>();
334                                         Datatype type = (Datatype) type_serializer.deserialize(b, ids);
335                                         Binding cb = params.bindingScheme.getBinding(type);
336                                         Serializer value_serializer = params.serializerScheme.getSerializer( cb );
337                                         Object lv = value_serializer.deserialize(b, ids);                       
338                                         
339                                         rollback = new ValueAssigned( Bindings.MUTABLE_VARIANT, new MutableVariant( cb, lv ));
340                                 }
341         
342                                 setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
343 //                              setContentValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
344                                 
345                                 return rollback;
346                         } else {
347                                 throw new AccessorException("Invalid event "+e.getClass().getName());                           
348                         }
349                 } catch (IOException ioe) {
350                         throw new AccessorException(ioe);
351                 } catch (RuntimeSerializerConstructionException rsce) {
352                         throw new AccessorException(rsce);
353                 } catch (SerializerConstructionException e2) {                  
354                         throw new AccessorException(e2);
355                 } catch (BindingConstructionException e2) {
356                         throw new AccessorException(e2);
357                 }
358         }
359
360         @Override
361         public void setValueNoflush(Binding variantBinding, Object variantValue)
362                         throws AccessorException {
363                 assert b.isOpen();
364                 writeLock();
365                 try {
366                         VariantBinding vb = (VariantBinding) variantBinding;
367                         Object vv = variantValue;
368                         Binding cb = vb.getContentBinding(vv);
369                         Object cv = vb.getContent(vv, cb);                      
370                         setContentValueNoflush(cb, cv);
371                 } catch (BindingException e) {
372                         throw new AccessorException( e ); 
373                 } finally {
374                         writeUnlock();
375                 }
376         }
377         
378         
379 }
380