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.java;
14 import java.lang.ref.SoftReference;
15 import java.util.concurrent.Executor;
17 import org.simantics.databoard.accessor.Accessor;
18 import org.simantics.databoard.accessor.RecordAccessor;
19 import org.simantics.databoard.accessor.error.AccessorConstructionException;
20 import org.simantics.databoard.accessor.error.AccessorException;
21 import org.simantics.databoard.accessor.error.ReferenceException;
22 import org.simantics.databoard.accessor.event.Event;
23 import org.simantics.databoard.accessor.event.ValueAssigned;
24 import org.simantics.databoard.accessor.impl.AccessorParams;
25 import org.simantics.databoard.accessor.impl.ListenerEntry;
26 import org.simantics.databoard.accessor.interestset.InterestSet;
27 import org.simantics.databoard.accessor.interestset.RecordInterestSet;
28 import org.simantics.databoard.accessor.reference.ChildReference;
29 import org.simantics.databoard.accessor.reference.IndexReference;
30 import org.simantics.databoard.accessor.reference.LabelReference;
31 import org.simantics.databoard.accessor.reference.NameReference;
32 import org.simantics.databoard.adapter.AdaptException;
33 import org.simantics.databoard.adapter.AdapterConstructionException;
34 import org.simantics.databoard.binding.Binding;
35 import org.simantics.databoard.binding.RecordBinding;
36 import org.simantics.databoard.binding.error.BindingException;
37 import org.simantics.databoard.binding.mutable.MutableVariant;
38 import org.simantics.databoard.type.RecordType;
41 * Accessor to a Java Object of Record Type.
43 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
45 public class JavaRecord extends JavaObject implements RecordAccessor {
47 /** Accessors to children */
48 SoftReference<JavaObject>[] children;
50 @SuppressWarnings("unchecked")
51 public JavaRecord(Accessor parent, RecordBinding binding, Object object, AccessorParams params) throws AccessorConstructionException {
52 super(parent, binding, object, params);
53 RecordType type = (RecordType) binding.type();
54 if (type.isReferable()) throw new AccessorConstructionException("Refereable record are not supported");
56 int count = binding.type().getComponentCount();
57 children = new SoftReference[count];
61 public RecordBinding getBinding() {
62 return (RecordBinding) binding;
66 public RecordType type() {
67 return (RecordType) getBinding().type();
72 return type().getComponentCount();
75 @SuppressWarnings("unchecked")
77 public <T extends Accessor> T getFieldAccessor(String fieldName) throws AccessorConstructionException
79 int fieldIndex = type().getComponentIndex(fieldName);
80 if (fieldIndex<0) throw new AccessorConstructionException("Field "+fieldName+" does not exist");
81 return (T) getFieldAccessor(fieldIndex);
84 @SuppressWarnings("unchecked")
86 public <T extends Accessor> T getFieldAccessor(int index) throws AccessorConstructionException
88 if (index<0 || index>=count()) throw new ReferenceException("Field index ("+index+") out of bounds ("+count()+")");
92 // Get existing or create new
93 SoftReference<JavaObject> ref = children[index];
94 JavaObject sa = (ref!=null)?(JavaObject)ref.get():null;
97 // Instantiate new accessor
98 Binding cb = getBinding().getComponentBindings()[index];
99 Object cv = getBinding().getComponent(object, index);
100 sa = createSubAccessor(this, cb, cv, params);
101 sa.keyInParent = index;
102 children[index] = new SoftReference<JavaObject>(sa);
104 // Add component interest sets
105 ListenerEntry le = listeners;
107 RecordInterestSet is = le.getInterestSet();
108 InterestSet cis = is.getComponentInterest(index);
111 ChildReference childPath = ChildReference.concatenate( le.path, new IndexReference(index) );
112 sa.addListener(le.listener, cis, childPath, le.executor);
113 } catch (AccessorException e) {
114 throw new AccessorConstructionException(e);
122 } catch (BindingException e) {
123 throw new AccessorConstructionException(e);
130 * Get existing sub accessor
132 * @return sub-accessor or <code>null</code>
134 JavaObject getExistingAccessor(int index)
136 if (index<0 || index>=count()) throw new RuntimeException("Field index ("+index+") out of bounds ("+count()+")");
138 // Get existing or create new
139 SoftReference<JavaObject> ref = children[index];
140 JavaObject accessor = (ref!=null)?(JavaObject)ref.get():null;
145 public Object getFieldValue(String fieldName, Binding fieldBinding)
146 throws AccessorException {
147 int fieldIndex = type().getComponentIndex(fieldName);
148 if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");
149 return getFieldValue(fieldIndex, fieldBinding);
153 public Object getFieldValue(int index, Binding fieldBinding)
154 throws AccessorException {
157 Object cv = getBinding().getComponent(object, index);
158 Binding cb = getBinding().getComponentBindings()[index];
159 return adapt(cv, cb, fieldBinding);
160 } catch (BindingException e) {
161 throw new AccessorException(e);
162 } catch (AdaptException e) {
163 throw new AccessorException(e);
164 } catch (AdapterConstructionException e) {
165 throw new AccessorException(e);
171 public void setFieldValue(String fieldName, Binding fieldBinding, Object value) throws AccessorException {
172 int fieldIndex = type().getComponentIndex(fieldName);
173 if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");
174 setFieldValue(fieldIndex, fieldBinding, value);
178 public void setFieldValue(int index, Binding binding, Object value)
179 throws AccessorException {
182 JavaObject sa = getExistingAccessor(index);
185 Binding lb = getBinding().getComponentBindings()[index];
186 Binding rb = binding;
187 //Object oldlv = getBinding().getComponent(object, index);
190 // Compare to existing value
191 // boolean equal = Bindings.equals(lb, oldlv, lb, rv);
192 // if (equal) return;
195 Object lv = adapt(rv, rb, lb);
196 getBinding().setComponent(object, index, lv);
199 ListenerEntry le = listeners;
201 RecordInterestSet is = le.getInterestSet();
202 if (is.inNotificationsOf(index)) {
203 MutableVariant newValue = is.inValuesOf(index) ? new MutableVariant(binding, value) : null;
204 if (is.inValuesOf(index)) newValue = new MutableVariant(binding, binding.isImmutable() ? value : binding.clone(value));
205 Event e = new ValueAssigned(new IndexReference(index), newValue);
211 // Recursive write using existing sub accessor
212 sa.setValue(binding, value);
214 } catch (BindingException e) {
215 throw new AccessorException(e);
216 } catch (AdaptException e) {
217 throw new AccessorException(e);
218 } catch (AdapterConstructionException e) {
219 throw new AccessorException(e);
226 public void addListener(Listener listener, InterestSet interestSet,
227 ChildReference path, Executor executor) throws AccessorException {
228 RecordInterestSet is = (RecordInterestSet) interestSet;
229 super.addListener(listener, interestSet, path, executor);
230 // Apply component interest set to existing sub-accessors
231 if (is.componentInterests!=null) {
232 for (int i=0; i<count(); i++) {
233 InterestSet cis = is.getComponentInterest(i);
234 if (cis==null) continue;
235 JavaObject childAccessor = getExistingAccessor(i);
236 if (childAccessor==null) continue;
237 ChildReference childPath = ChildReference.concatenate( path, new IndexReference(i));
238 childAccessor.addListener(listener, cis, childPath, executor);
244 public void removeListener(Listener listener) throws AccessorException {
245 ListenerEntry e = detachListener(listener);
247 RecordInterestSet is = (RecordInterestSet) e.interestSet;
249 // Apply component interest set to existing sub-accessors
250 if (is.componentInterests!=null) {
251 for (int i=0; i<count(); i++) {
252 InterestSet cis = is.getComponentInterest(i);
253 if (cis==null) continue;
254 JavaObject sa = getExistingAccessor(i);
255 if (sa==null) continue;
256 sa.removeListener(listener);
262 @SuppressWarnings("unchecked")
264 public <T extends Accessor> T getComponent(ChildReference reference) throws AccessorConstructionException {
265 if (reference==null) return (T) this;
267 if (reference instanceof LabelReference) {
268 LabelReference lr = (LabelReference) reference;
269 String fieldName = lr.label;
270 Integer index = type().getComponentIndex(fieldName);
271 if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
272 JavaObject sa = getFieldAccessor(index);
273 if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
277 if (reference instanceof IndexReference) {
278 IndexReference ref = (IndexReference) reference;
279 int index = ref.getIndex();
280 JavaObject sa = getFieldAccessor(index);
281 if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
285 if (reference instanceof NameReference) {
286 NameReference ref = (NameReference) reference;
287 String fieldName = ref.getName();
288 Integer index = type().getComponentIndex(fieldName);
289 if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
290 JavaObject sa = getFieldAccessor(index);
291 if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
295 throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType");
300 public void setValue(Binding binding, Object newValue) throws AccessorException {
303 RecordBinding rb = (RecordBinding) binding;
304 for (int i=0; i<count(); i++) {
305 Binding componentBinding = rb.getComponentBinding(i);
306 Object componentValue = rb.getComponent(newValue, i);
307 setFieldValue(i, componentBinding, componentValue);
309 } catch (BindingException e) {
310 throw new AccessorException(e);
318 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
319 Event rollback = null;
320 if (makeRollback) rollback = new ValueAssigned(getBinding(), getValue(getBinding()));
321 if (e instanceof ValueAssigned) {
322 ValueAssigned va = (ValueAssigned) e;
323 setValue(va.newValue.getBinding(), va.newValue.getValue());
326 throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Record Type");