]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/function/All.java
Tried to improve the implementation of getPossibleDomainProperty
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / function / All.java
1 package org.simantics.db.layer0.function;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.concurrent.atomic.AtomicReference;
11
12 import org.simantics.databoard.Bindings;
13 import org.simantics.databoard.accessor.reference.ChildReference;
14 import org.simantics.databoard.accessor.reference.IndexReference;
15 import org.simantics.databoard.adapter.AdaptException;
16 import org.simantics.databoard.binding.Binding;
17 import org.simantics.databoard.binding.error.BindingConstructionException;
18 import org.simantics.databoard.binding.error.BindingException;
19 import org.simantics.databoard.binding.mutable.Variant;
20 import org.simantics.databoard.type.ArrayType;
21 import org.simantics.databoard.type.Datatype;
22 import org.simantics.databoard.type.NumberType;
23 import org.simantics.databoard.util.Range;
24 import org.simantics.db.Issue;
25 import org.simantics.db.ReadGraph;
26 import org.simantics.db.Resource;
27 import org.simantics.db.Statement;
28 import org.simantics.db.WriteGraph;
29 import org.simantics.db.common.issue.StandardIssue;
30 import org.simantics.db.common.primitiverequest.PossibleRelatedValueImplied2;
31 import org.simantics.db.common.primitiverequest.PossibleResource;
32 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
33 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
34 import org.simantics.db.common.request.IsEnumeratedValue;
35 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
36 import org.simantics.db.common.utils.CommonDBUtils;
37 import org.simantics.db.common.utils.Functions;
38 import org.simantics.db.common.utils.ListUtils;
39 import org.simantics.db.common.utils.Logger;
40 import org.simantics.db.common.utils.NameUtils;
41 import org.simantics.db.common.validation.L0Validations;
42 import org.simantics.db.exception.DatabaseException;
43 import org.simantics.db.exception.DoesNotContainValueException;
44 import org.simantics.db.exception.NoSingleResultException;
45 import org.simantics.db.exception.RuntimeDatabaseException;
46 import org.simantics.db.layer0.exception.MissingVariableValueException;
47 import org.simantics.db.layer0.exception.PendingVariableException;
48 import org.simantics.db.layer0.exception.VariableException;
49 import org.simantics.db.layer0.request.PossibleURI;
50 import org.simantics.db.layer0.request.PropertyInfo;
51 import org.simantics.db.layer0.request.PropertyInfoRequest;
52 import org.simantics.db.layer0.request.UnescapedAssertedPropertyMapOfResource;
53 import org.simantics.db.layer0.request.UnescapedMethodMapOfResource;
54 import org.simantics.db.layer0.request.UnescapedPropertyMapOfResource;
55 import org.simantics.db.layer0.scl.CompileResourceValueRequest;
56 import org.simantics.db.layer0.scl.CompileValueRequest;
57 import org.simantics.db.layer0.util.Layer0Utils;
58 import org.simantics.db.layer0.util.PrimitiveValueParser;
59 import org.simantics.db.layer0.variable.AbstractVariable;
60 import org.simantics.db.layer0.variable.ChildVariableMapRequest;
61 import org.simantics.db.layer0.variable.ExternalSetValue;
62 import org.simantics.db.layer0.variable.PropertyVariableMapRequest;
63 import org.simantics.db.layer0.variable.StandardAssertedGraphPropertyVariable;
64 import org.simantics.db.layer0.variable.StandardComposedProperty;
65 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
66 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
67 import org.simantics.db.layer0.variable.SubliteralPropertyVariable;
68 import org.simantics.db.layer0.variable.SubliteralPropertyVariableDeprecated;
69 import org.simantics.db.layer0.variable.ValueAccessor;
70 import org.simantics.db.layer0.variable.Variable;
71 import org.simantics.db.layer0.variable.VariableBuilder;
72 import org.simantics.db.layer0.variable.VariableMap;
73 import org.simantics.db.layer0.variable.VariableMapImpl;
74 import org.simantics.db.layer0.variable.VariableNode;
75 import org.simantics.db.layer0.variable.VariableNodeReadRunnable;
76 import org.simantics.db.layer0.variable.VariableUtils;
77 import org.simantics.db.layer0.variable.Variables;
78 import org.simantics.db.layer0.variable.Variables.NodeStructure;
79 import org.simantics.db.service.ClusteringSupport;
80 import org.simantics.db.service.UndoRedoSupport;
81 import org.simantics.issues.ontology.IssueResource;
82 import org.simantics.layer0.Layer0;
83 import org.simantics.scl.reflection.annotations.SCLValue;
84 import org.simantics.scl.runtime.SCLContext;
85 import org.simantics.scl.runtime.function.Function4;
86 import org.simantics.scl.runtime.function.FunctionImpl1;
87 import org.simantics.scl.runtime.function.FunctionImpl2;
88 import org.simantics.simulator.variable.exceptions.NodeManagerException;
89 import org.simantics.utils.Development;
90 import org.simantics.utils.datastructures.Pair;
91 import org.simantics.utils.strings.StringInputValidator;
92
93 import gnu.trove.map.hash.THashMap;
94 import gnu.trove.set.hash.THashSet;
95
96 public class All {
97
98         public static Object standardGetValue1(ReadGraph graph, Variable context) throws DatabaseException {
99
100                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
101
102         // First from node
103         if(variable.node != null) {
104             Variant value = Variables.requestNodeValue(graph, variable.node);
105             if(Variables.PENDING_NODE_VALUE == value) throw new PendingVariableException("");
106             return value.getValue();
107         }
108                 
109                 try {
110
111             if(variable.property.hasEnumerationRange) {
112                         Resource object = variable.getRepresents(graph);
113                                 if(graph.sync(new IsEnumeratedValue(object))) {
114                             Layer0 L0 = Layer0.getInstance(graph);
115                                         if(graph.isInstanceOf(object, L0.Literal)) {
116                                                 return graph.getValue(object);
117                                         } else {
118                                                 String label = graph.getPossibleRelatedValue2(variable.getRepresents(graph), L0.HasLabel, Bindings.STRING);
119                                                 if(label == null) label = graph.getPossibleRelatedValue(variable.getRepresents(graph), L0.HasName, Bindings.STRING);
120                                                 if(label == null) label = "<no label>";
121                                                 return label;
122                                         }
123                                 }
124             }
125                         
126             if (variable.isAsserted()) {
127                                 if (variable.parentResource != null) {
128                                         Map<String, Pair<PropertyInfo, Resource>> assertions = graph.syncRequest(
129                                                         new UnescapedAssertedPropertyMapOfResource(variable.parentResource),
130                                                         TransientCacheAsyncListener.instance());
131
132                                         // NOTE: This optimization assumes the property
133                                         // variable's representation is the asserted object.
134                                         Resource object = variable.getPossibleRepresents(graph);
135                                         if (object != null) {
136                                                 return graph.getValue2(object, variable);
137                                         } else {
138                                                 for (Pair<PropertyInfo, Resource> assertion : assertions.values()) {
139                                                         if (assertion.first.predicate.equals(variable.property.predicate)) {
140                                                                 return graph.getValue2(assertion.second, variable);
141                                                         }
142                                                 }
143                                         }
144                                 }
145             }
146                 
147                         return graph.getValue2(variable.getRepresents(graph), variable);
148                         
149                 } catch (NoSingleResultException e) {
150                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
151                 } catch (DoesNotContainValueException e) {
152                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
153                 } catch (DatabaseException e) {
154                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
155                 }
156
157         }
158         
159         public static Object standardGetValue2(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {           
160                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
161
162         // First from node
163         if(variable.node != null) {
164             try {
165                 Variant value = Variables.requestNodeValue(graph, variable.node, binding);
166                 if(Variables.PENDING_NODE_VALUE == value) throw new PendingVariableException("");
167                 if(value == null) throw new MissingVariableValueException(variable.getPossibleURI(graph));
168                 return value.getValue(binding);
169             } catch (AdaptException e) {
170                 throw new DatabaseException(e);
171             }
172         }
173                 
174         try {
175                         
176                 if(variable.property.hasEnumerationRange) {
177                 Resource object = variable.getRepresents(graph);
178                 if(graph.sync(new IsEnumeratedValue(object))) {
179                         Layer0 L0 = Layer0.getInstance(graph);
180                         if(graph.isInstanceOf(object, L0.Literal)) {
181                                 return graph.getValue(object, binding);
182                         } else {
183                                 return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel, binding);
184                         }
185                 }
186             }
187                         
188                 if (variable.isAsserted()) {
189                         if (variable.parentResource != null) {
190                                         Map<String, Pair<PropertyInfo, Resource>> assertions = graph.syncRequest(
191                                                         new UnescapedAssertedPropertyMapOfResource(variable.parentResource),
192                                                         TransientCacheAsyncListener.instance());
193
194                                         // NOTE: This optimization assumes the property
195                                         // variable's representation is the asserted object.
196                                         Resource object = variable.getPossibleRepresents(graph);
197                                         if (object != null) {
198                                                 return graph.getValue2(object, variable, binding);
199                                         } else {
200                                                 for (Pair<PropertyInfo, Resource> assertion : assertions.values()) {
201                                                         if (assertion.first.predicate.equals(variable.property.predicate)) {
202                                                                 return graph.getValue2(assertion.second, variable, binding);
203                                                         }
204                                                 }
205                                         }
206                         }
207                 }
208                         
209                         return graph.getValue2(variable.getRepresents(graph), context, binding);
210                         
211                 } catch (NoSingleResultException e) {
212                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
213                 } catch (DoesNotContainValueException e) {
214                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
215                 } catch (DatabaseException e) {
216                         throw new MissingVariableValueException(variable.getPossibleURI(graph), e);
217                 }
218
219         }
220
221         public static void standardSetValue2(WriteGraph graph, Variable context, final Object value) throws DatabaseException {
222                 
223             if(context instanceof StandardGraphPropertyVariable) {
224
225                 final StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; 
226                 
227                 // First from node
228             if(variable.node != null) {
229
230                 final Binding binding = Layer0Utils.getDefaultBinding(graph, variable);
231
232                 final AtomicReference<Object> oldValueRef = new AtomicReference<Object>();
233                 try {
234                     variable.node.support.manager.getRealm().syncExec(new Runnable() {
235                         @Override
236                         public void run() {
237                             try {
238                                 oldValueRef.set(getNodeValue(variable, binding));
239                                 setNodeValue(variable, value, binding);
240                             } catch (NodeManagerException e) {
241                                 throw new RuntimeException(e);
242                             } catch (BindingException e) {
243                                     throw new RuntimeException(e);
244                                 }
245                         }
246                     });
247                 } catch(RuntimeException e) {
248                     if(e.getCause() instanceof NodeManagerException || e.getCause() instanceof BindingException)
249                         throw new DatabaseException(e.getCause());
250                     else
251                         throw e;
252                 } catch (InterruptedException e) {
253                     throw new DatabaseException(e);
254                 }
255
256                 ExternalSetValue ext = new ExternalSetValue(variable.node.support.manager, variable.node.node,
257                         oldValueRef.get(), value, binding);
258                 graph.getService(UndoRedoSupport.class).addExternalOperation(graph, ext);
259
260                 return;
261             }
262                 
263             }
264             
265                 Function4<WriteGraph, Variable, Object, Object, String> modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
266                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
267                 try {
268                         modifier.apply(graph, context, value, Bindings.getBinding(value.getClass()));
269                 } catch (BindingConstructionException e) {
270                         throw new DatabaseException(e);
271                 }
272
273         }
274
275         public static void standardSetValue3(final WriteGraph graph, Variable context, final Object value, final Binding binding) throws DatabaseException {
276
277         // First from node
278         if(context instanceof StandardGraphPropertyVariable) {
279
280             final StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; 
281             
282             // First from node
283             if(variable.node != null) {
284                 
285                 try {
286                     
287                     variable.node.support.manager.getRealm().syncExec(new Runnable() {
288
289                         @Override
290                         public void run() {
291                             try {
292                                 Object oldValue = getNodeValue(variable, binding);
293                                 setNodeValue(variable, value, binding);
294                                 ExternalSetValue ext = new ExternalSetValue(variable.node.support.manager, variable.node.node, oldValue, value, binding);
295                                 graph.getService(UndoRedoSupport.class).addExternalOperation(graph, ext);
296                             } catch (NodeManagerException | BindingException e) {
297                                 Logger.defaultLogError(e);
298                             }
299                         }
300
301                         
302                     });
303                     
304                     return;
305                     
306                 } catch (InterruptedException e) {
307                     throw new DatabaseException(e);
308                 }
309                 
310             }
311             
312         }
313             
314                 Function4<WriteGraph, Variable, Object, Object, String> modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
315                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
316                 modifier.apply(graph, context, value, binding);
317
318         }
319
320         public static Datatype getDatatypeFromValue(ReadGraph graph, Variable context) throws DatabaseException {
321                 if (context instanceof AbstractVariable) {
322                         Binding defaultBinding = ((AbstractVariable)context).getPossibleDefaultBinding(graph);
323                         if (defaultBinding != null)
324                                 return defaultBinding.type();
325                 }
326                         
327         Variant value = context.getVariantValue(graph);
328         if (value.getBinding() == null)
329                 throw new DatabaseException("No value binding for " + context.getURI(graph));
330         
331         return value.getBinding().type();
332         }
333
334     @SuppressWarnings("rawtypes")
335     private static class DatatypeGetter implements VariableNodeReadRunnable {
336         final VariableNode node;
337         Datatype type;
338         Exception exception;
339
340         public DatatypeGetter(VariableNode node) {
341             this.node = node;
342         }
343
344         @SuppressWarnings("unchecked")
345         @Override
346         public void run() {
347             try {
348                 type = node.support.manager.getDatatype(node.node);
349             } catch (NodeManagerException e) {
350                 exception = e;
351             }
352         }
353         @Override
354         public String toString() {
355             return "DatatypeGetter(" + node.node + ")";
356         }
357     }
358
359     public static Datatype standardGetDatatype(ReadGraph graph, Variable context) throws DatabaseException {
360         if (context instanceof AbstractVariable) {
361                 final AbstractVariable variable = (AbstractVariable)context;
362                 if (variable.node != null) {
363                         try {
364                                 DatatypeGetter request = new DatatypeGetter(variable.node);
365                                 
366                                         variable.node.support.manager.getRealm().syncExec(request);
367                                         
368                                         if (request.exception != null)
369                                                 throw new DatabaseException(request.exception);
370                                         
371                                         return request.type;
372                                 } catch (InterruptedException e) {
373                                 }
374                 }
375         }
376         
377         return getDatatypeFromValue(graph, context);
378         }
379
380 //      @SCLValue(type = "ValueAccessor")
381 //      public static ValueAccessor standardValueAccessor = new ValueAccessor() {
382 //
383 //              @Override
384 //              public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
385 //                      return standardGetValue(graph, (StandardGraphPropertyVariable)context);
386 //              }
387 //
388 //              @Override
389 //              public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
390 //                      return standardGetValue(graph, context, binding);
391 //              }
392 //
393 //              @Override
394 //              public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
395 //                      standardSetValue(graph, context, value);
396 //              }
397 //
398 //              @Override
399 //              public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
400 //                      standardSetValue(graph, context, value, binding);
401 //              }
402 //
403 //              @Override
404 //              public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
405 //                      return standardGetDatatype(graph, context);
406 //              }
407 //              
408 //      };
409         
410         @SCLValue(type = "ValueAccessor")
411         public static ValueAccessor standardValueAccessor = new ValueAccessor() {
412
413                 @Override
414                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
415                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
416                         if(accessor != null) return accessor.getValue(graph, context);
417                         else 
418                                 return standardGetValue1(graph, context);
419                 }
420
421                 @Override
422                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
423                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
424                         if(accessor != null) return accessor.getValue(graph, context, binding);
425                         else 
426                                 return standardGetValue2(graph, context, binding);
427                 }
428
429                 @Override
430                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
431                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
432                         if(accessor != null) accessor.setValue(graph, context, value);
433                         else 
434                                 standardSetValue2(graph, context, value);
435                 }
436
437                 @Override
438                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
439                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
440                         if(accessor != null) accessor.setValue(graph, context, value, binding);
441                         else 
442                                 standardSetValue3(graph, context, value, binding);
443                 }
444
445                 @Override
446                 public Datatype getDatatype(ReadGraph graph, Variable context)
447                                 throws DatabaseException {
448                         ValueAccessor accessor = getPossibleValueValueAccessor(graph, context);
449                         if(accessor != null) return accessor.getDatatype(graph, context);
450                         else 
451                                 return standardGetDatatype(graph, context);
452                 }
453                 
454         };
455
456         public static Variable getStandardChildDomainPropertyVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
457         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
458         PropertyInfo graphProperty = getPossiblePropertyInfoFromContext(graph, variable, variable.resource, name);
459         return getStandardChildDomainPropertyVariable(graph, context, graphProperty, name);
460     }
461
462     public static Resource getPossiblePropertyResource(ReadGraph graph, AbstractVariable parent, Object node) throws DatabaseException {
463         if(parent != null && parent.node != null && parent.node.node != null && parent.node.support != null) {
464             String propertyURI = getPossiblePropertyURI(parent, node);
465             if(propertyURI != null)
466                 return graph.getPossibleResource(propertyURI);
467         }
468         return null;
469     }
470
471         public static Variable getStandardChildDomainPropertyVariable(ReadGraph graph, Variable context, PropertyInfo graphProperty, String name) throws DatabaseException {
472         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
473         Object propertyNode = getPossibleNodeProperty(graph, variable, name, true);
474         if(graphProperty != null && graphProperty.builder != null)
475             return buildPropertyVariable(graph, variable, variable.resource, graphProperty, propertyNode);
476         if(propertyNode != null) {
477             // Fallback: try to ask property resource uri from NodeManager
478             return createStandardGraphPropertyVariable(graph, variable, propertyNode);
479         }
480         // Final fallback: check types corresponding to
481         // node classification(s) and look for asserted
482         // properties from the URIs specified.
483         if (variable.node != null) {
484             try {
485                 @SuppressWarnings("unchecked")
486                 Set<String> classifications = variable.node.support.manager.getClassifications(variable.node.node);
487                 if (!classifications.isEmpty()) {
488                     for (String uri : classifications) {
489                         Resource type = graph.syncRequest(
490                                 new PossibleResource(uri),
491                                 TransientCacheAsyncListener.instance());
492                         if (type == null)
493                             continue;
494                         Map<String, Pair<PropertyInfo, Resource>> pm = graph.syncRequest(
495                                 new UnescapedAssertedPropertyMapOfResource(type),
496                                 TransientCacheAsyncListener.instance());
497                         Pair<PropertyInfo, Resource> pi = pm.get(name);
498                         if (pi != null) {
499                             return new StandardAssertedGraphPropertyVariable(graph, context, null, type, pi.first.predicate, pi.second);
500                         }
501                     }
502                 }
503             } catch(NodeManagerException e) {
504                 throw new DatabaseException(e);
505             }
506         }
507         return null;
508     }
509
510     public static Map<String, Variable> getStandardChildDomainPropertyVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
511         // Get properties with null identification
512         return getStandardChildDomainPropertyVariables(graph, context, null, map);
513     }
514
515     public static Map<String, Variable> getStandardChildDomainPropertyVariables(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
516         
517         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
518         
519         Collection<Object> nodeProperties = getPossibleNodeProperties(graph, variable);
520         if(!nodeProperties.isEmpty()) {
521
522             // Get variables for properties read from the graph
523             Map<String,PropertyInfo> graphProperties = collectPropertyInfosFromContext(graph, variable, variable.resource);
524             
525             Set<String> used = new THashSet<String>(nodeProperties.size());
526             
527             map = ensureVariableMap(map, graphProperties.size() + nodeProperties.size());
528             
529             // Process NodeManager property nodes
530             for(Object nodeProperty : nodeProperties) {
531                 String name = getNodeName(variable, nodeProperty);
532                 used.add(name);
533                 
534                 PropertyInfo graphProperty = graphProperties.get(name); 
535                 if(graphProperty != null && graphProperty.builder != null) {
536                     if (classification != null && !graphProperty.hasClassification(classification)) continue;
537                     
538                     // Combine with identically named graph property
539                     map.put(name, buildPropertyVariable(graph, variable, variable.resource, graphProperty, nodeProperty));
540                     continue;
541                 }
542                 
543                 map.put(name, createStandardGraphPropertyVariable(graph, variable, nodeProperty));
544             }
545             
546             // Process graph properties
547             for(PropertyInfo info : graphProperties.values()) {
548                 String name = info.name;
549                 if(used != null && used.contains(name)) continue;
550                 if (classification != null && !info.hasClassification(classification)) continue;
551                 if (info.builder != null) {
552                     map.put(name, buildPropertyVariable(graph, variable, variable.resource, info, null));
553                 }
554             }
555             return map;
556                 
557         } else {
558
559                 if(variable.resource == null) return map;
560
561                 // Only graph properties
562                 Collection<Resource> predicates = graph.getPredicates(variable.resource);
563                 if(predicates.isEmpty()) return map;
564                 
565                 map = ensureVariableMap(map, predicates.size());
566                 
567             // Process graph properties
568             for(Resource predicate : predicates) {
569                 
570                         PropertyInfo info = graph.isImmutable(predicate) ?
571                                         graph.syncRequest(new PropertyInfoRequest(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
572                                                 graph.syncRequest(new PropertyInfoRequest(predicate));
573
574                         if(!info.isHasProperty) continue;
575                                         
576                 if (classification != null && !info.hasClassification(classification)) continue;
577                 if (info.builder != null) {
578                     map.put(info.name, buildPropertyVariable(graph, variable, variable.resource, info, null));
579                 }
580                 
581             }
582             
583             return map;
584                 
585         }
586         
587      }
588         
589     @SCLValue(type = "VariableMap")
590         public static VariableMap standardChildDomainProperties = new VariableMapImpl() {
591         
592                 @Override
593                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
594                 return getStandardChildDomainPropertyVariable(graph, context, name);
595                 }
596
597                 @Override
598                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
599                     return getStandardChildDomainPropertyVariables(graph, context, map);
600                 }
601                 
602         };
603         
604     @SCLValue(type = "VariableMap")
605         public static VariableMap methodsPropertyDomainProperties = new VariableMapImpl() {
606         
607                 @Override
608                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
609                         Variable parent = context.getParent(graph);
610                         Resource container = parent.getPossibleRepresents(graph);
611                         if(container == null)
612                             return null;
613                         Map<String,Resource> methods = graph.syncRequest(new UnescapedMethodMapOfResource(container));
614                         Resource predicate = methods.get(name);
615                         if(predicate != null) {
616                                 Layer0 L0 = Layer0.getInstance(graph);
617                                 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(L0.Entity_method));
618                                 Resource value = graph.getSingleObject(container, predicate);
619                                 return new StandardGraphPropertyVariable(context, null, container, info, value);
620                         }
621                         return null;
622                 }
623
624                 @Override
625                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
626                         Variable parent = context.getParent(graph);
627                         Resource container = parent.getPossibleRepresents(graph);
628                         Map<String,Resource> methods = graph.syncRequest(new UnescapedMethodMapOfResource(container));
629                         for(Map.Entry<String, Resource> entry : methods.entrySet()) {
630                                 String name = entry.getKey();
631                                 Resource predicate = entry.getValue();
632                                 Layer0 L0 = Layer0.getInstance(graph);
633                                 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(L0.Entity_method));
634                                 Resource value = graph.getSingleObject(container, predicate);
635                                 if(map == null) map = new HashMap<>();
636                                 map.put(name, new StandardGraphPropertyVariable(context, null, container, info, value));
637                         }
638                         return map;
639                 }
640                 
641         };
642
643         public static Variable getStandardPropertyDomainPropertyVariableFromValue(ReadGraph graph, Variable context, String name) throws DatabaseException {
644
645                 if(context instanceof StandardGraphPropertyVariable) {
646                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
647                 Resource literal = variable.getPossibleRepresents(graph);
648                 Object propertyNode = getPossibleNodeProperty(graph, variable, name, false);
649
650                 if(literal != null) {
651                         Variable result = getPossiblePropertyFromContext(graph, variable, literal, name, propertyNode);
652                         if(result != null) return result;
653                 }
654                 
655                 Variable result = getPossibleSubliteralPropertyFromContext(graph, variable, name);
656                 if(result != null) return result;
657                 result = getPossiblePropertyFromContext(graph, variable, variable.property.predicate, name, propertyNode);
658                 if (result != null) return result;
659                 
660                 // Get possible property from NodeManager
661                 if (propertyNode != null)
662                         return createStandardGraphPropertyVariable(graph, variable, propertyNode);
663                 return null;
664                 } else if (context instanceof StandardGraphChildVariable) {
665                         return standardChildDomainProperties.getVariable(graph, context, name);
666                 } else {
667                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
668                 }                       
669                 
670         }
671         
672         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
673
674                 if(context instanceof StandardGraphPropertyVariable) {
675                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
676                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, map);
677                         if (variable.parentResource != null) {
678                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
679                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, map);
680                                 map=collectSubliteralProperties(graph, variable, map);
681                         }
682
683                         // Get properties from VariableNode
684                         map = getStandardNodePropertyVariables(graph, context, map);
685                         return map;
686                 } else if (context instanceof StandardGraphChildVariable) {
687                         return standardChildDomainProperties.getVariables(graph, context, map);
688                 } else {
689                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
690                 }
691                 
692         }
693         
694         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
695
696                 if(context instanceof StandardGraphPropertyVariable) {
697                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
698                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, classification, map);
699                         if (variable.parentResource != null) {
700                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
701                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, classification, map);
702                         }
703                         
704                         // Get properties from VariableNode
705                         map = getStandardNodePropertyVariables(graph, context, map);
706                         return map;
707                 } else if (context instanceof StandardGraphChildVariable) {
708                         return standardChildDomainProperties.getVariables(graph, context, map);
709                 } else {
710                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
711                 }       
712                 
713         }       
714         
715     @SCLValue(type = "VariableMap")
716         public static VariableMap standardPropertyDomainProperties = new VariableMapImpl() {
717
718         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
719                 Resource represents = context.getPossibleRepresents(graph);
720                 if(represents == null) return null;
721                 
722                 VariableMap map = graph.isImmutable(represents) ?
723                                 graph.syncRequest(new PropertyVariableMapRequest(represents), TransientCacheListener.<VariableMap>instance()) :
724                                         (VariableMap)graph.getPossibleRelatedValue2(represents, Layer0.getInstance(graph).domainProperties, represents);
725                 
726                 if(map == standardPropertyDomainProperties) return null;
727                 else return map;
728                 
729         }
730         
731                 @Override
732                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
733                         VariableMap valueMap = getValueVariableMap(graph, context);
734                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
735                         return getStandardPropertyDomainPropertyVariableFromValue(graph, context, name);
736                 }
737
738                 @Override
739                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
740                         VariableMap valueMap = getValueVariableMap(graph, context);
741                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
742                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, map);
743                 }
744                 
745                 @Override
746                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
747                         VariableMap valueMap = getValueVariableMap(graph, context);
748                         if(valueMap != null) return valueMap.getVariables(graph, context, classification, map);
749                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, classification, map);
750                 }
751                 
752         };
753
754     public static Resource getPossibleGraphChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
755         Resource resource = variable.getPossibleRepresents(graph);
756         if(resource == null) return null;
757         Map<String, Resource> graphChildren = graph.syncRequest(new UnescapedChildMapOfResource(resource));
758         return graphChildren.get(name);
759     }
760
761     public static Map<String,Resource> getPossibleGraphChildren(ReadGraph graph, Variable variable) throws DatabaseException {
762         Resource resource = variable.getPossibleRepresents(graph);
763         if(resource == null) return Collections.emptyMap();
764         return graph.syncRequest(new UnescapedChildMapOfResource(resource));
765     }
766
767     public static Object getPossibleNodeChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
768         if (!(variable instanceof AbstractVariable)) return null;
769         VariableNode<?> node = ((AbstractVariable)variable).node;
770         if(node == null) return null;
771         NodeStructure structure = Variables.requestNodeStructure(graph, node);
772         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
773         return structure.children.get(name);
774         }
775         
776     public static Collection<Object> getPossibleNodeChildren(ReadGraph graph, Variable variable) throws DatabaseException {
777         if (!(variable instanceof AbstractVariable)) return null;
778         VariableNode<?> node = ((AbstractVariable)variable).node;
779         if(node == null) return Collections.emptyList();
780         NodeStructure structure = Variables.requestNodeStructure(graph, node);
781         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
782         return structure.children.values();
783     }
784     
785     public static Object getPossibleNodeProperty(ReadGraph graph, Variable variable, String name, boolean throwPending) throws DatabaseException {
786         if (!(variable instanceof AbstractVariable)) return null;
787         VariableNode<?> node = ((AbstractVariable)variable).node;
788         if(node == null) return null;
789         NodeStructure structure = Variables.requestNodeStructure(graph, node);
790         if(throwPending && Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
791         return structure.properties.get(name);
792     }
793     
794     public static Collection<Object> getPossibleNodeProperties(ReadGraph graph, Variable variable) throws DatabaseException {
795         if (!(variable instanceof AbstractVariable)) return null;
796         VariableNode<?> node = ((AbstractVariable)variable).node;
797         if(node == null) return Collections.emptyList();
798         NodeStructure structure = Variables.requestNodeStructure(graph, node);
799         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
800         return structure.properties.values();
801     }
802
803     @SuppressWarnings({ "rawtypes", "unchecked" })
804     public static VariableNode build(VariableNode parent, Object node) {
805         if(node == null) return null;
806         return new VariableNode(parent.support, node);
807     }
808
809     @Deprecated
810     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
811         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
812     }
813
814     @Deprecated
815     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, Resource graphChild, String name) throws DatabaseException {
816         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, graphChild, name);
817     }
818         
819     @Deprecated
820     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
821         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
822     }
823
824     @Deprecated
825     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String,Resource> graphChildren, Map<String, Variable> map) throws DatabaseException {
826         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, graphChildren, map);
827     }
828     
829     /**
830      * Get a map of child Variables from a node manager-based Variable, combined with the existing variables in #map.
831      * @param graph  The read graph.
832      * @param context  The parent Variable.
833      * @param map  A map of variables into which the new variables are merged.
834      * @return  A map from variable names to instances
835      * @throws DatabaseException
836      */
837     public static Map<String, Variable> getStandardNodeChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
838         AbstractVariable variable = (AbstractVariable)context;
839         if (variable.node == null) return map;
840         
841         Collection<Object> nodeChildren = getPossibleNodeChildren(graph, variable);
842         if (nodeChildren.isEmpty()) return map;
843         
844         map = ensureVariableMap(map, nodeChildren.size());
845
846         for(Object nodeChild : nodeChildren) {
847             String name = getNodeName(variable, nodeChild);
848             if (!map.containsKey(name))
849                 map.put(name, createStandardGraphChildVariable(variable, nodeChild));
850         }
851
852         return map;
853     }
854
855     /**
856      * Get a map of property Variables from a node manager-based Variable, combined with the existing variables in #map.
857      * @param graph  The read graph.
858      * @param context  The parent Variable.
859      * @param map  A map of variables into which the new variables are merged.
860      * @return  A map from variable names to instances
861      * @throws DatabaseException
862      */
863     public static Map<String, Variable> getStandardNodePropertyVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
864         AbstractVariable variable = (AbstractVariable)context;
865         if (variable.node == null) return map;
866         
867         Collection<Object> nodeProperties = getPossibleNodeProperties(graph, variable);
868         if (nodeProperties.isEmpty()) return map;
869         
870         map = ensureVariableMap(map, nodeProperties.size());
871
872         for(Object nodeProperty : nodeProperties) {
873             String name = getNodeName(variable, nodeProperty);
874             if (!map.containsKey(name)) {
875                 map.put(name, createStandardGraphPropertyVariable(graph, variable, nodeProperty));
876             }
877         }
878
879         return map;
880     }    
881
882         @SCLValue(type = "VariableMap")
883         public static VariableMap standardChildDomainChildren = new VariableMapImpl() {
884
885                 @Override
886                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
887                         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
888                 }
889
890                 @Override
891                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
892                     return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
893                 }
894                 
895         };
896
897     @SCLValue(type = "VariableMap")
898         public static VariableMap standardPropertyDomainChildren = new VariableMapImpl() {
899
900         /**
901          * Get a possible non-standard VariableMap defined in the graph.
902          * @param graph  The graph
903          * @param context  The context node
904          * @return  A non-standard VariableMap instance for the context node,
905          *          or null, if not defined or defined as this instance.
906          * @throws DatabaseException
907          */
908         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
909                 Resource represents = context.getPossibleRepresents(graph);
910                 if(represents == null) return null;
911                         VariableMap map = graph.syncRequest(new ChildVariableMapRequest(represents));
912                 if(map == standardPropertyDomainChildren) return null;
913                 else return map;
914         }
915         
916                 @Override
917                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
918                         // Delegate call to a non-standard variable map?
919                         VariableMap valueMap = getValueVariableMap(graph, context);
920                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
921                         
922                         if(context instanceof StandardGraphPropertyVariable) {
923                                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
924                                 Datatype dt = variable.getDatatype(graph);
925                                 if (dt instanceof ArrayType) {
926                                         ChildReference ref = getPossibleIndexReference(name);
927                                         if (ref != null)
928                                                 return new SubliteralPropertyVariableDeprecated(variable, ref);
929                                 }
930                                 
931                                 // Check for a child node provided by the NodeManager
932                                 if (variable.node != null) {
933                                         Object childNode = getPossibleNodeChild(graph, variable, name);
934                                         if (childNode != null)
935                                                 return createStandardGraphChildVariable(variable, childNode);
936                                 }
937                                 return standardChildDomainChildren.getVariable(graph, context, name);
938                         } else if (context instanceof StandardGraphChildVariable) {
939                                 return standardChildDomainChildren.getVariable(graph, context, name);
940                         } else {
941                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
942                         }
943                 }
944
945                 @Override
946                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
947                         // Delegate call to a non-standard variable map?
948                         VariableMap valueMap = getValueVariableMap(graph, context);
949                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
950                         
951                         if(context instanceof StandardGraphPropertyVariable) {
952                                 // Get child variables provided by the NodeManager
953                                 Map<String, Variable> result = getStandardNodeChildVariables(graph, context, map); 
954                                 return standardChildDomainChildren.getVariables(graph, context, result);
955                         } else if (context instanceof StandardGraphChildVariable) {
956                                 return standardChildDomainChildren.getVariables(graph, context, map);
957                         } else {
958                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
959                         }       
960                 }
961                 
962         };
963         
964         protected static ChildReference getPossibleIndexReference(String name) {
965                 if (name.startsWith("i-")) {
966                     try {
967                         int index = Integer.parseInt(name.substring(2));
968                         return new IndexReference(index);
969                     } catch (NumberFormatException e) {}
970                 }
971                 return null;
972         }
973
974         protected static ValueAccessor getPossiblePropertyValueAccessor(ReadGraph graph, StandardGraphPropertyVariable variable) throws DatabaseException {
975             if(variable.property == null) return null;
976             return variable.property.valueAccessor;
977 //        return graph.syncRequest(new PropertyValueAccessorRequest(variable.property), TransientCacheAsyncListener.<ValueAccessor>instance());
978 //              return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(variable.property, Layer0.getInstance(graph).valueAccessor));
979         }
980
981         public static ValueAccessor getPossibleValueValueAccessor(ReadGraph graph, Variable variable) throws DatabaseException {
982             Resource value = variable.getPossibleRepresents(graph);
983             if(value == null) return null;
984             //return graph.syncRequest(new PropertyValueAccessorRequest(value));
985                 return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(value, Layer0.getInstance(graph).valueAccessor));      
986         }
987         
988         public static PropertyInfo getPossiblePropertyInfoFromContext(ReadGraph graph, Variable variable, Resource context, String name) throws DatabaseException {
989             if(context == null) return null;
990                 Map<String, PropertyInfo> predicates = graph.syncRequest(new UnescapedPropertyMapOfResource(context));
991                 return predicates.get(name);
992         }
993
994     public static Variable getPossiblePropertyFromContext(ReadGraph graph, Variable variable, Resource context, String name, Object propertyNode) throws DatabaseException {
995         PropertyInfo info = getPossiblePropertyInfoFromContext(graph, variable, context, name);
996         if(info == null || info.builder == null) return null;
997         return buildPropertyVariable(graph, variable, context, info, propertyNode);
998     }
999     
1000     public static Variable getPossibleSubliteralPropertyFromContext(ReadGraph graph, StandardGraphPropertyVariable variable, String name) throws DatabaseException {
1001         
1002                 Resource predicate = variable.property.predicate;
1003                 if(predicate == null) return null;
1004
1005         PropertyInfo info = getPropertyInfo(graph, predicate);
1006         Pair<Resource, ChildReference> p = info.subliteralPredicates.get(name);
1007         if(p == null) return null;
1008         
1009                 return new SubliteralPropertyVariable(graph, variable, p.first, p.second);
1010         
1011     }
1012
1013     public static Map<String, PropertyInfo> collectPropertyInfosFromContext(ReadGraph graph, Variable variable, Resource context) throws DatabaseException {
1014         if(context == null) return Collections.emptyMap();
1015                 return graph.isImmutable(context) ?
1016                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
1017                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
1018     }
1019
1020         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, Map<String, Variable> map) throws DatabaseException {
1021
1022                 Map<String,PropertyInfo> properties = graph.isImmutable(context) ?
1023                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
1024                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
1025                                 
1026                 if(properties.isEmpty()) return map;
1027                 
1028                 map = ensureVariableMap(map, properties.size());
1029                 
1030                 for(PropertyInfo info : properties.values()) {
1031                         String name = info.name;
1032                         if (info.builder != null) {
1033                                 Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1034                                 map.put(name, v);
1035                         }
1036                 }
1037                 
1038                 return map;
1039                 
1040         }
1041
1042         public static Map<String, Variable> collectSubliteralProperties(ReadGraph graph, StandardGraphPropertyVariable variable, Map<String, Variable> map) throws DatabaseException {
1043
1044                 Resource predicate = variable.property.predicate;
1045                 if(predicate == null) return map;
1046                 
1047                 PropertyInfo info = getPropertyInfo(graph, predicate);
1048                 if(info.subliteralPredicates.isEmpty()) return map;
1049                 
1050                 map = ensureVariableMap(map, info.subliteralPredicates.size());
1051                 
1052                 for(Map.Entry<String, Pair<Resource, ChildReference>> entry : info.subliteralPredicates.entrySet()) {
1053                         String key = entry.getKey();
1054                         Pair<Resource, ChildReference> p = entry.getValue();
1055                         if(map == null) map = new THashMap<String,Variable>();
1056                         map.put(key, new SubliteralPropertyVariable(graph, variable, p.first, p.second));
1057                 }
1058         
1059         return map;
1060                 
1061         }
1062
1063         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, String classification, Map<String, Variable> map) throws DatabaseException {
1064
1065                 if(graph.isImmutable(context)) {
1066
1067                         Map<String,PropertyInfo> properties = graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance());
1068                         for(PropertyInfo info : properties.values()) {
1069
1070                                 if(info.classifications.contains(classification) && info.builder != null) {
1071                                         String name = info.name;
1072                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1073                                         if(map == null) map = new THashMap<String,Variable>();
1074                                         map.put(name, v);
1075                                 }
1076
1077                         }
1078
1079                 } else {
1080                                 
1081                         Collection<Resource> predicates = graph.getPredicates(context);
1082                                         
1083                         if(predicates.isEmpty()) return map;
1084                         
1085                         map = ensureVariableMap(map, predicates.size());
1086                 
1087                         for(Resource predicate : predicates) {
1088                                 
1089                                 PropertyInfo info = graph.isImmutable(predicate) ?
1090                                                 graph.syncRequest(new PropertyInfoRequest(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
1091                                                         graph.syncRequest(new PropertyInfoRequest(predicate));
1092                                                 
1093                                 if(!info.isHasProperty) continue;
1094         
1095                                 if(info.classifications.contains(classification) && info.builder != null) {
1096                                         String name = info.name;
1097                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1098                                         if(map == null) map = new THashMap<String,Variable>();
1099                                         map.put(name, v);
1100                                 }
1101                                 
1102                         }
1103                         
1104                 }
1105                 
1106                 return map;
1107                 
1108         }       
1109         
1110     @SCLValue(type = "ReadGraph -> Resource -> a -> String")
1111     public static String entityLabel(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1112         if(context instanceof Resource) {
1113                 return NameUtils.getSafeLabel(graph, ((Resource)context));      
1114         } else if (context instanceof Variable) {
1115                 Variable parent = ((Variable)context).getParent(graph);
1116                 Resource represents = parent.getRepresents(graph);
1117                 return NameUtils.getSafeLabel(graph, represents);
1118         } else {
1119                 throw new DatabaseException("Unknown context " + context);
1120         }
1121     }
1122
1123     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1124     public static Object listResources(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1125         return ListUtils.toList(graph, resource);
1126     }
1127
1128     @SCLValue(type = "ReadGraph -> Resource -> Variable -> [String]")
1129     public static List<String> standardClassifications(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1130         ArrayList<String> result = new ArrayList<String>();
1131         Resource predicate = context.getParent(graph).getPossiblePredicateResource(graph);
1132         if(predicate != null) {
1133                 for(Resource type : graph.getTypes(predicate)) {
1134                         String uri = graph.getPossibleURI(type);
1135                         if(uri != null) result.add(uri);
1136                 }
1137         }
1138         return result;
1139     }
1140
1141     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1142     public static Boolean standardValidValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1143         return Boolean.TRUE;
1144     }
1145
1146     @SCLValue(type = "ReadGraph -> Resource -> a -> StringInputValidator")
1147     public static StringInputValidator standardValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1148         return StringInputValidator.PASS;
1149     }
1150
1151     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1152     public static Boolean standardRequiredValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1153         return Boolean.FALSE;
1154     }
1155
1156     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean")
1157     public static Boolean standardDefaultValue(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1158         Variable property = context.getParent(graph);
1159         if(property instanceof StandardGraphPropertyVariable) {
1160                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)property;
1161                 if (variable.parentResource != null) {
1162                         Statement stm = graph.getPossibleStatement(variable.parentResource, variable.property.predicate);
1163                         return stm != null && stm.isAsserted(variable.parentResource);
1164                         }
1165         }
1166         return Boolean.FALSE;
1167     }
1168
1169     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1170     public static Boolean standardReadOnlyValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1171         return Boolean.FALSE;
1172     }
1173
1174     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1175     public static Object resourceAsValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1176         return resource;
1177     }
1178     
1179     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1180     public static Object functionApplication(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1181         return Functions.exec(graph, resource, graph, resource, context);
1182     }
1183
1184     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1185     public static Object computeExpression(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1186         if(context instanceof Variable) {
1187             return CompileValueRequest.compileAndEvaluate(graph, (Variable)context);
1188         } if (context instanceof Resource) {
1189             return CompileResourceValueRequest.compileAndEvaluate(graph, (Resource)converter);
1190         } else {
1191                 throw new IllegalStateException("Unknown context " + context);
1192         }
1193     }
1194
1195     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1196     public static Object composedPropertyValue(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1197         if(context instanceof Variable) {
1198                 return new StandardComposedProperty();
1199         } if (context instanceof Resource) {
1200                 return new StandardComposedProperty();
1201         } else {
1202                 throw new IllegalStateException("Unknown context " + context);
1203         }
1204     }
1205     
1206     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1207     public static Object numberInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1208         
1209         class Validator extends FunctionImpl1<String, String> {
1210
1211             private final Datatype datatype;
1212             
1213             public Validator(Datatype datatype) {
1214                 this.datatype = datatype;
1215             }
1216             
1217             @Override
1218             public String apply(String input) {
1219                 
1220                 if(datatype == null) return null;
1221                 
1222                 try {
1223
1224                     if(datatype instanceof NumberType) {
1225                         
1226                         Number number = (Number)PrimitiveValueParser.parse(input, datatype);
1227                         NumberType nt = (NumberType)datatype;
1228                         Range r = nt.getRange();
1229                         if(r != null) {
1230                             if(!r.contains(number)) return "Value is out of valid range";
1231                         }
1232                     }
1233                     return null;
1234                     
1235                 } catch (NumberFormatException e) {
1236                     return "Not a valid floating-point number";
1237                 } catch (IllegalArgumentException e) {
1238                     return "Not a valid floating-point number";
1239                 }
1240                 
1241             }
1242             
1243         }
1244
1245         if(context instanceof Variable) {
1246             
1247             Variable variable = (Variable)context;
1248             Variable property = variable.getParent(graph);
1249             Datatype datatype = property.getPossibleDatatype(graph);
1250             return new Validator(datatype);
1251             
1252         } else if (context instanceof Resource) {
1253
1254             Layer0 L0 = Layer0.getInstance(graph);
1255             Resource literal = (Resource)context;
1256             Datatype datatype = graph.getRelatedValue(literal, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
1257             return new Validator(datatype);
1258             
1259         } else {
1260             
1261             return new Validator(null);
1262             
1263         }
1264         
1265     }
1266     
1267     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1268     public static Object booleanInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1269         
1270         return new FunctionImpl1<String, String>() {
1271
1272             @Override
1273             public String apply(String input) {
1274                 
1275                 String lower = input.toLowerCase();
1276                 if("true".equals(lower) || "false".equals(lower)) return null;
1277
1278                 return "Not a valid boolean: " + input;
1279                 
1280             }
1281             
1282         };
1283         
1284     }
1285
1286     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Resource")
1287     public static Resource hasStandardResource(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1288         Variable parent = context.getParent(graph);
1289         if(parent instanceof StandardGraphChildVariable) {
1290                 StandardGraphChildVariable variable = (StandardGraphChildVariable)parent;
1291                 return variable.resource;
1292         }
1293         return null;
1294     }
1295
1296
1297     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1298         public static Object valueWithoutBinding(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1299
1300         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1301                 
1302                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1303                         Layer0 L0 = Layer0.getInstance(graph);
1304                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel);
1305                 }
1306
1307                 if (variable.parentResource == null)
1308                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1309
1310                 try {
1311                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1312                 } catch (NoSingleResultException e) {
1313                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1314                 } catch (DoesNotContainValueException e) {
1315                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1316                 }
1317                 
1318         }
1319
1320     @SCLValue(type = "ReadGraph -> Variable -> Binding -> a")
1321         public static Object valueWithBinding(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
1322
1323         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1324                 
1325                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1326                         Layer0 L0 = Layer0.getInstance(graph);
1327                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel, binding);
1328                 }
1329
1330                 if (variable.parentResource == null)
1331                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1332
1333                 try {
1334                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1335                 } catch (NoSingleResultException e) {
1336                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1337                 } catch (DoesNotContainValueException e) {
1338                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1339                 }
1340                 
1341         }
1342
1343     @SCLValue(type = "WriteGraph -> Variable -> a -> Binding -> b")
1344         public static Object valueSetterWithBinding(WriteGraph graph, Variable variable, Object value, Binding binding) throws DatabaseException {
1345                 
1346                 Function4<WriteGraph, Variable, Object, Object, String> modifier = variable.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
1347                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
1348                 modifier.apply(graph, variable, value, binding);
1349                 return null;
1350                 
1351         }
1352     
1353     static class L0Issue extends StandardIssue {
1354         
1355         private final String description;
1356         
1357         public L0Issue(String description, Resource type, Resource ... contexts) {
1358             super(type, contexts);
1359             this.description = description;
1360         }
1361
1362         @Override
1363         public Resource write(WriteGraph graph, Resource source) throws DatabaseException {
1364             Layer0 L0 = Layer0.getInstance(graph);
1365             IssueResource IR = IssueResource.getInstance(graph);
1366             Resource issue = super.write(graph, source);
1367             graph.claim(issue, IR.Issue_HasSeverity, IR.Severity_Fatal);
1368             graph.addLiteral(issue, L0.HasDescription, L0.HasDescription_Inverse, description, Bindings.STRING);
1369             return issue;
1370         }
1371         
1372     }
1373     
1374         private static List<Issue> reportInconsistency(ReadGraph graph, Resource subject, String description, List<Issue> issues) throws DatabaseException {
1375             if(issues == null) issues = new ArrayList<Issue>();
1376                 System.err.println("Change set validation reports the following issue: " + NameUtils.getSafeName(graph, subject, true) + ": " + description);
1377                 IssueResource IR = IssueResource.getInstance(graph);
1378                 issues.add(new L0Issue(description, IR.Issue, subject));
1379                 return issues;
1380         }
1381     
1382     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1383     public static List<Issue> relationValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1384
1385         Layer0 L0 = Layer0.getInstance(graph);
1386
1387         List<Issue> issues = null;
1388         
1389         for(Statement stm : graph.getStatements(resource, L0.IsWeaklyRelatedTo)) {
1390                 Resource predicate = stm.getPredicate();
1391                 Resource object = stm.getObject();
1392                 if(!isRelation(graph, L0, predicate)) {
1393                         issues = reportInconsistency(graph, resource, "The predicate of a statement must be a relation: " + NameUtils.toString(graph, stm), issues);
1394                 }
1395                 if(graph.isInstanceOf(predicate, L0.FunctionalRelation)) {
1396                         if(graph.getObjects(resource, predicate).size() > 1)
1397                                 issues = reportInconsistency(graph, resource, 
1398                                                 "Relation " +
1399                                                                 NameUtils.getSafeName(graph, predicate)
1400                                                                 + " is functional.", issues);
1401                 }
1402                 {
1403                         Collection<Resource> domain = graph.getObjects(predicate, L0.HasDomain);
1404                         if (!isInstanceOfAny(graph, resource, domain, true)) {
1405                                 StringBuilder sb = new StringBuilder()
1406                                 .append("The domain of ")
1407                                 .append(NameUtils.getSafeName(graph, predicate))
1408                                 .append(" relation is ");
1409                                 orString(graph, sb, domain).append(".");
1410                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1411                         }
1412                 }
1413                 {
1414                         Collection<Resource> range = graph.getObjects(predicate, L0.HasRange);
1415                         if (!isInstanceOfAny(graph, object, range, true) && !graph.isInstanceOf(object, L0.SCLValue)) {
1416                                 StringBuilder sb = new StringBuilder()
1417                                 .append("The range of ")
1418                                 .append(NameUtils.getSafeName(graph, predicate))
1419                                 .append(" relation is ");
1420                                 orString(graph, sb, range).append(" but current object is ")
1421                                 .append(NameUtils.getSafeName(graph, object)).append(".");
1422                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1423                         }
1424                 }               
1425         }
1426                 
1427                 return issues != null ? issues : Collections.<Issue>emptyList();
1428
1429     }
1430
1431     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1432     public static List<Issue> propertyValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1433
1434         List<Issue> issues = null;
1435
1436         Layer0 L0 = Layer0.getInstance(graph);
1437         for(Statement stm : graph.getStatements(resource, L0.HasProperty)) {
1438                 Resource subject = stm.getSubject();
1439                 Resource predicate = stm.getPredicate();
1440                 String error = L0Validations.checkValueType(graph, subject, predicate);
1441                 if(error != null) issues = reportInconsistency(graph, subject, error, issues);
1442         }
1443                 
1444         return issues != null ? issues : Collections.<Issue>emptyList();
1445
1446     }
1447     
1448     
1449     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1450     public static List<Issue> valueValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1451
1452         List<Issue> issues = null;
1453
1454         Layer0 L0 = Layer0.getInstance(graph);
1455         if(graph.hasValue(resource)) {
1456                 if(!graph.isInstanceOf(resource, L0.Literal)) {
1457                         issues = reportInconsistency(graph, resource, 
1458                                         "Resource has a value but it is not a literal.", issues);
1459                 }
1460                 else {
1461                         // TODO check that the value is valid for the data type
1462                 }
1463         }
1464         else {
1465                 if(graph.isInstanceOf(resource, L0.Literal)) {
1466                         issues = reportInconsistency(graph, resource, 
1467                                         "Resource is a literal but it does not have a value.", issues);
1468                 }
1469         }
1470         
1471         return issues != null ? issues : Collections.<Issue>emptyList();
1472         
1473     }
1474
1475     
1476     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1477     public static List<Issue> uriValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1478         
1479         List<Issue> issues = null;
1480
1481         Layer0 L0 = Layer0.getInstance(graph);
1482         Resource parent = graph.getPossibleObject(resource, L0.PartOf);
1483         if(parent != null) {
1484             String parentURI = graph.syncRequest(new PossibleURI(parent));
1485             if(parentURI != null) {
1486                         String name = graph.getPossibleRelatedValue(resource, L0.HasName);
1487                         if(name == null) {
1488                                 issues = reportInconsistency(graph, resource, "Resource has a parent with URI but has no valid HasName.", issues);
1489                         }
1490             }
1491         }
1492
1493         return issues != null ? issues : Collections.<Issue>emptyList();
1494         
1495     }
1496     
1497     private static Resource getPossibleNearestClusterSet(ReadGraph graph, Resource base, Resource resource) throws DatabaseException {
1498
1499         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1500         if(cs.isClusterSet(resource) && !base.equals(resource)) return resource;
1501         
1502         Resource nearest = CommonDBUtils.getNearestOwner(graph, Collections.singletonList(resource));
1503         if(nearest == null) return null;
1504         
1505         return getPossibleNearestClusterSet(graph, base, nearest);
1506
1507     }
1508
1509     private static boolean quirks(ReadGraph graph, Resource resource) throws DatabaseException {
1510
1511         if(!resource.isPersistent()) return true;
1512         if(graph.isImmutable(resource)) return true;
1513         if(resource.getResourceId() < 0x2000) return true;
1514
1515         return false;
1516         
1517     }
1518     
1519     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1520     public static List<Issue> clusterValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1521
1522         if(!Development.DEVELOPMENT) return Collections.<Issue>emptyList();
1523         
1524         if(quirks(graph, resource)) return Collections.<Issue>emptyList();
1525         
1526         List<Issue> issues = null;
1527
1528         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1529         Resource set = cs.getClusterSetOfCluster(resource);
1530         
1531         if(set == null) return reportInconsistency(graph, resource, "Resource cluster is not part of any cluster set", issues);
1532         
1533         Resource nearestSet = getPossibleNearestClusterSet(graph, resource, resource);
1534         if(nearestSet == null) {
1535                 // This means that there is no owner since RootLibrary is a cluster set
1536                 return Collections.<Issue>emptyList();
1537         }
1538         
1539         if(!set.equals(nearestSet)) return reportInconsistency(graph, resource, "The cluster set of a resource is not the nearest owner set", issues);
1540
1541         return Collections.<Issue>emptyList();
1542         
1543     }
1544
1545     private static boolean isInstanceOfAny(ReadGraph graph, Resource r, Collection<Resource> types, boolean ifEmpty) throws DatabaseException {
1546                 if (types.isEmpty())
1547                         return ifEmpty;
1548                 for (Resource type : types) {
1549                         if (graph.isInstanceOf(r, type)) {
1550                                 return true;
1551                         }
1552                 }
1553                 return false;
1554         }
1555
1556         private static StringBuilder orString(ReadGraph graph, StringBuilder sb, Collection<Resource> rs) throws DatabaseException {
1557                 sb.append("(");
1558                 boolean first = true;
1559                 for (Resource r : rs) {
1560                         if (!first)
1561                                 sb.append(" | ");
1562                         first = false;
1563                         sb.append(NameUtils.getSafeName(graph, r));
1564                 }
1565                 sb.append(")");
1566                 return sb;
1567         }
1568
1569     public static boolean isRelation(ReadGraph g, Layer0 l0, Resource relation) throws DatabaseException {
1570                 return g.hasStatement(relation, l0.SubrelationOf) || relation == l0.IsWeaklyRelatedTo;
1571         }
1572         
1573         public static boolean isType(ReadGraph g, Layer0 l0, Resource type) throws DatabaseException {
1574                 return g.hasStatement(type, l0.Inherits) || type == l0.Entity;
1575         }
1576         
1577     public static Variable buildChildVariable(ReadGraph graph, Variable context, Resource graphChild, Object nodeChild) throws DatabaseException {
1578         VariableBuilder builder = graph.adapt(graphChild, VariableBuilder.class);
1579         return builder.buildChild(graph, context, build(((AbstractVariable)context).node, nodeChild), graphChild);
1580         }
1581
1582         private static Variable buildPropertyVariable(ReadGraph graph, Variable variable, Resource parentResource, PropertyInfo graphProperty, Object propertyNode) throws DatabaseException {
1583                 VariableNode<?> node = variable instanceof AbstractVariable ? build(((AbstractVariable)variable).node, propertyNode) : null;
1584                 return graphProperty.builder.buildProperty(graph, variable, node, parentResource, graphProperty.predicate);
1585         }
1586         
1587         static StandardGraphChildVariable createStandardGraphChildVariable(
1588                         AbstractVariable parent, Object child) {
1589                 return new StandardGraphChildVariable(parent, build(parent.node, child), null);
1590         }
1591
1592         private static StandardGraphPropertyVariable createStandardGraphPropertyVariable(
1593                         ReadGraph graph, AbstractVariable variable, Object nodeProperty) throws DatabaseException {
1594         Resource propertyResource = getPossiblePropertyResource(graph, variable, nodeProperty);
1595         return new StandardGraphPropertyVariable(graph, variable, build(variable.node, nodeProperty), null, propertyResource);
1596         }
1597         
1598         static Map<String, Variable> ensureVariableMap(
1599                         Map<String, Variable> map, int size) {
1600                 if(map == null) map = new THashMap<String,Variable>(size);
1601                 return map;
1602         }
1603
1604         private static PropertyInfo getPropertyInfo(ReadGraph graph, Resource predicate) throws DatabaseException {
1605                 return graph.syncRequest(new PropertyInfoRequest(predicate));
1606         }
1607
1608         @SuppressWarnings("unchecked")
1609         static String getNodeName(AbstractVariable parent, Object child) {
1610                 return parent.node.support.manager.getName(child);
1611         }
1612
1613         @SuppressWarnings("unchecked")
1614         private static Object getNodeValue(final AbstractVariable variable, final Binding binding) throws NodeManagerException, BindingException {
1615                 return variable.node.support.manager.getValue(variable.node.node, binding);
1616         }
1617
1618         @SuppressWarnings("unchecked")
1619         private static void setNodeValue(final AbstractVariable variable, final Object value, final Binding binding) throws NodeManagerException, BindingException {
1620                 variable.node.support.manager.setValue(variable.node.node, value, binding);
1621         }
1622         
1623         @SuppressWarnings("unchecked")
1624         private static String getPossiblePropertyURI(AbstractVariable parent, Object node) {
1625                 return parent.node.support.manager.getPropertyURI(parent.node.node, node);
1626         }
1627     
1628
1629     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1630     public static Object defaultInstantiateUnder(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1631         return new FunctionImpl2<Resource, Resource, Resource>() {
1632             public Resource apply(Resource container, Resource type) {
1633                 try {
1634                     WriteGraph graph = (WriteGraph)SCLContext.getCurrent().get("graph");
1635
1636                     Layer0 L0 = Layer0.getInstance(graph);
1637                     CommonDBUtils.selectClusterSet(graph, container);
1638                     Resource result = graph.newResource();
1639                     String name = NameUtils.findFreshInstanceName(graph, type, container);
1640                     graph.claim(result, L0.InstanceOf, type);
1641                     graph.addLiteral(result, L0.HasName, L0.NameOf, name, Bindings.STRING);
1642                     graph.claim(container, L0.ConsistsOf, L0.PartOf, result);
1643
1644                     return result;
1645                 } catch (DatabaseException e) {
1646                     throw new RuntimeDatabaseException(e);
1647                 }
1648             }
1649         };
1650     }
1651
1652 }