7f49fd753bd8b2b65ff0c654cd4a059a7c186794
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / SuperBlockDiagram.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2009 - DIGITEO - Antoine ELIAS
4  * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos.graph;
15
16 import java.io.File;
17 import java.io.Serializable;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.Hashtable;
22 import java.util.List;
23 import java.util.logging.Logger;
24
25 import org.scilab.modules.types.ScilabDouble;
26 import org.scilab.modules.xcos.Xcos;
27 import org.scilab.modules.xcos.block.BasicBlock;
28 import org.scilab.modules.xcos.block.SuperBlock;
29 import org.scilab.modules.xcos.block.io.ContextUpdate;
30 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
31 import org.scilab.modules.xcos.block.io.EventInBlock;
32 import org.scilab.modules.xcos.block.io.EventOutBlock;
33 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
34 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
35 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
36 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
37 import org.scilab.modules.xcos.utils.XcosEvent;
38 import org.scilab.modules.xcos.utils.XcosMessages;
39
40 import com.mxgraph.model.mxICell;
41 import com.mxgraph.util.mxEvent;
42 import com.mxgraph.util.mxEventObject;
43
44 public final class SuperBlockDiagram extends XcosDiagram implements Serializable, Cloneable {
45
46     private static final String PARENT_DIAGRAM_WAS_NULL = "Parent diagram was null";
47     private static final long serialVersionUID = -402918614723713301L;
48
49     private static final String IN = "in";
50     private static final String OUT = "out";
51     private static final String EIN = "ein";
52     private static final String EOUT = "eout";
53
54     private SuperBlock container;
55
56     /**
57      * Constructor
58      */
59     public SuperBlockDiagram() {
60         super();
61     }
62
63     /**
64      * @param superBlock
65      *            parent super block
66      */
67     public SuperBlockDiagram(SuperBlock superBlock) {
68         super();
69         setContainer(superBlock);
70     }
71
72     /**
73      * @return parent super block
74      */
75     public SuperBlock getContainer() {
76         return container;
77     }
78
79     /**
80      * @param container
81      *            parent super block
82      */
83     public void setContainer(SuperBlock container) {
84         this.container = container;
85     }
86
87     @Override
88     public File getSavedFile() {
89         if (getContainer() != null) {
90             XcosDiagram parent = getContainer().getParentDiagram();
91             if (parent != null) {
92                 return parent.getSavedFile();
93             }
94         }
95
96         return super.getSavedFile();
97     }
98
99     /**
100      * Concatenate the context with the parent one
101      *
102      * @return the context
103      * @see org.scilab.modules.xcos.graph.XcosDiagram#getContext()
104      */
105     @Override
106     public String[] getContext() {
107         final SuperBlock block = getContainer();
108         XcosDiagram graph = block.getParentDiagram();
109         if (graph == null) {
110             block.setParentDiagram(Xcos.findParent(block));
111             graph = block.getParentDiagram();
112             Logger.getLogger(SuperBlockDiagram.class.getName()).finest(PARENT_DIAGRAM_WAS_NULL);
113         }
114
115         final String[] parent;
116         if (graph == null) {
117             parent = new String[] {};
118         } else {
119             parent = graph.getContext();
120         }
121
122         final String[] current = super.getContext();
123
124         String[] full = new String[current.length + parent.length];
125         System.arraycopy(parent, 0, full, 0, parent.length);
126         System.arraycopy(current, 0, full, parent.length, current.length);
127         return full;
128     }
129
130     /**
131      * Validate I/O ports.
132      *
133      * @param cell
134      *            Cell that represents the cell to validate.
135      * @param context
136      *            Hashtable that represents the global validation state.
137      */
138     @SuppressWarnings("unchecked")
139     @Override
140     public String validateCell(final Object cell, final Hashtable<Object, Object> context) {
141         String err = null;
142
143         /*
144          * Only validate I/O blocks
145          */
146
147         // get the key
148         final String key;
149         if (cell instanceof ExplicitInBlock || cell instanceof ImplicitInBlock) {
150             key = IN;
151         } else if (cell instanceof ExplicitOutBlock || cell instanceof ImplicitOutBlock) {
152             key = OUT;
153         } else if (cell instanceof EventInBlock) {
154             key = EIN;
155         } else if (cell instanceof EventOutBlock) {
156             key = EOUT;
157         } else {
158             key = null;
159         }
160
161         final BasicBlock block;
162         if (key != null) {
163             block = (BasicBlock) cell;
164         } else {
165             return err;
166         }
167
168         /*
169          * Prepare validation
170          */
171
172         // fill the context
173         fillContext(context);
174
175         /*
176          * Validate with ipar
177          */
178
179         // get the real index
180         final List <? extends BasicBlock > blocks = (List <? extends BasicBlock > ) context.get(key);
181         final int realIndex = blocks.indexOf(block) + 1;
182
183         // get the user index
184         final ScilabDouble data = (ScilabDouble) block.getIntegerParameters();
185         if (data.getWidth() < 1 || data.getHeight() < 1) {
186             return err;
187         }
188         final int userIndex = (int) data.getRealPart()[0][0];
189
190         // if the indexes are not equals, alert the user.
191         if (realIndex != userIndex) {
192             final StringBuilder str = new StringBuilder();
193             str.append("<html><body><em>");
194             str.append(XcosMessages.WRONG_PORT_NUMBER);
195             str.append("</em><br/>");
196             str.append(String.format(XcosMessages.EXPECTING_NUMBER, realIndex));
197             str.append("</body></html>    ");
198
199             err = str.toString();
200         }
201
202         return err;
203     }
204
205     /**
206      * Fill the context with I/O port
207      *
208      * @param context
209      *            the context to fill
210      */
211     @SuppressWarnings("unchecked")
212     private void fillContext(final Hashtable<Object, Object> context) {
213         if (!context.containsKey(IN)) {
214             context.put(IN, iparSort(getAllTypedBlock(new Class[] { ExplicitInBlock.class, ImplicitInBlock.class })));
215         }
216         if (!context.containsKey(OUT)) {
217             context.put(OUT, iparSort(getAllTypedBlock(new Class[] { ExplicitOutBlock.class, ImplicitOutBlock.class })));
218         }
219         if (!context.containsKey(EIN)) {
220             context.put(EIN, iparSort(getAllTypedBlock(EventInBlock.class)));
221         }
222         if (!context.containsKey(EOUT)) {
223             context.put(EOUT, iparSort(getAllTypedBlock(EventOutBlock.class)));
224         }
225     }
226
227     /**
228      * Sort the blocks per first integer parameter value
229      *
230      * @param blocks
231      *            the block list
232      * @return the sorted block list (same instance)
233      */
234     private List <? extends BasicBlock > iparSort(final List <? extends BasicBlock > blocks) {
235         Collections.sort(blocks, new Comparator<BasicBlock>() {
236
237             @Override
238             public int compare(BasicBlock o1, BasicBlock o2) {
239                 final ScilabDouble data1 = (ScilabDouble) o1.getIntegerParameters();
240                 final ScilabDouble data2 = (ScilabDouble) o2.getIntegerParameters();
241
242                 int value1 = 0;
243                 int value2 = 0;
244
245                 if (data1.getWidth() >= 1 && data1.getHeight() >= 1) {
246                     value1 = (int) data1.getRealPart()[0][0];
247                 }
248                 if (data2.getWidth() >= 1 && data2.getHeight() >= 1) {
249                     value2 = (int) data2.getRealPart()[0][0];
250                 }
251
252                 return value1 - value2;
253             }
254         });
255         return blocks;
256     }
257
258     /**
259      * @param <T>
260      *            The type to work on
261      * @param klass
262      *            the class instance to work on
263      * @return list of typed block
264      */
265     @SuppressWarnings("unchecked")
266     private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T> klass) {
267         final List<T> list = new ArrayList<T>();
268
269         int blockCount = getModel().getChildCount(getDefaultParent());
270
271         for (int i = 0; i < blockCount; i++) {
272             Object cell = getModel().getChildAt(getDefaultParent(), i);
273             if (klass.isInstance(cell)) {
274                 // According to the test we are sure that the cell is an
275                 // instance of T. Thus we can safely cast it.
276                 list.add((T) cell);
277             }
278         }
279         return list;
280     }
281
282     /**
283      * @param <T>
284      *            The type to work on
285      * @param klasses
286      *            the class instance list to work on
287      * @return list of typed block
288      */
289     private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T>[] klasses) {
290         final List<T> list = new ArrayList<T>();
291         for (Class<T> klass : klasses) {
292             list.addAll(getAllTypedBlock(klass));
293         }
294         return list;
295     }
296
297     /**
298      * Listener for SuperBlock diagram events.
299      */
300     @SuppressWarnings(value = { "serial" })
301     private static final class GenericSuperBlockListener implements mxIEventListener, Serializable {
302         private static GenericSuperBlockListener instance;
303
304         /**
305          * Reduce constructor visibility
306          */
307         private GenericSuperBlockListener() {
308             super();
309         }
310
311         /**
312          * Mono-threaded singleton implementation getter
313          *
314          * @return The unique instance
315          */
316         public static GenericSuperBlockListener getInstance() {
317             if (instance == null) {
318                 instance = new GenericSuperBlockListener();
319             }
320             return instance;
321         }
322
323         /**
324          * Update the IOPorts colors and values.
325          *
326          * @param arg0
327          *            the source
328          * @param arg1
329          *            the event data
330          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
331          *      com.mxgraph.util.mxEventObject)
332          */
333         @Override
334         public void invoke(Object arg0, mxEventObject arg1) {
335             final SuperBlockDiagram graph = ((SuperBlockDiagram) arg0);
336             final SuperBlock block = graph.getContainer();
337             if (block != null) {
338                 block.updateExportedPort();
339             }
340         }
341     }
342
343     /**
344      * Listener for SuperBlock diagram events.
345     @SuppressWarnings(value = { "serial" })
346      */
347     private static final class ContentChangedListener implements mxIEventListener, Serializable {
348         private static ContentChangedListener instance;
349
350         /**
351          * Reduce constructor visibility
352          */
353         private ContentChangedListener() {
354             super();
355         }
356
357         /**
358          * Mono-threaded singleton implementation getter
359          *
360          * @return The unique instance
361          */
362         public static ContentChangedListener getInstance() {
363             if (instance == null) {
364                 instance = new ContentChangedListener();
365             }
366             return instance;
367         }
368
369         /**
370          * Update the IOPorts colors and values.
371          *
372          * @param arg0
373          *            the source
374          * @param arg1
375          *            the event data
376          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
377          *      com.mxgraph.util.mxEventObject)
378          */
379         @Override
380         public void invoke(Object arg0, mxEventObject arg1) {
381             final SuperBlockDiagram graph = ((SuperBlockDiagram) arg0);
382             final SuperBlock block = graph.getContainer();
383             if (block != null) {
384                 block.invalidateRpar();
385             }
386         }
387     }
388
389     /**
390     @SuppressWarnings(value = { "serial" })
391      * Update the diagram labels
392      */
393     private static final class LabelBlockListener implements mxIEventListener, Serializable {
394         private static LabelBlockListener instance;
395
396         /**
397          * Default Constructor
398          */
399         private LabelBlockListener() {
400             super();
401         }
402
403         /**
404          * @return the instance
405          */
406         public static LabelBlockListener getInstance() {
407             if (instance == null) {
408                 instance = new LabelBlockListener();
409             }
410             return instance;
411         }
412
413         /** {@inheritDoc} */
414         @Override
415         public void invoke(Object sender, mxEventObject evt) {
416             final String value = (String) evt.getProperty("value");
417             final Object parent = evt.getProperty("parent");
418             if (parent instanceof ContextUpdate) {
419                 final ContextUpdate block = (ContextUpdate) parent;
420                 final ScilabDouble data = (ScilabDouble) block.getIntegerParameters();
421
422                 final int index;
423                 if (data.getHeight() > 0 && data.getWidth() > 0) {
424                     index = (int) data.getRealPart()[0][0];
425                 } else {
426                     index = 1;
427                 }
428
429                 final SuperBlock container = ((SuperBlockDiagram) sender).getContainer();
430                 if (container == null) {
431                     return;
432                 }
433
434                 container.sortChildren();
435                 final List<mxICell> ports = IOBlocks.getPorts(container, block.getClass());
436
437                 XcosDiagram graph = container.getParentDiagram();
438                 if (graph == null) {
439                     container.setParentDiagram(Xcos.findParent(container));
440                     graph = container.getParentDiagram();
441                     Logger.getLogger(SuperBlockDiagram.class.getName()).finest(PARENT_DIAGRAM_WAS_NULL);
442                 }
443
444                 if (index > 0 && index <= ports.size()) {
445                     container.getParentDiagram().cellLabelChanged(ports.get(index - 1), value, false);
446                 }
447             }
448         }
449     }
450
451     /**
452      * Install the specific listeners for {@link SuperBlockDiagram}.
453      */
454     public void installSuperBlockListeners() {
455         removeListener(GenericSuperBlockListener.getInstance());
456         removeListener(LabelBlockListener.getInstance());
457         removeListener(ContentChangedListener.getInstance());
458
459         addListener(mxEvent.CELLS_ADDED, GenericSuperBlockListener.getInstance());
460         addListener(mxEvent.CELLS_REMOVED, GenericSuperBlockListener.getInstance());
461         addListener(XcosEvent.IO_PORT_VALUE_UPDATED, GenericSuperBlockListener.getInstance());
462         addListener(mxEvent.LABEL_CHANGED, LabelBlockListener.getInstance());
463
464         addListener(mxEvent.CELLS_ADDED, ContentChangedListener.getInstance());
465         addListener(mxEvent.CELLS_MOVED, ContentChangedListener.getInstance());
466         addListener(mxEvent.CELLS_REMOVED, ContentChangedListener.getInstance());
467         addListener(mxEvent.CELLS_RESIZED, ContentChangedListener.getInstance());
468     }
469
470     /**
471      * This function set the SuperBlock diagram and all its parents in a
472      * modified state or not.
473      *
474      * @param modified
475      *            status
476      */
477     @Override
478     public void setModified(boolean modified) {
479         super.setModified(modified);
480
481         if (getContainer() != null && getContainer().getParentDiagram() != null) {
482             getContainer().getParentDiagram().setModified(modified);
483         }
484     }
485
486     /**
487      * This function set the SuperBlock diagram in a modified state or not.
488      *
489      * It doesn't perform recursively on the parent diagrams. If you want such a
490      * behavior use setModified instead.
491      *
492      * @param modified
493      *            status
494      * @see #setModified
495      */
496     public void setModifiedNonRecursively(boolean modified) {
497         super.setModified(modified);
498     }
499
500     /** {@inheritDoc} */
501     // CSOFF: SuperClone
502     @Override
503     public Object clone() throws CloneNotSupportedException {
504         final SuperBlockDiagram clone = new SuperBlockDiagram();
505         clone.installListeners();
506         clone.installSuperBlockListeners();
507
508         clone.setScicosParameters((ScicosParameters) getScicosParameters().clone());
509         clone.addCells(cloneCells(getChildCells(getDefaultParent())), clone.getDefaultParent());
510
511         return clone;
512     }
513     // CSON: SuperClone
514 }