2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
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
13 package org.scilab.modules.xcos.block;
15 import java.io.IOException;
16 import java.util.Hashtable;
17 import java.util.List;
19 import java.util.logging.Logger;
21 import org.scilab.modules.graph.ScilabGraph;
22 import org.scilab.modules.gui.contextmenu.ContextMenu;
23 import org.scilab.modules.gui.menu.Menu;
24 import org.scilab.modules.gui.menu.ScilabMenu;
25 import org.scilab.modules.types.ScilabDouble;
26 import org.scilab.modules.types.ScilabList;
27 import org.scilab.modules.types.ScilabMList;
28 import org.scilab.modules.types.ScilabType;
29 import org.scilab.modules.xcos.Xcos;
30 import org.scilab.modules.xcos.XcosTab;
31 import org.scilab.modules.xcos.block.actions.CodeGenerationAction;
32 import org.scilab.modules.xcos.block.actions.SuperblockMaskCreateAction;
33 import org.scilab.modules.xcos.block.actions.SuperblockMaskCustomizeAction;
34 import org.scilab.modules.xcos.block.actions.SuperblockMaskRemoveAction;
35 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
36 import org.scilab.modules.xcos.graph.PaletteDiagram;
37 import org.scilab.modules.xcos.graph.SuperBlockDiagram;
38 import org.scilab.modules.xcos.graph.XcosDiagram;
39 import org.scilab.modules.xcos.graph.swing.GraphComponent;
40 import org.scilab.modules.xcos.io.codec.XcosCodec;
41 import org.scilab.modules.xcos.io.scicos.DiagramElement;
42 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
43 import org.scilab.modules.xcos.port.BasicPort;
44 import org.scilab.modules.xcos.utils.FileUtils;
45 import org.scilab.modules.xcos.utils.XcosConstants;
46 import org.scilab.modules.xcos.utils.XcosEvent;
47 import org.scilab.modules.xcos.utils.XcosMessages;
48 import org.w3c.dom.Node;
50 import com.mxgraph.model.mxICell;
51 import com.mxgraph.util.mxEvent;
52 import com.mxgraph.util.mxEventObject;
55 * A SuperBlock contains an entire diagram on it. Thus it can be easily
56 * customized by the user.
58 * A SuperBlock can be created from any part of the diagram y selecting blocks
60 * {@link org.scilab.modules.xcos.block.actions.RegionToSuperblockAction}.
62 * It can also appear to users as a normal block by applying a mask on it. In
63 * this case the creator can use any SuperBlock context defined variable on a
66 * @see SuperBlockDiagram
67 * @see SuperblockMaskCreateAction
68 * @see SuperblockMaskCustomizeAction
69 * @see SuperblockMaskRemoveAction
71 // CSOFF: ClassDataAbstractionCoupling
72 // CSOFF: ClassFanOutComplexity
73 @SuppressWarnings(value = { "serial" })
74 public final class SuperBlock extends BasicBlock {
76 * The interfunction name (linked to Xcos-core)
78 public static final String INTERFUNCTION_NAME = "SUPER_f";
81 * The simulation name (linked to Xcos-core)
83 private static final String SIMULATION_NAME = "super";
85 * The simulation name on a masked status (linked to Xcos-core)
87 private static final String MASKED_SIMULATION_NAME = "csuper";
89 * The interfunction name on a masked status (linked to Xcos-core)
91 private static final String MASKED_INTERFUNCTION_NAME = "DSUPER";
93 private SuperBlockDiagram child;
106 protected SuperBlock(String label) {
115 protected SuperBlock(boolean masked) {
128 protected SuperBlock(String label, boolean masked) {
136 * Initialize the block with the default values
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());
148 setNbZerosCrossing(new ScilabDouble(0));
149 setNmode(new ScilabDouble(0));
153 public ScilabType getRealParameters() {
155 return super.getRealParameters();
159 * Encode the children as a new rpar.
161 final ScilabType rpar = new DiagramElement().encode(child);
162 super.setRealParameters(rpar);
163 hasAValidRpar = true;
165 return super.getRealParameters();
169 * openBlockSettings this method is called when a double click occurred on a
173 * parent diagram context
174 * @see BasicBlock#openBlockSettings(String[])
177 public void openBlockSettings(String[] context) {
179 // prevent to open twice
185 * Do nothing when something happen on the Palette
187 if (getParentDiagram() instanceof PaletteDiagram) {
192 * Specific case when we want to generate code.
194 if (getChild() == null && getSimulationFunctionType().compareTo(SimulationFunctionType.DEFAULT) != 0) {
199 * When the block is masked it perform actions like any other blocks.
202 super.openBlockSettings(context);
207 // Lock the block because we are really performing actions
211 * Compatibility with older diagrams.
213 * Before Scilab 5.2.2, saved diagrams don't contains XML children
214 * but use a pseudo scs_m structure instead.
216 * In this case child was null and we need to reconstruct child
217 * diagram from scs_m.
219 if (getChild() == null || getChild().getChildVertices(getChild().getDefaultParent()).length == 0) {
221 createChildDiagram();
223 // reassociate (useful on clone and load operation)
224 getChild().setContainer(this);
225 getChild().setComponent(new GraphComponent(getChild()));
227 getChild().initComponent();
228 getChild().installStylesheet();
230 getChild().installListeners();
231 getChild().installSuperBlockListeners();
235 * Construct the view or set it visible.
237 if (XcosTab.get(getChild()) == null) {
238 XcosTab.restore(getChild());
240 XcosTab.get(getChild()).setCurrent();
243 getChild().setModifiedNonRecursively(false);
245 getChild().getAsComponent().validateGraph();
246 getChild().fireEvent(new mxEventObject(mxEvent.ROOT));
247 getChild().getView().invalidate();
250 * Update the cells from the context values.
252 getChild().updateCellsContext();
263 public void openContextMenu(ScilabGraph graph) {
264 ContextMenu menu = null;
266 if (getParentDiagram() instanceof PaletteDiagram) {
267 menu = createPaletteContextMenu(graph);
269 menu = createContextMenu(graph);
270 menu.getAsSimpleContextMenu().addSeparator();
271 menu.add(CodeGenerationAction.createMenu(graph));
273 Menu maskMenu = ScilabMenu.createMenu();
274 maskMenu.setText(XcosMessages.SUPERBLOCK_MASK);
277 maskMenu.add(SuperblockMaskRemoveAction.createMenu(graph));
280 maskMenu.add(SuperblockMaskCreateAction.createMenu(graph));
282 maskMenu.add(SuperblockMaskCustomizeAction.createMenu(graph));
286 menu.setVisible(true);
292 public boolean createChildDiagram() {
293 return createChildDiagram(false);
297 * @param generatedUID
298 * does we need to generated a new unique ID
301 public boolean createChildDiagram(boolean generatedUID) {
303 final ScilabType rpar = getRealParameters();
305 final DiagramElement element = new DiagramElement();
306 if (!element.canDecode(rpar)) {
310 child = new SuperBlockDiagram(this);
311 child.installListeners();
314 element.decode(rpar, child, false);
315 } catch (ScicosFormatException e) {
316 Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
320 child.installSuperBlockListeners();
322 final XcosDiagram parent = Xcos.findParent(this);
323 if (parent != null) {
324 Xcos.getInstance().addDiagram(parent.getSavedFile(), child);
336 public SuperBlockDiagram getChild() {
344 public void setChild(SuperBlockDiagram child) {
349 * Function that insert one port on the concerned superblock
350 * and gives it the right order.
355 private void insertOnePort(int order, BasicBlock basicblock) {
357 for (IOBlocks b : IOBlocks.values()) {
358 if (basicblock.getClass().equals(b.getReferencedClass())) {
360 port = b.getReferencedPortClass().newInstance();
361 port.setOrdering(order);
365 } catch (InstantiationException e) {
366 Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
367 } catch (IllegalAccessException e) {
368 Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
369 } catch (Exception e)
371 Logger.getLogger(SuperBlock.class.getName()).severe(e.toString());
376 * Function that remove one port on the concerned superblock
383 private boolean removeOnePort(int order, BasicPort basicport, BasicBlock basicblock) {
384 boolean port_deleted = false;
386 for (IOBlocks b : IOBlocks.values()) {
387 if (basicport.getClass().equals(b.getReferencedPortClass())) {
388 if (!basicblock.getClass().equals(b.getReferencedClass())) {
390 removePort(basicport);
403 private void removeOnePort(BasicPort basicport) {
405 removePort(basicport);
409 * Function that returns a hashtable of IOBlocks contained in the superdiagram
410 * depending on their direction.
414 * @return the hashtable
416 private Hashtable<String, List<? extends mxICell>> extractContextBlocks() {
417 // get a map of all the IOBlocks of the superdiagram
418 final Map<IOBlocks, List<BasicBlock>> blocksMap = IOBlocks.getAllBlocks(this);
420 // create a map of all the blocks of the superdiagram depending on their type
421 Hashtable<String, List<? extends mxICell>> context_block = new Hashtable<String, List<? extends mxICell>> ();
423 if (!context_block.containsKey(XcosDiagram.IN)) {
424 List<BasicBlock> cell_list = blocksMap.get(IOBlocks.ExplicitInBlock);
425 cell_list.addAll(blocksMap.get(IOBlocks.ImplicitInBlock));
426 context_block.put(XcosDiagram.IN, cell_list);
428 if (!context_block.containsKey(XcosDiagram.OUT)) {
429 List<BasicBlock> cell_list = blocksMap.get(IOBlocks.ExplicitOutBlock);
430 cell_list.addAll(blocksMap.get(IOBlocks.ImplicitOutBlock));
431 context_block.put(XcosDiagram.OUT, cell_list);
433 if (!context_block.containsKey(XcosDiagram.EIN)) {
434 List<BasicBlock> cell_list = blocksMap.get(IOBlocks.EventInBlock);
435 context_block.put(XcosDiagram.EIN, cell_list);
437 if (!context_block.containsKey(XcosDiagram.EOUT)) {
438 List<BasicBlock> cell_list = blocksMap.get(IOBlocks.EventOutBlock);
439 context_block.put(XcosDiagram.EOUT, cell_list);
442 return context_block;
446 * Function that returns a hashtable of the superblock ports
447 * depending on their direction.
451 * @return the hashtable
453 private Hashtable<String, List<? extends mxICell>> extractContextPorts() {
454 // get a map of all the ports of the superblock
455 final Map<IOBlocks, List<mxICell>> portsMap = IOBlocks.getAllPorts(this);
457 // create a map of all the ports of the superblock depending on their type
458 Hashtable<String, List<? extends mxICell>> context_port = new Hashtable<String, List<? extends mxICell>> ();
460 if (!context_port.containsKey(XcosDiagram.IN)) {
461 List<mxICell> cell_list = portsMap.get(IOBlocks.ExplicitInBlock);
462 cell_list.addAll(portsMap.get(IOBlocks.ImplicitInBlock));
463 context_port.put(XcosDiagram.IN, cell_list);
465 if (!context_port.containsKey(XcosDiagram.OUT)) {
466 List<mxICell> cell_list = portsMap.get(IOBlocks.ExplicitOutBlock);
467 cell_list.addAll(portsMap.get(IOBlocks.ImplicitOutBlock));
468 context_port.put(XcosDiagram.OUT, cell_list);
470 if (!context_port.containsKey(XcosDiagram.EIN)) {
471 List<mxICell> cell_list = portsMap.get(IOBlocks.EventInBlock);
472 context_port.put(XcosDiagram.EIN, cell_list);
474 if (!context_port.containsKey(XcosDiagram.EOUT)) {
475 List<mxICell> cell_list = portsMap.get(IOBlocks.EventOutBlock);
476 context_port.put(XcosDiagram.EOUT, cell_list);
483 * Function that add a port to a superblock. The function should be called only when
484 * the number of superdiagram blocks is higher than the superblock port number.
487 * direction of the block ports
488 * @param context_block
490 * @param context_port
493 private void addPorts(String key, Hashtable<String, List<? extends mxICell>> context_block, Map<String, List<? extends mxICell>> context_port)
495 // iterate on the superdiagram blocks
496 for (mxICell cell : context_block.get(key)) {
497 if (cell instanceof BasicBlock) {
498 BasicBlock basicblock = (BasicBlock) cell;
499 int order = (int) ((ScilabDouble) basicblock.getIntegerParameters()).getRealPart()[0][0];
501 // get the new added block if found
502 List<? extends mxICell> port_list = context_port.get(key);
503 boolean port_found = false;
504 for (mxICell port : port_list) {
505 if (port instanceof BasicPort) {
506 BasicPort basicport = (BasicPort) port;
507 if (order == basicport.getOrdering()) {
514 if (port_found == false) {
515 // add the port on the superblock
516 insertOnePort(order, basicblock);
523 * Function that remove a port to a superblock. The function should be called only when
524 * the number of superdiagram blocks is less than the superblock port number.
527 * direction of the block ports
528 * @param context_block
530 * @param context_port
533 private void removePorts(String key, Hashtable<String, List<? extends mxICell>> context_block, Map<String, List<? extends mxICell>> context_port) {
534 // iterate on the superblock ports
535 for (mxICell cell : context_port.get(key)) {
536 if (cell instanceof BasicPort) {
537 BasicPort basicport = (BasicPort) cell;
538 int order = basicport.getOrdering();
540 // get the port to remove
541 List<? extends mxICell> block_list = context_block.get(key);
542 boolean block_found = false;
543 for (mxICell block : block_list) {
544 if (block instanceof BasicBlock) {
545 BasicBlock basicblock = (BasicBlock) block;
546 int block_order = (int) ((ScilabDouble) basicblock.getIntegerParameters()).getRealPart()[0][0];
547 if (order == block_order)
555 if (block_found == false) {
557 removeOnePort(basicport);
564 * Function that remove dead ports from the superblock. A dead port is a port which has not
565 * a corresponding superdiagram IOBlock
568 * direction of the block ports
569 * @param context_block
571 * @param context_port
574 private void removeDeadPorts(String key, Hashtable<String, List<? extends mxICell>> context_block, Map<String, List<? extends mxICell>> context_port) {
575 // first remove dead ports
576 for (mxICell cell : context_port.get(key)) {
577 if (cell instanceof BasicPort) {
578 BasicPort basicport = (BasicPort) cell;
579 int order = basicport.getOrdering();
581 // get the port to remove
582 List<? extends mxICell> block_list = context_block.get(key);
583 boolean block_found = false;
584 for (mxICell block : block_list) {
585 if (block instanceof BasicBlock) {
586 BasicBlock basicblock = (BasicBlock) block;
587 int block_order = (int) ((ScilabDouble) basicblock.getIntegerParameters()).getRealPart()[0][0];
588 if (order == block_order) {
595 if (block_found == false) {
597 removeOnePort(basicport);
604 * Function that replace a port of a superblock
605 * if its numbering has changed
608 * direction of the block ports
609 * @param context_block
611 * @param context_port
614 private void replacePort(String key, Hashtable<String, List<? extends mxICell>> context_block, Map<String, List<? extends mxICell>> context_port) {
615 // iterate on the superdiagram blocks
616 for (mxICell cell : context_block.get(key)) {
617 if (cell instanceof BasicBlock) {
618 BasicBlock basicblock = (BasicBlock) cell;
619 int order = (int) ((ScilabDouble) basicblock.getIntegerParameters()).getRealPart()[0][0];
621 // verify superblock port coherence
622 List<? extends mxICell> port_list = context_port.get(key);
623 BasicPort basicport = null;
624 boolean port_found = false;
625 for (mxICell port : port_list) {
626 if (port instanceof BasicPort) {
627 basicport = (BasicPort) port;
628 if (order == basicport.getOrdering()) {
635 if (port_found == true) {
636 boolean port_deleted = false;
637 port_deleted = removeOnePort(order, basicport, basicblock);
639 // add the port on the superblock if deleted
640 if (port_deleted == true) {
641 insertOnePort(order, basicblock);
644 insertOnePort(order, basicblock);
651 * Function that updates super block ports in parent diagram
653 public void updateExportedPort() {
657 if (getParentDiagram() == null) {
658 setParentDiagram(Xcos.findParent(this));
661 // extracting blocks from the superdiagram
662 Hashtable<String, List<? extends mxICell>> context_block = extractContextBlocks();
663 // extracting ports from the superblock
664 Hashtable<String, List<? extends mxICell>> context_port = extractContextPorts();
666 for (String key : context_block.keySet()) {
667 if (context_block.get(key).size() > context_port.get(key).size()) {
668 // adding ports of the superblock
669 addPorts(key, context_block, context_port);
670 } else if (context_block.get(key).size() < context_port.get(key).size()) {
671 // removing ports of the superblock
672 removePorts(key, context_block, context_port);
674 // reordering ports of the superblock
675 removeDeadPorts(key, context_block, context_port);
676 replacePort(key, context_block, context_port);
680 getParentDiagram().fireEvent(new mxEventObject(XcosEvent.SUPER_BLOCK_UPDATED, XcosConstants.EVENT_BLOCK_UPDATED, this));
684 * Mask the SuperBlock
687 setInterfaceFunctionName(MASKED_INTERFUNCTION_NAME);
688 setSimulationFunctionName(MASKED_SIMULATION_NAME);
689 setIntegerParameters(new ScilabDouble(1));
693 * Unmask the SuperBlock
695 public void unmask() {
696 setInterfaceFunctionName(INTERFUNCTION_NAME);
697 setSimulationFunctionName(SIMULATION_NAME);
698 setIntegerParameters(new ScilabDouble());
702 * @return True is the SuperBlock is masked, false otherwise
704 public boolean isMasked() {
705 return getInterfaceFunctionName().compareTo(INTERFUNCTION_NAME) != 0;
709 * Customize the parent diagram on name change
713 * @see com.mxgraph.model.mxCell#setValue(java.lang.Object)
716 public void setValue(Object value) {
717 super.setValue(value);
723 if (getChild() != null) {
724 getChild().setTitle(FileUtils.toValidCIdentifier(value.toString()));
725 setRealParameters(new DiagramElement().encode(getChild()));
730 * Clone the child safely.
732 * @return a new clone instance
733 * @throws CloneNotSupportedException
735 * @see org.scilab.modules.xcos.block.BasicBlock#clone()
738 public Object clone() throws CloneNotSupportedException {
739 SuperBlock clone = (SuperBlock) super.clone();
743 clone.child = (SuperBlockDiagram) child.clone();
744 clone.child.setContainer(clone);
752 * Serializable custom implementation need to handle any copy / DnD case.
756 * Encode the block as xml
760 * @throws IOException
763 private void writeObject(java.io.ObjectOutputStream out) throws IOException {
764 out.writeObject(new XcosCodec().encode(this));
768 * Decode the block as xml
772 * @throws IOException
774 * @throws ClassNotFoundException
777 private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
778 new XcosCodec().decode((Node) in.readObject(), this);
781 * Specific post serialization things
783 if (this.child == null) {
784 this.child = new SuperBlockDiagram(this);
785 this.child.installListeners();
787 this.child.setContainer(this);
789 this.child.installSuperBlockListeners();
792 // CSON: ClassDataAbstractionCoupling
793 // CSON: ClassFanOutComplexity