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