35ab5e912aecfd57a1e7c22514f7bb7fada49a0d
[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.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.EnumSet;
24 import java.util.Optional;
25
26 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
27 import org.scilab.modules.graph.utils.ScilabExported;
28 import org.scilab.modules.xcos.JavaController;
29 import org.scilab.modules.xcos.Kind;
30 import org.scilab.modules.xcos.ObjectProperties;
31 import org.scilab.modules.xcos.VectorOfDouble;
32 import org.scilab.modules.xcos.VectorOfScicosID;
33 import org.scilab.modules.xcos.Xcos;
34 import org.scilab.modules.xcos.XcosView;
35 import org.scilab.modules.xcos.block.AfficheBlock;
36 import org.scilab.modules.xcos.block.BasicBlock;
37 import org.scilab.modules.xcos.block.SplitBlock;
38 import org.scilab.modules.xcos.block.SuperBlock;
39 import org.scilab.modules.xcos.block.TextBlock;
40 import org.scilab.modules.xcos.block.io.EventInBlock;
41 import org.scilab.modules.xcos.block.io.EventOutBlock;
42 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
43 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
44 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
45 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
46 import org.scilab.modules.xcos.block.positionning.BigSom;
47 import org.scilab.modules.xcos.block.positionning.GroundBlock;
48 import org.scilab.modules.xcos.block.positionning.Product;
49 import org.scilab.modules.xcos.block.positionning.RoundBlock;
50 import org.scilab.modules.xcos.block.positionning.Summation;
51 import org.scilab.modules.xcos.block.positionning.VoltageSensorBlock;
52 import org.scilab.modules.xcos.graph.XcosDiagram;
53 import org.scilab.modules.xcos.link.BasicLink;
54 import org.scilab.modules.xcos.link.commandcontrol.CommandControlLink;
55 import org.scilab.modules.xcos.link.explicit.ExplicitLink;
56 import org.scilab.modules.xcos.link.implicit.ImplicitLink;
57 import org.scilab.modules.xcos.port.BasicPort;
58 import org.scilab.modules.xcos.port.command.CommandPort;
59 import org.scilab.modules.xcos.port.control.ControlPort;
60 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
61 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
62 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
63 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
64 import org.scilab.modules.xcos.utils.BlockPositioning;
65
66 import com.mxgraph.model.mxCell;
67 import com.mxgraph.model.mxGeometry;
68 import com.mxgraph.model.mxICell;
69 import com.mxgraph.util.mxPoint;
70
71 /**
72  * Ease the creation of any {@link Kind} of graphical object
73  */
74 public final class XcosCellFactory {
75
76     // DAC: As this is the constructor for all the block classes, this class is
77     // very coupled with *Block classes
78     // CSOFF: ClassDataAbstractionCoupling
79     /**
80      * List the specific block interface function name. <BR>
81      * <BR>
82      * <EM>Specific instance must be registered before generic ones in order
83      * to serialized all the non-default values.</EM>
84      */
85     public static enum BlockInterFunction {
86         /** @see TextBlock */
87         TEXT_f(TextBlock.class),
88         /** @see SuperBlock */
89         DSUPER(SuperBlock.class),
90         /** @see SuperBlock */
91         SUPER_f(SuperBlock.class),
92         /** @see AfficheBlock */
93         AFFICH_m(AfficheBlock.class),
94         /** @see AfficheBlock */
95         AFFICH_f(AfficheBlock.class),
96         /** @see ExplicitInBlock */
97         IN_f(ExplicitInBlock.class),
98         /** @see ExplicitOutBlock */
99         OUT_f(ExplicitOutBlock.class),
100         /** @see ImplicitInBlock */
101         INIMPL_f(ImplicitInBlock.class),
102         /** @see ImplicitOutBlock */
103         OUTIMPL_f(ImplicitOutBlock.class),
104         /** @see EventInBlock */
105         CLKINV_f(EventInBlock.class),
106         /** @see EventOutBlock */
107         CLKOUTV_f(EventOutBlock.class),
108         /** @see EventOutBlock */
109         CLKOUT_f(EventOutBlock.class),
110         /** @see SplitBlock */
111         SPLIT_f(SplitBlock.class),
112         /** @see SplitBlock */
113         IMPSPLIT_f(SplitBlock.class),
114         /** @see SplitBlock */
115         CLKSPLIT_f(SplitBlock.class),
116         /** @see GroundBlock */
117         Ground(GroundBlock.class),
118         /** @see VoltageSensorBlock */
119         VoltageSensor(VoltageSensorBlock.class),
120         /** @see RoundBlock */
121         SUM_f(RoundBlock.class),
122         /** @see RoundBlock */
123         PROD_f(RoundBlock.class),
124         /** @see RoundBlock */
125         CLKSOM_f(RoundBlock.class),
126         /** @see RoundBlock */
127         CLKSOMV_f(RoundBlock.class),
128         /** @see BigSom */
129         BIGSOM_f(BigSom.class),
130         /** @see Summation */
131         SUMMATION(Summation.class),
132         /** @see Product */
133         PRODUCT(Product.class),
134         /** @see BasicBlock */
135         BASIC_BLOCK(BasicBlock.class);
136
137         private final Class<? extends BasicBlock> klass;
138
139         /**
140          * Default constructor
141          *
142          * @param block
143          *            The reference instance
144          */
145         private BlockInterFunction(Class<? extends BasicBlock> klass) {
146             this.klass = klass;
147         }
148
149         /**
150          * @return the class to instantiate
151          */
152         public Class<? extends BasicBlock> getKlass() {
153             return klass;
154         }
155     }
156
157     private static class ScicosObjectOwner {
158         final long uid;
159         final Kind kind;
160
161         public ScicosObjectOwner(long uid, Kind kind) {
162             this.uid = uid;
163             this.kind = kind;
164
165             JavaController controller = new JavaController();
166             controller.referenceObject(uid);
167         }
168
169         public long getUID() {
170             return uid;
171         }
172
173         public Kind getKind() {
174             return kind;
175         }
176
177         @Override
178         protected void finalize() throws Throwable {
179             JavaController controller = new JavaController();
180             controller.deleteObject(uid);
181         }
182     }
183
184     // CSON: ClassDataAbstractionCoupling
185
186     /** Default singleton constructor */
187     private XcosCellFactory() {
188         // This class is a static singleton
189     }
190
191     /**
192      * This is a notify method mapped as a Scilab gateway used to alert with the loaded UID
193      * @param uid the loaded UID
194      * @param kind the kind of the created object (as an int)
195      */
196     @ScilabExported(module = "xcos", filename = "XcosCellFactory.giws.xml")
197     public static void created(long uid, int kind) {
198         lastCreated = new ScicosObjectOwner(uid, Kind.values()[kind]);
199
200     }
201     private static ScicosObjectOwner lastCreated = null;
202
203     /*
204      * Diagram management
205      */
206
207     /**
208      * Allocate a Java XcosDiagram from a COSF file.
209      *
210      * This method execute the file and register a
211      *
212      * @param controller the controller
213      * @param filename the file to execute
214      * @return an allocated XcosDiagram
215      */
216     public static XcosDiagram createDiagramFromCOSF(final JavaController controller, String filename) {
217         XcosView view = (XcosView) JavaController.lookup_view(Xcos.class.getName());
218         JavaController.unregister_view(view);
219
220         XcosDiagram diagram;
221         try {
222             synchronousScilabExec(
223                 "function f(), " +
224                 buildCall("exec", filename, -1) +
225                 buildCall("xcosCellCreated", "scs_m".toCharArray()) +
226                 "endfunction; f();");
227
228             if (lastCreated.getKind() == Kind.DIAGRAM) {
229                 diagram = new XcosDiagram(lastCreated.getUID(), lastCreated.getKind());
230                 insertChildren(controller, diagram);
231                 lastCreated = null;
232             } else {
233                 diagram = null;
234             }
235         } catch (InterpreterException e) {
236             diagram = null;
237         } finally {
238             JavaController.register_view(Xcos.class.getName(), view);
239         }
240
241         return diagram;
242     }
243
244     /**
245      * Insert the diagram MVC children into the JGraphX model
246      * @param controller the shared controller
247      * @param diagram the current diagram instance
248      */
249     public static void insertChildren(JavaController controller, XcosDiagram diagram) {
250         VectorOfScicosID children = new VectorOfScicosID();
251         controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
252         final int childrenLen = children.size();
253
254         /*
255          * Allocation some pre-sized stash data
256          */
257         final ArrayList<BasicLink> links = new ArrayList<>(childrenLen / 2);
258         final ArrayList<BasicPort> ports = new ArrayList<>(childrenLen);
259
260         /*
261          * Create the XcosCell objects and store some of them for later use
262          */
263         XcosCell[] cells = new XcosCell[childrenLen];
264         for (int i = 0; i < childrenLen; i++) {
265             final long uid = children.get(i);
266             final Kind kind = controller.getKind(uid);
267
268             switch (kind) {
269                 case ANNOTATION:
270                 case BLOCK:
271                     BasicBlock b = createBlock(controller, uid, kind);
272                     cells[i] = b;
273                     BlockPositioning.updatePortsPosition(diagram, b);
274                     b.getTypedChildrenIndexes(BasicPort.class).stream()
275                     .map(index -> b.getChildAt(index))
276                     .filter(c -> c instanceof BasicPort)
277                     .forEach( c -> ports.add((BasicPort) c));
278                     break;
279                 case LINK:
280                     BasicLink l = createLink(controller, uid, kind);
281                     cells[i] = l;
282                     links.add(l);
283                     break;
284                 default:
285                     break;
286             }
287         }
288
289         /*
290          * Relink the links on the XcosCell part
291          */
292         Comparator<XcosCell> compare = (c1, c2) -> (int) (c1.getUID() - c2.getUID());
293         Collections.sort(ports, compare);
294         for (BasicLink l : links) {
295             long[] src = new long[1];
296             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.SOURCE_PORT, src);
297
298             long[] dest = new long[1];
299             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.DESTINATION_PORT, dest);
300
301             int srcIndex = Collections.binarySearch(ports, new XcosCell(src[0], Kind.PORT), compare);
302             if (srcIndex > 0) {
303                 l.setSource(ports.get(srcIndex));
304             }
305
306             int destIndex = Collections.binarySearch(ports, new XcosCell(dest[0], Kind.PORT), compare);
307             if (destIndex > 0) {
308                 l.setTarget(ports.get(destIndex));
309             }
310         }
311
312         diagram.addCells(cells);
313     }
314
315
316
317     /*
318      * Block and Annotation management
319      */
320
321     /**
322      * Instantiate a new block with the specified interface function.
323      *
324      * @param func
325      *            the interface function
326      * @return A new instance of a block.
327      */
328     public static BasicBlock createBlock(BlockInterFunction func) {
329         return createBlock(func, func.name());
330     }
331
332     /**
333      * Instantiate a new block with the specified UID value and interface function
334      *
335      * @param uid
336      *            The associated UID value
337      * @param interfaceFunction the interface function
338      * @return A new instance of a block.
339      */
340     public static BasicBlock createBlock(String interfaceFunction) {
341         Optional<BlockInterFunction> func = EnumSet.allOf(BlockInterFunction.class).stream()
342                                             .filter(f -> f.name().equals(interfaceFunction))
343                                             .findFirst();
344
345         final BasicBlock block;
346         if (func.isPresent()) {
347             block = createBlock(func.get());
348         } else {
349             block = createBlock(BlockInterFunction.BASIC_BLOCK, interfaceFunction);
350         }
351         block.setStyle(interfaceFunction);
352
353         return block;
354     }
355
356     private static BasicBlock createBlock(BlockInterFunction func, String interfaceFunction) {
357         return createBlock(new JavaController(), func, interfaceFunction);
358     }
359
360     private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction) {
361         BasicBlock block;
362         try {
363             synchronousScilabExec("xcosCellCreated(" + interfaceFunction + "(\"define\")); ");
364
365             if (EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(lastCreated.getKind())) {
366                 block = createBlock(controller, func, interfaceFunction, lastCreated.getUID());
367                 lastCreated = null;
368             } else {
369                 block = null;
370             }
371         } catch (InterpreterException e) {
372             block = null;
373         }
374
375         return block;
376     }
377
378     private static BasicBlock createBlock(final JavaController controller, long uid, Kind kind) {
379         String[] interfaceFunction = new String[1];
380         controller.getObjectProperty(uid, kind, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
381
382         return createBlock(controller, interfaceFunction[0], uid);
383     }
384
385     private static BasicBlock createBlock(final JavaController controller, String interfaceFunction, long uid) {
386         Optional<BlockInterFunction> optFunc = EnumSet.allOf(BlockInterFunction.class).stream()
387                                                .filter(f -> f.name().equals(interfaceFunction))
388                                                .findFirst();
389
390         final BlockInterFunction func;
391         if (optFunc.isPresent()) {
392             func = optFunc.get();
393         } else {
394             func = BlockInterFunction.BASIC_BLOCK;
395         }
396
397         return createBlock(controller, func, interfaceFunction, uid);
398     }
399
400     /**
401      * Instantiate a new block with the specified interface function and uid.
402      *
403      * @param controller the Java controller to use
404      * @param func the interface function as an enum
405      * @param interfaceFunction the interface function name
406      * @param uid the allocated uid
407      * @return A new instance of a block.
408      */
409     private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction, long uid) {
410         BasicBlock block = null;
411         try {
412             block = func.getKlass().getConstructor(Long.TYPE).newInstance(uid);
413         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
414             // Something goes wrong, print it.
415             e.printStackTrace();
416             return block;
417         }
418
419         /*
420          * Synchronize model information back to the JGraphX data
421          */
422         insertPortChildren(controller, block);
423
424         block.setStyle(interfaceFunction);
425
426         VectorOfDouble geom = new VectorOfDouble(4);
427         controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.GEOMETRY, geom);
428         block.setGeometry(new mxGeometry(geom.get(0), geom.get(1), geom.get(2), geom.get(3)));
429
430         // FIXME find a way to reuse the Scicos compat handler from org.scilab.modules.xcos.io.scicos
431
432         return block;
433     }
434
435     /*
436      * Port management
437      */
438
439     /**
440      * Helper used to create port children on a parent block.
441      *
442      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
443      *
444      * @param controller is the shared controller instance
445      * @param parent is the parent {@link mxCell} to modify
446      */
447     private static void insertPortChildren(final JavaController controller, final XcosCell parent) {
448         final EnumSet<ObjectProperties> properties = EnumSet.of(ObjectProperties.INPUTS, ObjectProperties.OUTPUTS, ObjectProperties.EVENT_INPUTS, ObjectProperties.EVENT_OUTPUTS);
449         insertPortChildren(controller, properties, parent);
450     }
451
452
453     /**
454      * Helper used to create port children on a parent block.
455      *
456      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
457      *
458      * @param controller is the shared controller instance
459      * @param properties specify the kind of port to insert and should be some of : <UL>
460      *        <LI>{@link ObjectProperties#INPUTS}
461      *        <LI>{@link ObjectProperties#OUTPUTS}
462      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
463      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
464      * @param parent is the parent {@link mxCell} to modify
465      */
466     private static void insertPortChildren(final JavaController controller, final EnumSet<ObjectProperties> properties, final XcosCell parent) {
467         for (ObjectProperties property : properties) {
468             insertPortChildren(controller, property, parent);
469         }
470     }
471
472     /**
473      * Helper used to create port children on a parent block.
474      *
475      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
476      *
477      * @param controller is the shared controller instance
478      * @param property specify the kind of port to insert and should be one of : <UL>
479      *        <LI>{@link ObjectProperties#INPUTS}
480      *        <LI>{@link ObjectProperties#OUTPUTS}
481      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
482      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
483      * @param parent is the parent {@link mxCell} to modify
484      */
485     private static void insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
486         if (parent.getKind() != Kind.BLOCK) {
487             return;
488         }
489
490         VectorOfScicosID modelChildren = new VectorOfScicosID();
491         controller.getObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
492
493         mxICell[] children = new mxICell[modelChildren.size()];
494         for (int i = 0; i < children.length; i++) {
495             children[i] = createPort(controller, modelChildren.get(i), property);
496         }
497         Arrays.stream(children).forEach(c -> parent.insert(c));
498     }
499
500     /**
501      * Create a port for a specific uid
502      *
503      * @param controller is the shared controller instance
504      * @param uid represent the allocated UID on the MVC
505      * @param property specify the kind of port to create and should be one of : <UL>
506      *        <LI>{@link ObjectProperties#INPUTS}
507      *        <LI>{@link ObjectProperties#OUTPUTS}
508      *        <LI>{@link ObjectProperties#EVENT_INPUTS}
509      *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
510      * @return a newly allocated port
511      */
512     private static final BasicPort createPort(final JavaController controller, long uid, final ObjectProperties property) {
513         boolean[] isImplicit = {false};
514
515         switch (property) {
516             case INPUTS:
517                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
518                 if (isImplicit[0]) {
519                     return new ImplicitInputPort(uid);
520                 } else {
521                     return new ExplicitInputPort(uid);
522                 }
523             case OUTPUTS:
524                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
525                 if (isImplicit[0]) {
526                     return new ImplicitOutputPort(uid);
527                 } else {
528                     return new ExplicitOutputPort(uid);
529                 }
530             case EVENT_INPUTS:
531                 return new ControlPort(uid);
532             case EVENT_OUTPUTS:
533                 return new CommandPort(uid);
534             default:
535                 return null;
536         }
537     }
538
539     /*
540      * Link management
541      */
542
543     private static BasicLink createLink(JavaController controller, long uid, Kind kind) {
544         int[] type = new int[1];
545         controller.getObjectProperty(uid, kind, ObjectProperties.KIND, type);
546
547         BasicLink link;
548         switch (type[0]) {
549             case -1:
550                 link = new CommandControlLink(uid);
551                 break;
552             case 1:
553                 link = new ExplicitLink(uid);
554                 break;
555             case 2:
556                 link = new ImplicitLink(uid);
557                 break;
558             default:
559                 return null;
560         }
561
562         /*
563          * Synchronize model information back to the JGraphX data
564          */
565         VectorOfDouble controlPoints = new VectorOfDouble();
566         controller.getObjectProperty(uid, kind, ObjectProperties.CONTROL_POINTS, controlPoints);
567         final int controlPointsLen = controlPoints.size() / 2;
568
569         ArrayList<mxPoint> points = new ArrayList<>(controlPointsLen);
570         for (int i = 0 ; i < controlPointsLen; i++) {
571             points.add(new mxPoint(controlPoints.get(i), controlPoints.get(i + controlPointsLen)));
572         }
573
574         mxGeometry geom = new mxGeometry();
575         geom.setPoints(points);
576
577         link.setGeometry(geom);
578         return link;
579     }
580
581 }