]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/function/All.java
Fixed yet another potential NPE.
[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                         if(container == null)
629                                 return Collections.emptyMap();
630                         Map<String,Resource> methods = graph.syncRequest(new UnescapedMethodMapOfResource(container));
631                         for(Map.Entry<String, Resource> entry : methods.entrySet()) {
632                                 String name = entry.getKey();
633                                 Resource predicate = entry.getValue();
634                                 Layer0 L0 = Layer0.getInstance(graph);
635                                 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(L0.Entity_method));
636                                 Resource value = graph.getSingleObject(container, predicate);
637                                 if(map == null) map = new HashMap<>();
638                                 map.put(name, new StandardGraphPropertyVariable(context, null, container, info, value));
639                         }
640                         return map;
641                 }
642                 
643         };
644
645         public static Variable getStandardPropertyDomainPropertyVariableFromValue(ReadGraph graph, Variable context, String name) throws DatabaseException {
646
647                 if(context instanceof StandardGraphPropertyVariable) {
648                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
649                 Resource literal = variable.getPossibleRepresents(graph);
650                 Object propertyNode = getPossibleNodeProperty(graph, variable, name, false);
651
652                 if(literal != null) {
653                         Variable result = getPossiblePropertyFromContext(graph, variable, literal, name, propertyNode);
654                         if(result != null) return result;
655                 }
656                 
657                 Variable result = getPossibleSubliteralPropertyFromContext(graph, variable, name);
658                 if(result != null) return result;
659                 result = getPossiblePropertyFromContext(graph, variable, variable.property.predicate, name, propertyNode);
660                 if (result != null) return result;
661                 
662                 // Get possible property from NodeManager
663                 if (propertyNode != null)
664                         return createStandardGraphPropertyVariable(graph, variable, propertyNode);
665                 return null;
666                 } else if (context instanceof StandardGraphChildVariable) {
667                         return standardChildDomainProperties.getVariable(graph, context, name);
668                 } else {
669                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
670                 }                       
671                 
672         }
673         
674         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
675
676                 if(context instanceof StandardGraphPropertyVariable) {
677                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
678                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, map);
679                         if (variable.parentResource != null) {
680                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
681                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, map);
682                                 map=collectSubliteralProperties(graph, variable, map);
683                         }
684
685                         // Get properties from VariableNode
686                         map = getStandardNodePropertyVariables(graph, context, map);
687                         return map;
688                 } else if (context instanceof StandardGraphChildVariable) {
689                         return standardChildDomainProperties.getVariables(graph, context, map);
690                 } else {
691                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
692                 }
693                 
694         }
695         
696         public static Map<String, Variable> getStandardPropertyDomainPropertyVariablesFromValue(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
697
698                 if(context instanceof StandardGraphPropertyVariable) {
699                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
700                         map = collectPropertiesFromContext(graph, variable, variable.property.predicate, classification, map);
701                         if (variable.parentResource != null) {
702                                 Resource literal = graph.getPossibleObject(variable.parentResource, variable.property.predicate);
703                                 if(literal != null) map=collectPropertiesFromContext(graph, variable, literal, classification, map);
704                         }
705                         
706                         // Get properties from VariableNode
707                         map = getStandardNodePropertyVariables(graph, context, map);
708                         return map;
709                 } else if (context instanceof StandardGraphChildVariable) {
710                         return standardChildDomainProperties.getVariables(graph, context, map);
711                 } else {
712                         throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
713                 }       
714                 
715         }       
716         
717     @SCLValue(type = "VariableMap")
718         public static VariableMap standardPropertyDomainProperties = new VariableMapImpl() {
719
720         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
721                 Resource represents = context.getPossibleRepresents(graph);
722                 if(represents == null) return null;
723                 
724                 VariableMap map = graph.isImmutable(represents) ?
725                                 graph.syncRequest(new PropertyVariableMapRequest(represents), TransientCacheListener.<VariableMap>instance()) :
726                                         (VariableMap)graph.getPossibleRelatedValue2(represents, Layer0.getInstance(graph).domainProperties, represents);
727                 
728                 if(map == standardPropertyDomainProperties) return null;
729                 else return map;
730                 
731         }
732         
733                 @Override
734                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
735                         VariableMap valueMap = getValueVariableMap(graph, context);
736                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
737                         return getStandardPropertyDomainPropertyVariableFromValue(graph, context, name);
738                 }
739
740                 @Override
741                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
742                         VariableMap valueMap = getValueVariableMap(graph, context);
743                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
744                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, map);
745                 }
746                 
747                 @Override
748                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, String classification, Map<String, Variable> map) throws DatabaseException {
749                         VariableMap valueMap = getValueVariableMap(graph, context);
750                         if(valueMap != null) return valueMap.getVariables(graph, context, classification, map);
751                         else return getStandardPropertyDomainPropertyVariablesFromValue(graph, context, classification, map);
752                 }
753                 
754         };
755
756     public static Resource getPossibleGraphChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
757         Resource resource = variable.getPossibleRepresents(graph);
758         if(resource == null) return null;
759         Map<String, Resource> graphChildren = graph.syncRequest(new UnescapedChildMapOfResource(resource));
760         return graphChildren.get(name);
761     }
762
763     public static Map<String,Resource> getPossibleGraphChildren(ReadGraph graph, Variable variable) throws DatabaseException {
764         Resource resource = variable.getPossibleRepresents(graph);
765         if(resource == null) return Collections.emptyMap();
766         return graph.syncRequest(new UnescapedChildMapOfResource(resource));
767     }
768
769     public static Object getPossibleNodeChild(ReadGraph graph, Variable variable, String name) throws DatabaseException {
770         if (!(variable instanceof AbstractVariable)) return null;
771         VariableNode<?> node = ((AbstractVariable)variable).node;
772         if(node == null) return null;
773         NodeStructure structure = Variables.requestNodeStructure(graph, node);
774         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
775         return structure.children.get(name);
776         }
777         
778     public static Collection<Object> getPossibleNodeChildren(ReadGraph graph, Variable variable) throws DatabaseException {
779         if (!(variable instanceof AbstractVariable)) return null;
780         VariableNode<?> node = ((AbstractVariable)variable).node;
781         if(node == null) return Collections.emptyList();
782         NodeStructure structure = Variables.requestNodeStructure(graph, node);
783         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
784         return structure.children.values();
785     }
786     
787     public static Object getPossibleNodeProperty(ReadGraph graph, Variable variable, String name, boolean throwPending) throws DatabaseException {
788         if (!(variable instanceof AbstractVariable)) return null;
789         VariableNode<?> node = ((AbstractVariable)variable).node;
790         if(node == null) return null;
791         NodeStructure structure = Variables.requestNodeStructure(graph, node);
792         if(throwPending && Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
793         return structure.properties.get(name);
794     }
795     
796     public static Collection<Object> getPossibleNodeProperties(ReadGraph graph, Variable variable) throws DatabaseException {
797         if (!(variable instanceof AbstractVariable)) return null;
798         VariableNode<?> node = ((AbstractVariable)variable).node;
799         if(node == null) return Collections.emptyList();
800         NodeStructure structure = Variables.requestNodeStructure(graph, node);
801         if(Variables.PENDING_NODE_STRUCTURE == structure) throw new PendingVariableException("");
802         return structure.properties.values();
803     }
804
805     @SuppressWarnings({ "rawtypes", "unchecked" })
806     public static VariableNode build(VariableNode parent, Object node) {
807         if(node == null) return null;
808         return new VariableNode(parent.support, node);
809     }
810
811     @Deprecated
812     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
813         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
814     }
815
816     @Deprecated
817     public static Variable getStandardChildDomainChildVariable(ReadGraph graph, Variable context, Resource graphChild, String name) throws DatabaseException {
818         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, graphChild, name);
819     }
820         
821     @Deprecated
822     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
823         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
824     }
825
826     @Deprecated
827     public static Map<String, Variable> getStandardChildDomainChildVariables(ReadGraph graph, Variable context, Map<String,Resource> graphChildren, Map<String, Variable> map) throws DatabaseException {
828         return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, graphChildren, map);
829     }
830     
831     /**
832      * Get a map of child Variables from a node manager-based Variable, combined with the existing variables in #map.
833      * @param graph  The read graph.
834      * @param context  The parent Variable.
835      * @param map  A map of variables into which the new variables are merged.
836      * @return  A map from variable names to instances
837      * @throws DatabaseException
838      */
839     public static Map<String, Variable> getStandardNodeChildVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
840         AbstractVariable variable = (AbstractVariable)context;
841         if (variable.node == null) return map;
842         
843         Collection<Object> nodeChildren = getPossibleNodeChildren(graph, variable);
844         if (nodeChildren.isEmpty()) return map;
845         
846         map = ensureVariableMap(map, nodeChildren.size());
847
848         for(Object nodeChild : nodeChildren) {
849             String name = getNodeName(variable, nodeChild);
850             if (!map.containsKey(name))
851                 map.put(name, createStandardGraphChildVariable(variable, nodeChild));
852         }
853
854         return map;
855     }
856
857     /**
858      * Get a map of property Variables from a node manager-based Variable, combined with the existing variables in #map.
859      * @param graph  The read graph.
860      * @param context  The parent Variable.
861      * @param map  A map of variables into which the new variables are merged.
862      * @return  A map from variable names to instances
863      * @throws DatabaseException
864      */
865     public static Map<String, Variable> getStandardNodePropertyVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
866         AbstractVariable variable = (AbstractVariable)context;
867         if (variable.node == null) return map;
868         
869         Collection<Object> nodeProperties = getPossibleNodeProperties(graph, variable);
870         if (nodeProperties.isEmpty()) return map;
871         
872         map = ensureVariableMap(map, nodeProperties.size());
873
874         for(Object nodeProperty : nodeProperties) {
875             String name = getNodeName(variable, nodeProperty);
876             if (!map.containsKey(name)) {
877                 map.put(name, createStandardGraphPropertyVariable(graph, variable, nodeProperty));
878             }
879         }
880
881         return map;
882     }    
883
884         @SCLValue(type = "VariableMap")
885         public static VariableMap standardChildDomainChildren = new VariableMapImpl() {
886
887                 @Override
888                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
889                         return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, name);
890                 }
891
892                 @Override
893                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
894                     return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, map);
895                 }
896                 
897         };
898
899     @SCLValue(type = "VariableMap")
900         public static VariableMap standardPropertyDomainChildren = new VariableMapImpl() {
901
902         /**
903          * Get a possible non-standard VariableMap defined in the graph.
904          * @param graph  The graph
905          * @param context  The context node
906          * @return  A non-standard VariableMap instance for the context node,
907          *          or null, if not defined or defined as this instance.
908          * @throws DatabaseException
909          */
910         VariableMap getValueVariableMap(ReadGraph graph, Variable context) throws DatabaseException {
911                 Resource represents = context.getPossibleRepresents(graph);
912                 if(represents == null) return null;
913                         VariableMap map = graph.syncRequest(new ChildVariableMapRequest(represents));
914                 if(map == standardPropertyDomainChildren) return null;
915                 else return map;
916         }
917         
918                 @Override
919                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
920                         // Delegate call to a non-standard variable map?
921                         VariableMap valueMap = getValueVariableMap(graph, context);
922                         if(valueMap != null) return valueMap.getVariable(graph, context, name);
923                         
924                         if(context instanceof StandardGraphPropertyVariable) {
925                                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
926                                 Datatype dt = variable.getDatatype(graph);
927                                 if (dt instanceof ArrayType) {
928                                         ChildReference ref = getPossibleIndexReference(name);
929                                         if (ref != null)
930                                                 return new SubliteralPropertyVariableDeprecated(variable, ref);
931                                 }
932                                 
933                                 // Check for a child node provided by the NodeManager
934                                 if (variable.node != null) {
935                                         Object childNode = getPossibleNodeChild(graph, variable, name);
936                                         if (childNode != null)
937                                                 return createStandardGraphChildVariable(variable, childNode);
938                                 }
939                                 return standardChildDomainChildren.getVariable(graph, context, name);
940                         } else if (context instanceof StandardGraphChildVariable) {
941                                 return standardChildDomainChildren.getVariable(graph, context, name);
942                         } else {
943                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
944                         }
945                 }
946
947                 @Override
948                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
949                         // Delegate call to a non-standard variable map?
950                         VariableMap valueMap = getValueVariableMap(graph, context);
951                         if(valueMap != null) return valueMap.getVariables(graph, context, map);
952                         
953                         if(context instanceof StandardGraphPropertyVariable) {
954                                 // Get child variables provided by the NodeManager
955                                 Map<String, Variable> result = getStandardNodeChildVariables(graph, context, map); 
956                                 return standardChildDomainChildren.getVariables(graph, context, result);
957                         } else if (context instanceof StandardGraphChildVariable) {
958                                 return standardChildDomainChildren.getVariables(graph, context, map);
959                         } else {
960                                 throw new DatabaseException("Unknown variable implementation " + context.getClass().getCanonicalName());
961                         }       
962                 }
963                 
964         };
965         
966         protected static ChildReference getPossibleIndexReference(String name) {
967                 if (name.startsWith("i-")) {
968                     try {
969                         int index = Integer.parseInt(name.substring(2));
970                         return new IndexReference(index);
971                     } catch (NumberFormatException e) {}
972                 }
973                 return null;
974         }
975
976         protected static ValueAccessor getPossiblePropertyValueAccessor(ReadGraph graph, StandardGraphPropertyVariable variable) throws DatabaseException {
977             if(variable.property == null) return null;
978             return variable.property.valueAccessor;
979 //        return graph.syncRequest(new PropertyValueAccessorRequest(variable.property), TransientCacheAsyncListener.<ValueAccessor>instance());
980 //              return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(variable.property, Layer0.getInstance(graph).valueAccessor));
981         }
982
983         public static ValueAccessor getPossibleValueValueAccessor(ReadGraph graph, Variable variable) throws DatabaseException {
984             Resource value = variable.getPossibleRepresents(graph);
985             if(value == null) return null;
986             //return graph.syncRequest(new PropertyValueAccessorRequest(value));
987                 return graph.syncRequest(new PossibleRelatedValueImplied2<ValueAccessor>(value, Layer0.getInstance(graph).valueAccessor));      
988         }
989         
990         public static PropertyInfo getPossiblePropertyInfoFromContext(ReadGraph graph, Variable variable, Resource context, String name) throws DatabaseException {
991             if(context == null) return null;
992                 Map<String, PropertyInfo> predicates = graph.syncRequest(new UnescapedPropertyMapOfResource(context));
993                 return predicates.get(name);
994         }
995
996     public static Variable getPossiblePropertyFromContext(ReadGraph graph, Variable variable, Resource context, String name, Object propertyNode) throws DatabaseException {
997         PropertyInfo info = getPossiblePropertyInfoFromContext(graph, variable, context, name);
998         if(info == null || info.builder == null) return null;
999         return buildPropertyVariable(graph, variable, context, info, propertyNode);
1000     }
1001     
1002     public static Variable getPossibleSubliteralPropertyFromContext(ReadGraph graph, StandardGraphPropertyVariable variable, String name) throws DatabaseException {
1003         
1004                 Resource predicate = variable.property.predicate;
1005                 if(predicate == null) return null;
1006
1007         PropertyInfo info = getPropertyInfo(graph, predicate);
1008         Pair<Resource, ChildReference> p = info.subliteralPredicates.get(name);
1009         if(p == null) return null;
1010         
1011                 return new SubliteralPropertyVariable(graph, variable, p.first, p.second);
1012         
1013     }
1014
1015     public static Map<String, PropertyInfo> collectPropertyInfosFromContext(ReadGraph graph, Variable variable, Resource context) throws DatabaseException {
1016         if(context == null) return Collections.emptyMap();
1017                 return graph.isImmutable(context) ?
1018                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
1019                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
1020     }
1021
1022         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, Map<String, Variable> map) throws DatabaseException {
1023
1024                 Map<String,PropertyInfo> properties = graph.isImmutable(context) ?
1025                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance()) :
1026                                 graph.syncRequest(new UnescapedPropertyMapOfResource(context));
1027                                 
1028                 if(properties.isEmpty()) return map;
1029                 
1030                 map = ensureVariableMap(map, properties.size());
1031                 
1032                 for(PropertyInfo info : properties.values()) {
1033                         String name = info.name;
1034                         if (info.builder != null) {
1035                                 Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1036                                 map.put(name, v);
1037                         }
1038                 }
1039                 
1040                 return map;
1041                 
1042         }
1043
1044         public static Map<String, Variable> collectSubliteralProperties(ReadGraph graph, StandardGraphPropertyVariable variable, Map<String, Variable> map) throws DatabaseException {
1045
1046                 Resource predicate = variable.property.predicate;
1047                 if(predicate == null) return map;
1048                 
1049                 PropertyInfo info = getPropertyInfo(graph, predicate);
1050                 if(info.subliteralPredicates.isEmpty()) return map;
1051                 
1052                 map = ensureVariableMap(map, info.subliteralPredicates.size());
1053                 
1054                 for(Map.Entry<String, Pair<Resource, ChildReference>> entry : info.subliteralPredicates.entrySet()) {
1055                         String key = entry.getKey();
1056                         Pair<Resource, ChildReference> p = entry.getValue();
1057                         if(map == null) map = new THashMap<String,Variable>();
1058                         map.put(key, new SubliteralPropertyVariable(graph, variable, p.first, p.second));
1059                 }
1060         
1061         return map;
1062                 
1063         }
1064
1065         public static Map<String, Variable> collectPropertiesFromContext(ReadGraph graph, Variable variable, Resource context, String classification, Map<String, Variable> map) throws DatabaseException {
1066
1067                 if(graph.isImmutable(context)) {
1068
1069                         Map<String,PropertyInfo> properties = graph.syncRequest(new UnescapedPropertyMapOfResource(context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance());
1070                         for(PropertyInfo info : properties.values()) {
1071
1072                                 if(info.classifications.contains(classification) && info.builder != null) {
1073                                         String name = info.name;
1074                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1075                                         if(map == null) map = new THashMap<String,Variable>();
1076                                         map.put(name, v);
1077                                 }
1078
1079                         }
1080
1081                 } else {
1082                                 
1083                         Collection<Resource> predicates = graph.getPredicates(context);
1084                                         
1085                         if(predicates.isEmpty()) return map;
1086                         
1087                         map = ensureVariableMap(map, predicates.size());
1088                 
1089                         for(Resource predicate : predicates) {
1090                                 
1091                                 PropertyInfo info = graph.isImmutable(predicate) ?
1092                                                 graph.syncRequest(new PropertyInfoRequest(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
1093                                                         graph.syncRequest(new PropertyInfoRequest(predicate));
1094                                                 
1095                                 if(!info.isHasProperty) continue;
1096         
1097                                 if(info.classifications.contains(classification) && info.builder != null) {
1098                                         String name = info.name;
1099                                         Variable v = info.builder.buildProperty(graph, variable, null, context, info.predicate);
1100                                         if(map == null) map = new THashMap<String,Variable>();
1101                                         map.put(name, v);
1102                                 }
1103                                 
1104                         }
1105                         
1106                 }
1107                 
1108                 return map;
1109                 
1110         }       
1111         
1112     @SCLValue(type = "ReadGraph -> Resource -> a -> String")
1113     public static String entityLabel(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1114         if(context instanceof Resource) {
1115                 return NameUtils.getSafeLabel(graph, ((Resource)context));      
1116         } else if (context instanceof Variable) {
1117                 Variable parent = ((Variable)context).getParent(graph);
1118                 Resource represents = parent.getRepresents(graph);
1119                 return NameUtils.getSafeLabel(graph, represents);
1120         } else {
1121                 throw new DatabaseException("Unknown context " + context);
1122         }
1123     }
1124
1125     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1126     public static Object listResources(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1127         return ListUtils.toList(graph, resource);
1128     }
1129
1130     @SCLValue(type = "ReadGraph -> Resource -> Variable -> [String]")
1131     public static List<String> standardClassifications(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1132         ArrayList<String> result = new ArrayList<String>();
1133         Resource predicate = context.getParent(graph).getPossiblePredicateResource(graph);
1134         if(predicate != null) {
1135                 for(Resource type : graph.getTypes(predicate)) {
1136                         String uri = graph.getPossibleURI(type);
1137                         if(uri != null) result.add(uri);
1138                 }
1139         }
1140         return result;
1141     }
1142
1143     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1144     public static Boolean standardValidValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1145         return Boolean.TRUE;
1146     }
1147
1148     @SCLValue(type = "ReadGraph -> Resource -> a -> StringInputValidator")
1149     public static StringInputValidator standardValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1150         return StringInputValidator.PASS;
1151     }
1152
1153     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1154     public static Boolean standardRequiredValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1155         return Boolean.FALSE;
1156     }
1157
1158     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean")
1159     public static Boolean standardDefaultValue(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1160         Variable property = context.getParent(graph);
1161         if(property instanceof StandardGraphPropertyVariable) {
1162                 StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)property;
1163                 if (variable.parentResource != null) {
1164                         Statement stm = graph.getPossibleStatement(variable.parentResource, variable.property.predicate);
1165                         return stm != null && stm.isAsserted(variable.parentResource);
1166                         }
1167         }
1168         return Boolean.FALSE;
1169     }
1170
1171     @SCLValue(type = "ReadGraph -> Resource -> a -> Boolean")
1172     public static Boolean standardReadOnlyValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1173         return Boolean.FALSE;
1174     }
1175
1176     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1177     public static Object resourceAsValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1178         return resource;
1179     }
1180     
1181     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1182     public static Object functionApplication(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1183         return Functions.exec(graph, resource, graph, resource, context);
1184     }
1185
1186     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1187     public static Object computeExpression(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1188         if(context instanceof Variable) {
1189             return CompileValueRequest.compileAndEvaluate(graph, (Variable)context);
1190         } if (context instanceof Resource) {
1191             return CompileResourceValueRequest.compileAndEvaluate(graph, (Resource)converter);
1192         } else {
1193                 throw new IllegalStateException("Unknown context " + context);
1194         }
1195     }
1196
1197     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1198     public static Object composedPropertyValue(ReadGraph graph, Resource converter, Object context) throws DatabaseException {
1199         if(context instanceof Variable) {
1200                 return new StandardComposedProperty();
1201         } if (context instanceof Resource) {
1202                 return new StandardComposedProperty();
1203         } else {
1204                 throw new IllegalStateException("Unknown context " + context);
1205         }
1206     }
1207     
1208     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1209     public static Object numberInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1210         
1211         class Validator extends FunctionImpl1<String, String> {
1212
1213             private final Datatype datatype;
1214             
1215             public Validator(Datatype datatype) {
1216                 this.datatype = datatype;
1217             }
1218             
1219             @Override
1220             public String apply(String input) {
1221                 
1222                 if(datatype == null) return null;
1223                 
1224                 try {
1225
1226                     if(datatype instanceof NumberType) {
1227                         
1228                         Number number = (Number)PrimitiveValueParser.parse(input, datatype);
1229                         NumberType nt = (NumberType)datatype;
1230                         Range r = nt.getRange();
1231                         if(r != null) {
1232                             if(!r.contains(number)) return "Value is out of valid range";
1233                         }
1234                     }
1235                     return null;
1236                     
1237                 } catch (NumberFormatException e) {
1238                     return "Not a valid floating-point number";
1239                 } catch (IllegalArgumentException e) {
1240                     return "Not a valid floating-point number";
1241                 }
1242                 
1243             }
1244             
1245         }
1246
1247         if(context instanceof Variable) {
1248             
1249             Variable variable = (Variable)context;
1250             Variable property = variable.getParent(graph);
1251             Datatype datatype = property.getPossibleDatatype(graph);
1252             return new Validator(datatype);
1253             
1254         } else if (context instanceof Resource) {
1255
1256             Layer0 L0 = Layer0.getInstance(graph);
1257             Resource literal = (Resource)context;
1258             Datatype datatype = graph.getRelatedValue(literal, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
1259             return new Validator(datatype);
1260             
1261         } else {
1262             
1263             return new Validator(null);
1264             
1265         }
1266         
1267     }
1268     
1269     @SCLValue(type = "ReadGraph -> Resource -> a -> b")
1270     public static Object booleanInputValidator(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
1271         
1272         return new FunctionImpl1<String, String>() {
1273
1274             @Override
1275             public String apply(String input) {
1276                 
1277                 String lower = input.toLowerCase();
1278                 if("true".equals(lower) || "false".equals(lower)) return null;
1279
1280                 return "Not a valid boolean: " + input;
1281                 
1282             }
1283             
1284         };
1285         
1286     }
1287
1288     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Resource")
1289     public static Resource hasStandardResource(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
1290         Variable parent = context.getParent(graph);
1291         if(parent instanceof StandardGraphChildVariable) {
1292                 StandardGraphChildVariable variable = (StandardGraphChildVariable)parent;
1293                 return variable.resource;
1294         }
1295         return null;
1296     }
1297
1298
1299     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1300         public static Object valueWithoutBinding(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1301
1302         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1303                 
1304                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1305                         Layer0 L0 = Layer0.getInstance(graph);
1306                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel);
1307                 }
1308
1309                 if (variable.parentResource == null)
1310                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1311
1312                 try {
1313                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1314                 } catch (NoSingleResultException e) {
1315                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1316                 } catch (DoesNotContainValueException e) {
1317                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1318                 }
1319                 
1320         }
1321
1322     @SCLValue(type = "ReadGraph -> Variable -> Binding -> a")
1323         public static Object valueWithBinding(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
1324
1325         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context;
1326                 
1327                 if(graph.sync(new IsEnumeratedValue(variable.getRepresents(graph)))) {
1328                         Layer0 L0 = Layer0.getInstance(graph);
1329                         return graph.getRelatedValue2(variable.getRepresents(graph), L0.HasLabel, binding);
1330                 }
1331
1332                 if (variable.parentResource == null)
1333                         throw new VariableException("Variable is not represented by any resource (URI=" + variable.getPossibleURI(graph) + ").");
1334
1335                 try {
1336                         return graph.getRelatedValue2(variable.parentResource, variable.property.predicate, variable);
1337                 } catch (NoSingleResultException e) {
1338                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1339                 } catch (DoesNotContainValueException e) {
1340                         throw new MissingVariableValueException(variable.getPossibleURI(graph));
1341                 }
1342                 
1343         }
1344
1345     @SCLValue(type = "WriteGraph -> Variable -> a -> Binding -> b")
1346         public static Object valueSetterWithBinding(WriteGraph graph, Variable variable, Object value, Binding binding) throws DatabaseException {
1347                 
1348                 Function4<WriteGraph, Variable, Object, Object, String> modifier = variable.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER);
1349                 if(modifier == null) modifier = VariableUtils.defaultInputModifier; 
1350                 modifier.apply(graph, variable, value, binding);
1351                 return null;
1352                 
1353         }
1354     
1355     static class L0Issue extends StandardIssue {
1356         
1357         private final String description;
1358         
1359         public L0Issue(String description, Resource type, Resource ... contexts) {
1360             super(type, contexts);
1361             this.description = description;
1362         }
1363
1364         @Override
1365         public Resource write(WriteGraph graph, Resource source) throws DatabaseException {
1366             Layer0 L0 = Layer0.getInstance(graph);
1367             IssueResource IR = IssueResource.getInstance(graph);
1368             Resource issue = super.write(graph, source);
1369             graph.claim(issue, IR.Issue_HasSeverity, IR.Severity_Fatal);
1370             graph.addLiteral(issue, L0.HasDescription, L0.HasDescription_Inverse, description, Bindings.STRING);
1371             return issue;
1372         }
1373         
1374     }
1375     
1376         private static List<Issue> reportInconsistency(ReadGraph graph, Resource subject, String description, List<Issue> issues) throws DatabaseException {
1377             if(issues == null) issues = new ArrayList<Issue>();
1378                 System.err.println("Change set validation reports the following issue: " + NameUtils.getSafeName(graph, subject, true) + ": " + description);
1379                 IssueResource IR = IssueResource.getInstance(graph);
1380                 issues.add(new L0Issue(description, IR.Issue, subject));
1381                 return issues;
1382         }
1383     
1384     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1385     public static List<Issue> relationValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1386
1387         Layer0 L0 = Layer0.getInstance(graph);
1388
1389         List<Issue> issues = null;
1390         
1391         for(Statement stm : graph.getStatements(resource, L0.IsWeaklyRelatedTo)) {
1392                 Resource predicate = stm.getPredicate();
1393                 Resource object = stm.getObject();
1394                 if(!isRelation(graph, L0, predicate)) {
1395                         issues = reportInconsistency(graph, resource, "The predicate of a statement must be a relation: " + NameUtils.toString(graph, stm), issues);
1396                 }
1397                 if(graph.isInstanceOf(predicate, L0.FunctionalRelation)) {
1398                         if(graph.getObjects(resource, predicate).size() > 1)
1399                                 issues = reportInconsistency(graph, resource, 
1400                                                 "Relation " +
1401                                                                 NameUtils.getSafeName(graph, predicate)
1402                                                                 + " is functional.", issues);
1403                 }
1404                 {
1405                         Collection<Resource> domain = graph.getObjects(predicate, L0.HasDomain);
1406                         if (!isInstanceOfAny(graph, resource, domain, true)) {
1407                                 StringBuilder sb = new StringBuilder()
1408                                 .append("The domain of ")
1409                                 .append(NameUtils.getSafeName(graph, predicate))
1410                                 .append(" relation is ");
1411                                 orString(graph, sb, domain).append(".");
1412                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1413                         }
1414                 }
1415                 {
1416                         Collection<Resource> range = graph.getObjects(predicate, L0.HasRange);
1417                         if (!isInstanceOfAny(graph, object, range, true) && !graph.isInstanceOf(object, L0.SCLValue)) {
1418                                 StringBuilder sb = new StringBuilder()
1419                                 .append("The range of ")
1420                                 .append(NameUtils.getSafeName(graph, predicate))
1421                                 .append(" relation is ");
1422                                 orString(graph, sb, range).append(" but current object is ")
1423                                 .append(NameUtils.getSafeName(graph, object)).append(".");
1424                                 issues = reportInconsistency(graph, resource, sb.toString(), issues);
1425                         }
1426                 }               
1427         }
1428                 
1429                 return issues != null ? issues : Collections.<Issue>emptyList();
1430
1431     }
1432
1433     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1434     public static List<Issue> propertyValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1435
1436         List<Issue> issues = null;
1437
1438         Layer0 L0 = Layer0.getInstance(graph);
1439         for(Statement stm : graph.getStatements(resource, L0.HasProperty)) {
1440                 Resource subject = stm.getSubject();
1441                 Resource predicate = stm.getPredicate();
1442                 String error = L0Validations.checkValueType(graph, subject, predicate);
1443                 if(error != null) issues = reportInconsistency(graph, subject, error, issues);
1444         }
1445                 
1446         return issues != null ? issues : Collections.<Issue>emptyList();
1447
1448     }
1449     
1450     
1451     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1452     public static List<Issue> valueValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1453
1454         List<Issue> issues = null;
1455
1456         Layer0 L0 = Layer0.getInstance(graph);
1457         if(graph.hasValue(resource)) {
1458                 if(!graph.isInstanceOf(resource, L0.Literal)) {
1459                         issues = reportInconsistency(graph, resource, 
1460                                         "Resource has a value but it is not a literal.", issues);
1461                 }
1462                 else {
1463                         // TODO check that the value is valid for the data type
1464                 }
1465         }
1466         else {
1467                 if(graph.isInstanceOf(resource, L0.Literal)) {
1468                         issues = reportInconsistency(graph, resource, 
1469                                         "Resource is a literal but it does not have a value.", issues);
1470                 }
1471         }
1472         
1473         return issues != null ? issues : Collections.<Issue>emptyList();
1474         
1475     }
1476
1477     
1478     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1479     public static List<Issue> uriValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1480         
1481         List<Issue> issues = null;
1482
1483         Layer0 L0 = Layer0.getInstance(graph);
1484         Resource parent = graph.getPossibleObject(resource, L0.PartOf);
1485         if(parent != null) {
1486             String parentURI = graph.syncRequest(new PossibleURI(parent));
1487             if(parentURI != null) {
1488                         String name = graph.getPossibleRelatedValue(resource, L0.HasName);
1489                         if(name == null) {
1490                                 issues = reportInconsistency(graph, resource, "Resource has a parent with URI but has no valid HasName.", issues);
1491                         }
1492             }
1493         }
1494
1495         return issues != null ? issues : Collections.<Issue>emptyList();
1496         
1497     }
1498     
1499     private static Resource getPossibleNearestClusterSet(ReadGraph graph, Resource base, Resource resource) throws DatabaseException {
1500
1501         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1502         if(cs.isClusterSet(resource) && !base.equals(resource)) return resource;
1503         
1504         Resource nearest = CommonDBUtils.getNearestOwner(graph, Collections.singletonList(resource));
1505         if(nearest == null) return null;
1506         
1507         return getPossibleNearestClusterSet(graph, base, nearest);
1508
1509     }
1510
1511     private static boolean quirks(ReadGraph graph, Resource resource) throws DatabaseException {
1512
1513         if(!resource.isPersistent()) return true;
1514         if(graph.isImmutable(resource)) return true;
1515         if(resource.getResourceId() < 0x2000) return true;
1516
1517         return false;
1518         
1519     }
1520     
1521     @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
1522     public static List<Issue> clusterValidator(ReadGraph graph, Resource resource) throws DatabaseException {
1523
1524         if(!Development.DEVELOPMENT) return Collections.<Issue>emptyList();
1525         
1526         if(quirks(graph, resource)) return Collections.<Issue>emptyList();
1527         
1528         List<Issue> issues = null;
1529
1530         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
1531         Resource set = cs.getClusterSetOfCluster(resource);
1532         
1533         if(set == null) return reportInconsistency(graph, resource, "Resource cluster is not part of any cluster set", issues);
1534         
1535         Resource nearestSet = getPossibleNearestClusterSet(graph, resource, resource);
1536         if(nearestSet == null) {
1537                 // This means that there is no owner since RootLibrary is a cluster set
1538                 return Collections.<Issue>emptyList();
1539         }
1540         
1541         if(!set.equals(nearestSet)) return reportInconsistency(graph, resource, "The cluster set of a resource is not the nearest owner set", issues);
1542
1543         return Collections.<Issue>emptyList();
1544         
1545     }
1546
1547     private static boolean isInstanceOfAny(ReadGraph graph, Resource r, Collection<Resource> types, boolean ifEmpty) throws DatabaseException {
1548                 if (types.isEmpty())
1549                         return ifEmpty;
1550                 for (Resource type : types) {
1551                         if (graph.isInstanceOf(r, type)) {
1552                                 return true;
1553                         }
1554                 }
1555                 return false;
1556         }
1557
1558         private static StringBuilder orString(ReadGraph graph, StringBuilder sb, Collection<Resource> rs) throws DatabaseException {
1559                 sb.append("(");
1560                 boolean first = true;
1561                 for (Resource r : rs) {
1562                         if (!first)
1563                                 sb.append(" | ");
1564                         first = false;
1565                         sb.append(NameUtils.getSafeName(graph, r));
1566                 }
1567                 sb.append(")");
1568                 return sb;
1569         }
1570
1571     public static boolean isRelation(ReadGraph g, Layer0 l0, Resource relation) throws DatabaseException {
1572                 return g.hasStatement(relation, l0.SubrelationOf) || relation == l0.IsWeaklyRelatedTo;
1573         }
1574         
1575         public static boolean isType(ReadGraph g, Layer0 l0, Resource type) throws DatabaseException {
1576                 return g.hasStatement(type, l0.Inherits) || type == l0.Entity;
1577         }
1578         
1579     public static Variable buildChildVariable(ReadGraph graph, Variable context, Resource graphChild, Object nodeChild) throws DatabaseException {
1580         VariableBuilder builder = graph.adapt(graphChild, VariableBuilder.class);
1581         return builder.buildChild(graph, context, build(((AbstractVariable)context).node, nodeChild), graphChild);
1582         }
1583
1584         private static Variable buildPropertyVariable(ReadGraph graph, Variable variable, Resource parentResource, PropertyInfo graphProperty, Object propertyNode) throws DatabaseException {
1585                 VariableNode<?> node = variable instanceof AbstractVariable ? build(((AbstractVariable)variable).node, propertyNode) : null;
1586                 return graphProperty.builder.buildProperty(graph, variable, node, parentResource, graphProperty.predicate);
1587         }
1588         
1589         static StandardGraphChildVariable createStandardGraphChildVariable(
1590                         AbstractVariable parent, Object child) {
1591                 return new StandardGraphChildVariable(parent, build(parent.node, child), null);
1592         }
1593
1594         private static StandardGraphPropertyVariable createStandardGraphPropertyVariable(
1595                         ReadGraph graph, AbstractVariable variable, Object nodeProperty) throws DatabaseException {
1596         Resource propertyResource = getPossiblePropertyResource(graph, variable, nodeProperty);
1597         return new StandardGraphPropertyVariable(graph, variable, build(variable.node, nodeProperty), null, propertyResource);
1598         }
1599         
1600         static Map<String, Variable> ensureVariableMap(
1601                         Map<String, Variable> map, int size) {
1602                 if(map == null) map = new THashMap<String,Variable>(size);
1603                 return map;
1604         }
1605
1606         private static PropertyInfo getPropertyInfo(ReadGraph graph, Resource predicate) throws DatabaseException {
1607                 return graph.syncRequest(new PropertyInfoRequest(predicate));
1608         }
1609
1610         @SuppressWarnings("unchecked")
1611         static String getNodeName(AbstractVariable parent, Object child) {
1612                 return parent.node.support.manager.getName(child);
1613         }
1614
1615         @SuppressWarnings("unchecked")
1616         private static Object getNodeValue(final AbstractVariable variable, final Binding binding) throws NodeManagerException, BindingException {
1617                 return variable.node.support.manager.getValue(variable.node.node, binding);
1618         }
1619
1620         @SuppressWarnings("unchecked")
1621         private static void setNodeValue(final AbstractVariable variable, final Object value, final Binding binding) throws NodeManagerException, BindingException {
1622                 variable.node.support.manager.setValue(variable.node.node, value, binding);
1623         }
1624         
1625         @SuppressWarnings("unchecked")
1626         private static String getPossiblePropertyURI(AbstractVariable parent, Object node) {
1627                 return parent.node.support.manager.getPropertyURI(parent.node.node, node);
1628         }
1629     
1630
1631     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1632     public static Object defaultInstantiateUnder(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1633         return new FunctionImpl2<Resource, Resource, Resource>() {
1634             public Resource apply(Resource container, Resource type) {
1635                 try {
1636                     WriteGraph graph = (WriteGraph)SCLContext.getCurrent().get("graph");
1637
1638                     Layer0 L0 = Layer0.getInstance(graph);
1639                     CommonDBUtils.selectClusterSet(graph, container);
1640                     Resource result = graph.newResource();
1641                     String name = NameUtils.findFreshInstanceName(graph, type, container);
1642                     graph.claim(result, L0.InstanceOf, type);
1643                     graph.addLiteral(result, L0.HasName, L0.NameOf, name, Bindings.STRING);
1644                     graph.claim(container, L0.ConsistsOf, L0.PartOf, result);
1645
1646                     return result;
1647                 } catch (DatabaseException e) {
1648                     throw new RuntimeDatabaseException(e);
1649                 }
1650             }
1651         };
1652     }
1653
1654 }