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