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