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