972f6be3bd3144502d45d5317fcd420889fab07b
[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) 2015-2015 - Scilab Enterprises - Clement DAVID
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 package org.scilab.modules.xcos.graph.model;
17
18 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.buildCall;
19 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.synchronousScilabExec;
20
21 import java.lang.reflect.InvocationTargetException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.EnumSet;
25 import java.util.HashMap;
26 import java.util.Optional;
27 import java.util.logging.Logger;
28
29 import javax.swing.SwingUtilities;
30
31 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
32 import org.scilab.modules.graph.utils.ScilabExported;
33 import org.scilab.modules.xcos.JavaController;
34 import org.scilab.modules.xcos.Kind;
35 import org.scilab.modules.xcos.ObjectProperties;
36 import org.scilab.modules.xcos.VectorOfDouble;
37 import org.scilab.modules.xcos.VectorOfScicosID;
38 import org.scilab.modules.xcos.Xcos;
39 import org.scilab.modules.xcos.XcosView;
40 import org.scilab.modules.xcos.block.BasicBlock;
41 import org.scilab.modules.xcos.graph.XcosDiagram;
42 import org.scilab.modules.xcos.link.BasicLink;
43 import org.scilab.modules.xcos.link.CommandControlLink;
44 import org.scilab.modules.xcos.link.ExplicitLink;
45 import org.scilab.modules.xcos.link.ImplicitLink;
46 import org.scilab.modules.xcos.port.BasicPort;
47 import org.scilab.modules.xcos.port.command.CommandPort;
48 import org.scilab.modules.xcos.port.control.ControlPort;
49 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
50 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
51 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
52 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
53 import org.scilab.modules.xcos.utils.BlockPositioning;
54
55 import com.mxgraph.model.mxCell;
56 import com.mxgraph.model.mxGeometry;
57 import com.mxgraph.util.mxPoint;
58 import java.lang.reflect.Constructor;
59 import java.util.EnumMap;
60 import org.scilab.modules.xcos.block.SplitBlock;
61 import org.scilab.modules.xcos.block.custom.RoundBlock;
62 ;
63
64 /**
65  * Ease the creation of any {@link Kind} of graphical object
66  */
67 public final class XcosCellFactory {
68
69     /** Size compatibility for user defined blocks */
70     private static final double DEFAULT_SIZE_FACTOR = 20.0;
71     private static final Logger LOG = Logger.getLogger(XcosCellFactory.class.getName());
72
73     /** Default singleton constructor */
74     private XcosCellFactory() {
75         // This class is a static singleton
76     }
77
78     /**
79      * This is a notify method mapped as a Scilab gateway used to alert with the loaded UID
80      *
81      * @param uid
82      *            the loaded UID
83      * @param kind
84      *            the kind of the created object (as an int)
85      */
86     @ScilabExported(module = "xcos", filename = "XcosCellFactory.giws.xml")
87     public static synchronized void created(long uid, int kind) {
88         lastCreated = new ScicosObjectOwner(uid, Kind.values()[kind]);
89
90     }
91
92     /**
93      * This is a notify method mapped as a Scilab gateway used to update an UID
94      *
95      * @param uid
96      *            the loaded UID
97      * @param kind
98      *            the kind of the created object (as an int)
99      */
100     @ScilabExported(module = "xcos", filename = "XcosCellFactory.giws.xml")
101     public static synchronized void update(long uid, int kind) {
102         lastCreated = new ScicosObjectOwner(uid, Kind.values()[kind]);
103
104         try {
105             SwingUtilities.invokeAndWait(() -> {
106                 if (kind == Kind.BLOCK.ordinal()) {
107                     JavaController controller = new JavaController();
108
109                     XcosDiagram graph = previousDiagram;
110                     graph.getModel().beginUpdate();
111                     try {
112                         BasicBlock block = createBlock(controller, lastCreated);
113                         previousBlock.updateBlockSettings(controller, graph, block);
114                         BlockPositioning.updateBlockView(graph, block);
115                     } finally {
116                         graph.getModel().beginUpdate();
117                     }
118                 }
119             });
120         } catch (InvocationTargetException | InterruptedException e) {
121             e.printStackTrace();
122         }
123     }
124
125     private static ScicosObjectOwner lastCreated = null;
126     private static BasicBlock previousBlock = null;
127     private static XcosDiagram previousDiagram = null;
128
129     /**
130      * Retrieve and clear the last created object (<pre>xcosCellCreated</pre> call)
131      * @return the last created object
132      */
133     public static synchronized ScicosObjectOwner getLastCreated() {
134         ScicosObjectOwner last = lastCreated;
135         lastCreated = null;
136         return last;
137     }
138
139     /**
140      * Set an object available for update (<pre>xcosUpdateBlock</pre> call)
141      * @param previous the object to track for update
142      */
143     public static synchronized void setPrevious(BasicBlock block, XcosDiagram diagram) {
144         previousBlock = block;
145         previousDiagram = diagram;
146     }
147
148     /*
149      * Diagram management
150      */
151
152     /**
153      * Allocate a Java XcosDiagram from a COSF file.
154      *
155      * This method execute the file and register a
156      *
157      * @param controller
158      *            the controller
159      * @param filename
160      *            the file to execute
161      * @return an allocated XcosDiagram
162      */
163     public static XcosDiagram createDiagramFromCOSF(final JavaController controller, String filename) {
164         XcosView view = (XcosView) JavaController.lookup_view(Xcos.class.getName());
165         JavaController.unregister_view(view);
166
167         XcosDiagram diagram;
168         try {
169             synchronousScilabExec(
170                 "function f(), " + buildCall("exec", filename, -1) + "; " + buildCall("xcosCellCreated", "scs_m".toCharArray()) + "endfunction; f();");
171
172             ScicosObjectOwner last = getLastCreated();
173             if (last.getKind() == Kind.DIAGRAM) {
174                 String[] strUID = new String[1];
175                 controller.getObjectProperty(last.getUID(), last.getKind(), ObjectProperties.UID, strUID);
176
177                 diagram = new XcosDiagram(controller, last.getUID(), last.getKind(), strUID[0]);
178                 insertChildren(controller, diagram);
179             } else {
180                 diagram = null;
181             }
182         } catch (InterpreterException e) {
183             diagram = null;
184         } finally {
185             JavaController.register_view(Xcos.class.getName(), view);
186         }
187
188         return diagram;
189     }
190
191     /**
192      * Insert the diagram MVC children into the JGraphX model
193      *
194      * @param controller
195      *            the shared controller
196      * @param diagram
197      *            the current diagram instance
198      */
199     public static void insertChildren(JavaController controller, XcosDiagram diagram) {
200         /*
201          * Retrieve the children
202          */
203         VectorOfScicosID children = new VectorOfScicosID();
204         controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
205         final int childrenLen = children.size();
206
207         /*
208          * Allocation some pre-sized stash data
209          */
210         final ArrayList<BasicLink> links = new ArrayList<>(childrenLen / 2);
211         final HashMap<Long, BasicPort> ports = new HashMap<>(childrenLen);
212
213         /*
214          * Create the XcosCell objects and store some of them for later use
215          */
216         XcosCell[] cells = new XcosCell[childrenLen];
217         for (int i = 0; i < childrenLen; i++) {
218             final long uid = children.get(i);
219             final Kind kind = controller.getKind(uid);
220
221             switch (kind) {
222                 case ANNOTATION:
223                 case BLOCK:
224                     BasicBlock b = createBlock(controller, uid, kind);
225                     cells[i] = b;
226                     BlockPositioning.updatePortsPosition(diagram, b);
227                     b.getTypedChildrenIndexes(BasicPort.class).stream()
228                     .map(index -> b.getChildAt(index))
229                     .filter(c -> c instanceof BasicPort)
230                     .map(c -> (BasicPort) c)
231                     .forEach(c -> ports.put(c.getUID(), c));
232                     break;
233                 case LINK:
234                     BasicLink l = createLink(controller, uid, kind);
235                     cells[i] = l;
236                     links.add(l);
237                     break;
238                 default:
239                     break;
240             }
241         }
242
243         /*
244          * Relink the links on the XcosCell part
245          */
246         for (BasicLink l : links) {
247             long[] src = new long[1];
248             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.SOURCE_PORT, src);
249
250             long[] dest = new long[1];
251             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.DESTINATION_PORT, dest);
252
253             BasicPort srcPort = ports.get(src[0]);
254             if (srcPort != null) {
255                 l.setSource(srcPort);
256             } else {
257                 LOG.severe("Unable to connect link " + l.getId() + " : invalid source " + src[0]);
258             }
259
260             BasicPort destPort = ports.get(dest[0]);
261             if (destPort != null) {
262                 l.setTarget(destPort);
263             } else {
264                 LOG.severe("Unable to connect link " + l.getId() + " : invalid target " + dest[0]);
265             }
266         }
267
268         // re-add the children cells without duplicating them
269         children.clear();
270         controller.setObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
271
272         // add all the children using the diagram modification tracking features
273         diagram.addCells(cells);
274
275         // each cell has been referenced twice (CHILDREN insert and addCells), derefence them all by one
276         Arrays.stream(cells).forEach(c -> controller.deleteObject(c.getUID()));
277     }
278
279     /*
280      * Block and Annotation management
281      */
282
283     /**
284      * Instantiate a new block with the specified interface function.
285      *
286      * @param func
287      *            the interface function
288      * @return A new instance of a block.
289      */
290     public static BasicBlock createBlock(BlockInterFunction func) throws InterpreterException {
291         return createBlock(func, func.name());
292     }
293
294     /**
295      * Instantiate a new block with the specified UID value and interface function
296      *
297      * @param uid
298      *            The associated UID value
299      * @param interfaceFunction
300      *            the interface function
301      * @return A new instance of a block.
302      */
303     public static BasicBlock createBlock(String interfaceFunction) throws InterpreterException {
304         Optional<BlockInterFunction> func = EnumSet.allOf(BlockInterFunction.class).stream().filter(f -> f.name().equals(interfaceFunction)).findFirst();
305
306         final BasicBlock block;
307         if (func.isPresent()) {
308             block = createBlock(func.get());
309         } else {
310             block = createBlock(BlockInterFunction.BASIC_BLOCK, interfaceFunction);
311         }
312         block.setStyle(interfaceFunction);
313
314         return block;
315     }
316
317     private static BasicBlock createBlock(BlockInterFunction func, String interfaceFunction) throws InterpreterException {
318         return createBlock(new JavaController(), func, interfaceFunction);
319     }
320
321     private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction) throws InterpreterException {
322         BasicBlock block;
323         ScicosObjectOwner last;
324
325         if (BlockInterFunction.BASIC_BLOCK.name().equals(interfaceFunction)) {
326             // deliver all the MVC speed for the casual case
327             last = new ScicosObjectOwner(controller.createObject(Kind.BLOCK), Kind.BLOCK);
328         } else {
329             // allocate an empty block that will be filled later
330             synchronousScilabExec("xcosCellCreated(" + interfaceFunction + "(\"define\")); ");
331             last = getLastCreated();
332         }
333
334         // defensive programming
335         if (last == null) {
336             throw new InterpreterException("XcosCellFactory#createBlock : unable to allocate " + interfaceFunction);
337         }
338
339         if (EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(last.getKind())) {
340             block = createBlock(controller, func, interfaceFunction, last.getUID(), last.getKind());
341         } else {
342             block = null;
343         }
344
345         return block;
346     }
347
348     public static BasicBlock createBlock(final JavaController controller, long uid, Kind kind) {
349         String[] interfaceFunction = new String[1];
350         if (kind == Kind.BLOCK) {
351             controller.getObjectProperty(uid, kind, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
352         } else { // ANNOTATION
353             interfaceFunction[0] = "TEXT_f";
354         }
355
356         final BlockInterFunction func = lookForInterfunction(interfaceFunction[0]);
357
358         return createBlock(controller, func, interfaceFunction[0], uid, kind);
359     }
360
361     public static BlockInterFunction lookForInterfunction(String interfaceFunction) {
362         Optional<BlockInterFunction> optFunc = EnumSet.allOf(BlockInterFunction.class).stream().filter(f -> f.name().equals(interfaceFunction)).findFirst();
363
364         final BlockInterFunction func;
365         if (optFunc.isPresent()) {
366             func = optFunc.get();
367         } else {
368             func = BlockInterFunction.BASIC_BLOCK;
369         }
370         return func;
371     }
372
373     /**
374      * Instantiate a new block with the specified interface function and uid.
375      *
376      * @param controller
377      *            the Java controller to use
378      * @param func
379      *            the interface function as an enum
380      * @param interfaceFunction
381      *            the interface function name
382      * @param uid
383      *            the allocated uid
384      * @return A new instance of a block.
385      */
386     public static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction, long uid, Kind kind) {
387
388         final EnumMap<ObjectProperties, Integer> properties = new EnumMap<>(ObjectProperties.class);
389         properties.put(ObjectProperties.INPUTS, 0);
390         properties.put(ObjectProperties.OUTPUTS, 0);
391         properties.put(ObjectProperties.EVENT_INPUTS, 0);
392         properties.put(ObjectProperties.EVENT_OUTPUTS, 0);
393
394
395         /*
396          * Retrieve the JGraphX data before cell creation
397          */
398         String[] strUID = new String[1];
399         controller.getObjectProperty(uid, kind, ObjectProperties.UID, strUID);
400
401         String[] style = new String[1];
402         controller.getObjectProperty(uid, kind, ObjectProperties.STYLE, style);
403         if (style[0].isEmpty()) {
404             style[0] = interfaceFunction;
405         }
406
407         String value;
408         String[] description = new String[] { "" };
409         controller.getObjectProperty(uid, kind, ObjectProperties.DESCRIPTION, description);
410         value = description[0];
411
412         VectorOfDouble geom = new VectorOfDouble(4);
413         controller.getObjectProperty(uid, kind, ObjectProperties.GEOMETRY, geom);
414
415         double x = geom.get(0);
416         double y = geom.get(1);
417         double w = geom.get(2);
418         double h = geom.get(3);
419         mxGeometry geometry = new mxGeometry(x, y, w, h);
420
421         /*
422          * Instanciate the block
423          */
424         BasicBlock block = null;
425         try {
426             Constructor<? extends BasicBlock> cstr = func.getKlass().getConstructor(JavaController.class, Long.TYPE, Kind.class, Object.class, mxGeometry.class, String.class, String.class);
427             block = cstr.newInstance(controller, uid, kind, value, geometry, style[0], strUID[0]);
428         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
429                      | SecurityException e) {
430             // Something goes wrong, print it.
431             e.printStackTrace();
432             return block;
433         }
434
435         /*
436          * Synchronize model information back to the JGraphX data
437          *
438          * Annotations have no inputs/outputs
439          */
440
441         if (block.getKind() == Kind.BLOCK) {
442             insertPortChildren(controller, properties, block);
443         }
444
445
446         /*
447          * Compatibility to ease user definition :
448          *   * split are not updated as they have an hard-coded fixed-size
449          *   * round blocks : ports globally layout around the block
450          *   * generic case : layout the ports per kind per block-side
451          */
452         boolean convertGeometry;
453         if (block instanceof SplitBlock) {
454             convertGeometry = false;
455         } else if (block instanceof RoundBlock) {
456             int numberOfPorts = properties.get(ObjectProperties.INPUTS) + 1 +
457                                 properties.get(ObjectProperties.OUTPUTS) + 1 +
458                                 properties.get(ObjectProperties.EVENT_INPUTS) + 1 +
459                                 properties.get(ObjectProperties.EVENT_OUTPUTS) + 1;
460             convertGeometry = (2 * w + 2 * h) < (numberOfPorts * BasicPort.DEFAULT_PORTSIZE);
461         } else {
462             double minimalHeight = Math.max((properties.get(ObjectProperties.INPUTS) + 1) * BasicPort.DEFAULT_PORTSIZE, (properties.get(ObjectProperties.OUTPUTS) + 1) * BasicPort.DEFAULT_PORTSIZE);
463             double minimalWidth = Math.max((properties.get(ObjectProperties.EVENT_INPUTS) + 1) * BasicPort.DEFAULT_PORTSIZE, (properties.get(ObjectProperties.EVENT_OUTPUTS) + 1) * BasicPort.DEFAULT_PORTSIZE);
464
465             convertGeometry = h < minimalHeight | w < minimalWidth;
466             convertGeometry |= h * w < minimalHeight * minimalWidth;
467         }
468
469         if (convertGeometry) {
470             w = w * DEFAULT_SIZE_FACTOR;
471             h = h * DEFAULT_SIZE_FACTOR;
472
473             /*
474              * Invert the y-axis value and translate it.
475              */
476             y = -y - h;
477
478             block.setGeometry(new mxGeometry(x, y, w, h));
479         }
480
481         return block;
482     }
483
484     /**
485      * Instantiate a new block for an already created MVC object
486      *
487      * @param lastCreated the owned MVC object
488      * @return a block or null
489      */
490     public static BasicBlock createBlock(final JavaController controller, ScicosObjectOwner lastCreated) {
491         // pre-condition
492         if (lastCreated.getKind() != Kind.ANNOTATION && lastCreated.getKind() != Kind.BLOCK) {
493             return null;
494         }
495
496         String[] interfaceFunction = new String[1];
497         BlockInterFunction func = lookForInterfunction(interfaceFunction[0]);
498
499         return createBlock(controller, func, interfaceFunction[0], lastCreated.getUID(), lastCreated.getKind());
500     }
501
502     /*
503      * Port management
504      */
505
506     /**
507      * Helper used to create port children on a parent block.
508      *
509      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
510      *
511      * @param controller
512      *            is the shared controller instance
513      * @param properties
514      *            specify the kind of port to insert and should be some of :
515      *            <UL>
516      *            <LI>{@link ObjectProperties#INPUTS}
517      *            <LI>{@link ObjectProperties#OUTPUTS}
518      *            <LI>{@link ObjectProperties#EVENT_INPUTS}
519      *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
520      *            </UL>
521      *            This method will fill the value with the number of added ports
522      * @param parent
523      *            is the parent {@link mxCell} to modify
524      */
525     private static void insertPortChildren(final JavaController controller, final EnumMap<ObjectProperties, Integer> properties, final XcosCell parent) {
526         for (ObjectProperties property : properties.keySet()) {
527             properties.put(property, insertPortChildren(controller, property, parent));
528         }
529     }
530
531     /**
532      * Helper used to create port children on a parent block.
533      *
534      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
535      *
536      * @param controller
537      *            is the shared controller instance
538      * @param property
539      *            specify the kind of port to insert and should be one of :
540      *            <UL>
541      *            <LI>{@link ObjectProperties#INPUTS}
542      *            <LI>{@link ObjectProperties#OUTPUTS}
543      *            <LI>{@link ObjectProperties#EVENT_INPUTS}
544      *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
545      *            </UL>
546      * @param parent
547      *            is the parent {@link mxCell} to modify
548      * @return the number of inserted children
549      */
550     private static int insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
551         VectorOfScicosID modelChildren = new VectorOfScicosID();
552         controller.getObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
553
554         XcosCell[] children = new XcosCell[modelChildren.size()];
555         for (int i = 0; i < children.length; i++) {
556             XcosCell child = createPort(controller, modelChildren.get(i), property);
557             children[i] = child;
558         }
559
560         modelChildren.clear();
561         controller.setObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
562
563         Arrays.stream(children).forEach(c -> {
564             parent.insert(c);
565             controller.deleteObject(c.getUID());
566         });
567
568         return children.length;
569     }
570
571     /**
572      * Create a port for a specific uid
573      *
574      * @param controller
575      *            is the shared controller instance
576      * @param uid
577      *            represent the allocated UID on the MVC
578      * @param property
579      *            specify the kind of port to create and should be one of :
580      *            <UL>
581      *            <LI>{@link ObjectProperties#INPUTS}
582      *            <LI>{@link ObjectProperties#OUTPUTS}
583      *            <LI>{@link ObjectProperties#EVENT_INPUTS}
584      *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
585      * @return a newly allocated port
586      */
587     private static final BasicPort createPort(final JavaController controller, long uid, final ObjectProperties property) {
588         BasicPort port;
589         boolean[] isImplicit = { false };
590
591         String[] strUID = new String[] { "" };
592         controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.UID, strUID);
593
594         String[] style = new String[] { "" };
595         controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.STYLE, style);
596
597         String[] value = new String[] { "" };
598         controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.LABEL, value);
599
600         switch (property) {
601             case INPUTS:
602                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
603                 if (isImplicit[0]) {
604                     port = new ImplicitInputPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
605                 } else {
606                     port = new ExplicitInputPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
607                 }
608                 break;
609             case OUTPUTS:
610                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
611                 if (isImplicit[0]) {
612                     port = new ImplicitOutputPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
613                 } else {
614                     port = new ExplicitOutputPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
615                 }
616                 break;
617             case EVENT_INPUTS:
618                 port = new ControlPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
619                 break;
620             case EVENT_OUTPUTS:
621                 port = new CommandPort(controller, uid, Kind.PORT, value[0], style[0], strUID[0]);
622                 break;
623             default:
624                 return null;
625         }
626
627         /*
628          * Setup JGraphX properties
629          */
630
631         return port;
632     }
633
634     /*
635      * Link management
636      */
637
638     private static BasicLink createLink(JavaController controller, long uid, Kind kind) {
639         int[] type = new int[1];
640         controller.getObjectProperty(uid, kind, ObjectProperties.KIND, type);
641
642         /*
643          * Synchronize model information back to the JGraphX data
644          */
645
646         String[] value = new String[] { "" };
647         controller.getObjectProperty(uid, kind, ObjectProperties.LABEL, value);
648
649         String[] style = new String[] { "" };
650         controller.getObjectProperty(uid, kind, ObjectProperties.STYLE, style);
651
652         String[] strUID = new String[] { "" };
653         controller.getObjectProperty(uid, kind, ObjectProperties.UID, strUID);
654
655         VectorOfDouble controlPoints = new VectorOfDouble();
656         controller.getObjectProperty(uid, kind, ObjectProperties.CONTROL_POINTS, controlPoints);
657         final int pointsLen = controlPoints.size() / 2;
658
659         mxGeometry geom = new mxGeometry();
660
661         // as the link is supposed to be connected and accordingly to the JGraphX rules : do not add the origin and destination point
662         ArrayList<mxPoint> points = new ArrayList<>();
663         int i = 0;
664         // ignore origin
665         i++;
666         // loop for points
667         for (; i < pointsLen - 1; i++) {
668             points.add(new mxPoint(controlPoints.get(2 * i), controlPoints.get(2 * i + 1)));
669         }
670         // ignore destination
671         i++;
672
673         geom.setPoints(points);
674
675         /*
676          * Allocate the link
677          */
678         BasicLink link;
679         switch (type[0]) {
680             case -1:
681                 link = new CommandControlLink(controller, uid, kind, value[0], geom, style[0], strUID[0]);
682                 break;
683             case 1:
684                 link = new ExplicitLink(controller, uid, kind, value[0], geom, style[0], strUID[0]);
685                 break;
686             case 2:
687                 link = new ImplicitLink(controller, uid, kind, value[0], geom, style[0], strUID[0]);
688                 break;
689             default:
690                 return null;
691         }
692
693         return link;
694     }
695 }