--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010- Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.accessor.impl;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.concurrent.Executor;\r
+\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.accessor.RecordAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.error.ReferenceException;\r
+import org.simantics.databoard.accessor.event.Event;\r
+import org.simantics.databoard.accessor.event.ValueAssigned;\r
+import org.simantics.databoard.accessor.interestset.InterestSet;\r
+import org.simantics.databoard.accessor.interestset.RecordInterestSet;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.IndexReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.accessor.reference.NameReference;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.type.RecordType;\r
+\r
+/**\r
+ * Record is an accessor to a record where fields defined from a composition of\r
+ * other accessors.\r
+ * \r
+ * Fields should not be modified while CompositeRecord is in accessor usage. \r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class CompositeRecord implements RecordAccessor {\r
+\r
+ protected List<Accessor> fields = new ArrayList<Accessor>();\r
+ protected Map<String, Accessor> names = new HashMap<String, Accessor>();\r
+ protected RecordType type = new RecordType();\r
+ protected Accessor parent;\r
+ protected AccessorParams params;\r
+ \r
+ public CompositeRecord() {\r
+ params = AccessorParams.DEFAULT;\r
+ }\r
+ \r
+ public CompositeRecord(Accessor parent) {\r
+ this.parent = parent;\r
+ params = AccessorParams.DEFAULT;\r
+ }\r
+ \r
+ public CompositeRecord(Accessor parent, AccessorParams params) {\r
+ this.parent = parent;\r
+ this.params = params;\r
+ }\r
+ \r
+ public void addField(String name, Accessor field) {\r
+ fields.add( field );\r
+ names.put(name, field);\r
+ type.addComponent(name, field.type());\r
+ }\r
+ \r
+ public void removeField(String name) {\r
+ Accessor a = names.remove(name);\r
+ fields.remove(a);\r
+ type.removeComponent(name);\r
+ }\r
+\r
+ @Override\r
+ public int count() {\r
+ return fields.size();\r
+ }\r
+\r
+ @Override\r
+ public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {\r
+ RecordInterestSet is = (RecordInterestSet) interestSet;\r
+\r
+ try {\r
+ if (is.componentInterests!=null) {\r
+ for (int i=0; i<count(); i++) {\r
+ InterestSet cis = is.getComponentInterest(i);\r
+ if (cis==null) continue;\r
+ ChildReference childPath = ChildReference.concatenate(path, new IndexReference(i) );\r
+ Accessor ca = getFieldAccessor(i);\r
+ ca.addListener(listener, cis, childPath, executor); \r
+ }\r
+ }\r
+ } catch (AccessorConstructionException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void removeListener(Listener listener) throws AccessorException {\r
+ for (Accessor a : fields)\r
+ a.removeListener(listener); \r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T extends Accessor> T getFieldAccessor(int index) throws AccessorConstructionException {\r
+ if (index<0 || index>=fields.size()) \r
+ throw new AccessorConstructionException("Field index "+index+" does not exist"); \r
+ return (T) fields.get(index);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T extends Accessor> T getFieldAccessor(String fieldName) throws AccessorConstructionException {\r
+ Accessor accessor = names.get(fieldName);\r
+ if (accessor == null) \r
+ throw new AccessorConstructionException("Field by name "+fieldName+" was not found");\r
+ return (T) accessor;\r
+ }\r
+\r
+ @Override\r
+ public Object getFieldValue(String fieldName, Binding fieldBinding)\r
+ throws AccessorException {\r
+ int fieldIndex = type().getComponentIndex(fieldName);\r
+ if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist"); \r
+ return getFieldValue(fieldIndex, fieldBinding);\r
+ }\r
+ \r
+ @Override\r
+ public Object getFieldValue(int index, Binding fieldBinding) throws AccessorException {\r
+ if (index<0 || index>=fields.size()) \r
+ throw new AccessorException("Field index "+index+" does not exist");\r
+ return fields.get(index).getValue(fieldBinding);\r
+ }\r
+\r
+ @Override\r
+ public void setFieldValue(String fieldName, Binding fieldBinding, Object value) throws AccessorException {\r
+ int fieldIndex = type().getComponentIndex(fieldName);\r
+ if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");\r
+ setFieldValue(fieldIndex, fieldBinding, value);\r
+ }\r
+ \r
+ public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
+ try {\r
+ Accessor a = getComponent(path);\r
+ a.setValue(binding, obj);\r
+ return true;\r
+ } catch (ReferenceException re) {\r
+ return false;\r
+ } catch (AccessorConstructionException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
+ try {\r
+ Accessor a = getComponent(path);\r
+ a.getValue(binding, obj);\r
+ return true;\r
+ } catch (ReferenceException re) {\r
+ return false;\r
+ } catch (AccessorConstructionException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ } \r
+ \r
+ public Object getValue(ChildReference path, Binding binding) throws AccessorException {\r
+ try {\r
+ Accessor a = getComponent(path);\r
+ return a.getValue(binding);\r
+ } catch (ReferenceException re) {\r
+ return null;\r
+ } catch (AccessorConstructionException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void setFieldValue(int index, Binding fieldBinding, Object value)\r
+ throws AccessorException {\r
+ if (index<0 || index>=fields.size()) \r
+ throw new AccessorException("Field index "+index+" does not exist");\r
+ fields.get(index).setValue(fieldBinding, value);\r
+ }\r
+\r
+ @Override\r
+ public RecordType type() { \r
+ return type;\r
+ }\r
+\r
+ @Override\r
+ public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {\r
+ try {\r
+ boolean makeRollback = rollback != null;\r
+ ArrayList<Event> single = new ArrayList<Event>();\r
+ \r
+ for (Event e : cs) {\r
+ if (e.reference==null) {\r
+ Event rbe = applyLocal(e, makeRollback);\r
+ if (makeRollback) {\r
+ rbe.reference = e.reference;\r
+ rollback.addFirst( rbe );\r
+ } \r
+ } else {\r
+ Accessor sa = getComponent(e.reference);\r
+ // Apply changes\r
+ single.clear();\r
+ Event noRefEvent = e.clone(null);\r
+ single.add(noRefEvent);\r
+ sa.apply(single, rollback);\r
+ }\r
+ }\r
+ \r
+ } catch (AccessorConstructionException ae) {\r
+ throw new AccessorException(ae);\r
+ }\r
+ \r
+ } \r
+\r
+ Event applyLocal(Event e, boolean makeRollback) throws AccessorException {\r
+ try {\r
+ if (e instanceof ValueAssigned) {\r
+ ValueAssigned va = (ValueAssigned) e;\r
+ Event rollback = null;\r
+ if (makeRollback) {\r
+ Binding binding = params.bindingScheme.getBinding(type());\r
+ rollback = new ValueAssigned(binding, getValue(binding)); \r
+ }\r
+ setValue(va.newValue.getBinding(), va.newValue.getValue());\r
+ return rollback;\r
+ } else {\r
+ throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Record Type"); \r
+ }\r
+ } catch (BindingConstructionException e2) {\r
+ throw new AccessorException( e2 );\r
+ }\r
+ } \r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T extends Accessor> T getComponent(ChildReference reference)\r
+ throws AccessorConstructionException {\r
+ if (reference==null) return (T) this;\r
+ \r
+ if (reference instanceof LabelReference) {\r
+ LabelReference lr = (LabelReference) reference;\r
+ String fieldName = lr.label;\r
+ Integer index = type().getComponentIndex(fieldName);\r
+ if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");\r
+ Accessor sa = getFieldAccessor(index);\r
+ if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());\r
+ return (T) sa; \r
+ } \r
+ \r
+ if (reference instanceof IndexReference) {\r
+ IndexReference ref = (IndexReference) reference;\r
+ int index = ref.getIndex();\r
+ Accessor sa = getFieldAccessor(index);\r
+ if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());\r
+ return (T) sa;\r
+ } \r
+ \r
+ if (reference instanceof NameReference) {\r
+ NameReference ref = (NameReference) reference;\r
+ String fieldName = ref.getName();\r
+ Integer index = type().getComponentIndex(fieldName);\r
+ if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");\r
+ Accessor sa = getFieldAccessor(index);\r
+ if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());\r
+ return (T) sa; \r
+ } \r
+ \r
+ throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType"); \r
+ }\r
+\r
+ @Override\r
+ public Object getValue(Binding binding) throws AccessorException {\r
+ if (!binding.type().equals(type))\r
+ throw new AccessorException("Type mismatch");\r
+ RecordBinding rb = (RecordBinding) binding;\r
+\r
+ try {\r
+ Object values[] = new Object[rb.getComponentCount()];\r
+ \r
+ for (int i=0; i<values.length; i++) {\r
+ Binding cb = rb.getComponentBinding(i);\r
+ Object cv = fields.get(i).getValue(cb);\r
+ values[i] = cv;\r
+ }\r
+ \r
+ return rb.create(values);\r
+ } catch (BindingException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void getValue(Binding dstBinding, Object dst) throws AccessorException {\r
+ if (!dstBinding.type().equals(type)) throw new AccessorException("Type mismatch");\r
+ RecordBinding db = (RecordBinding) dstBinding;\r
+ \r
+ try {\r
+ for (int i=0; i<db.getComponentCount(); i++) {\r
+ Object c = db.getComponent(dst, i);\r
+ Binding dcb = db.getComponentBinding(i);\r
+ fields.get(i).getValue(dcb, c);\r
+ db.setComponent(dst, i, c);\r
+ }\r
+ } catch (BindingException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setValue(Binding binding, Object newValue)\r
+ throws AccessorException {\r
+ if (!binding.type().equals(type))\r
+ throw new AccessorException("Type mismatch");\r
+ RecordBinding rb = (RecordBinding) binding;\r
+\r
+ try {\r
+ for (int i=0; i<rb.getComponentCount(); i++) {\r
+ Binding cb = rb.getComponentBinding(i);\r
+ Object cv;\r
+ cv = rb.getComponent(newValue, i);\r
+ fields.get(i).setValue(cb, cv);\r
+ }\r
+ } catch (BindingException e) {\r
+ throw new AccessorException( e );\r
+ } \r
+ }\r
+\r
+ \r
+}\r
+\r