* Bug #12264 fixed - ENDBLK block threw nullpointer exception when double-clicking...
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / block / SuperBlock.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
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;
14
15 import java.io.IOException;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.logging.Logger;
19
20 import org.scilab.modules.graph.ScilabGraph;
21 import org.scilab.modules.gui.contextmenu.ContextMenu;
22 import org.scilab.modules.gui.menu.Menu;
23 import org.scilab.modules.gui.menu.ScilabMenu;
24 import org.scilab.modules.types.ScilabDouble;
25 import org.scilab.modules.types.ScilabList;
26 import org.scilab.modules.types.ScilabMList;
27 import org.scilab.modules.types.ScilabType;
28 import org.scilab.modules.xcos.Xcos;
29 import org.scilab.modules.xcos.XcosTab;
30 import org.scilab.modules.xcos.block.actions.CodeGenerationAction;
31 import org.scilab.modules.xcos.block.actions.SuperblockMaskCreateAction;
32 import org.scilab.modules.xcos.block.actions.SuperblockMaskCustomizeAction;
33 import org.scilab.modules.xcos.block.actions.SuperblockMaskRemoveAction;
34 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
35 import org.scilab.modules.xcos.graph.PaletteDiagram;
36 import org.scilab.modules.xcos.graph.SuperBlockDiagram;
37 import org.scilab.modules.xcos.graph.XcosDiagram;
38 import org.scilab.modules.xcos.graph.swing.GraphComponent;
39 import org.scilab.modules.xcos.io.codec.XcosCodec;
40 import org.scilab.modules.xcos.io.scicos.DiagramElement;
41 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
42 import org.scilab.modules.xcos.port.BasicPort;
43 import org.scilab.modules.xcos.utils.FileUtils;
44 import org.scilab.modules.xcos.utils.XcosConstants;
45 import org.scilab.modules.xcos.utils.XcosEvent;
46 import org.scilab.modules.xcos.utils.XcosMessages;
47 import org.w3c.dom.Node;
48
49 import com.mxgraph.model.mxICell;
50 import com.mxgraph.util.mxEvent;
51 import com.mxgraph.util.mxEventObject;
52
53 /**
54  * A SuperBlock contains an entire diagram on it. Thus it can be easily
55  * customized by the user.
56  *
57  * A SuperBlock can be created from any part of the diagram y selecting blocks
58  * and applying the
59  * {@link org.scilab.modules.xcos.block.actions.RegionToSuperblockAction}.
60  *
61  * It can also appear to users as a normal block by applying a mask on it. In
62  * this case the creator can use any SuperBlock context defined variable on a
63  * prompt to the user.
64  *
65  * @see SuperBlockDiagram
66  * @see SuperblockMaskCreateAction
67  * @see SuperblockMaskCustomizeAction
68  * @see SuperblockMaskRemoveAction
69  */
70 // CSOFF: ClassDataAbstractionCoupling
71 // CSOFF: ClassFanOutComplexity
72 @SuppressWarnings(value = { "serial" })
73 public final class SuperBlock extends BasicBlock {
74     /**
75      * The interfunction name (linked to Xcos-core)
76      */
77     public static final String INTERFUNCTION_NAME = "SUPER_f";
78
79     /**
80      * The simulation name (linked to Xcos-core)
81      */
82     private static final String SIMULATION_NAME = "super";
83     /**
84      * The simulation name on a masked status (linked to Xcos-core)
85      */
86     private static final String MASKED_SIMULATION_NAME = "csuper";
87     /**
88      * The interfunction name on a masked status (linked to Xcos-core)
89      */
90     private static final String MASKED_INTERFUNCTION_NAME = "DSUPER";
91
92     private SuperBlockDiagram child;
93     private boolean hasAValidRpar = false;
94
95     /**
96      * Constructor
97      */
98     public SuperBlock() {
99         super();
100     }
101
102     /**
103      * @param label
104      *            block label
105      */
106     protected SuperBlock(String label) {
107         this();
108         setValue(label);
109     }
110
111     /**
112      * @param masked
113      *            masked super block
114      */
115     protected SuperBlock(boolean masked) {
116         this();
117         if (masked) {
118             mask();
119         }
120     }
121
122     /**
123      * @param label
124      *            block label
125      * @param masked
126      *            masked super block
127      */
128     protected SuperBlock(String label, boolean masked) {
129         this(label);
130         if (masked) {
131             mask();
132         }
133     }
134
135     /**
136      * Initialize the block with the default values
137      */
138     @Override
139     protected void setDefaultValues() {
140         super.setDefaultValues();
141         setInterfaceFunctionName(INTERFUNCTION_NAME);
142         setSimulationFunctionName(SIMULATION_NAME);
143         setRealParameters(new ScilabMList());
144         setIntegerParameters(new ScilabDouble());
145         setObjectsParameters(new ScilabList());
146         setExprs(new ScilabDouble());
147         setBlockType("h");
148         setNbZerosCrossing(new ScilabDouble(0));
149         setNmode(new ScilabDouble(0));
150     }
151
152     @Override
153     public ScilabType getRealParameters() {
154         if (hasAValidRpar || child == null) {
155             return super.getRealParameters();
156         }
157
158         /*
159          * Encode the children as a new rpar.
160          */
161         final ScilabType rpar = new DiagramElement().encode(child);
162         super.setRealParameters(rpar);
163         hasAValidRpar = true;
164
165         return super.getRealParameters();
166     }
167
168     /**
169      * Invalide the rpar, a new child diagram encoding will be performed on
170      * demand.
171      */
172     public void invalidateRpar() {
173         hasAValidRpar = false;
174     }
175
176     /**
177      * openBlockSettings this method is called when a double click occurred on a
178      * super block
179      *
180      * @param context
181      *            parent diagram context
182      * @see BasicBlock#openBlockSettings(String[])
183      */
184     @Override
185     public void openBlockSettings(String[] context) {
186
187         // prevent to open twice
188         if (isLocked()) {
189             return;
190         }
191
192         /*
193          * Do nothing when something happen on the Palette
194          */
195         if (getParentDiagram() instanceof PaletteDiagram) {
196             return;
197         }
198
199         /*
200          * Specific case when we want to generate code.
201          */
202         if (getChild() == null && getSimulationFunctionType().compareTo(SimulationFunctionType.DEFAULT) != 0) {
203             return;
204         }
205
206         /*
207          * When the block is masked it perform actions like any other blocks.
208          */
209         if (isMasked()) {
210             super.openBlockSettings(context);
211             return;
212         }
213
214         try {
215             // Lock the block because we are really performing actions
216             setLocked(true);
217
218             /*
219              * Compatibility with older diagrams.
220              *
221              * Before Scilab 5.2.2, saved diagrams don't contains XML children
222              * but use a pseudo scs_m structure instead.
223              *
224              * In this case child was null and we need to reconstruct child
225              * diagram from scs_m.
226              */
227             if (getChild() == null || getChild().getChildVertices(getChild().getDefaultParent()).length == 0) {
228                 child = null;
229                 createChildDiagram();
230             } else {
231                 // reassociate (useful on clone and load operation)
232                 getChild().setContainer(this);
233                 getChild().setComponent(new GraphComponent(getChild()));
234
235                 getChild().initComponent();
236                 getChild().installStylesheet();
237
238                 getChild().installListeners();
239                 getChild().installSuperBlockListeners();
240             }
241
242             /*
243              * Construct the view or set it visible.
244              */
245             if (XcosTab.get(getChild()) == null) {
246                 XcosTab.restore(getChild());
247             } else {
248                 XcosTab.get(getChild()).setCurrent();
249             }
250
251             getChild().setModifiedNonRecursively(false);
252
253             getChild().getAsComponent().validateGraph();
254             getChild().fireEvent(new mxEventObject(mxEvent.ROOT));
255             getChild().getView().invalidate();
256
257             /*
258              * Update the cells from the context values.
259              */
260             getChild().updateCellsContext();
261         } finally {
262             setLocked(false);
263         }
264     }
265
266     /**
267      * @param graph
268      *            parent diagram
269      */
270     @Override
271     public void openContextMenu(ScilabGraph graph) {
272         ContextMenu menu = null;
273
274         if (getParentDiagram() instanceof PaletteDiagram) {
275             menu = createPaletteContextMenu(graph);
276         } else {
277             menu = createContextMenu(graph);
278             menu.getAsSimpleContextMenu().addSeparator();
279             menu.add(CodeGenerationAction.createMenu(graph));
280
281             Menu maskMenu = ScilabMenu.createMenu();
282             maskMenu.setText(XcosMessages.SUPERBLOCK_MASK);
283
284             if (isMasked()) {
285                 maskMenu.add(SuperblockMaskRemoveAction.createMenu(graph));
286                 menu.add(maskMenu);
287             } else {
288                 maskMenu.add(SuperblockMaskCreateAction.createMenu(graph));
289             }
290             maskMenu.add(SuperblockMaskCustomizeAction.createMenu(graph));
291             menu.add(maskMenu);
292
293         }
294         menu.setVisible(true);
295     }
296
297     /**
298      * @return status
299      */
300     public boolean createChildDiagram() {
301         return createChildDiagram(false);
302     }
303
304     /**
305      * @param generatedUID
306      *            does we need to generated a new unique ID
307      * @return status
308      */
309     public boolean createChildDiagram(boolean generatedUID) {
310         if (child == null) {
311             final ScilabType rpar = getRealParameters();
312
313             final DiagramElement element = new DiagramElement();
314             if (!element.canDecode(rpar)) {
315                 return false;
316             }
317
318             child = new SuperBlockDiagram(this);
319             child.installListeners();
320
321             try {
322                 element.decode(rpar, child, false);
323             } catch (ScicosFormatException e) {
324                 Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
325                 return false;
326             }
327
328             child.installSuperBlockListeners();
329
330             final XcosDiagram parent = Xcos.findParent(this);
331             if (parent != null) {
332                 Xcos.getInstance().addDiagram(parent.getSavedFile(), child);
333             }
334         } else {
335             return false;
336         }
337
338         return true;
339     }
340
341     /**
342      * @return diagram
343      */
344     public SuperBlockDiagram getChild() {
345         return child;
346     }
347
348     /**
349      * @param child
350      *            update diagram
351      */
352     public void setChild(SuperBlockDiagram child) {
353         this.child = child;
354     }
355
356     /**
357      * update super block ports in parent diagram
358      */
359     public void updateExportedPort() {
360         if (child == null) {
361             return;
362         }
363         if (getParentDiagram() == null) {
364             setParentDiagram(Xcos.findParent(this));
365         }
366
367         final Map<IOBlocks, List<mxICell>> blocksMap = IOBlocks.getAllBlocks(this);
368         final Map<IOBlocks, List<mxICell>> portsMap = IOBlocks.getAllPorts(this);
369         for (IOBlocks block : IOBlocks.values()) {
370             final int blockCount = blocksMap.get(block).size();
371             int portCount = portsMap.get(block).size();
372
373             // add ports if required
374             while (blockCount > portCount) {
375                 try {
376                     BasicPort port;
377                     port = block.getReferencedPortClass().newInstance();
378                     addPort(port);
379                 } catch (InstantiationException e) {
380                     Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
381                 } catch (IllegalAccessException e) {
382                     Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
383                 }
384                 portCount++;
385             }
386
387             // remove ports if required
388             while (portCount > blockCount) {
389                 removePort((BasicPort) portsMap.get(block).get(portCount - 1));
390                 portCount--;
391             }
392         }
393         getParentDiagram().fireEvent(new mxEventObject(XcosEvent.SUPER_BLOCK_UPDATED, XcosConstants.EVENT_BLOCK_UPDATED, this));
394     }
395
396     /**
397      * Mask the SuperBlock
398      */
399     public void mask() {
400         setInterfaceFunctionName(MASKED_INTERFUNCTION_NAME);
401         setSimulationFunctionName(MASKED_SIMULATION_NAME);
402         setIntegerParameters(new ScilabDouble(1));
403     }
404
405     /**
406      * Unmask the SuperBlock
407      */
408     public void unmask() {
409         setInterfaceFunctionName(INTERFUNCTION_NAME);
410         setSimulationFunctionName(SIMULATION_NAME);
411         setIntegerParameters(new ScilabDouble());
412     }
413
414     /**
415      * @return True is the SuperBlock is masked, false otherwise
416      */
417     public boolean isMasked() {
418         return getInterfaceFunctionName().compareTo(INTERFUNCTION_NAME) != 0;
419     }
420
421     /**
422      * Customize the parent diagram on name change
423      *
424      * @param value
425      *            the new name
426      * @see com.mxgraph.model.mxCell#setValue(java.lang.Object)
427      */
428     @Override
429     public void setValue(Object value) {
430         super.setValue(value);
431
432         if (value == null) {
433             return;
434         }
435
436         if (getChild() != null) {
437             getChild().setTitle(FileUtils.toValidCIdentifier(value.toString()));
438             setRealParameters(new DiagramElement().encode(getChild()));
439         }
440     }
441
442     /**
443      * Clone the child safely.
444      *
445      * @return a new clone instance
446      * @throws CloneNotSupportedException
447      *             never
448      * @see org.scilab.modules.xcos.block.BasicBlock#clone()
449      */
450     @Override
451     public Object clone() throws CloneNotSupportedException {
452         SuperBlock clone = (SuperBlock) super.clone();
453
454         // clone the diagram
455         if (child != null) {
456             clone.child = (SuperBlockDiagram) child.clone();
457             clone.child.setContainer(clone);
458         }
459
460         return clone;
461
462     }
463
464     /*
465      * Serializable custom implementation need to handle any copy / DnD case.
466      */
467
468     /**
469      * Encode the block as xml
470      *
471      * @param out
472      *            the output stream
473      * @throws IOException
474      *             on error
475      */
476     private void writeObject(java.io.ObjectOutputStream out) throws IOException {
477         out.writeObject(new XcosCodec().encode(this));
478     }
479
480     /**
481      * Decode the block as xml
482      *
483      * @param in
484      *            the input stream
485      * @throws IOException
486      *             on error
487      * @throws ClassNotFoundException
488      *             on error
489      */
490     private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
491         new XcosCodec().decode((Node) in.readObject(), this);
492
493         /*
494          * Specific post serialization things
495          */
496         if (this.child == null) {
497             this.child = new SuperBlockDiagram(this);
498             this.child.installListeners();
499         } else {
500             this.child.setContainer(this);
501         }
502         this.child.installSuperBlockListeners();
503     }
504 }
505 // CSON: ClassDataAbstractionCoupling
506 // CSON: ClassFanOutComplexity