]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
66132ab91a2088480ca0eb9d5ef86519d5d6fc76
[simantics/sysdyn.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2010 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.sysdyn.ui.properties.widgets.expressions;\r
13 \r
14 import java.awt.BasicStroke;\r
15 import java.awt.Frame;\r
16 import java.awt.event.ActionEvent;\r
17 import java.awt.event.ActionListener;\r
18 import java.awt.geom.Point2D;\r
19 import java.io.StringReader;\r
20 import java.util.ArrayList;\r
21 import java.util.Arrays;\r
22 import java.util.List;\r
23 import java.util.Map;\r
24 \r
25 import javax.swing.Timer;\r
26 \r
27 import org.eclipse.jface.layout.GridDataFactory;\r
28 import org.eclipse.jface.layout.GridLayoutFactory;\r
29 import org.eclipse.jface.text.BadLocationException;\r
30 import org.eclipse.jface.text.IDocument;\r
31 import org.eclipse.swt.SWT;\r
32 import org.eclipse.swt.awt.SWT_AWT;\r
33 import org.eclipse.swt.custom.VerifyKeyListener;\r
34 import org.eclipse.swt.events.FocusAdapter;\r
35 import org.eclipse.swt.events.FocusEvent;\r
36 import org.eclipse.swt.events.FocusListener;\r
37 import org.eclipse.swt.events.KeyListener;\r
38 import org.eclipse.swt.events.ModifyEvent;\r
39 import org.eclipse.swt.events.ModifyListener;\r
40 import org.eclipse.swt.graphics.Point;\r
41 import org.eclipse.swt.widgets.Composite;\r
42 import org.eclipse.swt.widgets.Label;\r
43 import org.eclipse.swt.widgets.Table;\r
44 import org.jfree.chart.ChartFactory;\r
45 import org.jfree.chart.ChartPanel;\r
46 import org.jfree.chart.JFreeChart;\r
47 import org.jfree.chart.plot.PlotOrientation;\r
48 import org.jfree.data.xy.XYDataset;\r
49 import org.jfree.data.xy.XYSeries;\r
50 import org.jfree.data.xy.XYSeriesCollection;\r
51 import org.simantics.databoard.Bindings;\r
52 import org.simantics.db.ReadGraph;\r
53 import org.simantics.db.Resource;\r
54 import org.simantics.db.VirtualGraph;\r
55 import org.simantics.db.WriteGraph;\r
56 import org.simantics.db.common.CommentMetadata;\r
57 import org.simantics.db.common.request.WriteRequest;\r
58 import org.simantics.db.common.utils.ListUtils;\r
59 import org.simantics.db.exception.DatabaseException;\r
60 import org.simantics.db.layer0.variable.Variable;\r
61 import org.simantics.db.layer0.variable.Variables;\r
62 import org.simantics.db.procedure.Listener;\r
63 import org.simantics.db.request.Read;\r
64 import org.simantics.layer0.Layer0;\r
65 import org.simantics.layer0.utils.direct.GraphUtils;\r
66 import org.simantics.sysdyn.SysdynResource;\r
67 import org.simantics.sysdyn.tableParser.ParseException;\r
68 import org.simantics.sysdyn.tableParser.TableParser;\r
69 import org.simantics.sysdyn.tableParser.Token;\r
70 import org.simantics.sysdyn.ui.utils.SyntaxError;\r
71 import org.simantics.sysdyn.utils.SheetUtils;\r
72 import org.simantics.ui.SimanticsUI;\r
73 \r
74 public class WithLookupExpression implements IExpression {\r
75 \r
76     private ExpressionField expression;\r
77     private ExpressionField lookup;\r
78     private ExpressionField lastSelectedText = expression;\r
79     private Timer updateChartTimer;\r
80 \r
81     private ChartPanel smallPanel;\r
82     private Frame smallFrame;\r
83 \r
84     private final ExpressionWidgetInput input;\r
85     private Resource expr;\r
86 \r
87     public WithLookupExpression(ExpressionWidgetInput input) {\r
88         this.input = input;\r
89         this.expr = input.expression;\r
90     }\r
91 \r
92     @Override\r
93     public void createExpressionFields(Composite parent, final Map<String, Object> data, Table allowedVariables) {\r
94         GridLayoutFactory.fillDefaults().numColumns(3).applyTo(parent);\r
95 \r
96         updateChartTimer = new Timer(1000, new ActionListener() {\r
97 \r
98             @Override\r
99             public void actionPerformed(ActionEvent e) {\r
100                 updateChart();\r
101             }\r
102         });\r
103         updateChartTimer.setRepeats(false);\r
104 \r
105         String equation = data.get("equation") != null ? (String)data.get("equation") : "";\r
106         String lookupTable = data.get("lookup") != null ? (String)data.get("lookup") : "";\r
107 \r
108         Label l = new Label(parent, SWT.NONE);\r
109         l.setText("With\nLookup");\r
110 \r
111         expression = new ExpressionField(parent, SWT.BORDER, allowedVariables, true ,input);\r
112         expression.setExpression(equation);\r
113         GridDataFactory.fillDefaults().grab(true, true).applyTo(expression);\r
114 \r
115         expression.getSourceViewer().getTextWidget().addFocusListener(new FocusAdapter() {\r
116 \r
117             @Override\r
118             public void focusLost(FocusEvent e) {\r
119                 lastSelectedText = expression;\r
120             }\r
121         });\r
122 \r
123         Composite chartContainer = new Composite(parent, SWT.NONE);\r
124         createChart(chartContainer, data);\r
125 \r
126 \r
127         l = new Label(parent, SWT.NONE);\r
128         l.setText("Lookup\ntable");\r
129 \r
130         lookup = new ExpressionField(parent, SWT.BORDER, null, false, input);\r
131         lookup.setExpression(lookupTable);\r
132         GridDataFactory.fillDefaults().grab(true, true).applyTo(lookup);\r
133 \r
134         lookup.getSourceViewer().getTextWidget().addFocusListener(new FocusAdapter() {\r
135 \r
136             @Override\r
137             public void focusLost(FocusEvent e) {\r
138                 lastSelectedText = lookup;\r
139                 save(expr, data);\r
140             }\r
141         });\r
142 \r
143         lookup.getSourceViewer().getTextWidget().addModifyListener(new ModifyListener() {\r
144 \r
145             @Override\r
146             public void modifyText(ModifyEvent e) {\r
147                 if(!updateChartTimer.isRunning())\r
148                     updateChartTimer.start();\r
149                 else\r
150                     updateChartTimer.restart();\r
151             }\r
152         });\r
153 \r
154 \r
155         SimanticsUI.getSession().asyncRequest(new Read<String>() {\r
156 \r
157             @Override\r
158             public String perform(ReadGraph graph) throws DatabaseException {\r
159                 SysdynResource sr = SysdynResource.getInstance(graph);\r
160                 String result = "";\r
161                 if (expr != null && graph.isInstanceOf(expr, sr.WithLookupExpression)) {\r
162                     result = graph.getPossibleRelatedValue(expr, sr.WithLookupExpression_lookup);\r
163                 }\r
164                 return result;\r
165             }\r
166         }, new Listener<String>() {\r
167 \r
168             @Override\r
169             public void exception(Throwable t) {\r
170                 t.printStackTrace();\r
171             }\r
172 \r
173             @Override\r
174             public void execute(final String result) {\r
175                 if(lookup != null)\r
176                     lookup.getDisplay().asyncExec(new Runnable() {\r
177 \r
178                         @Override\r
179                         public void run() {\r
180                             lookup.setExpression(result);       \r
181                             updateChart();\r
182                         }\r
183                     });\r
184             }\r
185 \r
186             @Override\r
187             public boolean isDisposed() {\r
188                 if(lookup != null && !lookup.isDisposed()) {\r
189                     return false;\r
190                 }\r
191                 return true;\r
192             }\r
193         });\r
194 \r
195         updateChart();\r
196     }\r
197 \r
198     @Override\r
199     public void focus() {\r
200         if(this.lastSelectedText != null) this.lastSelectedText.focus();        \r
201     }\r
202 \r
203     @Override\r
204     public List<ExpressionField> getExpressionFields() {\r
205         return Arrays.asList(this.expression, this.lookup);\r
206     }\r
207 \r
208     @Override\r
209     public void readData(final Resource expression, Map<String, Object> data) {\r
210 \r
211         class Auxiliary {\r
212             String equation, lookup;\r
213         }\r
214 \r
215         Auxiliary results = null;\r
216 \r
217         if (data.get("equation") == null) {\r
218             try {\r
219                 results = SimanticsUI.getSession().syncRequest(new Read<Auxiliary>() {\r
220 \r
221                     @Override\r
222                     public Auxiliary perform(ReadGraph graph) throws DatabaseException {\r
223                         Auxiliary results = new Auxiliary();\r
224                         SysdynResource sr = SysdynResource.getInstance(graph);\r
225                         if (expression != null && graph.isInstanceOf(expression, sr.WithLookupExpression)) {\r
226                             results.equation = graph.getPossibleRelatedValue(expression, sr.WithLookupExpression_expression);\r
227                             results.lookup = graph.getPossibleRelatedValue(expression, sr.WithLookupExpression_lookup);\r
228                         } else {\r
229                             results.equation = "";\r
230                             results.lookup = "";\r
231                         }\r
232                         return results;\r
233                     }\r
234                 });\r
235             } catch (DatabaseException e1) {\r
236                 e1.printStackTrace();\r
237             }\r
238             data.put("equation", results.equation == null ? "" : results.equation);\r
239             data.put("lookup", results.lookup == null ? "" : results.lookup);\r
240         }\r
241 \r
242     }\r
243 \r
244     @Override\r
245     public void replaceSelection(String var) {\r
246         if(lastSelectedText != null) {\r
247             IDocument doc = lastSelectedText.getDocument();\r
248             try {\r
249                 Point selection = lastSelectedText.getSelection();\r
250                 doc.replace(selection.x, selection.y, var);\r
251                 lastSelectedText.setSelection(selection.x + var.length());\r
252             } catch (BadLocationException e) {\r
253                 e.printStackTrace();\r
254             }\r
255         }        \r
256     }\r
257 \r
258     @Override\r
259     public void save(final Resource expression, Map<String, Object> data) {\r
260         final String currentExpression = this.expression.getExpression();\r
261         final String currentLookupTable = lookup.getExpression();\r
262         String oldExpression = (String)data.get("equation");\r
263         String oldLookupTable = (String)data.get("lookup");\r
264 \r
265         if(oldExpression == null || oldLookupTable == null ||\r
266                 (currentExpression != null && currentLookupTable != null\r
267                 && (!currentExpression.equals(oldExpression) || \r
268                         !currentLookupTable.equals(oldLookupTable)))) {\r
269             data.putAll(data);\r
270             data.put("equation", currentExpression);\r
271             data.put("lookup", currentLookupTable);\r
272             SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
273                 @Override\r
274                 public void perform(WriteGraph g)\r
275                         throws DatabaseException {\r
276                     SysdynResource sr = SysdynResource.getInstance(g);\r
277                     Layer0 l0 = Layer0.getInstance(g);\r
278 \r
279                     if(!g.isInstanceOf(expr, sr.WithLookupExpression)) {\r
280 \r
281 \r
282                         final Resource newExpression = GraphUtils.create2(g, sr.WithLookupExpression,\r
283                                 sr.WithLookupExpression_minX, 0.0,\r
284                                 sr.WithLookupExpression_maxX, 10.0,\r
285                                 sr.WithLookupExpression_minY, 0.0,\r
286                                 sr.WithLookupExpression_maxY, 10.0);\r
287                         String arrayRange = g.getPossibleRelatedValue(expression, sr.Expression_arrayRange, Bindings.STRING);\r
288                         if(arrayRange != null)\r
289                             g.claimLiteral(newExpression, sr.Expression_arrayRange, arrayRange);\r
290 \r
291                         final Resource variable = g.getSingleObject(expression, l0.PartOf);\r
292                         Resource expressions = g.getPossibleObject(variable, sr.Variable_expressionList);\r
293                         Resource node = ListUtils.getNode(g, expressions, expression);\r
294                         g.deny(node, l0.List_Element);\r
295                         g.claim(node, l0.List_Element, newExpression);\r
296 \r
297                         g.deny(expression, l0.PartOf);\r
298                         g.claim(newExpression, l0.PartOf, variable);\r
299 \r
300                         VirtualGraph runtime = g.getService(VirtualGraph.class);\r
301                         g.syncRequest(new WriteRequest(runtime) {\r
302                             @Override\r
303                             public void perform(WriteGraph graph) throws DatabaseException {\r
304                                 SysdynResource sr = SysdynResource.getInstance(graph);\r
305                                 if(graph.hasStatement(variable, sr.IndependentVariable_activeExpression))\r
306                                     graph.deny(variable, sr.IndependentVariable_activeExpression);\r
307                                 graph.claim(variable, sr.IndependentVariable_activeExpression, newExpression);\r
308                             }\r
309                         }\r
310                                 );\r
311                         expr = newExpression;\r
312 \r
313                     }\r
314                     g.claimLiteral(expr, sr.WithLookupExpression_expression, currentExpression);\r
315                     g.claimLiteral(expr, sr.WithLookupExpression_lookup, currentLookupTable);\r
316                     \r
317                     g.markUndoPoint();\r
318                                 CommentMetadata cm = g.getMetadata(CommentMetadata.class);\r
319                                 g.addMetadata(cm.add("Set lookup expression"));\r
320                     \r
321                 }\r
322             });\r
323         }\r
324 \r
325     }\r
326 \r
327     @Override\r
328     public void updateData(Map<String, Object> data) {\r
329         if(this.expression != null && this.expression.getExpression() != null)\r
330             data.put("equation", this.expression.getExpression());\r
331         if(this.lookup != null && this.lookup.getExpression() != null)\r
332             data.put("lookup", this.lookup.getExpression());   \r
333     }\r
334 \r
335     @Override\r
336     public void addKeyListener(KeyListener listener) {\r
337         this.expression.getSourceViewer().getTextWidget().addKeyListener(listener);\r
338         this.lookup.getSourceViewer().getTextWidget().addKeyListener(listener);\r
339     }\r
340 \r
341     @Override\r
342     public void addVerifyKeyListener(VerifyKeyListener listener) {\r
343         this.expression.getSourceViewer().getTextWidget().addVerifyKeyListener(listener);\r
344         this.lookup.getSourceViewer().getTextWidget().addVerifyKeyListener(listener);\r
345     }\r
346 \r
347     @Override\r
348     public void addModifyListener(ModifyListener listener) {\r
349         this.expression.getSourceViewer().getTextWidget().addModifyListener(listener);\r
350         this.lookup.getSourceViewer().getTextWidget().addModifyListener(listener);\r
351     }\r
352 \r
353     @Override\r
354     public void addFocusListener(FocusListener listener) {\r
355         this.expression.getSourceViewer().getTextWidget().addFocusListener(listener);\r
356         this.lookup.getSourceViewer().getTextWidget().addFocusListener(listener);\r
357     }\r
358 \r
359     private void createChart(Composite composite, final Map<String, Object> data) {\r
360         GridLayoutFactory.fillDefaults().applyTo(composite);\r
361         GridDataFactory.fillDefaults().span(1, 2).hint(150, SWT.DEFAULT).applyTo(composite);\r
362         final Composite chartComposite = new Composite(composite, \r
363                 SWT.NO_BACKGROUND | SWT.EMBEDDED);\r
364         GridDataFactory.fillDefaults().grab(true, true).applyTo(chartComposite);\r
365         smallFrame = SWT_AWT.new_Frame(chartComposite);\r
366 \r
367         XYDataset dataset = new XYSeriesCollection(new XYSeries("Lookup Table"));\r
368         JFreeChart chart = createChart(dataset);\r
369         smallPanel = new ChartPanel(chart);\r
370         smallFrame.add(smallPanel);\r
371 \r
372     }\r
373 \r
374     private static JFreeChart createChart(XYDataset dataset) {\r
375         JFreeChart chart = ChartFactory.createXYLineChart(\r
376                 null,\r
377                 null,\r
378                 null,\r
379                 dataset,\r
380                 PlotOrientation.VERTICAL,\r
381                 true,\r
382                 true,\r
383                 false\r
384                 );\r
385         chart.removeLegend();\r
386         chart.getXYPlot().getDomainAxis().setTickLabelsVisible(true);\r
387         chart.getXYPlot().getDomainAxis().setAxisLineVisible(false);\r
388         chart.getXYPlot().getDomainAxis().setTickMarksVisible(true);\r
389         chart.getXYPlot().getRangeAxis().setTickLabelsVisible(true);\r
390         chart.getXYPlot().getRangeAxis().setAxisLineVisible(false);\r
391         chart.getXYPlot().getRangeAxis().setTickMarksVisible(true);\r
392         chart.getXYPlot().getRenderer().setSeriesStroke(0, new BasicStroke(3.0f));\r
393         return chart;\r
394     }\r
395 \r
396     private void updateChart() {\r
397         ArrayList<Point2D> dataPoints = new ArrayList<Point2D>();\r
398         TableParser parser = new TableParser(new StringReader(""));\r
399         parser.ReInit(new StringReader(lookup.getExpression()));\r
400         try {\r
401             parser.table();\r
402             ArrayList<Token> xTokens = parser.getXTokens();\r
403             ArrayList<Token> yTokens = parser.getYTokens();\r
404             for(int i = 0; i < xTokens.size(); i++) {\r
405                 dataPoints.add(new Point2D.Double(\r
406                         Double.parseDouble(xTokens.get(i).image), \r
407                         Double.parseDouble(yTokens.get(i).image)));\r
408             }\r
409         } catch (ParseException e1) {\r
410             if(lookup.getExpression().matches("[a-zA-Z0-9]*\\([a-zA-Z0-9:]*\\)")) {\r
411                 // Might be a sheet reference\r
412                 try {\r
413                     final String name = lookup.getExpression().substring(0, lookup.getExpression().indexOf("("));\r
414                     final String range = lookup.getExpression().substring(lookup.getExpression().indexOf("(") + 1, lookup.getExpression().indexOf(")"));\r
415                     String possibleTable = SimanticsUI.getSession().syncRequest(new Read<String>() {\r
416 \r
417                         @Override\r
418                         public String perform(ReadGraph graph) throws DatabaseException {\r
419                             Variable variable = input.variable;\r
420                             Resource model = Variables.getModel(graph, variable);\r
421                             return SheetUtils.getStringRepresentation(graph, model, name, range);\r
422                         }\r
423 \r
424                     });\r
425                     \r
426 \r
427                     try {\r
428                         if(possibleTable == null) {\r
429                             ParseException e = new ParseException(e1.getMessage());\r
430                             e.currentToken = e1.currentToken;\r
431                             throw e;\r
432                         }\r
433                             \r
434                         parser.ReInit(new StringReader(possibleTable));\r
435                         parser.table();\r
436                         ArrayList<Token> xTokens = parser.getXTokens();\r
437                         ArrayList<Token> yTokens = parser.getYTokens();\r
438                         for(int i = 0; i < xTokens.size(); i++) {\r
439                             dataPoints.add(new Point2D.Double(\r
440                                     Double.parseDouble(xTokens.get(i).image), \r
441                                     Double.parseDouble(yTokens.get(i).image)));\r
442                         }\r
443                     } catch (ParseException e2) {\r
444                         this.lookup.setSyntaxError(new SyntaxError(e2.currentToken, "Syntax Error"));\r
445                         System.out.println("MESSAGE: " + e2.getMessage());\r
446                         return;\r
447                     }\r
448                 } catch (DatabaseException e) {\r
449                     e.printStackTrace();\r
450                 }\r
451             }\r
452         }\r
453 \r
454         XYSeries series = new XYSeries("Lookup Table");\r
455         for(Point2D point : dataPoints) {\r
456             series.add(point.getX(), point.getY());\r
457         }\r
458         XYSeriesCollection dataset =  new XYSeriesCollection(series);\r
459         smallPanel.getChart().getXYPlot().setDataset(dataset);\r
460     }\r
461 \r
462 }\r