Improvements to resolving primitive properties in document server
[simantics/platform.git] / bundles / org.simantics.document.server / src / org / simantics / document / server / Functions.java
1 package org.simantics.document.server;
2
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.TreeMap;
12
13 import org.simantics.Simantics;
14 import org.simantics.databoard.Bindings;
15 import org.simantics.databoard.Datatypes;
16 import org.simantics.databoard.binding.Binding;
17 import org.simantics.databoard.type.Datatype;
18 import org.simantics.db.AsyncReadGraph;
19 import org.simantics.db.DirectStatements;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.RequestProcessor;
22 import org.simantics.db.Resource;
23 import org.simantics.db.Session;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.primitiverequest.Adapter;
27 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
28 import org.simantics.db.common.request.UnaryRead;
29 import org.simantics.db.common.request.UniqueRead;
30 import org.simantics.db.common.request.WriteResultRequest;
31 import org.simantics.db.common.utils.Logger;
32 import org.simantics.db.common.utils.NameUtils;
33 import org.simantics.db.exception.DatabaseException;
34 import org.simantics.db.layer0.function.All;
35 import org.simantics.db.layer0.request.ProjectModels;
36 import org.simantics.db.layer0.request.PropertyInfo;
37 import org.simantics.db.layer0.request.PropertyInfoRequest;
38 import org.simantics.db.layer0.request.VariableProperty;
39 import org.simantics.db.layer0.request.VariableRead;
40 import org.simantics.db.layer0.request.VariableValueWithBinding;
41 import org.simantics.db.layer0.variable.ConstantPropertyVariable;
42 import org.simantics.db.layer0.variable.ProxyChildVariable;
43 import org.simantics.db.layer0.variable.ProxySessionRequest;
44 import org.simantics.db.layer0.variable.ProxyVariables;
45 import org.simantics.db.layer0.variable.StandardAssertedGraphPropertyVariable;
46 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
47 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
48 import org.simantics.db.layer0.variable.Variable;
49 import org.simantics.db.layer0.variable.VariableMap;
50 import org.simantics.db.layer0.variable.VariableMapImpl;
51 import org.simantics.db.layer0.variable.Variables;
52 import org.simantics.db.procedure.AsyncProcedure;
53 import org.simantics.db.service.DirectQuerySupport;
54 import org.simantics.document.base.ontology.DocumentationResource;
55 import org.simantics.document.server.bean.Command;
56 import org.simantics.document.server.bean.DataDefinition;
57 import org.simantics.document.server.handler.AbstractEventHandler;
58 import org.simantics.document.server.handler.AbstractResponseHandler;
59 import org.simantics.document.server.handler.EventHandler;
60 import org.simantics.document.server.io.CommandContext;
61 import org.simantics.document.server.io.CommandContextImpl;
62 import org.simantics.document.server.io.CommandContextMutable;
63 import org.simantics.document.server.io.CommandResult;
64 import org.simantics.document.server.io.IConsole;
65 import org.simantics.document.server.request.NodeRequest;
66 import org.simantics.document.server.request.ServerSCLHandlerValueRequest;
67 import org.simantics.document.server.request.ServerSCLValueRequest;
68 import org.simantics.document.server.serverResponse.ServerResponse;
69 import org.simantics.document.server.serverResponse.SuccessResponse;
70 import org.simantics.modeling.ModelingResources;
71 import org.simantics.modeling.scl.SCLRealm;
72 import org.simantics.modeling.scl.SCLSessionManager;
73 import org.simantics.modeling.services.CaseInsensitiveComponentFunctionNamingStrategy;
74 import org.simantics.modeling.services.ComponentNamingStrategy;
75 import org.simantics.operation.Layer0X;
76 import org.simantics.project.IProject;
77 import org.simantics.scl.compiler.types.TCon;
78 import org.simantics.scl.compiler.types.Type;
79 import org.simantics.scl.compiler.types.Types;
80 import org.simantics.scl.reflection.annotations.SCLValue;
81 import org.simantics.scl.runtime.SCLContext;
82 import org.simantics.scl.runtime.function.Function;
83 import org.simantics.scl.runtime.function.Function1;
84 import org.simantics.scl.runtime.function.FunctionImpl1;
85 import org.simantics.scl.runtime.function.FunctionImpl4;
86 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
87 import org.simantics.scl.runtime.tuple.Tuple;
88 import org.simantics.scl.runtime.tuple.Tuple0;
89 import org.simantics.scl.runtime.tuple.Tuple2;
90 import org.simantics.scl.runtime.tuple.Tuple3;
91 import org.simantics.scl.runtime.tuple.Tuple4;
92 import org.simantics.scl.runtime.tuple.Tuple5;
93 import org.simantics.simulation.experiment.IExperiment;
94 import org.simantics.simulation.ontology.SimulationResource;
95 import org.simantics.simulation.project.IExperimentManager;
96 import org.simantics.structural2.variables.Connection;
97 import org.simantics.structural2.variables.StandardProceduralChildVariable;
98 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
99
100 import gnu.trove.map.hash.THashMap;
101
102 public class Functions {
103
104     private static class PrimitivePropertyStatementsProcedure implements AsyncProcedure<DirectStatements> {
105
106         public DirectStatements result;
107
108         @Override
109         public void execute(AsyncReadGraph graph, DirectStatements result) {
110             this.result = result;
111         }
112
113         @Override
114         public void exception(AsyncReadGraph graph, Throwable throwable) {
115         }
116
117     }
118
119     @SCLValue(type = "VariableMap")
120     public static VariableMap primitiveProperties = new VariableMapImpl() {
121         private void storePropertyValueAndExceptions(ReadGraph graph, Variable parent, String name, Variable property, Map<String, Variable> map) {
122                 try {
123                         Object value = property.getValue(graph);
124                                 map.put(name, new ConstantPropertyVariable(parent, name, value, null));
125                 } catch (DatabaseException e) {
126                         Variable propertyExceptions = map.get(NodeRequest.PROPERTY_VALUE_EXCEPTIONS);
127                         Map<String, Exception> exceptionMap;
128                         if (propertyExceptions == null) {
129                                 exceptionMap = new TreeMap<String, Exception>();
130                                 propertyExceptions = new ConstantPropertyVariable(parent, NodeRequest.PROPERTY_VALUE_EXCEPTIONS, exceptionMap, null);
131                                 map.put(NodeRequest.PROPERTY_VALUE_EXCEPTIONS, propertyExceptions);
132                         } else {
133                                 try {
134                                                 exceptionMap = propertyExceptions.getValue(graph);
135                                         } catch (DatabaseException e1) {
136                                                 Logger.defaultLogError(e1);
137                                                 return;
138                                         }
139                         }
140                         String label = name;
141                         try {
142                                 label = property.getLabel(graph);
143                         } catch (DatabaseException e2) {
144
145                         }
146                         exceptionMap.put(label, e);
147                 }
148         }
149         
150         @Override
151         public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
152             return All.getStandardPropertyDomainPropertyVariableFromValue(graph, context, name);
153         }
154
155         @Override
156         public Map<String, Variable> getVariables(final ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
157
158             if(map == null) map = new HashMap<String,Variable>();
159
160             Variable parent = context.getParent(graph);
161
162             DocumentationResource DOC = DocumentationResource.getInstance(graph);
163
164             if(parent instanceof StandardProceduralChildVariable) {
165
166                 StandardProceduralChildVariable procedural = (StandardProceduralChildVariable)parent;
167                 for(Variable property : procedural.getProperties(graph/*, DocumentationResource.URIs.Document_AttributeRelation*/)) {
168                     if(property instanceof StandardAssertedGraphPropertyVariable) {
169                         StandardAssertedGraphPropertyVariable ass = (StandardAssertedGraphPropertyVariable)property;
170                         if("dataDefinitions".equals(ass.property.name) || "commands".equals(ass.property.name)  || "pollingFunction".equals(ass.property.name)) {
171                                 storePropertyValueAndExceptions(graph, parent, ass.property.name, property, map);
172                         }
173                         continue;
174                     }
175                     Resource predicate = property.getPossiblePredicateResource(graph);
176                     if(predicate != null) {
177                         PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate));
178                         if(info.hasClassification(DocumentationResource.URIs.Document_AttributeRelation)) {
179                                 Variable prop = parent.getProperty(graph, predicate);
180                             storePropertyValueAndExceptions(graph, parent, info.name, prop, map);
181                         }
182                     }
183                 }
184
185             } else {
186
187                 Resource parentRes = parent.getRepresents(graph);
188                 {
189                         Variable prop = new StandardGraphPropertyVariable(graph, parent, DOC.Properties_commands);
190                         storePropertyValueAndExceptions(graph, parent, "commands", prop, map);
191                 }
192
193                 if (graph.getPossibleObject(parentRes, DOC.Properties_dataDefinitions) != null) {
194                         Variable prop = new StandardGraphPropertyVariable(graph, parent, DOC.Properties_dataDefinitions);
195                         storePropertyValueAndExceptions(graph, parent, "dataDefinitions", prop, map);
196                 }
197                 
198                 DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
199                 PrimitivePropertyStatementsProcedure foo = new PrimitivePropertyStatementsProcedure();
200
201                 dqs.forEachDirectPersistentStatement(graph, parentRes, foo);
202
203                 for(Statement stm : foo.result) {
204                     Resource predicate = stm.getPredicate();
205                     PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate));
206
207                     if(info.isHasProperty && info.hasClassification(DocumentationResource.URIs.Document_AttributeRelation)) {
208                         Variable prop = new StandardGraphPropertyVariable(graph, parent, predicate);
209                         storePropertyValueAndExceptions(graph, parent, info.name, prop, map);
210                     } else {
211                         Resource definition = graph.getPossibleObject(predicate, DOC.Document_definesAttributeRelation);
212                         if(definition != null) {
213                                 PropertyInfo info2 = graph.syncRequest(new PropertyInfoRequest(definition));
214                                 Variable prop = new StandardGraphPropertyVariable(graph, parent, definition);
215                             map.put(info2.name, new PrimitiveValueVariable(parent, info2.name, prop));
216                         }
217                     }
218                 }
219             }
220             return map;
221
222         }
223
224     };
225
226     @SCLValue(type = "VariableMap")
227     public static VariableMap inputSpaceChildren = new VariableMapImpl() {
228
229         private Variable getProxy(ReadGraph graph, Variable context) throws DatabaseException {
230             Variable root = Variables.getRootVariable(graph);
231             return new DocumentProxyChildVariable(context, context, root, ProxyChildVariable.CONTEXT_BEGIN);
232         }
233
234         @Override
235         public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
236
237             if(ProxyChildVariable.CONTEXT_BEGIN.equals(name)) return getProxy(graph, context);
238             return All.standardChildDomainChildren.getVariable(graph, context, name);
239
240         }
241
242         @Override
243         public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
244
245             map = All.standardChildDomainChildren.getVariables(graph, context, map);
246             if(map == null) map = new THashMap<String,Variable>();
247             map.put(ProxyChildVariable.CONTEXT_BEGIN, getProxy(graph, context));
248             return map;
249
250         }
251
252     };
253
254     static class DocumentProxyChildVariable extends ProxyChildVariable {
255
256         public DocumentProxyChildVariable(Variable base, Variable parent, Variable other, String name) {
257             super(base, parent, other, name);
258         }
259
260         @Override
261         public Variable create(Variable base, Variable parent, Variable other, String name) {
262             return new DocumentProxyChildVariable(base, parent, other, name);
263         }
264
265         public Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException {
266
267                 if(CONTEXT_END.equals(name)) {
268                 if(other instanceof ProxyChildVariable) {
269                         // The context is also a proxy - let it do the job
270                     return super.getPossibleChild(graph, name);
271                 } else {
272                         return new RootVariable(this, base.getRepresents(graph));
273                 }
274                 }
275
276             return super.getPossibleChild(graph, name);
277
278         }
279
280         public Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException {
281
282             Collection<Variable> result = super.getChildren(graph);
283             if(!(base instanceof ProxyChildVariable)) {
284                 result.add(new RootVariable(this, base.getRepresents(graph)));
285             }
286             return result;
287
288         }
289
290     }
291
292     static class RootVariable extends StandardGraphChildVariable {
293
294         public RootVariable(DocumentProxyChildVariable parent, Resource resource) {
295             super(parent, null, resource);
296         }
297
298         @Override
299         public String getName(ReadGraph graph) throws DatabaseException {
300             return ProxyChildVariable.CONTEXT_END;
301         }
302
303         @SuppressWarnings("deprecation")
304         @Override
305         public Variable getNameVariable(ReadGraph graph) throws DatabaseException {
306             return new ConstantPropertyVariable(this, Variables.NAME, ProxyChildVariable.CONTEXT_END, Bindings.STRING);
307         }
308
309     }
310
311     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
312     public static Variable input(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
313         Variable session = graph.syncRequest(new ProxySessionRequest(context));
314         DocumentationResource DOC = DocumentationResource.getInstance(graph);
315         String uri = session.getPossiblePropertyValue(graph, DOC.Session_inputURI);
316         if(uri == null) {
317                 // TODO HAXX - Please fix this
318                 // we have no URI so this probably means that someone has inserted a non-session 
319                 // into the proxy variable => return that instead
320                 return session;
321         }
322         return Variables.getVariable(graph, uri);
323     }
324
325     public static Variable stateVariable(ReadGraph graph, Variable self) throws DatabaseException {
326         Variable session = graph.syncRequest(new ProxySessionRequest(self));
327         if (session == null)
328                 throw new DatabaseException("No state for " + self.getURI(graph));
329         return session.getPossibleChild(graph, "__scl__");
330     }
331
332     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
333     public static Variable state(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
334         Variable session = graph.syncRequest(new ProxySessionRequest(context));
335         if (session == null)
336             throw new DatabaseException("No state for " + context.getURI(graph));
337         return session.getPossibleChild(graph, "__scl__");
338     }
339
340     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
341     public static Variable icstate(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
342         Variable session = graph.syncRequest(new ProxySessionRequest(context));
343         return session.getPossibleChild(graph, "__icstate__");
344     }
345
346     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
347     public static Variable session(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
348         return graph.syncRequest(new ProxySessionRequest(context));
349     }
350
351     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
352     public static Variable experiment(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
353         // Try if experiment (run) has been used as the input
354         Variable var = input(graph, converter, context);
355         SimulationResource SR = SimulationResource.getInstance(graph);
356         while(var != null && !graph.isInstanceOf(var.getRepresents(graph), SR.Run)) {
357             var = var.getParent(graph);
358         }
359
360         if(var != null) {
361             IExperiment exp = getExperiment(graph, var);
362             if(exp == null)
363                 return null;
364         }
365
366         return var;
367     }
368
369     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
370     public static Variable model(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
371         Variable var = input(graph, converter, context);
372         ModelingResources MOD = ModelingResources.getInstance(graph);
373         while(var != null && !graph.isInstanceOf(var.getRepresents(graph), MOD.StructuralModel)) {
374             var = var.getParent(graph);
375         }
376
377         return var;
378     }
379
380     private static Collection<Variable> getBroadcasted(ReadGraph graph, Variable target) throws DatabaseException {
381
382         ArrayList<Variable> result = new ArrayList<Variable>();
383
384         DocumentationResource DOC = DocumentationResource.getInstance(graph);
385
386         Variable broadcasted = target.getPossibleProperty(graph, DOC.Relations_broadcasted);
387         if(broadcasted != null) result.add(broadcasted);
388
389         for(Variable child : DocumentServerUtils.getChildrenInOrdinalOrder(graph, target)) {
390                 result.addAll(getBroadcasted(graph, child));
391         }
392
393         return result;
394
395     }
396
397     private static List<Command> getCommands(ReadGraph graph, Collection<Variable> commandVariables, String trigger, CommandContext constants, boolean broadcast) throws DatabaseException {
398
399         if(commandVariables.isEmpty()) return Collections.emptyList();
400
401         String t = trigger;
402         TreeMap<Integer, List<Command>> sequences = new TreeMap<Integer, List<Command>>();
403
404         DocumentationResource DOC = DocumentationResource.getInstance(graph);
405
406         int count = 0;
407         for (Variable c : commandVariables) {
408
409             if(trigger == null)
410                 t = c.getName(graph);
411
412             Connection conn = c.getValue(graph);
413             Variable targetConnectionPoint = DocumentServerUtils.getPossibleCommandTriggerConnectionPoint(graph, c, conn);
414             if (targetConnectionPoint != null) {
415                 Variable target = targetConnectionPoint.getParent(graph);
416                 if (target != null) {
417
418                     Boolean enabled = target.getPossiblePropertyValue(graph, DOC.Properties_exists, Bindings.BOOLEAN);
419                     if(enabled != null && !enabled) continue;
420
421                     Integer ordinal;
422                     if (broadcast) {
423                         ordinal = ++count;
424                     } else {
425                         ordinal = 1;
426                             try {
427                                 String o = c.getPossiblePropertyValue(graph, "ordinal");
428                                 if(o != null)
429                                     ordinal = Integer.parseInt(o);
430                             } catch (NumberFormatException e) {}
431                     }
432
433                     String constantKey = target.getPossiblePropertyValue(graph, "constantKey");
434                     String constantValue = target.getPossiblePropertyValue(graph, "constantValue");
435
436                     CommandContextMutable newConstants = (CommandContextMutable)constants; 
437                     if(constantKey != null && constantValue != null) {
438                         if(!constantKey.isEmpty()) {
439                                 newConstants = new CommandContextImpl().merge(constants);
440                                 newConstants.putString(constantKey, constantValue);
441                         }
442                     }
443
444                     String requiredKey = target.getPossiblePropertyValue(graph, "requiredKey");
445                     if(requiredKey != null && !requiredKey.isEmpty()) {
446                         if (newConstants == constants) {
447                                 newConstants = new CommandContextImpl().merge(constants);
448                         }
449                                 newConstants.putRow(CommandContext.REQUIRED_KEYS, Collections.<Object>singletonList(requiredKey));
450                     }
451
452                     String forbiddenKey = target.getPossiblePropertyValue(graph, "forbiddenKey");
453                     if(forbiddenKey != null && !forbiddenKey.isEmpty()) {
454                         if (newConstants == constants) {
455                                 newConstants = new CommandContextImpl().merge(constants);
456                         }
457                                 newConstants.putRow(CommandContext.FORBIDDEN_KEYS, Collections.<Object>singletonList(forbiddenKey));
458                     }
459
460                     if(DOC.Relations_broadcast.equals(targetConnectionPoint.getPredicateResource(graph))) {
461
462                         // This is a broadcast terminal of a container
463                         List<Command> broadcastCommands = getCommands(graph, getBroadcasted(graph, target), t, newConstants, true);
464                         sequences.put(ordinal, broadcastCommands);
465
466                     } else {
467
468                         Command command = new Command(DocumentServerUtils.getId(graph, target), t, 
469                                         targetConnectionPoint.getName(graph), newConstants);
470                         sequences.put(ordinal, Collections.singletonList(command));
471
472                     }
473
474                 }
475             }
476         }
477
478         List<Command> commands = new ArrayList<Command>();
479         for (List<Command> commandList : sequences.values()) {
480                 for (Command command : commandList) {
481                         commands.add(command);
482                 }
483         }
484
485         return commands;
486     }
487
488     /**
489      * Commands
490      * 
491      * @param graph
492      * @param variable
493      * @return
494      * @throws DatabaseException
495      */
496     public static List<Command> commandList(ReadGraph graph, Variable variable) throws DatabaseException {
497         return getCommands(graph, DocumentServerUtils.getTriggerCommands(graph, variable.getParent(graph)), null, new CommandContextImpl(), false);
498     }
499
500     /**
501      * Data definitions
502      * 
503      * @param graph
504      * @param variable
505      * @return
506      * @throws DatabaseException
507      */
508     public static List<DataDefinition> dataDefinitions(ReadGraph graph, Variable variable) throws DatabaseException {
509         DocumentationResource DOC = DocumentationResource.getInstance(graph); 
510         ArrayList<DataDefinition> dataDefinitions = new ArrayList<DataDefinition>();
511         // Find data definition connections
512         for (Variable dataDefinitionRelation : DocumentServerUtils.getDataDefinitions(graph, variable.getParent(graph))) {
513             Connection dataDefinitionConnection = dataDefinitionRelation.getValue(graph);
514             // Find data the other end of definition connection
515             Collection<Variable> dataDefinitionProviders = DocumentServerUtils.getPossibleOtherConnectionPoints(graph,
516                     dataDefinitionRelation, dataDefinitionConnection);
517             if (dataDefinitionProviders != null) {
518
519                 for(Variable dataDefinitionProvider : dataDefinitionProviders) {
520
521                     Variable dataDefinition = dataDefinitionProvider.getParent(graph);
522                     if (dataDefinition != null) {
523                         // Found other end. Is should contain ONE data
524 // definition connection to the actual data
525                         Collection<Variable> dataCollection = DocumentServerUtils.getDataRelations(graph, dataDefinition);
526                         if (dataCollection.size() == 1) {
527                             Variable dataRelation = dataCollection.iterator().next();
528                             Connection dataConnection = dataRelation.getValue(graph);
529                             // Find data the other end of definition connection
530                             Variable dataConnectionPoint = DocumentServerUtils.getPossibleOtherConnectionPoint(graph,
531                                     dataRelation, dataConnection);
532                             if (dataConnectionPoint != null) {
533                                 Variable data = dataConnectionPoint.getParent(graph);
534                                 Resource type = dataDefinition.getPossibleType(graph, DOC.Components_Component);
535
536                                 if (graph.isInheritedFrom(type, DOC.Components_DefVar)) {
537
538                                         String sourceProperty = dataDefinition.getPropertyValue(graph, DOC.Properties_source,
539                                                 Bindings.STRING);
540                                         String targetProperty = dataDefinition.getPropertyValue(graph, DOC.Properties_target,
541                                                 Bindings.STRING);
542
543                                         dataDefinitions.add(new DataDefinition(DocumentServerUtils.getId(graph, data),
544                                                         sourceProperty, targetProperty));
545
546                                 } else if (graph.isInheritedFrom(type, DOC.Components_DefVars)) {
547
548                                         List<String> sourcesProperty = toList(dataDefinition.getPropertyValue(graph, DOC.Properties_sources), String.class);
549                                         List<String> targetsProperty = toList(dataDefinition.getPropertyValue(graph, DOC.Properties_targets), String.class);
550
551                                         for (int i = 0; i < Math.min(sourcesProperty.size(), targetsProperty.size()); i++) {
552                                                 dataDefinitions.add(new DataDefinition(DocumentServerUtils.getId(graph, data),
553                                                         sourcesProperty.get(i), targetsProperty.get(i)));
554                                         }
555                                 }
556                             }
557                         }
558                     }
559                 }
560             }
561         }
562         return dataDefinitions;
563     }
564
565     @SuppressWarnings("unchecked")
566         private static <T> List<T> toList(Object o, Class<T> c) {
567         List<T> result = null;
568         if (o instanceof List) {
569                 return (List<T>)o;
570         } else if (o instanceof Object[]) {
571                 result = new ArrayList<T>(((Object[])o).length);
572                 for (T item : (T[])o) {
573                         result.add(item);
574                 }
575                 return result;
576         } else {
577                 return Collections.<T>emptyList();
578         }
579     }
580
581     public static AbstractEventHandler emptyOnClick(ReadGraph graph) throws DatabaseException {
582         return new EventHandler() {
583             @Override
584             public ServerResponse handle(ReadGraph graph, CommandContext parameters) throws DatabaseException {
585                 return null;
586             }
587         };
588     }
589
590     public static AbstractEventHandler writeEventHandler(ReadGraph graph, final Variable variable, final Function fn) {
591
592         final Session session = graph.getSession();
593
594         return new AbstractEventHandler() {
595
596                         @Override
597                         public CommandResult handle(final CommandContext parameters) {
598
599                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
600
601                 try {
602
603                                         String result = session.sync(new WriteResultRequest<String>() {
604
605                                             @Override
606                                             public String perform(WriteGraph graph) throws DatabaseException {
607                                                 SCLContext sclContext = SCLContext.getCurrent();
608                                                 Object oldGraph = sclContext.put("graph", graph);
609                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
610                                                 try {
611                                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
612                                                         @Override
613                                                         public String apply(String key) {
614                                                             return parameters.getString(key);
615                                                         }
616                                                     }; 
617                                                     Object response = (String)fn.apply(variable, pf);
618                                                     if(response instanceof String) {
619                                                         return (String)response;
620                                                     }
621                                                     return null;
622                                                 } catch (Throwable t) {
623                                                     t.printStackTrace();
624                                                 } finally {
625                                                         sclContext.put("graph", oldGraph);
626                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
627                                                 }
628
629                                                 return null;
630
631                                             }
632
633                                         });
634
635                         return new SuccessResponse(result);
636
637                                 } catch (Throwable e) {
638                                         Logger.defaultLogError(e);
639                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
640                                 }
641
642                         }
643
644         };
645     }
646
647     public static AbstractEventHandler readEventHandler(ReadGraph graph, final Variable variable, final Function fn) {
648
649         final Session session = graph.getSession();
650
651         return new AbstractEventHandler() {
652
653                         @Override
654                         public CommandResult handle(final CommandContext parameters) {
655
656                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
657
658                 try {
659
660                                         String result = session.sync(new UniqueRead<String>() {
661
662                                             @Override
663                                             public String perform(ReadGraph graph) throws DatabaseException {
664                                                 SCLContext sclContext = SCLContext.getCurrent();
665                                                 Object oldGraph = sclContext.put("graph", graph);
666                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
667                                                 try {
668                                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
669                                                         @Override
670                                                         public String apply(String key) {
671                                                             return parameters.getString(key);
672                                                         }
673                                                     }; 
674                                                     Object response = (String)fn.apply(variable, pf);
675                                                     if(response instanceof String) {
676                                                         return (String)response;
677                                                     }
678                                                     return null;
679                                                 } catch (Throwable t) {
680                                                     t.printStackTrace();
681                                                 } finally {
682                                                         sclContext.put("graph", oldGraph);
683                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
684                                                 }
685
686                                                 return null;
687
688                                             }
689
690                                         });
691
692                         return new SuccessResponse(result);
693
694                                 } catch (Throwable e) {
695                                         Logger.defaultLogError(e);
696                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
697                                 }
698
699                         }
700
701         };
702     }
703
704     public static AbstractEventHandler readEventHandler2(ReadGraph graph, final Function fn) {
705
706         final Session session = graph.getSession();
707
708         return new AbstractEventHandler() {
709
710                         @Override
711                         public CommandResult handle(final CommandContext parameters) {
712
713                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
714
715                 try {
716
717                         CommandResult result = session.sync(new UniqueRead<CommandResult>() {
718
719                                             @Override
720                                             public CommandResult perform(ReadGraph graph) throws DatabaseException {
721                                                 SCLContext sclContext = SCLContext.getCurrent();
722                                                 Object oldGraph = sclContext.put("graph", graph);
723                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
724                                                 try {
725                                                     Object response = fn.apply(parameters);
726                                                     if(response instanceof CommandResult) {
727                                                         return (CommandResult)response;
728                                                     }
729                                                     return null;
730                                                 } catch (Throwable t) {
731                                                     t.printStackTrace();
732                                                 } finally {
733                                                         sclContext.put("graph", oldGraph);
734                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
735                                                 }
736
737                                                 return null;
738
739                                             }
740
741                                         });
742
743                         return result;
744
745                                 } catch (Throwable e) {
746                                         Logger.defaultLogError(e);
747                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
748                                 }
749
750                         }
751
752         };
753     }
754
755     public static AbstractEventHandler responseHandler(ReadGraph graph, Variable self, String function) throws DatabaseException {
756
757         Variable anyFunction = self.browse(graph, ".#" + function);
758         if(anyFunction == null) return null;
759
760         final List<TCon> effects = ServerSCLHandlerValueRequest.getEffects(graph, anyFunction);
761
762         final Function1<CommandContext, CommandResult> fn = anyFunction.getValue(graph);
763         String expression = anyFunction.getPropertyValue(graph, "expression");
764
765         final Session session = graph.getSession();
766
767         return new AbstractResponseHandler(expression) {
768
769                 private String formatError(RequestProcessor proc, Throwable t) {
770
771                         try {
772
773                                         return proc.syncRequest(new UniqueRead<String>() {
774
775                                                 @Override
776                                                 public String perform(ReadGraph graph) throws DatabaseException {
777                                                         Variable proxy = ProxyVariables.proxyVariableRoot(graph, anyFunction);
778                                                         String uri2 = proxy.getURI(graph);
779                                                         String uri = self.getParent(graph).getURI(graph);
780
781                                                         String path = uri.substring(uri2.length());
782
783                                                         String expr = anyFunction.getPossiblePropertyValue(graph, "expression");
784                                                         StringBuilder message = new StringBuilder();
785                                                         message.append("Handler execution failed\n");
786                                                         message.append(" handler=" + path + "\n");
787                                                         message.append(" expression=" + expr + "\n");
788                                                         message.append(" message=" + t.getMessage() + "\n");
789
790                                                         StringWriter sw = new StringWriter();
791                                                         t.printStackTrace(new PrintWriter(sw));
792                                                         message.append(" stack trace=" + sw);
793
794                                                         return message.toString();
795                                                 }
796
797                                         });
798
799                                 } catch (DatabaseException e) {
800
801                                         return e.getMessage();
802
803                                 }
804
805                 }
806
807                         @Override
808                         public CommandResult handle(final CommandContext parameters) {
809
810                                 IConsole console = parameters.getValue("__console__");
811                                 SCLReportingHandler printer = (console != null) ? new ConsoleSCLReportingHandler(console)
812                                                 : (SCLReportingHandler) SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
813
814                 try {
815
816                         Object result = null;
817                         if(effects.contains(Types.WRITE_GRAPH)) {
818
819                                 result = session.syncRequest(new WriteResultRequest<Object>() {
820
821                                                         @Override
822                                                         public Object perform(WriteGraph graph)
823                                                                         throws DatabaseException {
824                                                 SCLContext sclContext = SCLContext.getCurrent();
825                                                 Object oldGraph = sclContext.put("graph", graph);
826                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
827                                                 try {
828                                                         Object response = fn.apply(parameters);
829                                                         return response;
830                                                 } catch (Throwable t) {
831                                                         return new org.simantics.document.server.serverResponse.Error(formatError(graph, t));
832                                                 } finally {
833                                                         sclContext.put("graph", oldGraph);
834                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
835                                                 }
836
837                                                         }
838                                 });
839
840                         } else if(effects.contains(Types.READ_GRAPH)) {
841
842                                 result = session.sync(new UniqueRead<Object>() {
843
844                                         @Override
845                                         public Object perform(ReadGraph graph) throws DatabaseException {
846
847                                                 SCLContext sclContext = SCLContext.getCurrent();
848                                                 Object oldGraph = sclContext.put("graph", graph);
849                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
850
851                                                 try {
852                                                         Object response = fn.apply(parameters);
853                                                         return response;
854                                                 } catch (Throwable t) {
855                                                         return new org.simantics.document.server.serverResponse.Error(formatError(graph, t));
856                                                 } finally {
857                                                         sclContext.put("graph", oldGraph);
858                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
859                                                 }
860
861                                         }
862                                 });
863
864                         } else {
865
866                                                 SCLContext sclContext = SCLContext.getCurrent();
867                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
868                                                 try {
869                                                         result = fn.apply(parameters);
870                                                 } finally {
871                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
872                                                 }
873
874                         }
875
876                         if (result instanceof org.simantics.document.server.serverResponse.Error) {
877                                 return (CommandResult)result;
878                         }
879
880                         if (result instanceof CommandResult) {
881                                 return (CommandResult)result;
882                         } else {
883                                 CommandContextMutable assignments = new CommandContextImpl();
884                                 assignments.putValue("result", result);
885                                 return new ServerResponse(200, "", assignments);
886                         }
887
888                                 } catch (Throwable e) {
889                                         return new org.simantics.document.server.serverResponse.Error(formatError(Simantics.getSession(), e));
890                                 }
891
892                         }
893
894         };
895     }
896
897     public static AbstractEventHandler writeEventHandler2(ReadGraph graph, final Function fn) {
898
899         final Session session = graph.getSession();
900
901         return new AbstractEventHandler() {
902
903                         @Override
904                         public CommandResult handle(final CommandContext parameters) {
905
906                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
907
908                 try {
909
910                         CommandResult result = session.syncRequest(new WriteResultRequest<CommandResult>() {
911
912                                             @Override
913                                             public CommandResult perform(WriteGraph graph) throws DatabaseException {
914                                                 SCLContext sclContext = SCLContext.getCurrent();
915                                                 Object oldGraph = sclContext.put("graph", graph);
916                                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
917                                                 try {
918                                                     Object response = fn.apply(parameters);
919                                                     if(response instanceof CommandResult) {
920                                                         return (CommandResult)response;
921                                                     }
922                                                     return null;
923                                                 } catch (Throwable t) {
924                                                     t.printStackTrace();
925                                                 } finally {
926                                                         sclContext.put("graph", oldGraph);
927                                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
928                                                 }
929
930                                                 return null;
931
932                                             }
933
934                                         });
935
936                         return result;
937
938                                 } catch (Throwable e) {
939                                         Logger.defaultLogError(e);
940                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
941                                 }
942
943                         }
944
945         };
946     }
947
948     public static AbstractEventHandler eventHandler2(ReadGraph graph, final Function fn) {
949
950         return new AbstractEventHandler() {
951
952                         @Override
953                         public CommandResult handle(final CommandContext parameters) {
954
955                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
956
957                 try {
958
959                         SCLContext sclContext = SCLContext.getCurrent();
960                         Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
961                         try {
962                                 Object response = fn.apply(parameters);
963                                 if(response instanceof CommandResult) {
964                                         return (CommandResult)response;
965                                 }
966                                 return null;
967                         } catch (Throwable t) {
968                                 t.printStackTrace();
969                         } finally {
970                                 sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
971                         }
972
973                         return null;
974
975                                 } catch (Throwable e) {
976                                         Logger.defaultLogError(e);
977                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
978                                 }
979
980                         }
981
982         };
983     }
984
985     public static AbstractEventHandler eventHandler(ReadGraph graph, final Function fn) {
986
987         return new AbstractEventHandler() {
988
989                         @Override
990                         public CommandResult handle(final CommandContext parameters) {
991
992                 final SCLReportingHandler printer = (SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER);
993
994                 try {
995
996                         String result = "";
997
998                                 SCLContext sclContext = SCLContext.getCurrent();
999                                 Object oldPrinter = sclContext.put(SCLReportingHandler.REPORTING_HANDLER, printer);
1000                                 try {
1001                                     Function1<String,String> pf = new FunctionImpl1<String,String>() {
1002                                         @Override
1003                                         public String apply(String key) {
1004                                             return parameters.getString(key);
1005                                         }
1006                                     }; 
1007                                     Object response = (String)fn.apply(pf);
1008                                     if(response instanceof String) {
1009                                         result = (String)response;
1010                                     }
1011                                 } catch (Throwable t) {
1012                                     t.printStackTrace();
1013                                 } finally {
1014                                         sclContext.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
1015                                 }
1016
1017                         return new SuccessResponse(result);
1018
1019                                 } catch (Throwable e) {
1020                                         Logger.defaultLogError(e);
1021                                         return new org.simantics.document.server.serverResponse.Error(e.getMessage()); 
1022                                 }
1023
1024                         }
1025
1026         };
1027     }
1028
1029     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1030     public static Object sclValue(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1031         return ServerSCLValueRequest.compileAndEvaluate(graph, context);
1032     }
1033
1034     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
1035     public static Object sclHandlerValue(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1036         return ServerSCLHandlerValueRequest.compileAndEvaluate(graph, context);
1037     }
1038
1039     @SCLValue(type = "ReadGraph -> Resource -> Resource -> ComponentNamingStrategy")
1040     public static ComponentNamingStrategy componentNamingStrategy(ReadGraph graph, Resource converter, Resource context) throws DatabaseException {
1041         return createComponentNamingStrategy(graph);
1042     }
1043
1044     public static CaseInsensitiveComponentFunctionNamingStrategy createComponentNamingStrategy(ReadGraph graph) throws DatabaseException {
1045         Layer0X L0X = Layer0X.getInstance(graph);
1046         @SuppressWarnings({ "unchecked", "rawtypes" })
1047         final Function dependencies = graph.syncRequest(new Adapter(L0X.DependencyResources, Function.class), TransientCacheListener.<Function>instance());
1048         @SuppressWarnings("rawtypes")
1049         Function moduleNameFunction = new FunctionImpl4<ReadGraph, Resource, String, Integer, List<Map<String, Object>>>() {
1050             @Override
1051             public Object apply(ReadGraph p0, Resource p1, String p2) {
1052                 return apply(p0, p1, p2);
1053             }
1054             @SuppressWarnings("unchecked")
1055             @Override
1056             public List<Map<String, Object>> apply(ReadGraph graph, Resource model, String search, Integer maxResults) {
1057                 return (List<Map<String, Object>>)dependencies.apply(graph, model, search, maxResults);
1058             }
1059         };
1060         return new CaseInsensitiveComponentFunctionNamingStrategy("%s%02d", moduleNameFunction);
1061     }
1062
1063     public static IExperiment getExperiment(ReadGraph graph, Variable variable) throws DatabaseException {
1064         if (variable == null)
1065             return null;
1066
1067         SimulationResource SIMU = SimulationResource.getInstance(graph);
1068
1069         Variable var = variable;
1070         Resource represents = var.getRepresents(graph);
1071         Resource activeRun = null;
1072         if (represents != null && graph.isInstanceOf(represents, SIMU.Run)) {
1073             activeRun = represents;
1074         }
1075
1076         IProject project = Simantics.peekProject();
1077         if (activeRun != null && project != null) {
1078             IExperimentManager expMan = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER);
1079             if (expMan != null)
1080                 return expMan.getExperiment(NameUtils.getSafeName(graph, activeRun));
1081         }
1082
1083         return null;
1084     }
1085
1086     public static CommandContextMutable putTuple(CommandContextMutable context, String key, Object tuple) {
1087         List<Object> list = new ArrayList<Object>();
1088         if (tuple instanceof Tuple) {
1089                 Collections.addAll(list, ((Tuple)tuple).toArray());
1090         } else {
1091                 list.add(tuple);
1092         }
1093         context.putRow(key, list);
1094         return context;
1095     }
1096
1097     public static List<Object> getTuples(CommandContext context, String key) {
1098         List<List<Object>> rows = context.getRows(key);
1099         List<Object> tuples = new ArrayList<Object>();
1100         if (rows != null) {
1101                 for (List<Object> row : rows) {
1102                         switch (row.size()) {
1103                         case 0: tuples.add(Tuple0.INSTANCE); break;
1104                         case 1: tuples.add(row.get(1)); break;
1105                         case 2: tuples.add(new Tuple2(row.get(0), row.get(1))); break;
1106                         case 3: tuples.add(new Tuple3(row.get(0), row.get(1), row.get(2))); break;
1107                         case 4: tuples.add(new Tuple4(row.get(0), row.get(1), row.get(2), row.get(3))); break;
1108                         case 5: tuples.add(new Tuple5(row.get(0), row.get(1), row.get(2), row.get(3), row.get(4))); break;
1109                         }
1110                 }
1111         }
1112         return tuples;
1113     }
1114
1115     public static String printContext(CommandContext context) {
1116         return context.toString();
1117     }
1118
1119     @SCLValue(type = "AbstractEventHandler")
1120     public static AbstractEventHandler emptyEvent = new AbstractEventHandler() {
1121
1122                 @Override
1123                 public CommandResult handle(CommandContext context) {
1124                         return null;
1125                 }
1126
1127     };
1128
1129     public static String sclStateKey(ReadGraph graph, Variable base, Variable self, String ref) throws DatabaseException {
1130
1131         String baseURI = base.getURI(graph);
1132
1133         String selfURI = self.getURI(graph);
1134
1135         String prefix = selfURI.substring(0, selfURI.indexOf(ProxyChildVariable.CONTEXT_BEGIN));
1136         String suffix = selfURI.substring(selfURI.lastIndexOf(ProxyChildVariable.CONTEXT_END) + ProxyChildVariable.CONTEXT_END.length());
1137         String stripped = prefix + suffix;
1138
1139         String relative = Variables.getRelativeRVI(baseURI, stripped); 
1140
1141         return Variables.getRVI(relative, ref);
1142
1143     }
1144
1145     public static Variable sclStateVariable(ReadGraph graph, Variable base, Variable self, String ref) throws DatabaseException {
1146
1147         String id = sclStateKey(graph, base, self, ref);
1148
1149         Variable sclVar = base.getPossibleChild(graph, "__scl__");
1150         if(sclVar == null) return null;
1151
1152         return sclVar.getPossibleProperty(graph, id);
1153
1154     }
1155
1156     public static Object sclStateValueOrDefault(ReadGraph graph, Variable base, Variable self, String ref, Object defaultValue) throws DatabaseException {
1157
1158         Variable stateVariable = sclStateVariable(graph, base, self, ref);
1159         if (stateVariable != null) {
1160
1161                 return stateVariable.getValue(graph);
1162
1163         } else {
1164
1165                 String id = sclStateKey(graph, base, self, ref);
1166
1167                 SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph) + "/__scl__");
1168                 realm.getConnection().setVariable(id, getSCLType(defaultValue), defaultValue);
1169
1170                 return defaultValue;
1171
1172         }
1173     }
1174
1175     public static void setSclStateValue(WriteGraph graph, Variable base, Variable self, String ref, Object value) throws DatabaseException {
1176
1177         String id = sclStateKey(graph, base, self, ref);
1178
1179         SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph)+"/__scl__");
1180         realm.getConnection().setVariable(id, getSCLType(value), value);
1181         realm.refreshVariablesSync();
1182
1183     }
1184
1185     public static Object projectComponentState(ReadGraph graph, Variable self, String ref, Object defaultValue) throws DatabaseException {
1186         Resource project = Simantics.getProjectResource();
1187         Variable component = self.getParent(graph);
1188         Variable projectVariable = Variables.getVariable(graph, project);
1189         return sclStateValueOrDefault(graph, projectVariable, component, ref, defaultValue);
1190     }
1191
1192     public static void setProjectComponentState(WriteGraph graph, Variable self, String ref, Object value) throws DatabaseException {
1193         Resource project = Simantics.getProjectResource();
1194         Variable component = self.getParent(graph);
1195         Variable projectVariable = Variables.getVariable(graph, project);
1196         setSclStateValue(graph, projectVariable, component, ref, value);
1197     }
1198
1199     private static Type getSCLType(Object value) throws DatabaseException {
1200         Binding b = Bindings.getBindingUnchecked(value.getClass());
1201         Datatype t = b.type();
1202         if(Datatypes.STRING.equals(t)) return Types.STRING;
1203         if(Datatypes.DOUBLE.equals(t)) return Types.DOUBLE;
1204         throw new DatabaseException("Type not supported");
1205     }
1206
1207     public static List<Variable> documentModelContribution(ReadGraph graph, Resource doc) throws DatabaseException {
1208         Resource project = Simantics.getProjectResource();
1209         ArrayList<Variable> result = new ArrayList<Variable>();
1210         for(Resource model : graph.syncRequest(new ProjectModels(project))) {
1211                 result.add(Variables.getVariable(graph, model));
1212         }
1213         return result;
1214     }
1215
1216     public static String documentModelContributionLabel(ReadGraph graph, Variable var) throws DatabaseException {
1217         return var.getName(graph);
1218     }
1219
1220     public static Object getPropertyValueCached(ReadGraph graph, Variable variable, String name, Binding binding) throws DatabaseException {
1221         Variable property = graph.syncRequest(new VariableProperty(variable, name));
1222         return graph.syncRequest(new VariableValueWithBinding<Object>(property, binding));
1223     }
1224
1225     public static class ParentExistsRequest extends VariableRead<Boolean> {
1226
1227                 public ParentExistsRequest(Variable parent) {
1228                         super(parent);
1229                 }
1230
1231                 @Override
1232                 public Boolean perform(ReadGraph graph) throws DatabaseException {
1233                         
1234                         Variable existsProperty = variable.getPossibleProperty(graph, "exists");
1235                         if(existsProperty == null) return true;
1236                         
1237                         Boolean exists = existsProperty.getPossibleValue(graph, Bindings.BOOLEAN);
1238                         if (exists == null || !exists) return false;
1239
1240                 return graph.syncRequest(new ParentExistsRequest(variable.getParent(graph)));
1241
1242                 }
1243
1244     } 
1245
1246     public static class PathExistsRequest extends VariableRead<Boolean> {
1247
1248                 public PathExistsRequest(Variable variable) {
1249                         super(variable);
1250                 }
1251
1252                 @Override
1253                 public Boolean perform(ReadGraph graph) throws DatabaseException {
1254
1255                         Variable widget = variable.getParent(graph);
1256
1257                 Boolean exists = widget.getPossiblePropertyValue(graph, "exists");
1258                         if (exists == null || !exists) return false;
1259
1260                 if (!graph.syncRequest(new ParentExistsRequest(widget.getParent(graph)))) return false;
1261
1262                 DocumentationResource DOC = DocumentationResource.getInstance(graph);
1263                 Collection <Variable> cps = widget.getProperties(graph, DocumentationResource.URIs.Relations_parentRelation);
1264                 for (Variable cp : cps) {
1265
1266                         Connection conn = cp.getValue(graph);
1267                                 Variable otherCp = DocumentServerUtils.getPossibleChildConnectionPoint(graph, cp, conn);
1268                                 if (otherCp != null) {
1269                                         Variable parentWidget = otherCp.getParent(graph);
1270                                         if (parentWidget.<Boolean>getPropertyValue(graph, "pathExists")) {
1271                                                 return true;
1272                                         }
1273                                 } else {
1274                                         Variable parentCp = graph.sync(new UnaryRead<Connection, Variable>(conn) {
1275                                     @Override
1276                                     public Variable perform(ReadGraph graph) throws DatabaseException {
1277                                         DocumentationResource DOC = DocumentationResource.getInstance(graph);
1278                                         Collection<VariableConnectionPointDescriptor> descs = parameter.getConnectionPointDescriptors(graph, null);
1279
1280                                                 for(VariableConnectionPointDescriptor desc : descs) {
1281                                                         if (DOC.Relations_partN.equals(desc.getConnectionPointResource(graph))) {
1282                                                                 return desc.getVariable(graph);
1283                                                         }
1284                                                 }
1285                                                 return null;
1286                                     }
1287                                 });
1288                                         if (parentCp != null) {
1289                                                 Variable parentWidget = parentCp.getParent(graph);
1290                                                 if (parentWidget.<Boolean>getPropertyValue(graph, "pathExists")) {
1291                                                         return true;
1292                                                 }
1293                                         }
1294                                 }
1295                         }
1296
1297                         Resource type = widget.getType(graph);
1298                         return graph.isInheritedFrom(type, DOC.Components_ParentlessComponent) ||
1299                                 (graph.isInheritedFrom(type, DOC.DocumentComponent) && cps.isEmpty());
1300                 }
1301     }
1302
1303     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean")
1304     public static boolean pathExists(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
1305         return graph.syncRequest(new PathExistsRequest(context));
1306     }
1307
1308 }