a2f7138b44bdd048c8b5dee813d1d9e9bfaaee85
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / utils / HeadlessComponentTypePropertiesResultRequest.java
1 package org.simantics.modeling.utils;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6
7 import org.simantics.databoard.Bindings;
8 import org.simantics.databoard.binding.Binding;
9 import org.simantics.databoard.binding.error.BindingException;
10 import org.simantics.databoard.type.Datatype;
11 import org.simantics.databoard.type.NumberType;
12 import org.simantics.db.ReadGraph;
13 import org.simantics.db.RequestProcessor;
14 import org.simantics.db.Resource;
15 import org.simantics.db.common.NamedResource;
16 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
17 import org.simantics.db.common.request.IsEnumeratedValue;
18 import org.simantics.db.common.request.UniqueRead;
19 import org.simantics.db.common.utils.CommonDBUtils;
20 import org.simantics.db.common.utils.NameUtils;
21 import org.simantics.db.exception.DatabaseException;
22 import org.simantics.db.layer0.request.PropertyInfo;
23 import org.simantics.db.layer0.request.PropertyInfoRequest;
24 import org.simantics.db.layer0.util.Layer0Utils;
25 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
26 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
27 import org.simantics.db.layer0.variable.Variable;
28 import org.simantics.layer0.Layer0;
29 import org.simantics.modeling.scl.CompileProceduralSCLMonitorRequest;
30 import org.simantics.modeling.scl.CompileSCLMonitorRequest;
31 import org.simantics.operation.Layer0X;
32 import org.simantics.scl.runtime.SCLContext;
33 import org.simantics.scl.runtime.function.Function1;
34 import org.simantics.structural.stubs.StructuralResource2;
35 import org.simantics.utils.ui.ErrorLogger;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 public class HeadlessComponentTypePropertiesResultRequest extends UniqueRead<ComponentTypePropertiesResult> {
40     
41     private static final Logger LOGGER = LoggerFactory.getLogger(HeadlessComponentTypePropertiesResultRequest.class);
42
43     protected final Resource componentType;
44
45     /**
46      * @param componentTypeViewer
47      */
48     public HeadlessComponentTypePropertiesResultRequest(Resource componentType) {
49         this.componentType = componentType;
50     }
51
52     protected void readSectionSpecificData(ReadGraph graph, ComponentTypeViewerPropertyInfo info) throws DatabaseException {
53     }
54     
55     @Override
56     public ComponentTypePropertiesResult perform(ReadGraph graph) throws DatabaseException {
57         List<ComponentTypeViewerPropertyInfo> result = new ArrayList<>();
58         List<NamedResource> connectionPoints = new ArrayList<>();
59         Layer0 L0 = Layer0.getInstance(graph);
60         Layer0X L0X = Layer0X.getInstance(graph);
61         StructuralResource2 STR = StructuralResource2.getInstance(graph);
62
63         boolean typeIsImmutable = graph.isImmutable(componentType)
64                 || graph.hasStatement(componentType, STR.ComponentType_Locked)
65                 || Layer0Utils.isPublished(graph, componentType)
66                 || Layer0Utils.isContainerPublished(graph, componentType);
67
68         for(Resource relation : graph.getObjects(componentType, L0.DomainOf)) {
69             if(graph.isSubrelationOf(relation, L0.HasProperty)) {
70                 String name = graph.getRelatedValue(relation, L0.HasName);
71                 String type =  graph.getPossibleRelatedValue(relation, L0.RequiresValueType);
72                 String label = graph.getPossibleRelatedValue(relation, L0.HasLabel);
73                 if (label == null)
74                     label = ""; //$NON-NLS-1$
75                 String description = graph.getPossibleRelatedValue(relation, L0.HasDescription);
76                 if (description == null)
77                     description = ""; //$NON-NLS-1$
78                 NumberType numberType = null;
79                 if(type == null)
80                     type = "Double"; //$NON-NLS-1$
81                 String unit = graph.getPossibleRelatedValue(relation, L0X.HasUnit, Bindings.STRING);
82                 String defaultValue = "0"; //$NON-NLS-1$
83                 String expression = null;
84                 
85                 for(Resource assertion : graph.getAssertedObjects(componentType, relation)) {
86                     try {
87                         expression = graph.getPossibleRelatedValue(assertion, L0.SCLValue_expression, Bindings.STRING);
88                         if(expression != null) {
89                                 defaultValue = "=" + expression; //$NON-NLS-1$
90                         } else if (graph.sync(new IsEnumeratedValue(assertion))) {
91                             defaultValue = CommonDBUtils.getEnumerationValueName(graph, assertion);
92                         } else {
93                             Datatype dt = getPossibleDatatype(graph, assertion);
94                             if (dt == null)
95                                 continue;
96                             if (dt instanceof NumberType)
97                                 numberType = (NumberType) dt;
98                             Binding binding = Bindings.getBinding(dt);
99                             Object value = graph.getValue(assertion, binding);
100                             try {
101                                 defaultValue = binding.toString(value, true);
102                             } catch (BindingException e) {
103                                 ErrorLogger.defaultLogError(e);
104                             }
105                         }
106                     } catch(DatabaseException e) {
107                         ErrorLogger.defaultLogError(e);
108                     }
109                 }
110                 
111                 String valid = expression != null ? validateMonitorExpression(graph, componentType, relation, expression) : null; 
112
113                 boolean immutable = typeIsImmutable || graph.isImmutable(relation);
114                 ComponentTypeViewerPropertyInfo info =
115                         new ComponentTypeViewerPropertyInfo(relation, name, type, defaultValue, numberType, unit, label, description, expression, valid, immutable);
116                 
117                 readSectionSpecificData(graph, info);
118
119                 result.add(info);
120
121             } else if (graph.isInstanceOf(relation, STR.ConnectionRelation)) {
122                 NamedResource nr = new NamedResource(NameUtils.getSafeName(graph, relation), relation);
123                 connectionPoints.add(nr);
124             }
125         }
126         Collections.sort(result);
127         return new ComponentTypePropertiesResult(result, connectionPoints, typeIsImmutable);
128     }
129     
130     private Datatype getPossibleDatatype(ReadGraph graph, Resource literal) throws DatabaseException {
131         Binding binding = Bindings.getBindingUnchecked(Datatype.class);
132         for (Resource dataTypeResource : graph.getObjects(literal, Layer0.getInstance(graph).HasDataType)) {
133             Datatype dt = graph.getPossibleValue(dataTypeResource, binding);
134             if (dt != null)
135                 return dt;
136         }
137         return null;
138     }
139
140     public static String validateMonitorExpression(final RequestProcessor processor, final Resource componentType, final Resource relation, final String expression) {
141
142         try {
143             return processor.sync(new UniqueRead<String>() {
144
145                 @Override
146                 public String perform(ReadGraph graph) throws DatabaseException {
147
148                     StructuralResource2 STR = StructuralResource2.getInstance(graph);
149
150                     // TODO: this is a bit hackish but should get the job done in most parts and
151                     // importantly indicates something for the user
152                     PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheAsyncListener.<PropertyInfo>instance());
153                     Variable parent = new StandardGraphChildVariable(null, null, componentType) {
154                         
155                         public Resource getType(ReadGraph graph) throws DatabaseException {
156                             return componentType;
157                         };
158                         
159                         public Variable getPossibleProperty(ReadGraph graph, String name) throws DatabaseException {
160                             Variable prop = super.getPossibleProperty(graph, name);
161                             if (prop != null) {
162                                 return prop;
163                             } else {
164                                 return getChild(graph, name);
165                             }
166                         };
167                     };
168                     
169                     for(Resource literal : graph.getAssertedObjects(componentType, relation)) {
170
171                         try {
172                             Variable context = new StandardGraphPropertyVariable(parent, null, null, info, literal);
173                             if(graph.isInstanceOf(componentType, STR.ProceduralComponentType)) {
174                                 CompileProceduralSCLMonitorRequest.compileAndEvaluate(graph, context);
175                             } else {
176                                 compileAndEvaluate(graph, context, expression);
177                             }
178                             
179                         } catch (Exception e) {
180                             String msg = e.getMessage();
181                             int index = msg.indexOf(":"); //$NON-NLS-1$
182                             if(index > 0) msg = msg.substring(index);
183                             return msg;
184                         }
185                     }
186                     return null;
187                 }
188                 
189             });
190         } catch (DatabaseException e) {
191             LOGGER.error("Could not validate ", e);
192         }
193         return null;
194     }
195     
196     public static void compileAndEvaluate(ReadGraph graph, Variable context, String expression) throws DatabaseException {
197         SCLContext sclContext = SCLContext.getCurrent();
198         Object oldGraph = sclContext.get("graph");
199         try {
200             CompileSCLMonitorRequest compileSCLMonitorRequest = new ValidationCompilationRequest(graph, context, expression);
201             Function1<Variable,Object> exp = graph.syncRequest(compileSCLMonitorRequest);
202             sclContext.put("graph", graph);
203             //return exp.apply(context.getParent(graph));
204         } catch (DatabaseException e) {
205             throw (DatabaseException)e;
206         } catch (Throwable t) {
207             throw new DatabaseException(t);
208         } finally {
209             sclContext.put("graph", oldGraph);
210         }
211     }
212
213     private static final class ValidationCompilationRequest extends CompileSCLMonitorRequest {
214         private final String expression;
215     
216         private ValidationCompilationRequest(ReadGraph graph, Variable context, String expression)
217                 throws DatabaseException {
218             super(graph, context);
219             this.expression = expression;
220         }
221     
222         @Override
223         protected String getExpressionText(ReadGraph graph) throws DatabaseException {
224             return expression;
225         }
226     
227         @Override
228         public int hashCode() {
229             return super.hashCode() + 37 * expression.hashCode();
230         }
231     
232         @Override
233         public boolean equals(Object obj) {
234             return super.equals(obj) && ((ValidationCompilationRequest)obj).expression.equals(expression);
235         }
236     }
237     
238 }