X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.simulator.toolkit%2Fsrc%2Forg%2Fsimantics%2Fsimulator%2Ftoolkit%2FStandardNodeManager.java;fp=bundles%2Forg.simantics.simulator.toolkit%2Fsrc%2Forg%2Fsimantics%2Fsimulator%2Ftoolkit%2FStandardNodeManager.java;h=87ae4f32f3b7394d1e8168c705c2584ba0ae3f8c;hb=14a4f7a9d486fba5be815e511fb2a497fca4eb70;hp=0000000000000000000000000000000000000000;hpb=751ee12501d220832b672dd433655a4d65806fd9;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java b/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java new file mode 100644 index 000000000..87ae4f32f --- /dev/null +++ b/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java @@ -0,0 +1,397 @@ +/******************************************************************************* + * Copyright (c) 2013 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 + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.simulator.toolkit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.databoard.adapter.Adapter; +import org.simantics.databoard.adapter.AdapterConstructionException; +import org.simantics.databoard.binding.Binding; +import org.simantics.databoard.binding.VariantBinding; +import org.simantics.databoard.binding.error.BindingException; +import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; +import org.simantics.databoard.binding.mutable.Variant; +import org.simantics.databoard.type.Datatype; +import org.simantics.simulator.variable.NodeManager; +import org.simantics.simulator.variable.Realm; +import org.simantics.simulator.variable.exceptions.NoSuchNodeException; +import org.simantics.simulator.variable.exceptions.NodeManagerException; +import org.simantics.simulator.variable.exceptions.NotInRealmException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.procedure.TObjectProcedure; +import gnu.trove.set.hash.THashSet; + +/** + * StandardNodeManager gives default implementations to some methods + * of NodeManager. + * + * @author Antti Villberg + */ +public abstract class StandardNodeManager> implements NodeManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(StandardNodeManager.class); + + private final Node root; + private final StandardRealm realm; + + static final Binding NO_BINDING = new VariantBinding() { + + @Override + public Object getContent(Object variant, Binding contentBinding) throws BindingException { + throw new Error(); + } + + @Override + public Object getContent(Object variant) throws BindingException { + throw new Error(); + } + + @Override + public Datatype getContentType(Object variant) throws BindingException { + throw new Error(); + } + + @Override + public Binding getContentBinding(Object variant) throws BindingException { + throw new Error(); + } + + @Override + public Object create(Binding contentBinding, Object content) throws BindingException { + throw new Error(); + } + + @Override + public void setContent(Object variant, Binding contentBinding, Object content) throws BindingException { + throw new Error(); + } + + @Override + public boolean isInstance(Object obj) { + return true; + } + + @Override + public void assertInstaceIsValid(Object obj, Set validInstances) throws BindingException { + throw new Error(); + } + + @Override + public int compare(Object o1, Object o2) throws org.simantics.databoard.binding.error.RuntimeBindingException { + if(o1 == null) { + if(o2 == null) { + return 0; + } else { + return - System.identityHashCode(o2); + } + } else { + if(o2 == null) { + return System.identityHashCode(o1); + } else { + if(o1.equals(o2)) return 0; + return System.identityHashCode(o1) - System.identityHashCode(o2); + } + } + } + + }; + + THashMap valueCache = new THashMap<>(); + protected THashMap> listeners = new THashMap<>(); + + AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false); + Runnable fireNodeListeners = new Runnable() { + @Override + public void run() { + fireNodeListenersScheduled.set(false); + TObjectProcedure procedure = r -> { + r.run(); + return true; + }; + synchronized(listeners) { + listeners.forEachValue(set -> { + set.forEach(procedure); + return true; + }); + } + } + }; + + Runnable clearValueCache = () -> valueCache.clear(); + + public StandardNodeManager(StandardRealm realm, Node root) { + assert(realm != null); + assert(root != null); + this.realm = realm; + this.root = root; + } + + @Override + public List getChildNames(Node node) throws NodeManagerException { + List children = getChildren(node); + ArrayList names = new ArrayList<>(children.size()); + for(Node child : children) + names.add(getName(child)); + return names; + } + + @Override + public List getPropertyNames(Node node) throws NodeManagerException { + List properties = getProperties(node); + ArrayList names = new ArrayList<>(properties.size()); + for(Node property : properties) + names.add(getName(property)); + return names; + } + + @Override + public Object getValue(Node node, String propertyName, Binding binding) + throws NodeManagerException, BindingException { + Node property = getProperty(node, propertyName); + if(property == null) + throw new NoSuchNodeException("Didn't find a property " + propertyName); + return getValue(property, binding); + } + + @Override + public void setValue(Node node, String propertyName, Object value, + Binding binding) throws NodeManagerException, BindingException { + Node property = getProperty(node, propertyName); + if(property == null) + throw new NoSuchNodeException("Didn't find a property " + propertyName); + setValue(property, value, binding); + } + + @Override + public Variant getValue(Node node, String propertyName) + throws NodeManagerException { + Node property = getProperty(node, propertyName); + if(property == null) + throw new NoSuchNodeException("Didn't find a property " + propertyName); + return getValue(property); + } + + @Override + public Object getValue(Node node, Binding binding) throws NodeManagerException, BindingException { + try { + return getValue(node).getValue(binding); + } catch (AdaptException e) { + throw new BindingException(e); + } + } + + @Override + public String getPropertyURI(Node parent, Node property) { + return null; + } + + @Override + public Realm getRealm() { + return realm; + } + + public StandardRealm getStandardRealm() { + return realm; + } + + protected String getRealmId() { + return realm.getId(); + } + + public Node getRoot() { + return root; + } + + protected boolean isRoot(Node node) { + return root.equals(node); + } + + @Override + public void addNodeListener(Node node, Runnable listener) { + synchronized(listeners) { + THashSet l = listeners.get(node); + if(l == null) { + l = new THashSet<>(); + listeners.put(node, l); + } + l.add(listener); + } + getRealm().asyncExec(listener); + } + + @Override + public void removeNodeListener(Node node, Runnable listener) { + synchronized(listeners) { + THashSet l = listeners.get(node); + if(l != null) { + l.remove(listener); + if(l.isEmpty()) + listeners.remove(node); + } + } + } + + public void fireNodeListeners() { + if(!fireNodeListenersScheduled.getAndSet(true)) + realm.asyncExec(fireNodeListeners); + } + + public void fireNodeListenersSync() { + try { + realm.syncExec(fireNodeListeners); + } catch (InterruptedException e) { + LOGGER.error("Synchronous node listener firing was interrupted.", e); + } + } + + public void refreshVariables() { + realm.asyncExec(clearValueCache); + fireNodeListeners(); + } + + public void refreshVariablesSync() { + try { + realm.syncExec(clearValueCache); + } catch (InterruptedException e) { + LOGGER.error("Synchronous value cache refresh was interrupted.", e); + } + fireNodeListenersSync(); + } + + protected Variant getEngineVariantOrCached(Node node) throws NodeManagerException { + Variant variant = valueCache.get(node); + if(variant == null) { + Object value = realm.getEngine().getEngineValue(node); + Binding binding = realm.getEngine().getEngineBinding(node); + variant = new Variant(binding, value); + valueCache.put(node, variant); + } + return variant; + } + + @Override + public Variant getValue(Node node) throws NodeManagerException { + checkThreadAccess(); + return getEngineVariantOrCached(node); + } + + protected void checkThreadAccess() throws NodeManagerException { + if(Thread.currentThread() != realm.getThread()) + throw new NotInRealmException(); + } + + protected Datatype getDatatypeForValue(Object value) { + Binding binding = Bindings.getBindingUnchecked(value.getClass()); + if(binding == null) return null; + else return binding.type(); + } + + @Override + public void setValue(Node node, Object value, Binding binding) + throws NodeManagerException { + checkThreadAccess(); + Binding targetBinding = realm.getEngine().getEngineBinding(node); + if(binding.equals(targetBinding)) { + Variant variant = new Variant(binding, value); + valueCache.put(node, variant); + realm.getEngine().setEngineValue(node, value); + } else { + try { + Adapter adapter = Bindings.getAdapter(binding, targetBinding); + Object targetValue = adapter.adapt(value); + Variant variant = new Variant(targetBinding, targetValue); + valueCache.put(node, variant); + realm.getEngine().setEngineValue(node, targetValue); + } catch (AdapterConstructionException e) { + throw new NodeManagerException(e); + } catch (AdaptException e) { + throw new NodeManagerException(e); + } + } + refreshVariables(); + } + + @Override + public String getName(Node node) { + if(isRoot(node)) { + String id = getRealmId(); + int lastSlash = id.lastIndexOf("/"); + if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id); + String name = id.substring(lastSlash+1); + return name; + } else { + return realm.getEngine().getName(node); + } + } + + @Override + public Node getNode(String path) throws NodeManagerException { + checkThreadAccess(); + throw new UnsupportedOperationException(); + } + + @Override + public Node getChild(Node node, String name) throws NodeManagerException { + checkThreadAccess(); + Map map = realm.getEngine().getChildren(node); + return map.get(name); + } + + @Override + public Node getProperty(Node node, String name) throws NodeManagerException { + checkThreadAccess(); + Map map = realm.getEngine().getProperties(node); + return map.get(name); + } + + @Override + public List getChildren(Node node) throws NodeManagerException { + checkThreadAccess(); + return new ArrayList(realm.getEngine().getChildren(node).values()); + } + + @Override + public List getProperties(Node node) throws NodeManagerException { + checkThreadAccess(); + return new ArrayList(realm.getEngine().getProperties(node).values()); + } + + @Override + public Datatype getDatatype(Node node) throws NodeManagerException { + checkThreadAccess(); + try { + Variant v = getEngineVariantOrCached(node); + Binding b = v.getBinding(); + if(b == null) return null; + return b.type(); + } catch (RuntimeBindingConstructionException e) { + // There is no datatype for all values + } + return null; + } + + public void clear() { + valueCache.clear(); + listeners.clear(); + } + +} \ No newline at end of file