Xcos: format source code
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / block / io / ContextUpdate.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Clement DAVID
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 package org.scilab.modules.xcos.block.io;
14
15 import java.beans.PropertyChangeEvent;
16 import java.beans.PropertyChangeListener;
17 import java.io.File;
18 import java.io.IOException;
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.EnumMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.regex.Pattern;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
29 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
30 import org.scilab.modules.types.ScilabDouble;
31 import org.scilab.modules.types.ScilabList;
32 import org.scilab.modules.types.ScilabString;
33 import org.scilab.modules.types.ScilabType;
34 import org.scilab.modules.xcos.block.BasicBlock;
35 import org.scilab.modules.xcos.block.SuperBlock;
36 import org.scilab.modules.xcos.graph.SuperBlockDiagram;
37 import org.scilab.modules.xcos.io.scicos.H5RWHandler;
38 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
39 import org.scilab.modules.xcos.port.BasicPort;
40 import org.scilab.modules.xcos.port.command.CommandPort;
41 import org.scilab.modules.xcos.port.control.ControlPort;
42 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
43 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
44 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
45 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
46 import org.scilab.modules.xcos.utils.FileUtils;
47 import org.scilab.modules.xcos.utils.XcosEvent;
48
49 import com.mxgraph.model.mxGraphModel;
50 import com.mxgraph.model.mxICell;
51 import com.mxgraph.model.mxIGraphModel;
52 import com.mxgraph.util.mxEventObject;
53
54 /**
55  * Common class for the SuperBlock I/O blocks (represent ports)
56  */
57 public abstract class ContextUpdate extends BasicBlock {
58
59     private static final Log LOG_LOCAL = LogFactory.getLog(ContextUpdate.class);
60     private static final long serialVersionUID = 6076826729067963560L;
61
62     /**
63      * Implement a listener which update the value and refresh the view when the
64      * index of the port change.
65      */
66     private static final class IndexChangeAdapter implements
67             PropertyChangeListener, Serializable {
68         private static IndexChangeAdapter instance;
69
70         /**
71          * Default constructor.
72          */
73         private IndexChangeAdapter() {
74         }
75
76         /**
77          * @return the instance
78          */
79         public static synchronized IndexChangeAdapter getInstance() {
80             if (instance == null) {
81                 instance = new IndexChangeAdapter();
82             }
83             return instance;
84         }
85
86         /**
87          * Update the value and refresh the graph view.
88          * 
89          * @param evt
90          *            the event
91          * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
92          */
93         @Override
94         public void propertyChange(PropertyChangeEvent evt) {
95             ScilabType data = (ScilabType) evt.getNewValue();
96             ContextUpdate ioBlock = (ContextUpdate) evt.getSource();
97
98             if (!data.isEmpty()) {
99                 int newIndex = (int) ((ScilabDouble) data).getRealPart()[0][0];
100
101                 int oldIndex;
102                 if (evt.getOldValue() instanceof ScilabDouble
103                         && !((ScilabDouble) evt.getOldValue()).isEmpty()) {
104                     oldIndex = (int) ((ScilabDouble) evt.getOldValue())
105                             .getRealPart()[0][0];
106                 } else {
107                     oldIndex = -1;
108                 }
109
110                 ioBlock.setValue(newIndex);
111
112                 if (ioBlock.getParentDiagram() != null) {
113                     ioBlock.getParentDiagram().fireEvent(
114                             new mxEventObject(XcosEvent.IO_PORT_VALUE_UPDATED,
115                                     "block", ioBlock, "oldIndex", oldIndex,
116                                     "newIndex", newIndex));
117                 }
118             }
119         }
120
121     }
122
123     /**
124      * Implement a listener to update the
125      * {@link ContextUpdate#isContextDependent} flag.
126      */
127     private static final class ExprsChangeAdapter implements
128             PropertyChangeListener, Serializable {
129         private static final Pattern INTEGER_PATTERN = Pattern.compile("\\d+");
130
131         private static ExprsChangeAdapter instance;
132
133         /**
134          * Default constructor
135          */
136         public ExprsChangeAdapter() {
137         }
138
139         /**
140          * @return the shared instance
141          */
142         public static ExprsChangeAdapter getInstance() {
143             if (instance == null) {
144                 instance = new ExprsChangeAdapter();
145             }
146             return instance;
147         }
148
149         /**
150          * isContextDependant field
151          * 
152          * @param evt
153          *            the event
154          * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
155          */
156         @Override
157         public void propertyChange(final PropertyChangeEvent evt) {
158             final ScilabType data = (ScilabType) evt.getNewValue();
159             final ContextUpdate ioBlock = (ContextUpdate) evt.getSource();
160
161             if (!data.isEmpty()) {
162                 final String newIndex = ((ScilabString) data).getData()[0][0];
163
164                 if (!INTEGER_PATTERN.matcher(newIndex).matches()) {
165                     ioBlock.isContextDependent = true;
166                 } else {
167                     ioBlock.isContextDependent = false;
168                 }
169             }
170         }
171     }
172
173     /**
174      * This enum represent all the subclasses of ContextUpdate .
175      * 
176      * It is used to easily loop over a BasicBlock I/O blocks
177      */
178     public static enum IOBlocks {
179         /** Map a control port to an event input block */
180         EventInBlock(EventInBlock.class, ControlPort.class, CommandPort.class),
181         /** Map a command port to an event output block */
182         EventOutBlock(EventOutBlock.class, CommandPort.class, ControlPort.class),
183         /** Map an explicit input port to an explicit input block */
184         ExplicitInBlock(ExplicitInBlock.class, ExplicitInputPort.class,
185                 ExplicitOutputPort.class),
186         /** Map an explicit output port to an explicit output block */
187         ExplicitOutBlock(ExplicitOutBlock.class, ExplicitOutputPort.class,
188                 ExplicitInputPort.class),
189         /** Map an implicit input port to an implicit input block */
190         ImplicitInBlock(ImplicitInBlock.class, ImplicitInputPort.class,
191                 ImplicitOutputPort.class),
192         /** Map an implicit output port to an implicit output block */
193         ImplicitOutBlock(ImplicitOutBlock.class, ImplicitOutputPort.class,
194                 ImplicitInputPort.class);
195
196         private final Class<? extends ContextUpdate> ioBlock;
197         private final Class<? extends BasicPort> port;
198         private final Class<? extends BasicPort> opposite;
199
200         /**
201          * @param ioBlock
202          *            input/output block
203          * @param port
204          *            the associated port class
205          * @param opposite
206          *            the opposite port class
207          */
208         private IOBlocks(Class<? extends ContextUpdate> ioBlock,
209                 Class<? extends BasicPort> port,
210                 Class<? extends BasicPort> opposite) {
211             this.ioBlock = ioBlock;
212             this.port = port;
213             this.opposite = opposite;
214         }
215
216         /**
217          * Get all the port of the SuperBlock parent.
218          * 
219          * @param parent
220          *            the parent
221          * @return the port list mapped by port type
222          */
223         public static Map<IOBlocks, List<mxICell>> getAllPorts(SuperBlock parent) {
224             final EnumMap<IOBlocks, List<mxICell>> ret = new EnumMap<IOBlocks, List<mxICell>>(
225                     IOBlocks.class);
226
227             /* Allocation */
228             for (IOBlocks b : IOBlocks.values()) {
229                 ret.put(b, new ArrayList<mxICell>());
230             }
231
232             /* Loop all over the children */
233             final int childCount = parent.getChildCount();
234
235             for (int i = 0; i < childCount; i++) {
236                 final mxICell child = parent.getChildAt(i);
237
238                 /* if compatible add it to the list */
239                 for (IOBlocks b : IOBlocks.values()) {
240                     if (child.getClass().equals(b.getReferencedPortClass())) {
241                         ret.get(b).add(child);
242                     }
243                 }
244             }
245
246             return ret;
247         }
248
249         /**
250          * Get the ports of the super blocks with kind klass
251          * 
252          * @param parent
253          *            the parent {@link SuperBlock}
254          * @param klass
255          *            the filter klass
256          * @return the list of ports
257          */
258         public static List<mxICell> getPorts(SuperBlock parent,
259                 Class<? extends ContextUpdate> klass) {
260             List<mxICell> ret = new ArrayList<mxICell>();
261
262             /* Get the corresponding klass */
263             Class<? extends BasicPort> portKlass = null;
264             for (IOBlocks b : IOBlocks.values()) {
265                 if (b.getReferencedClass().equals(klass)) {
266                     portKlass = b.getReferencedPortClass();
267                     break;
268                 }
269             }
270
271             /* Loop all over the children */
272             final int childCount = parent.getChildCount();
273
274             for (int i = 0; i < childCount; i++) {
275                 final mxICell child = parent.getChildAt(i);
276                 if (portKlass.isInstance(child)) {
277                     ret.add(child);
278                 }
279             }
280
281             return ret;
282         }
283
284         /**
285          * Return the opposite of the port
286          * 
287          * @param klass
288          *            the klass
289          * @return the opposite of klass
290          */
291         public static Class<? extends BasicPort> getOpposite(
292                 Class<? extends BasicPort> klass) {
293             for (IOBlocks b : IOBlocks.values()) {
294                 if (b.getReferencedPortClass() == klass) {
295                     return b.getOppositeClass();
296                 }
297             }
298             return null;
299         }
300
301         /**
302          * Get all the I/O blocks of the SuperBlock parent.
303          * 
304          * @param parent
305          *            the parent
306          * @return the port list mapped by port type
307          */
308         public static Map<IOBlocks, List<mxICell>> getAllBlocks(
309                 SuperBlock parent) {
310             final EnumMap<IOBlocks, List<mxICell>> ret = new EnumMap<IOBlocks, List<mxICell>>(
311                     IOBlocks.class);
312
313             final SuperBlockDiagram graph = parent.getChild();
314             if (graph == null) {
315                 parent.createChildDiagram();
316             }
317
318             /* Allocation */
319             for (IOBlocks b : IOBlocks.values()) {
320                 ret.put(b, new ArrayList<mxICell>());
321             }
322
323             /* Loop all over the children */
324             final mxIGraphModel defaultModel = graph.getModel();
325             mxGraphModel.filterDescendants(defaultModel,
326                     new mxGraphModel.Filter() {
327                         @Override
328                         public boolean filter(Object cell) {
329                             if (cell instanceof BasicBlock) {
330                                 final BasicBlock block = (BasicBlock) cell;
331                                 /* if compatible add it to the list */
332                                 for (IOBlocks b : IOBlocks.values()) {
333                                     if (block.getClass().equals(
334                                             b.getReferencedClass())) {
335                                         ret.get(b).add(block);
336                                     }
337                                 }
338                             }
339                             return false;
340                         }
341                     });
342
343             return ret;
344         }
345
346         /**
347          * Create a corresponding I/O block
348          * 
349          * @param port
350          *            the port used as an output
351          * @return the corresponding block
352          */
353         public static ContextUpdate createBlock(BasicPort port) {
354             for (IOBlocks io : IOBlocks.values()) {
355                 if (io.getReferencedPortClass().isInstance(port)) {
356                     try {
357                         ContextUpdate block = io.getReferencedClass()
358                                 .newInstance();
359                         block.addPort(io.getOppositeClass().newInstance());
360
361                         return block;
362                     } catch (InstantiationException e) {
363                         LogFactory.getLog(IOBlocks.class).error(e);
364                     } catch (IllegalAccessException e) {
365                         LogFactory.getLog(IOBlocks.class).error(e);
366                     }
367                 }
368             }
369
370             return null;
371         }
372
373         /**
374          * @return referenced class
375          */
376         public Class<? extends ContextUpdate> getReferencedClass() {
377             return ioBlock;
378         }
379
380         /**
381          * @return the port referenced class
382          */
383         public Class<? extends BasicPort> getReferencedPortClass() {
384             return port;
385         }
386
387         /**
388          * @return the port opposite class
389          */
390         public Class<? extends BasicPort> getOppositeClass() {
391             return opposite;
392         }
393     }
394
395     private transient boolean isContextDependent;
396
397     /**
398      * Constructor.
399      */
400     public ContextUpdate() {
401         super();
402
403         getParametersPCS().addPropertyChangeListener(INTEGER_PARAMETERS,
404                 IndexChangeAdapter.getInstance());
405         getParametersPCS().addPropertyChangeListener(EXPRS,
406                 ExprsChangeAdapter.getInstance());
407     }
408
409     /**
410      * Initialize the block with the default values
411      */
412     @Override
413     protected void setDefaultValues() {
414         super.setDefaultValues();
415
416         /*
417          * Fill parameters with non empty values.
418          */
419         setNbZerosCrossing(new ScilabDouble(0));
420         setNmode(new ScilabDouble(0));
421         setODState(new ScilabList());
422         setRealParameters(new ScilabDouble());
423         setObjectsParameters(new ScilabDouble());
424
425         setValue(1);
426     }
427
428     /**
429      * @param context
430      *            new context
431      */
432     public void onContextChange(String[] context) {
433         // prevent to open twice
434         if (isLocked()) {
435             return;
436         }
437
438         // do not evaluate context is the block is not context dependent.
439         if (!isContextDependent) {
440             return;
441         }
442
443         LOG_LOCAL.trace("Update the I/O value from the context");
444
445         String tempOutput;
446         String tempInput;
447         String tempContext;
448         try {
449             tempInput = FileUtils.createTempFile();
450             new File(tempInput).deleteOnExit();
451
452             // Write scs_m
453             tempOutput = exportBlockStruct();
454             // Write context
455             tempContext = exportContext(context);
456
457             String cmd = ScilabInterpreterManagement.buildCall("xcosBlockEval",
458                     tempOutput, tempInput, getInterfaceFunctionName()
459                             .toCharArray(), tempContext);
460
461             try {
462                 ScilabInterpreterManagement.synchronousScilabExec(cmd);
463             } catch (InterpreterException e) {
464                 e.printStackTrace();
465             }
466             BasicBlock modifiedBlock = new H5RWHandler(tempInput).readBlock();
467             updateBlockSettings(modifiedBlock);
468
469         } catch (IOException e) {
470             LOG_LOCAL.error(e);
471         } catch (ScicosFormatException e) {
472             LOG_LOCAL.error(e);
473         }
474     }
475 }