]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/Variables.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / variable / Variables.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in 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.db.layer0.variable;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.Databoard;
23 import org.simantics.databoard.binding.Binding;
24 import org.simantics.databoard.binding.mutable.Variant;
25 import org.simantics.databoard.type.Datatype;
26 import org.simantics.databoard.type.NumberType;
27 import org.simantics.databoard.util.URIStringUtils;
28 import org.simantics.db.ReadGraph;
29 import org.simantics.db.RequestProcessor;
30 import org.simantics.db.Resource;
31 import org.simantics.db.WriteGraph;
32 import org.simantics.db.common.request.PossibleIndexRoot;
33 import org.simantics.db.common.request.TernaryRead;
34 import org.simantics.db.common.utils.CommonDBUtils;
35 import org.simantics.db.exception.DatabaseException;
36 import org.simantics.db.layer0.exception.InvalidVariableException;
37 import org.simantics.db.layer0.exception.MissingVariableException;
38 import org.simantics.db.layer0.exception.MissingVariableValueException;
39 import org.simantics.db.layer0.request.Model;
40 import org.simantics.db.layer0.request.PossibleActiveVariableFromVariable;
41 import org.simantics.db.layer0.request.PossibleVariableIndexRoot;
42 import org.simantics.db.layer0.request.PossibleVariableModel;
43 import org.simantics.db.layer0.request.PropertyInfo;
44 import org.simantics.db.layer0.request.PropertyInfoRequest;
45 import org.simantics.db.layer0.request.ResourceURIToVariable;
46 import org.simantics.db.layer0.request.VariableIndexRoot;
47 import org.simantics.db.layer0.request.VariableURI;
48 import org.simantics.layer0.Layer0;
49 import org.simantics.operation.Layer0X;
50 import org.simantics.project.ontology.ProjectResource;
51 import org.simantics.scl.runtime.function.Function1;
52 import org.simantics.scl.runtime.function.Function2;
53 import org.simantics.scl.runtime.function.Function3;
54 import org.simantics.simulation.ontology.SimulationResource;
55 import org.simantics.simulator.variable.NodeManager;
56 import org.simantics.simulator.variable.exceptions.NodeManagerException;
57 import org.simantics.utils.datastructures.Pair;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import gnu.trove.map.hash.TObjectIntHashMap;
62
63 final public class Variables {
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(Variables.class);
66
67     public static final Variant PENDING_NODE_VALUE = NodeManager.PENDING_NODE_VALUE;
68
69     public static final NodeStructure PENDING_NODE_STRUCTURE = new NodeStructure(Collections.emptyMap(), Collections.emptyMap()) {
70         public boolean equals(Object object) {
71             return this == object;
72         }
73     };
74
75     public static enum Role {
76
77         CHILD("/"), PROPERTY("#");
78
79         transient final String identifier;
80
81         private Role(String identifier) {
82             this.identifier = identifier;
83         }
84
85         final public String getIdentifier() {
86             return identifier;
87         }
88
89         public static Role getRole( String identifier ) {
90             for (Role role : Role.values()) if (role.identifier.equals( identifier )) return role;
91             return null;
92         }
93
94     }
95
96     // use getPredicate
97     //final public static String PREDICATE = "PREDICATE";
98
99     @Deprecated
100     // use getName
101     final public static String NAME = "HasName";
102     final public static String CLASSIFICATIONS = "classifications";
103     final public static String EXPRESSION = "HasExpression";
104     final public static String INPUT_VALIDATOR = "HasInputValidator";
105     final public static String INPUT_MODIFIER = "HasInputModifier";
106     final public static String FORMATTER = "HasFormatter";
107
108     final public static String STANDARD_RESOURCE = "hasStandardResource";
109     //@Deprecated
110     // use getPresents
111     //final public static String REPRESENTS = "Represents";
112     final public static String TYPE = "Type";
113     final public static String URI = "URI";
114     /**
115      * @deprecated use {@link Variable#getRVI(ReadGraph)} and {@link RVI} instead.
116      */
117 //    @Deprecated
118 //    final public static String SERIALISED = "Serialised";
119 //    @Deprecated
120     // use getParent
121     //final public static String PARENT = "Parent";
122     //final public static String ROLE = "Role";
123     //final public static String DATATYPE = "DATATYPE";
124     //final public static String UNIT = "UNIT";
125
126     final public static String VALID = "valid";
127     final public static String REQUIRED = "required";
128     final public static String DEFAULT = "default";
129     final public static String READONLY = "readOnly";
130     final public static String VALIDATOR = "validator";
131
132     final public static String LABEL = "HasLabel";
133
134     final public static String ENUMERATION_VALUES = "HasEnumerationValues";
135     final public static String CUSTOM_MODIFIER = "HasCustomModifier";
136
137     final public static String DISPLAY_COLUMN = "HasDisplayColumn";
138
139     final public static String DISPLAY_PROPERTY = "HasDisplayProperty";
140     final public static String DISPLAY_VALUE = "HasDisplayValue";
141     final public static String DISPLAY_UNIT = "HasDisplayUnit";
142
143     final public static String CONVERTED_VALUE = "convertedValue";
144
145     /**
146      * This property should exist for array valued property variables.
147      */
148     final public static String ARRAY_SIZE = "ARRAY_SIZE";
149
150
151     @Deprecated
152     // use etc. variable.adapt(graph, Interface.class).getResource()
153     final public static String RESOURCE = "Resource";
154     @Deprecated
155     final public static String CONTAINER_RESOURCE = "ContainerResource";
156     @Deprecated
157     final public static String PROPERTY_RESOURCE = "PROPERTY_RESOURCE";
158
159     @Deprecated
160     public final static String[] builtins = {
161             TYPE, RESOURCE, URI
162             //, SERIALISED
163     };
164
165     public static Variable getPossibleVariable(ReadGraph graph, Resource resource) throws DatabaseException {
166         String uri = graph.getPossibleURI(resource);
167         if (uri != null)
168             return getPossibleVariable(graph, uri);
169         Resource parent = CommonDBUtils.getPossibleOwner(graph, resource);
170         if (parent == null)
171             return null;
172         Variable possibleVariable = getPossibleVariable(graph, parent);
173         if (possibleVariable == null)
174             return null;
175         String possibleName = graph.getPossibleRelatedValue(resource, Layer0.getInstance(graph).HasName, Bindings.STRING);
176         if (possibleName == null)
177             possibleName = VariableUtils.unnamedResourceName(resource);
178         Variable possibleChild = possibleVariable.getPossibleChild(graph, possibleName);
179         if (possibleChild != null)
180             return possibleChild;
181         for (Variable v : possibleVariable.getChildren(graph)) {
182             Resource vr = v.getPossibleRepresents(graph);
183             if (vr != null && vr.equals(resource)) {
184                 return v;
185             }
186         }
187         return null;
188     }
189
190     public static Variable getPossibleVariable(ReadGraph graph, String uri) throws DatabaseException {
191         try {
192             return getVariable(graph, uri);
193         } catch (DatabaseException e) {
194             return null;
195         }
196     }
197
198     public static Variable getVariable(ReadGraph graph, Resource resource) throws DatabaseException {
199         return getVariable(graph, graph.getURI(resource));
200     }
201
202     public static Variable getVariable(ReadGraph graph, String uri) throws DatabaseException {
203         try {
204             return graph.sync(new ResourceURIToVariable(uri));
205         } catch (MissingVariableException e) {
206             return VariableRepository.get(graph, uri);
207         }
208     }
209
210     private static int commonPrefixLength(String a, String b) {
211         int maxC = Math.min(a.length(), b.length());
212         for(int c=0;c<maxC;++c) {
213             if(a.charAt(c) != b.charAt(c))
214                 return c;
215         }
216         return maxC;
217     }
218
219     private static boolean isSplitPos(String str, int p) {
220         if(p==str.length())
221             return true;
222         char c = str.charAt(p);
223         return c=='/' || c=='#';
224     }
225
226     private static int pathLength(String path) {
227         int count = 0;
228         for(int i=0;i<path.length();++i) {
229             char c = path.charAt(i);
230             if(c=='/' || c=='#')
231                 ++count;
232         }
233         return count;
234     }
235
236     private static String prefixByParentPath(int parentPathLength, String suffix) {
237         StringBuilder b = new StringBuilder();
238         for(int i=0;i<parentPathLength;++i)
239             b.append('.');
240         b.append(suffix);
241         return b.toString();
242     }
243
244     public static String getRVI(ReadGraph graph, Variable base, Variable other) throws DatabaseException {
245         String baseURI = graph.syncRequest(new VariableURI(base));
246         String otherURI = graph.syncRequest(new VariableURI(other));
247         return getRelativeRVI(baseURI, otherURI);
248     }
249
250     public static String getRelativeRVI(String baseURI, String otherURI) {
251         int prefixLength = commonPrefixLength(baseURI, otherURI);
252         if(!isSplitPos(baseURI, prefixLength) || !isSplitPos(otherURI, prefixLength)) {
253             for(--prefixLength;prefixLength > 0 && !isSplitPos(baseURI, prefixLength);--prefixLength);
254         }
255         if(prefixLength == baseURI.length())
256             return otherURI.substring(prefixLength);
257         else 
258             return prefixByParentPath(
259                     pathLength(baseURI.substring(prefixLength)), 
260                     otherURI.substring(prefixLength));      
261     }
262
263     public static String getRVI2(ReadGraph graph, Variable base, Variable other) throws DatabaseException {
264         TObjectIntHashMap<Variable> baseLength = new TObjectIntHashMap<Variable>();
265         for(int depth=0;base != null;base = base.getParent(graph),++depth)
266             baseLength.put(base, depth);
267         Variable cur;
268         for(cur=other;!baseLength.containsKey(cur);cur=cur.getParent(graph));
269
270         // To string
271         String curURI = cur.getURI(graph);
272         String otherURI = other.getURI(graph);
273
274         return prefixByParentPath(baseLength.get(cur), otherURI.substring(curURI.length()));
275     }
276
277     public static String getProjectRVI(ReadGraph graph, Variable variable) throws DatabaseException {
278         Resource project = getProject(graph, variable);
279         String projectURI = graph.getURI(project);
280         return variable.getURI(graph).substring(projectURI.length());
281     }
282
283     private static int getSegmentEnd(String suffix) {
284         int pos;
285         for(pos=1;pos<suffix.length();++pos) {
286             char c = suffix.charAt(pos);
287             if(c == '/' || c == '#')
288                 break;
289         }
290         return pos;
291     }
292
293     public static String getRVI(String rvi, String suffix) throws DatabaseException {
294         if(suffix.isEmpty()) return rvi;
295         switch(suffix.charAt(0)) {
296             case '.': {
297                 return getRVI(URIStringUtils.getRVIParent(rvi), suffix.substring(1));
298             }
299             case '#': {
300                 int segmentEnd = getSegmentEnd(suffix);
301                 return getRVI(rvi + "#" + suffix.substring(1, segmentEnd), suffix.substring(segmentEnd));
302             }
303             case '/': {
304                 int segmentEnd = getSegmentEnd(suffix);
305                 return getRVI(rvi + "/" + suffix.substring(1, segmentEnd), suffix.substring(segmentEnd));
306             }
307             default:
308                 return null;
309         }
310
311     }
312
313     public static Variable getRootVariable(ReadGraph graph) throws DatabaseException {
314         return graph.adapt(graph.getRootLibrary(), Variable.class);
315     }
316
317     public static Resource getPossibleIndexRoot(ReadGraph graph, Variable variable) throws DatabaseException {
318         return graph.syncRequest(new PossibleVariableIndexRoot(variable));
319     }
320
321     public static Resource getIndexRoot(ReadGraph graph, Variable variable) throws DatabaseException {
322         return graph.syncRequest(new VariableIndexRoot(variable));
323     }
324
325     public static Resource getModel(ReadGraph graph, Variable variable) throws DatabaseException {
326         String URI = variable.getURI(graph);
327         return VariablesImpl.getFirst(graph, SimulationResource.getInstance(graph).Model, URI, 8);
328     }
329
330     public static Resource getPossibleModel(ReadGraph graph, Variable variable) throws DatabaseException {
331         return graph.syncRequest(new PossibleVariableModel(variable));
332     }
333
334     public static Resource getProject(ReadGraph graph, Variable variable) throws DatabaseException {
335         String URI = variable.getURI(graph);
336         return VariablesImpl.getFirst(graph, ProjectResource.getInstance(graph).Project, URI, 8);
337     }
338
339     public static Variable getConfigurationContext(ReadGraph graph, Resource resource) throws DatabaseException {
340         SimulationResource SIMU = SimulationResource.getInstance(graph);
341         if(!graph.isInstanceOf(resource, SIMU.Model)) resource = graph.sync(new Model(resource));
342         Resource configurationResource = graph.getSingleObject(resource, SIMU.HasConfiguration);
343         return Variables.getVariable(graph, configurationResource);
344     }
345
346     public static Resource getConfigurationContextResource(ReadGraph graph, Resource resource) throws DatabaseException {
347         Variable config = getConfigurationContext(graph, resource);
348         return config.getRepresents(graph);
349     }
350
351     public static Variable getConfigurationContext(ReadGraph graph, Variable variable) throws DatabaseException {
352         SimulationResource SIMU = SimulationResource.getInstance(graph);
353         Resource model = Variables.getModel(graph, variable);
354         Resource configurationResource = graph.getSingleObject(model, SIMU.HasConfiguration);
355         return Variables.getVariable(graph, configurationResource);
356     }
357
358     public static Resource getPossibleConfigurationContextResource(ReadGraph graph, Resource resource) throws DatabaseException {
359         Variable config = getPossibleConfigurationContext(graph, resource);
360         return config != null ? config.getPossibleRepresents(graph) : null;
361     }
362
363     public static Variable getPossibleConfigurationContext(ReadGraph graph, Resource resource) throws DatabaseException {
364         SimulationResource SIMU = SimulationResource.getInstance(graph);
365         if (!graph.isInstanceOf(resource, SIMU.Model)) resource = graph.sync(new PossibleIndexRoot(resource));
366         if (resource == null)
367             return null;
368         Resource configurationResource = graph.getPossibleObject(resource, SIMU.HasConfiguration);
369         if (configurationResource == null)
370             return null;
371         return Variables.getPossibleVariable(graph, configurationResource);
372     }
373
374     public static Variable getPossibleConfigurationContext(ReadGraph graph, Variable variable) throws DatabaseException {
375         SimulationResource SIMU = SimulationResource.getInstance(graph);
376         Resource model = getPossibleIndexRoot(graph, variable);
377         if (model == null)
378             return null;
379         Resource configurationResource = graph.getPossibleObject(model, SIMU.HasConfiguration);
380         if (configurationResource == null)
381             return null;
382         return Variables.getPossibleVariable(graph, configurationResource);
383     }
384
385     public static Variable getConfigurationVariable(ReadGraph graph, Resource resource, String RVI) throws DatabaseException {
386         Variable context = getConfigurationContext(graph, resource);
387         return context.browse(graph, RVI);
388     }
389
390     public static Variable getConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException {
391         Variable context = getConfigurationContext(graph, variable);
392         RVI rvi = variable.getRVI(graph);
393         return rvi.resolve(graph, context);
394     }
395
396     public static Variable getPossibleConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException {
397         Variable context = getPossibleConfigurationContext(graph, variable);
398         if (context == null)
399             return null;
400         try {
401             RVI rvi = variable.getRVI(graph);
402             return rvi.resolvePossible(graph, context);
403         } catch (MissingVariableException e) {
404             // Ignore.
405             return null;
406         }
407     }
408
409     public static Datatype getDatatype(ReadGraph graph, Resource resource, RVI rvi) throws DatabaseException {
410         Variable var = rvi.resolve(graph, getConfigurationContext(graph, resource));
411         return var.getDatatype(graph);
412     }
413
414     @Deprecated
415     public static Resource getRealization(ReadGraph graph, Variable variable) throws DatabaseException {
416         String URI = variable.getURI(graph);
417         return VariablesImpl.getFirst(graph, Layer0X.getInstance(graph).Realization, URI, 8);
418     }
419
420     public static boolean isContext(ReadGraph graph, Variable variable) throws DatabaseException {
421         Resource type = variable.getPossibleType(graph);
422         if(type != null) {
423             if(graph.isInheritedFrom(type, Layer0.getInstance(graph).RVIContext)) return true;
424         }
425         return false;
426     }
427
428     public static Variable getPossibleContext(ReadGraph graph, Variable variable) throws DatabaseException {
429         if(isContext(graph, variable)) return variable;
430         Variable parent = variable.getParent(graph);
431         if(parent == null) return null;
432         return getPossibleContext(graph, parent);
433     }
434
435     public static Variable getContext(ReadGraph graph, Variable variable) throws DatabaseException {
436         Variable context = getPossibleContext(graph, variable);
437         if (context == null)
438             throw new MissingVariableException("No context found for " + variable.getURI(graph), variable.getPossibleRepresents(graph));
439         else return context;
440     }
441
442     public static RVI getRVI2(ReadGraph graph, Variable variable) throws DatabaseException {
443         return variable.getRVI(graph);
444     }
445
446     public static RVI getPossibleRVI2(ReadGraph graph, Variable variable) throws DatabaseException {
447         try {
448             return variable.getRVI(graph);
449         } catch (DatabaseException e) {
450             return null;
451         }
452     }
453
454     public static String getRVI(ReadGraph graph, Variable variable) throws DatabaseException {
455         Resource realizationResource = getRealization(graph, variable);
456         if (realizationResource == null)
457             throw new InvalidVariableException("No realization found for " + variable.getURI(graph));
458         return variable.getURI(graph).substring(graph.getURI(realizationResource).length()); 
459     }
460
461     public static String getRVI(ReadGraph graph, Resource config) throws DatabaseException {
462         Variable var = getVariable(graph, config);
463         return getRVI(graph, var);
464     }
465
466     public static String getPossibleRVI(ReadGraph graph, Resource config) throws DatabaseException {
467         Variable var = getPossibleVariable(graph, config);
468         return var != null ? getPossibleRVI(graph, var) : null;
469     }
470
471     public static String getPossibleRVI(ReadGraph graph, Variable variable) throws DatabaseException {
472         try {
473             return getRVI(graph, variable);
474         } catch (DatabaseException e) {
475             return null;
476         }
477     }
478
479     public static List<Variable> getPath(ReadGraph graph, Variable base, Variable var) throws DatabaseException {
480         if(!isChild(graph, base, var)) return null;
481         LinkedList<Variable> result = new LinkedList<Variable>();
482         var = var.getParent(graph);
483         while(!var.equals(base)) {
484             result.addFirst(var);
485             var = var.getParent(graph);
486         }
487         return result;
488     }
489
490     public static Variable getChild(ReadGraph graph, Variable base, Variable var) throws DatabaseException {
491         List<Variable> path = getPath(graph, base, var);
492         if(path == null || path.size() == 0) return null;
493         return path.get(0);
494     }
495
496     public static boolean isChild(ReadGraph graph, Variable base, Variable var) throws DatabaseException {
497         if(base.equals(var)) return false;
498         return var.getURI(graph).startsWith(base.getURI(graph));
499     }
500
501     public static Variable switchRealization(ReadGraph graph, Variable variable, Resource realization) throws DatabaseException {
502         Resource current = getRealization(graph, variable);
503         if (current == null)
504             throw new InvalidVariableException("No current realization found for variable");
505         return switchRealization(graph, variable, current, realization);
506     }
507
508     public static Variable switchPossibleContext(ReadGraph graph, Variable variable, Resource realization) throws DatabaseException {
509         Variable current = getPossibleContext(graph, variable);
510         if (current == null)
511             return null;
512         Resource currentContext = current.getPossibleRepresents(graph);
513         if (currentContext == null)
514             return null;
515         return switchPossibleRealization(graph, variable, currentContext, realization);
516     }
517
518     public static Variable switchRealization(ReadGraph graph, Variable variable, Variable realization) throws DatabaseException {
519         Resource current = getRealization(graph, variable);
520         return switchRealization(graph, variable, current, realization);
521     }
522
523     public static Variable switchRealization(ReadGraph graph, Variable variable, Resource currentRealization, Resource targetRealization) throws DatabaseException {
524         String currentURI = graph.getURI(currentRealization);
525         String targetURI = graph.getURI(targetRealization);
526         String variableURI = variable.getURI(graph);
527         String targetVariableURI = targetURI + variableURI.substring(currentURI.length());
528         return getVariable(graph, targetVariableURI);
529     }
530
531     public static Variable switchRealization(ReadGraph graph, Variable variable, Resource currentRealization, Variable targetRealization) throws DatabaseException {
532         String currentURI = graph.getURI(currentRealization);
533         String targetURI = targetRealization.getURI(graph);
534         String variableURI = variable.getURI(graph);
535         String targetVariableURI = targetURI + variableURI.substring(currentURI.length());
536         return getVariable(graph, targetVariableURI);
537     }
538
539     public static Variable switchPossibleRealization(ReadGraph graph, Variable variable, Resource currentRealization, Resource targetRealization) throws DatabaseException {
540         String currentURI = graph.getURI(currentRealization);
541         String targetURI = graph.getURI(targetRealization);
542         String variableURI = variable.getURI(graph);
543         String targetVariableURI = targetURI + variableURI.substring(currentURI.length());
544         return getPossibleVariable(graph, targetVariableURI);
545     }
546
547     public static Variable toConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException {
548         Variable config = getConfigurationContext(graph, variable);
549         return switchRealization(graph, variable, config);
550     }
551
552     public static Variable toPossibleConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException {
553
554         Resource represents = variable.getPossibleRepresents(graph);
555         if(represents == null) return null;
556         Resource config = getPossibleConfigurationContextResource(graph, represents);
557         if(config == null) return null;
558         return switchPossibleContext(graph, variable, config);
559
560     }
561
562     public static String toRVI(ReadGraph graph, List<Resource> compositePath) throws DatabaseException {
563         Layer0 L0 = Layer0.getInstance(graph);
564         StringBuilder rvi = new StringBuilder();
565         for (Resource composite : compositePath) {
566             String name = graph.getPossibleRelatedValue(composite, L0.HasName);
567             if (name == null)
568                 return null;
569
570             rvi.append('/');
571             String escapedName = URIStringUtils.escape(name);
572             rvi.append(escapedName);
573         }
574         return rvi.toString();
575     }
576
577     public static String appendRVI(ReadGraph graph, String modelURI, String rvi, Resource configuration) throws DatabaseException {
578         Layer0 L0 = Layer0.getInstance(graph);
579         String partName = graph.getPossibleRelatedValue(configuration, L0.HasName);
580         if (partName == null)
581             throw new MissingVariableException("Can not append a child corresponding to " + configuration + " to rvi '"
582                     + rvi + "' since there is no name.", configuration);
583         String escaped = URIStringUtils.escape(partName);
584         return rvi + "/" + escaped;
585     }
586
587     public static boolean isValid(ReadGraph graph, Variable variable) {
588         if(variable == null) return false;
589         try {
590             variable.getURI(graph);
591         } catch (DatabaseException e) {
592             return false;
593         }
594         return true;
595     }
596
597     public static Variable possibleChildWithType(ReadGraph graph, Variable variable, Resource targetType) throws DatabaseException {
598         Variable found = null;
599         for(Variable child : variable.getChildren(graph)) {
600             Resource type = child.getPossiblePropertyValue(graph, Variables.TYPE);
601             if(type != null && graph.isInheritedFrom(type, targetType)) {
602                 if(found != null) return null;
603                 found = child;
604             }
605         }
606         return found;
607     }
608
609     public static Collection<Variable> childrenWithType(ReadGraph graph, Variable variable, Resource targetType) throws DatabaseException {
610         ArrayList<Variable> result = new ArrayList<Variable>();
611         for(Variable child : variable.getChildren(graph)) {
612             Resource type = child.getPossiblePropertyValue(graph, Variables.TYPE);
613             if(graph.isInheritedFrom(type, targetType)) result.add(child);
614         }
615         return result;
616     }
617
618     public static <T> T adapt(ReadGraph graph, Variable variable, String property, Class<T> clazz) throws DatabaseException {
619         Resource resource = variable.getPropertyValue(graph, property);
620         return graph.adapt(resource, clazz);
621     }
622
623     public static <T> T getPossiblePropertyValue(RequestProcessor processor, Variable variable, Resource property, Binding binding) {
624         try {
625             return processor.sync(new TernaryRead<Variable,Resource,Binding,T>(variable, property, binding) {
626
627                 @Override
628                 public T perform(ReadGraph graph) throws DatabaseException {
629                     return parameter.getPossiblePropertyValue(graph, parameter2, parameter3);
630                 }
631
632             });
633         } catch (DatabaseException e) {
634             return null;
635         }
636     }
637
638     public static Variable possibleActiveVariable(ReadGraph graph, Variable variable) throws DatabaseException {
639         Variable activeVariable = graph.sync(new PossibleActiveVariableFromVariable(variable));
640         return activeVariable;
641     }
642
643     public static Variant requestNodeValue(ReadGraph graph, VariableNode<?> node) throws DatabaseException {
644         return requestNodeValue(graph, node, null);
645     }
646
647     public static Variant requestNodeValue(ReadGraph graph, VariableNode<?> node, final Binding binding) throws DatabaseException {
648         Variant value = graph.syncRequest(new NodeValueRequest(node, binding));
649         if(PENDING_NODE_VALUE == value && graph.getSynchronous()) {
650             // In this case a PENDING value was previously cached but now the value needs to be obtained for real.
651
652             ValueGetter getter = new ValueGetter(node, binding);
653             try {
654                 node.support.manager.getRealm().syncExec(getter);
655             } catch (InterruptedException e) {
656                 LOGGER.error("Error while getting node value", e);
657                 throw new InvalidVariableException("External data access error " + String.valueOf(node), e);
658             }
659
660             if (getter.exception != null)
661                 throw new MissingVariableValueException("No value for node " + node, getter.exception);
662
663             return getter.result;
664         }
665         return value; 
666     }
667
668     public static class NodeStructure {
669         // Immutable but wrapped with Collections.unmodifiableMap as an optimization
670         public final Map<String,Object> children;
671         // Immutable but not wrapped with Collections.unmodifiableMap as an optimization
672         public final Map<String,Object> properties;
673         private final int hash;
674
675         public NodeStructure(Map<String, Object> children, Map<String, Object> properties) {
676             this.children = children;
677             this.properties = properties;
678             this.hash = calcHash();
679         }
680
681         private int calcHash() {
682             return 31*children.hashCode() + 41*properties.hashCode();
683         }
684
685         @Override
686         public int hashCode() {
687             return hash;
688         }
689
690         @Override
691         public boolean equals(Object object) {
692             if (this == object)
693                 return true;
694             else if (object == null || object == Variables.PENDING_NODE_STRUCTURE)
695                 return false;
696             else if (!(object instanceof NodeStructure))
697                 return false;
698             NodeStructure r = (NodeStructure)object;
699             return r.children.equals(children) && r.properties.equals(properties);
700         }
701     }
702
703     public static NodeStructure requestNodeStructure(ReadGraph graph, VariableNode<?> node) throws DatabaseException {
704         NodeStructure value = graph.syncRequest(new NodeStructureRequest(node));
705         if (value == null)
706             throw new InvalidVariableException("External data access error " + String.valueOf(node));
707         if(PENDING_NODE_STRUCTURE == value && graph.getSynchronous()) {
708             // In this case a PENDING value was previously cached but now the value needs to be obtained for real.
709
710             StructureGetter getter = new StructureGetter(node);
711             try {
712                 node.support.manager.getRealm().syncExec(getter);
713             } catch (InterruptedException e) {
714                 LOGGER.error("Error while getting node structure", e);
715                 throw new InvalidVariableException("External data access error " + String.valueOf(node), e);
716             }
717
718             if (getter.exception != null)
719                 throw new InvalidVariableException("External data access error " + String.valueOf(node), getter.exception);
720             if (getter.result == null)
721                 throw new InvalidVariableException("External data access error " + String.valueOf(node));
722
723             return getter.result;
724
725         }
726         return value; 
727
728     }
729
730     public static String getPossibleUnit(ReadGraph graph, Variable variable) throws DatabaseException {
731
732         try {
733
734             Resource predicate = variable.getPossiblePredicateResource(graph);
735             if(predicate != null) {
736                 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate));
737                 if(info.definedUnit != null) return info.definedUnit;
738             }
739
740             Variant variant = variable.getVariantValue(graph);
741             Binding binding = variant.getBinding();
742             if(binding == null) return null;
743             Datatype dt = binding.type();
744             if(!(dt instanceof NumberType)) return null;
745             NumberType nt = (NumberType)dt;
746             return nt.getUnit();
747
748         } catch (DatabaseException e) {
749             return null;
750         }
751
752     }
753
754     /**
755      * @param graph
756      * @param rvi
757      * @param context1 primary context to use for resolving the specified RVI, must not be null
758      * @param context2 secondary context to use for resolving the specified RVI, may be <code>null</code>
759      * @return pair of variables where first is the resolved variable and second
760      *         is the context it was resolved with
761      * @throws DatabaseException
762      * @since 1.18.1
763      */
764     public static Pair<Variable, Variable> resolvePossible(ReadGraph graph, RVI rvi, Variable context1, Variable context2) throws DatabaseException {
765         Variable v = rvi.resolvePossible(graph, context1);
766         if (v != null)
767             return new Pair<>(v, context1);
768         if (context2 != null) {
769             v = rvi.resolvePossible(graph, context2);
770             if (v != null)
771                 return new Pair<>(v, context2);
772         }
773         return null;
774     }
775
776
777     @SuppressWarnings("rawtypes")
778     private static class ValueGetter implements VariableNodeReadRunnable {
779
780         final VariableNode n;
781         final Binding binding;
782         Variant result;
783         Exception exception;
784
785         public ValueGetter(VariableNode n, Binding binding) {
786             this.n = n;
787             this.binding = binding;
788         }
789
790         @SuppressWarnings("unchecked")
791         @Override
792         public void run() {
793             try {
794                 if (binding != null)
795                     result = new Variant(binding, n.support.manager.getValue(n.node, binding));
796                 else
797                     result = n.support.manager.getValue(n.node);
798             } catch (NodeManagerException e) {
799                 exception = e;
800             } catch (Exception e) {
801                 LOGGER.error("Error while getting node value", e);
802                 exception = e;
803             }
804         }
805
806     }
807
808     @SuppressWarnings("rawtypes")
809     private static class StructureGetter implements VariableNodeReadRunnable {
810
811         final VariableNode n;
812         NodeStructure result;
813         Exception exception;
814
815         public StructureGetter(VariableNode n) {
816             this.n = n;
817         }
818
819         @Override
820         public void run() {
821             try {
822                 result = NodeStructureRequest.get(n);
823             } catch (NodeManagerException e) {
824                 exception = e;
825             }
826          }
827
828     };
829
830     public static Variable tryGetProperty(ReadGraph graph, Resource entity, Resource property) throws DatabaseException {
831         Variable v = Variables.getPossibleVariable(graph, entity);
832         return v != null ? v.getPossibleProperty(graph, property) : null;
833     }
834
835     public static ValueAccessor createValueAccessor(Function1<Variable, Object> getValue1, Function2<Variable, Binding, Object> getValue2,
836             Function2<Variable, Object, Object> setValue2, Function3<Variable, Object, Binding, Object> setValue3,
837             Function1<Variable, Datatype> getDatatype) {
838         return new SCLValueAccessor(getValue1, getValue2, setValue2, setValue3, getDatatype);
839     }
840
841     public static void setRVIProperty(WriteGraph graph, Variable variable, RVI rvi) throws DatabaseException {
842         Layer0 L0 = Layer0.getInstance(graph);
843         Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class );
844         Resource predicate = variable.getPredicateResource(graph);
845         Resource subject = variable.getParent(graph).getRepresents(graph);
846         graph.deny(subject, predicate);
847         graph.claimLiteral(subject, predicate, L0.RVI, rvi, rviBinding);
848     }
849
850 }