]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/DerivedPropertiesSection.java
0d6ae80d88004f06079b87795a8c9c192641fc50
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / componentTypeEditor / DerivedPropertiesSection.java
1 package org.simantics.modeling.ui.componentTypeEditor;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7
8 import org.eclipse.jface.layout.GridDataFactory;
9 import org.eclipse.jface.layout.GridLayoutFactory;
10 import org.eclipse.jface.layout.TableColumnLayout;
11 import org.eclipse.jface.viewers.ColumnWeightData;
12 import org.eclipse.swt.SWT;
13 import org.eclipse.swt.custom.TableEditor;
14 import org.eclipse.swt.events.MouseAdapter;
15 import org.eclipse.swt.events.MouseEvent;
16 import org.eclipse.swt.events.SelectionAdapter;
17 import org.eclipse.swt.events.SelectionEvent;
18 import org.eclipse.swt.graphics.Color;
19 import org.eclipse.swt.graphics.Rectangle;
20 import org.eclipse.swt.layout.FillLayout;
21 import org.eclipse.swt.widgets.Button;
22 import org.eclipse.swt.widgets.Composite;
23 import org.eclipse.swt.widgets.Control;
24 import org.eclipse.swt.widgets.Table;
25 import org.eclipse.swt.widgets.TableColumn;
26 import org.eclipse.swt.widgets.TableItem;
27 import org.eclipse.ui.forms.widgets.Form;
28 import org.eclipse.ui.forms.widgets.FormToolkit;
29 import org.eclipse.ui.forms.widgets.Section;
30 import org.simantics.Simantics;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.RequestProcessor;
33 import org.simantics.db.Resource;
34 import org.simantics.db.WriteGraph;
35 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
36 import org.simantics.db.common.request.UniqueRead;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.layer0.request.PropertyInfo;
40 import org.simantics.db.layer0.request.PropertyInfoRequest;
41 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
42 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
43 import org.simantics.db.layer0.variable.Variable;
44 import org.simantics.layer0.Layer0;
45 import org.simantics.modeling.scl.CompileProceduralSCLMonitorRequest;
46 import org.simantics.modeling.scl.CompileSCLMonitorRequest;
47 import org.simantics.modeling.userComponent.ComponentTypeCommands;
48 import org.simantics.scl.runtime.SCLContext;
49 import org.simantics.scl.runtime.function.Function1;
50 import org.simantics.scl.runtime.function.Function4;
51 import org.simantics.structural.stubs.StructuralResource2;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class DerivedPropertiesSection implements ComponentTypeViewerSection {
56     private static final String[] COLUMN_NAMES = 
57             new String[] {"Name", "Type", "Expression", "Unit", "Label", "Description"};
58     private static final int[] COLUMN_LENGTHS =
59             new int[] { 120, 100, 100, 70, 100, 100 };
60     private static final int[] COLUMN_WEIGHTS =
61             new int[] { 0, 0, 100, 0, 0, 0 };
62     private static Function4<RequestProcessor, Resource, Resource, String, String> VALIDATE_MONITOR_EXPRESSION =
63             new Function4<RequestProcessor, Resource, Resource, String, String>() {
64         @Override
65         public String apply(RequestProcessor p0, Resource p1, Resource p2, String p3) {
66             return validateMonitorExpression(p0, p1, p2, p3);
67         }
68     };
69     
70     private static final Logger LOGGER = LoggerFactory.getLogger(DerivedPropertiesSection.class);
71     
72     ComponentTypeViewerData data;
73     Table table;
74     TableColumn[] columns;
75     TableEditor editor;
76     Button newProperty;
77     Button removeProperty;
78     
79     Section section;
80     
81     public DerivedPropertiesSection(ComponentTypeViewerData data) {
82         this.data = data;
83         FormToolkit tk = data.tk;
84         Form form = data.form;
85         section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);
86         section.setLayout(new FillLayout());
87         section.setText("Derived properties");
88
89         Composite sectionBody = tk.createComposite(section);
90         GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);
91         section.setClient(sectionBody);
92
93         Composite tableComposite = tk.createComposite(sectionBody);
94         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);
95         TableColumnLayout tcl = new TableColumnLayout();
96         tableComposite.setLayout(tcl);
97
98         table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
99         table.setLinesVisible(true);
100         table.setHeaderVisible(true);
101
102         columns = new TableColumn[COLUMN_NAMES.length];
103         for(int i=0;i<COLUMN_NAMES.length;++i) {
104             TableColumn column = new TableColumn(table, SWT.NONE);
105             columns[i] = column;
106             tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));
107             column.setText(COLUMN_NAMES[i]);
108         }
109
110         // Table editor
111         editor = new TableEditor(table);
112         editor.grabHorizontal = true;
113         editor.grabVertical = true;
114         editor.horizontalAlignment = SWT.LEFT;
115         table.addMouseListener(new MouseAdapter() {
116             @Override
117             public void mouseDown(MouseEvent e) {
118                 // Clean up any previous editor control
119                 Control oldEditor = editor.getEditor();
120                 if (oldEditor != null) oldEditor.dispose();
121
122                 if (data.readOnly)
123                     return;
124
125                 // Relative position
126                 Rectangle tableBounds = table.getClientArea();
127                 int rx = e.x - tableBounds.x;
128                 int ry = e.y - tableBounds.y;
129
130                 // Find cell
131                 TableItem selectedItem = null;
132                 int selectedColumn = -1;
133                 Rectangle selectedItemBounds = null;
134                 for(TableItem item : table.getItems()) {
135                     for(int column = 0;column < COLUMN_NAMES.length;++column) {
136                         Rectangle bounds = item.getBounds(column);
137                         if(bounds.contains(rx, ry)) {
138                             selectedItemBounds = bounds;
139                             selectedItem = item;
140                             selectedColumn = column;
141                             break;
142                         }
143                     }
144                 }
145                 if(selectedItem == null) {
146                     return;
147                 }
148
149                 // Table editor
150                 final int column = selectedColumn; 
151                 final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();
152                 final Resource resource = propertyInfo.resource;
153                 switch (column) {
154                 case 0:
155                     data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);
156                     break;
157
158                 case 1:
159                     data.editType(table, editor, propertyInfo, selectedItem, column, false);
160                     break;
161
162                 case 2:
163                     data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
164                         @Override
165                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
166                             ComponentTypeCommands.setMonitorExpression(graph, data.componentType, resource, newValue);
167                         }
168                     }, VALIDATE_MONITOR_EXPRESSION);
169                     break;
170
171                 case 3:
172                     data.editUnit(table, editor, propertyInfo, selectedItem, column);
173                     break;
174
175                 case 4:
176                     data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
177                         @Override
178                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
179                             graph.markUndoPoint();
180                             String value = newValue.isEmpty() ? null : newValue;
181                             ComponentTypeCommands.setLabel(graph, resource, value);
182                         }
183                     }, null);
184                     break;
185
186                 case 5:
187                     data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {
188                         @Override
189                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
190                             graph.markUndoPoint();
191                             String value = newValue.isEmpty() ? null : newValue;
192                             ComponentTypeCommands.setDescription(graph, resource, value);
193                         }
194                     });
195                     break;
196                 }
197             }
198
199         });
200
201         // Buttons
202
203         Composite buttons = tk.createComposite(sectionBody);
204         GridDataFactory.fillDefaults().applyTo(buttons);
205         GridLayoutFactory.fillDefaults().applyTo(buttons);
206
207         newProperty = tk.createButton(buttons, "New property", SWT.PUSH);
208         GridDataFactory.fillDefaults().applyTo(newProperty);
209         removeProperty = tk.createButton(buttons, "Remove property", SWT.PUSH);
210         GridDataFactory.fillDefaults().applyTo(removeProperty);
211
212         // Actions
213
214         newProperty.addSelectionListener(new SelectionAdapter() {
215             @Override
216             public void widgetSelected(SelectionEvent e) {
217                 if(editor.getEditor() != null)
218                     editor.getEditor().dispose();
219                 Simantics.getSession().async(new WriteRequest() {
220                     @Override
221                     public void perform(WriteGraph graph)
222                             throws DatabaseException {
223                         ComponentTypeCommands.createMonitorPropertyWithDefaults(graph, data.componentType);
224                     }
225                 });
226             }
227         });
228
229         removeProperty.addSelectionListener(new SelectionAdapter() {
230             @Override
231             public void widgetSelected(SelectionEvent e) {
232                 if(editor.getEditor() != null)
233                     editor.getEditor().dispose();
234                 final List<Resource> propertiesToBeRemoved = 
235                         new ArrayList<>();
236                 for(TableItem item : table.getSelection())
237                     propertiesToBeRemoved.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);
238                 //System.out.println("remove " + propertiesToBeRemoved.size() + " resources");
239                 if(!propertiesToBeRemoved.isEmpty())
240                     Simantics.getSession().async(new WriteRequest() {
241                         @Override
242                         public void perform(WriteGraph graph)
243                                 throws DatabaseException {
244                             graph.markUndoPoint();
245                             for(Resource property : propertiesToBeRemoved)
246                                 ComponentTypeCommands.removeProperty(graph, data.componentType, property);
247                         }
248                     });
249             }
250         });
251     }
252
253     @Override
254     public void setReadOnly(boolean readOnly) {
255         boolean e = !readOnly;
256         newProperty.setEnabled(e);
257         removeProperty.setEnabled(e);
258     }
259
260     public static String validateMonitorExpression(final RequestProcessor processor, final Resource componentType, final Resource relation, final String expression) {
261
262         if (expression.trim().isEmpty()) {
263             return "Expression is empty.";
264         }
265
266         if (expression.trim().isEmpty()) {
267             return "Expression is empty.";
268         }
269         try {
270             return processor.sync(new UniqueRead<String>() {
271
272                 @Override
273                 public String perform(ReadGraph graph) throws DatabaseException {
274
275                     StructuralResource2 STR = StructuralResource2.getInstance(graph);
276
277                     // TODO: this is a bit hackish but should get the job done in most parts and
278                     // importantly indicates something for the user
279                     PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheAsyncListener.<PropertyInfo>instance());
280                     Variable parent = new StandardGraphChildVariable(null, null, componentType) {
281                         
282                         public Resource getType(ReadGraph graph) throws DatabaseException {
283                             return componentType;
284                         };
285                         
286                         public Variable getPossibleProperty(ReadGraph graph, String name) throws DatabaseException {
287                             Variable prop = super.getPossibleProperty(graph, name);
288                             if (prop != null) {
289                                 return prop;
290                             } else {
291                                 return getChild(graph, name);
292                             }
293                         };
294                     };
295                     
296                     for(Resource literal : graph.getAssertedObjects(componentType, relation)) {
297
298                         try {
299                             Variable context = new StandardGraphPropertyVariable(parent, null, null, info, literal);
300                             if(graph.isInstanceOf(componentType, STR.ProceduralComponentType)) {
301                                 CompileProceduralSCLMonitorRequest.compileAndEvaluate(graph, context);
302                             } else {
303                                 compileAndEvaluate(graph, context, expression);
304                             }
305                             
306                         } catch (Exception e) {
307                             String msg = e.getMessage();
308                             int index = msg.indexOf(":");
309                             if(index > 0) msg = msg.substring(index);
310                             return msg;
311                         }
312                     }
313                     return null;
314                 }
315                 
316             });
317         } catch (DatabaseException e) {
318             LOGGER.error("Could not validate ", e);
319         }
320         return null;
321     }
322
323     public static void compileAndEvaluate(ReadGraph graph, Variable context, String expression) throws DatabaseException {
324         SCLContext sclContext = SCLContext.getCurrent();
325         Object oldGraph = sclContext.get("graph");
326         try {
327             CompileSCLMonitorRequest compileSCLMonitorRequest = new CompileSCLMonitorRequest(graph, context) {
328                 @Override
329                 protected String getExpressionText(ReadGraph graph) throws DatabaseException {
330                     return expression;
331                 }
332             };
333             Function1<Variable,Object> exp = graph.syncRequest(compileSCLMonitorRequest);
334             sclContext.put("graph", graph);
335             //return exp.apply(context.getParent(graph));
336         } catch (DatabaseException e) {
337             throw (DatabaseException)e;
338         } catch (Throwable t) {
339             throw new DatabaseException(t);
340         } finally {
341             sclContext.put("graph", oldGraph);
342         }
343     }
344     
345     @Override
346     public void update(ComponentTypePropertiesResult result) {
347         if (table.isDisposed())
348             return;
349
350         Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();
351         List<TableItem> selectedItems = new ArrayList<TableItem>(selected.size());
352         for (int i : table.getSelectionIndices()) {
353             TableItem item = table.getItem(i);
354             selected.add((ComponentTypeViewerPropertyInfo) item.getData());
355         }
356         
357         int topIndex = table.getTopIndex();
358         table.removeAll();
359         if(editor.getEditor() != null)
360             editor.getEditor().dispose();
361         
362         for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {
363             boolean immutable = result.isImmutable() || info.immutable;
364             Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
365             if(info.sectionSpecificData != MONITOR)
366                 continue;
367
368             TableItem item = new TableItem(table, SWT.NONE);
369
370             item.setText(0, info.valid != null ? info.name + " (!)" : info.name);
371             item.setText(1, info.type);
372             item.setText(2, info.expression);
373             item.setText(3, info.unitString());
374             item.setText(4, info.label);
375             item.setText(5, info.description);
376
377             item.setForeground(fg);
378
379             item.setData(info);
380
381             if (selected.contains(info))
382                 selectedItems.add(item);
383         }
384         
385         table.setTopIndex(topIndex);
386         table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));
387         table.redraw();
388     }
389
390     @Override
391     public Section getSection() {
392         return section;
393     }
394
395     @Override
396     public double getPriority() {
397         return 100.0;
398     }
399
400     private static final Object MONITOR = new Object();
401     
402     @Override
403     public Object getSectionSpecificData(ReadGraph graph,
404             ComponentTypeViewerPropertyInfo info) throws DatabaseException {
405         Layer0 L0 = Layer0.getInstance(graph);
406         StructuralResource2 STR = StructuralResource2.getInstance(graph);
407         Resource range = graph.getPossibleObject(info.resource, L0.HasRange);
408         if(range != null && graph.isInstanceOf(range, STR.MonitorValueType))
409             return MONITOR;
410         else
411             return null;
412     }
413     
414     @Override
415     public double getDataPriority() {
416         return 100.0;
417     }
418
419 }