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