Xcos GUI: split creation now works
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / model / XcosCellFactory.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.graph.model;
14
15 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.buildCall;
16 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.synchronousScilabExec;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.util.Arrays;
20 import java.util.EnumSet;
21 import java.util.Optional;
22
23 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
24 import org.scilab.modules.xcos.JavaController;
25 import org.scilab.modules.xcos.Kind;
26 import org.scilab.modules.xcos.ObjectProperties;
27 import org.scilab.modules.xcos.UpdateStatus;
28 import org.scilab.modules.xcos.VectorOfScicosID;
29 import org.scilab.modules.xcos.Xcos;
30 import org.scilab.modules.xcos.XcosView;
31 import org.scilab.modules.xcos.XcosViewListener;
32 import org.scilab.modules.xcos.block.AfficheBlock;
33 import org.scilab.modules.xcos.block.BasicBlock;
34 import org.scilab.modules.xcos.block.SplitBlock;
35 import org.scilab.modules.xcos.block.SuperBlock;
36 import org.scilab.modules.xcos.block.TextBlock;
37 import org.scilab.modules.xcos.block.io.EventInBlock;
38 import org.scilab.modules.xcos.block.io.EventOutBlock;
39 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
40 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
41 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
42 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
43 import org.scilab.modules.xcos.block.positionning.BigSom;
44 import org.scilab.modules.xcos.block.positionning.GroundBlock;
45 import org.scilab.modules.xcos.block.positionning.Product;
46 import org.scilab.modules.xcos.block.positionning.RoundBlock;
47 import org.scilab.modules.xcos.block.positionning.Summation;
48 import org.scilab.modules.xcos.block.positionning.VoltageSensorBlock;
49 import org.scilab.modules.xcos.graph.XcosDiagram;
50 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
51 import org.scilab.modules.xcos.port.BasicPort;
52 import org.scilab.modules.xcos.port.command.CommandPort;
53 import org.scilab.modules.xcos.port.control.ControlPort;
54 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
55 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
56 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
57 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
58 import com.mxgraph.model.mxCell;
59 import com.mxgraph.model.mxICell;
60
61 /**
62  * Ease the creation of any {@link Kind} of graphical object
63  */
64 public final class XcosCellFactory {
65
66     // DAC: As this is the constructor for all the block classes, this class is
67     // very coupled with *Block classes
68     // CSOFF: ClassDataAbstractionCoupling
69     /**
70      * List the specific block interface function name. <BR>
71      * <BR>
72      * <EM>Specific instance must be registered before generic ones in order
73      * to serialized all the non-default values.</EM>
74      */
75     public static enum BlockInterFunction {
76         /** @see TextBlock */
77         TEXT_f(TextBlock.class),
78         /** @see SuperBlock */
79         DSUPER(SuperBlock.class),
80         /** @see SuperBlock */
81         SUPER_f(SuperBlock.class),
82         /** @see AfficheBlock */
83         AFFICH_m(AfficheBlock.class),
84         /** @see AfficheBlock */
85         AFFICH_f(AfficheBlock.class),
86         /** @see ExplicitInBlock */
87         IN_f(ExplicitInBlock.class),
88         /** @see ExplicitOutBlock */
89         OUT_f(ExplicitOutBlock.class),
90         /** @see ImplicitInBlock */
91         INIMPL_f(ImplicitInBlock.class),
92         /** @see ImplicitOutBlock */
93         OUTIMPL_f(ImplicitOutBlock.class),
94         /** @see EventInBlock */
95         CLKINV_f(EventInBlock.class),
96         /** @see EventOutBlock */
97         CLKOUTV_f(EventOutBlock.class),
98         /** @see EventOutBlock */
99         CLKOUT_f(EventOutBlock.class),
100         /** @see SplitBlock */
101         SPLIT_f(SplitBlock.class),
102         /** @see SplitBlock */
103         IMPSPLIT_f(SplitBlock.class),
104         /** @see SplitBlock */
105         CLKSPLIT_f(SplitBlock.class),
106         /** @see GroundBlock */
107         Ground(GroundBlock.class),
108         /** @see VoltageSensorBlock */
109         VoltageSensor(VoltageSensorBlock.class),
110         /** @see RoundBlock */
111         SUM_f(RoundBlock.class),
112         /** @see RoundBlock */
113         PROD_f(RoundBlock.class),
114         /** @see RoundBlock */
115         CLKSOM_f(RoundBlock.class),
116         /** @see RoundBlock */
117         CLKSOMV_f(RoundBlock.class),
118         /** @see BigSom */
119         BIGSOM_f(BigSom.class),
120         /** @see Summation */
121         SUMMATION(Summation.class),
122         /** @see Product */
123         PRODUCT(Product.class),
124         /** @see BasicBlock */
125         BASIC_BLOCK(BasicBlock.class);
126
127         private final Class<? extends BasicBlock> klass;
128
129         /**
130          * Default constructor
131          *
132          * @param block
133          *            The reference instance
134          */
135         private BlockInterFunction(Class<? extends BasicBlock> klass) {
136             this.klass = klass;
137         }
138
139         /**
140          * @return the class to instantiate
141          */
142         public Class<? extends BasicBlock> getKlass() {
143             return klass;
144         }
145     }
146
147     // CSON: ClassDataAbstractionCoupling
148
149     /** Default singleton constructor */
150     private XcosCellFactory() {
151         // This class is a static singleton
152     }
153
154     /*
155      * Block and Annotation management
156      */
157
158
159
160     /**
161      * Instantiate a new block with the specified UID value and interface function
162      *
163      * @param uid
164      *            The associated UID value
165      * @param interfaceFunction the interface function
166      * @return A new instance of a block.
167      */
168     public static BasicBlock createBlock(String interfaceFunction) {
169         Optional<BlockInterFunction> func = EnumSet.allOf(BlockInterFunction.class).stream()
170                                             .filter(f -> f.name().equals(interfaceFunction))
171                                             .findFirst();
172
173         final BasicBlock block;
174         if (func.isPresent()) {
175             block = createBlock(func.get());
176         } else {
177             block = createBlock(BlockInterFunction.BASIC_BLOCK);
178         }
179         block.setStyle(interfaceFunction);
180
181         return block;
182     }
183
184
185
186     private static class BlockLoadedListener extends XcosViewListener {
187         private long uid;
188
189         public BlockLoadedListener() {
190             uid = 0;
191         }
192
193         public long getUID() {
194             return uid;
195         }
196
197         /**
198          * When a unique block is created then store it for later use.
199          */
200         @Override
201         public void objectCreated(long uid, Kind kind) {
202             if (!EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(kind)) {
203                 return;
204             }
205
206             this.uid = uid;
207         }
208
209         /**
210          * When a composite block is created we track the PARENT_BLOCK / CHILDREN association to store the parent.
211          */
212         @Override
213         public void propertyUpdated(long uid, Kind kind, ObjectProperties property, UpdateStatus status) {
214             if (status != UpdateStatus.SUCCESS || property != ObjectProperties.CHILDREN) {
215                 return;
216             }
217             if (!EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(kind)) {
218                 return;
219             }
220
221             this.uid = uid;
222         }
223     }
224
225     /**
226      * Instantiate a new block with the specified interface function.
227      *
228      * @param func
229      *            the interface function
230      * @return A new instance of a block.
231      */
232     public static BasicBlock createBlock(BlockInterFunction func) {
233         return createBlock(new JavaController(), func);
234     }
235
236     /**
237      * Instantiate a new block with the specified interface function.
238      *
239      * @param func
240      *            the interface function
241      * @return A new instance of a block.
242      */
243     public static BasicBlock createBlock(final JavaController controller, BlockInterFunction func) {
244         XcosView view = (XcosView) JavaController.lookup_view(Xcos.class.getSimpleName());
245
246         BlockLoadedListener blockLoaded = new BlockLoadedListener();
247         view.addXcosViewListener(blockLoaded, EnumSet.allOf(Kind.class), true, EnumSet.of(ObjectProperties.CHILDREN));
248
249         BasicBlock block;
250         try {
251             synchronousScilabExec(ScilabDirectHandler.BLK + " = " + buildCall(func.name(), "define"));
252             block = XcosCellFactory.createBlock(controller, func, blockLoaded.getUID());
253         } catch (InterpreterException e1) {
254             block = null;
255         } finally {
256             view.removeXcosViewListener(blockLoaded);
257         }
258
259         return block;
260     }
261
262     /**
263      * Instantiate a new block with the specified interface function and uid.
264      *
265      * @param controller the Java controller to use
266      * @param func the interface function
267      * @param uid the allocated uid
268      * @return A new instance of a block.
269      */
270     private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, long uid) {
271         BasicBlock block = null;
272         try {
273             block = func.getKlass().getConstructor(Long.TYPE).newInstance(uid);
274         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
275             // Something goes wrong, print it.
276             e.printStackTrace();
277             return block;
278         }
279
280         insertPortChildren(controller, block);
281         return block;
282     }
283
284     /*
285      * Port management
286      */
287
288
289     /**
290      * Helper used to create port children on a parent block.
291      *
292      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
293      *
294      * @param controller is the shared controller instance
295      * @param parent is the parent {@link mxCell} to modify
296      */
297     public static void insertPortChildren(final JavaController controller, final XcosCell parent) {
298         final EnumSet<ObjectProperties> properties = EnumSet.of(ObjectProperties.INPUTS, ObjectProperties.OUTPUTS, ObjectProperties.EVENT_INPUTS, ObjectProperties.EVENT_OUTPUTS);
299         insertPortChildren(controller, properties, parent);
300     }
301
302
303     /**
304      * Helper used to create port children on a parent block.
305      *
306      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
307      *
308      * @param controller is the shared controller instance
309      * @param properties specify the kind of port to insert and should be some of : <UL>
310      *        <LI>{@link ObjectProperties#INPUTS}
311      *        <LI>{@link ObjectProperties#OUTPUTS}
312      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
313      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
314      * @param parent is the parent {@link mxCell} to modify
315      */
316     public static void insertPortChildren(final JavaController controller, final EnumSet<ObjectProperties> properties, final XcosCell parent) {
317         for (ObjectProperties property : properties) {
318             insertPortChildren(controller, property, parent);
319         }
320     }
321
322     /**
323      * Helper used to create port children on a parent block.
324      *
325      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
326      *
327      * @param controller is the shared controller instance
328      * @param property specify the kind of port to insert and should be one of : <UL>
329      *        <LI>{@link ObjectProperties#INPUTS}
330      *        <LI>{@link ObjectProperties#OUTPUTS}
331      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
332      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
333      * @param parent is the parent {@link mxCell} to modify
334      */
335     public static void insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
336         if (parent.getKind() != Kind.BLOCK) {
337             return;
338         }
339
340         VectorOfScicosID modelChildren = new VectorOfScicosID();
341         controller.getObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
342
343         mxICell[] children = new mxICell[modelChildren.size()];
344         for (int i = 0; i < children.length; i++) {
345             children[i] = createPort(controller, modelChildren.get(i), property);
346         }
347         Arrays.stream(children).forEach(c -> parent.insert(c));
348     }
349
350     /**
351      * Create a port for a specific uid
352      *
353      * @param controller is the shared controller instance
354      * @param uid represent the allocated UID on the MVC
355      * @param property specify the kind of port to create and should be one of : <UL>
356      *        <LI>{@link ObjectProperties#INPUTS}
357      *        <LI>{@link ObjectProperties#OUTPUTS}
358      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
359      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
360      * @return a newly allocated port
361      */
362     private static final BasicPort createPort(final JavaController controller, long uid, final ObjectProperties property) {
363         boolean[] isImplicit = {false};
364
365         switch (property) {
366             case INPUTS:
367                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
368                 if (isImplicit[0]) {
369                     return new ImplicitInputPort(uid);
370                 } else {
371                     return new ExplicitInputPort(uid);
372                 }
373             case OUTPUTS:
374                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
375                 if (isImplicit[0]) {
376                     return new ImplicitOutputPort(uid);
377                 } else {
378                     return new ExplicitOutputPort(uid);
379                 }
380             case EVENT_INPUTS:
381                 return new ControlPort(uid);
382             case EVENT_OUTPUTS:
383                 return new CommandPort(uid);
384             default:
385                 return null;
386         }
387     }
388 }