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