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