Xcos load: do not duplicate children
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / model / XcosCellFactory.java
index 35ab5e9..a8ce59a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
- * Copyright (C) 2009 - DIGITEO - Clement DAVID
+ * Copyright (C) 2015-2015 - Scilab Enterprises - Clement DAVID
  *
  * This file must be used under the terms of the CeCILL.
  * This source file is licensed as described in the file COPYING, which
@@ -18,10 +18,10 @@ import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManag
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.Optional;
+import java.util.logging.Logger;
 
 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
 import org.scilab.modules.graph.utils.ScilabExported;
@@ -32,28 +32,12 @@ import org.scilab.modules.xcos.VectorOfDouble;
 import org.scilab.modules.xcos.VectorOfScicosID;
 import org.scilab.modules.xcos.Xcos;
 import org.scilab.modules.xcos.XcosView;
-import org.scilab.modules.xcos.block.AfficheBlock;
 import org.scilab.modules.xcos.block.BasicBlock;
-import org.scilab.modules.xcos.block.SplitBlock;
-import org.scilab.modules.xcos.block.SuperBlock;
-import org.scilab.modules.xcos.block.TextBlock;
-import org.scilab.modules.xcos.block.io.EventInBlock;
-import org.scilab.modules.xcos.block.io.EventOutBlock;
-import org.scilab.modules.xcos.block.io.ExplicitInBlock;
-import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
-import org.scilab.modules.xcos.block.io.ImplicitInBlock;
-import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
-import org.scilab.modules.xcos.block.positionning.BigSom;
-import org.scilab.modules.xcos.block.positionning.GroundBlock;
-import org.scilab.modules.xcos.block.positionning.Product;
-import org.scilab.modules.xcos.block.positionning.RoundBlock;
-import org.scilab.modules.xcos.block.positionning.Summation;
-import org.scilab.modules.xcos.block.positionning.VoltageSensorBlock;
 import org.scilab.modules.xcos.graph.XcosDiagram;
 import org.scilab.modules.xcos.link.BasicLink;
-import org.scilab.modules.xcos.link.commandcontrol.CommandControlLink;
-import org.scilab.modules.xcos.link.explicit.ExplicitLink;
-import org.scilab.modules.xcos.link.implicit.ImplicitLink;
+import org.scilab.modules.xcos.link.CommandControlLink;
+import org.scilab.modules.xcos.link.ExplicitLink;
+import org.scilab.modules.xcos.link.ImplicitLink;
 import org.scilab.modules.xcos.port.BasicPort;
 import org.scilab.modules.xcos.port.command.CommandPort;
 import org.scilab.modules.xcos.port.control.ControlPort;
@@ -65,123 +49,20 @@ import org.scilab.modules.xcos.utils.BlockPositioning;
 
 import com.mxgraph.model.mxCell;
 import com.mxgraph.model.mxGeometry;
-import com.mxgraph.model.mxICell;
 import com.mxgraph.util.mxPoint;
+import java.util.EnumMap;
+import org.scilab.modules.xcos.block.SplitBlock;
+import org.scilab.modules.xcos.block.positionning.RoundBlock;
+;
 
 /**
  * Ease the creation of any {@link Kind} of graphical object
  */
 public final class XcosCellFactory {
 
-    // DAC: As this is the constructor for all the block classes, this class is
-    // very coupled with *Block classes
-    // CSOFF: ClassDataAbstractionCoupling
-    /**
-     * List the specific block interface function name. <BR>
-     * <BR>
-     * <EM>Specific instance must be registered before generic ones in order
-     * to serialized all the non-default values.</EM>
-     */
-    public static enum BlockInterFunction {
-        /** @see TextBlock */
-        TEXT_f(TextBlock.class),
-        /** @see SuperBlock */
-        DSUPER(SuperBlock.class),
-        /** @see SuperBlock */
-        SUPER_f(SuperBlock.class),
-        /** @see AfficheBlock */
-        AFFICH_m(AfficheBlock.class),
-        /** @see AfficheBlock */
-        AFFICH_f(AfficheBlock.class),
-        /** @see ExplicitInBlock */
-        IN_f(ExplicitInBlock.class),
-        /** @see ExplicitOutBlock */
-        OUT_f(ExplicitOutBlock.class),
-        /** @see ImplicitInBlock */
-        INIMPL_f(ImplicitInBlock.class),
-        /** @see ImplicitOutBlock */
-        OUTIMPL_f(ImplicitOutBlock.class),
-        /** @see EventInBlock */
-        CLKINV_f(EventInBlock.class),
-        /** @see EventOutBlock */
-        CLKOUTV_f(EventOutBlock.class),
-        /** @see EventOutBlock */
-        CLKOUT_f(EventOutBlock.class),
-        /** @see SplitBlock */
-        SPLIT_f(SplitBlock.class),
-        /** @see SplitBlock */
-        IMPSPLIT_f(SplitBlock.class),
-        /** @see SplitBlock */
-        CLKSPLIT_f(SplitBlock.class),
-        /** @see GroundBlock */
-        Ground(GroundBlock.class),
-        /** @see VoltageSensorBlock */
-        VoltageSensor(VoltageSensorBlock.class),
-        /** @see RoundBlock */
-        SUM_f(RoundBlock.class),
-        /** @see RoundBlock */
-        PROD_f(RoundBlock.class),
-        /** @see RoundBlock */
-        CLKSOM_f(RoundBlock.class),
-        /** @see RoundBlock */
-        CLKSOMV_f(RoundBlock.class),
-        /** @see BigSom */
-        BIGSOM_f(BigSom.class),
-        /** @see Summation */
-        SUMMATION(Summation.class),
-        /** @see Product */
-        PRODUCT(Product.class),
-        /** @see BasicBlock */
-        BASIC_BLOCK(BasicBlock.class);
-
-        private final Class<? extends BasicBlock> klass;
-
-        /**
-         * Default constructor
-         *
-         * @param block
-         *            The reference instance
-         */
-        private BlockInterFunction(Class<? extends BasicBlock> klass) {
-            this.klass = klass;
-        }
-
-        /**
-         * @return the class to instantiate
-         */
-        public Class<? extends BasicBlock> getKlass() {
-            return klass;
-        }
-    }
-
-    private static class ScicosObjectOwner {
-        final long uid;
-        final Kind kind;
-
-        public ScicosObjectOwner(long uid, Kind kind) {
-            this.uid = uid;
-            this.kind = kind;
-
-            JavaController controller = new JavaController();
-            controller.referenceObject(uid);
-        }
-
-        public long getUID() {
-            return uid;
-        }
-
-        public Kind getKind() {
-            return kind;
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            JavaController controller = new JavaController();
-            controller.deleteObject(uid);
-        }
-    }
-
-    // CSON: ClassDataAbstractionCoupling
+    /** Size compatibility for user defined blocks */
+    private static final double DEFAULT_SIZE_FACTOR = 20.0;
+    private static final Logger LOG = Logger.getLogger(XcosCellFactory.class.getName());
 
     /** Default singleton constructor */
     private XcosCellFactory() {
@@ -190,16 +71,30 @@ public final class XcosCellFactory {
 
     /**
      * This is a notify method mapped as a Scilab gateway used to alert with the loaded UID
-     * @param uid the loaded UID
-     * @param kind the kind of the created object (as an int)
+     *
+     * @param uid
+     *            the loaded UID
+     * @param kind
+     *            the kind of the created object (as an int)
      */
     @ScilabExported(module = "xcos", filename = "XcosCellFactory.giws.xml")
-    public static void created(long uid, int kind) {
+    public static synchronized void created(long uid, int kind) {
         lastCreated = new ScicosObjectOwner(uid, Kind.values()[kind]);
 
     }
+
     private static ScicosObjectOwner lastCreated = null;
 
+    /**
+     * Retrieve and clear the last created object (<pre>xcosCellCreated</pre> call)
+     * @return the last created object
+     */
+    public static synchronized ScicosObjectOwner getLastCreated() {
+        ScicosObjectOwner last = lastCreated;
+        lastCreated = null;
+        return last;
+    }
+
     /*
      * Diagram management
      */
@@ -209,8 +104,10 @@ public final class XcosCellFactory {
      *
      * This method execute the file and register a
      *
-     * @param controller the controller
-     * @param filename the file to execute
+     * @param controller
+     *            the controller
+     * @param filename
+     *            the file to execute
      * @return an allocated XcosDiagram
      */
     public static XcosDiagram createDiagramFromCOSF(final JavaController controller, String filename) {
@@ -220,15 +117,12 @@ public final class XcosCellFactory {
         XcosDiagram diagram;
         try {
             synchronousScilabExec(
-                "function f(), " +
-                buildCall("exec", filename, -1) +
-                buildCall("xcosCellCreated", "scs_m".toCharArray()) +
-                "endfunction; f();");
+                "function f(), " + buildCall("exec", filename, -1) + "; " + buildCall("xcosCellCreated", "scs_m".toCharArray()) + "endfunction; f();");
 
-            if (lastCreated.getKind() == Kind.DIAGRAM) {
-                diagram = new XcosDiagram(lastCreated.getUID(), lastCreated.getKind());
+            ScicosObjectOwner last = getLastCreated();
+            if (last.getKind() == Kind.DIAGRAM) {
+                diagram = new XcosDiagram(last.getUID(), last.getKind());
                 insertChildren(controller, diagram);
-                lastCreated = null;
             } else {
                 diagram = null;
             }
@@ -243,10 +137,16 @@ public final class XcosCellFactory {
 
     /**
      * Insert the diagram MVC children into the JGraphX model
-     * @param controller the shared controller
-     * @param diagram the current diagram instance
+     *
+     * @param controller
+     *            the shared controller
+     * @param diagram
+     *            the current diagram instance
      */
     public static void insertChildren(JavaController controller, XcosDiagram diagram) {
+        /*
+         * Retrieve the children
+         */
         VectorOfScicosID children = new VectorOfScicosID();
         controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
         final int childrenLen = children.size();
@@ -255,7 +155,7 @@ public final class XcosCellFactory {
          * Allocation some pre-sized stash data
          */
         final ArrayList<BasicLink> links = new ArrayList<>(childrenLen / 2);
-        final ArrayList<BasicPort> ports = new ArrayList<>(childrenLen);
+        final HashMap<Long, BasicPort> ports = new HashMap<>(childrenLen);
 
         /*
          * Create the XcosCell objects and store some of them for later use
@@ -274,7 +174,8 @@ public final class XcosCellFactory {
                     b.getTypedChildrenIndexes(BasicPort.class).stream()
                     .map(index -> b.getChildAt(index))
                     .filter(c -> c instanceof BasicPort)
-                    .forEach( c -> ports.add((BasicPort) c));
+                    .map(c -> (BasicPort) c)
+                    .forEach(c -> ports.put(c.getUID(), c));
                     break;
                 case LINK:
                     BasicLink l = createLink(controller, uid, kind);
@@ -289,8 +190,6 @@ public final class XcosCellFactory {
         /*
          * Relink the links on the XcosCell part
          */
-        Comparator<XcosCell> compare = (c1, c2) -> (int) (c1.getUID() - c2.getUID());
-        Collections.sort(ports, compare);
         for (BasicLink l : links) {
             long[] src = new long[1];
             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.SOURCE_PORT, src);
@@ -298,21 +197,30 @@ public final class XcosCellFactory {
             long[] dest = new long[1];
             controller.getObjectProperty(l.getUID(), l.getKind(), ObjectProperties.DESTINATION_PORT, dest);
 
-            int srcIndex = Collections.binarySearch(ports, new XcosCell(src[0], Kind.PORT), compare);
-            if (srcIndex > 0) {
-                l.setSource(ports.get(srcIndex));
+            BasicPort srcPort = ports.get(src[0]);
+            if (srcPort != null) {
+                l.setSource(srcPort);
+            } else {
+                LOG.severe("Unable to connect link " + l.getId() + " : invalid source " + src[0]);
             }
 
-            int destIndex = Collections.binarySearch(ports, new XcosCell(dest[0], Kind.PORT), compare);
-            if (destIndex > 0) {
-                l.setTarget(ports.get(destIndex));
+            BasicPort destPort = ports.get(dest[0]);
+            if (destPort != null) {
+                l.setTarget(destPort);
+            } else {
+                LOG.severe("Unable to connect link " + l.getId() + " : invalid target " + dest[0]);
             }
         }
 
-        diagram.addCells(cells);
-    }
+        // re-add the children cells without duplicating them
+        children.clear();
+        controller.setObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
 
+        diagram.addCells(cells);
 
+        // each cell has been referenced twice (CHILDREN insert and addCells), derefence them all by one
+        Arrays.stream(cells).forEach(c -> controller.deleteObject(c.getUID()));
+    }
 
     /*
      * Block and Annotation management
@@ -334,13 +242,12 @@ public final class XcosCellFactory {
      *
      * @param uid
      *            The associated UID value
-     * @param interfaceFunction the interface function
+     * @param interfaceFunction
+     *            the interface function
      * @return A new instance of a block.
      */
     public static BasicBlock createBlock(String interfaceFunction) {
-        Optional<BlockInterFunction> func = EnumSet.allOf(BlockInterFunction.class).stream()
-                                            .filter(f -> f.name().equals(interfaceFunction))
-                                            .findFirst();
+        Optional<BlockInterFunction> func = EnumSet.allOf(BlockInterFunction.class).stream().filter(f -> f.name().equals(interfaceFunction)).findFirst();
 
         final BasicBlock block;
         if (func.isPresent()) {
@@ -360,11 +267,25 @@ public final class XcosCellFactory {
     private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction) {
         BasicBlock block;
         try {
-            synchronousScilabExec("xcosCellCreated(" + interfaceFunction + "(\"define\")); ");
+            ScicosObjectOwner last;
+
+            if (BlockInterFunction.BASIC_BLOCK.name().equals(interfaceFunction)) {
+                // deliver all the MVC speed for the casual case
+                last = new ScicosObjectOwner(controller.createObject(Kind.BLOCK), Kind.BLOCK);
+            } else {
+                // allocate an empty block that will be filled later
+                synchronousScilabExec("xcosCellCreated(" + interfaceFunction + "(\"define\")); ");
+                last = getLastCreated();
+            }
+
+            // defensive programming
+            if (last == null) {
+                System.err.println("XcosCellFactory#createBlock : unable to allocate " + interfaceFunction);
+                return null;
+            }
 
-            if (EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(lastCreated.getKind())) {
-                block = createBlock(controller, func, interfaceFunction, lastCreated.getUID());
-                lastCreated = null;
+            if (EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(last.getKind())) {
+                block = createBlock(controller, func, interfaceFunction, last.getUID());
             } else {
                 block = null;
             }
@@ -377,15 +298,19 @@ public final class XcosCellFactory {
 
     private static BasicBlock createBlock(final JavaController controller, long uid, Kind kind) {
         String[] interfaceFunction = new String[1];
-        controller.getObjectProperty(uid, kind, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
+        if (kind == Kind.BLOCK) {
+            controller.getObjectProperty(uid, kind, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
+        } else { // ANNOTATION
+            interfaceFunction[0] = "TEXT_f";
+        }
+
+        final BlockInterFunction func = lookForInterfunction(interfaceFunction[0]);
 
-        return createBlock(controller, interfaceFunction[0], uid);
+        return createBlock(controller, func, interfaceFunction[0], uid);
     }
 
-    private static BasicBlock createBlock(final JavaController controller, String interfaceFunction, long uid) {
-        Optional<BlockInterFunction> optFunc = EnumSet.allOf(BlockInterFunction.class).stream()
-                                               .filter(f -> f.name().equals(interfaceFunction))
-                                               .findFirst();
+    public static BlockInterFunction lookForInterfunction(String interfaceFunction) {
+        Optional<BlockInterFunction> optFunc = EnumSet.allOf(BlockInterFunction.class).stream().filter(f -> f.name().equals(interfaceFunction)).findFirst();
 
         final BlockInterFunction func;
         if (optFunc.isPresent()) {
@@ -393,24 +318,28 @@ public final class XcosCellFactory {
         } else {
             func = BlockInterFunction.BASIC_BLOCK;
         }
-
-        return createBlock(controller, func, interfaceFunction, uid);
+        return func;
     }
 
     /**
      * Instantiate a new block with the specified interface function and uid.
      *
-     * @param controller the Java controller to use
-     * @param func the interface function as an enum
-     * @param interfaceFunction the interface function name
-     * @param uid the allocated uid
+     * @param controller
+     *            the Java controller to use
+     * @param func
+     *            the interface function as an enum
+     * @param interfaceFunction
+     *            the interface function name
+     * @param uid
+     *            the allocated uid
      * @return A new instance of a block.
      */
-    private static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction, long uid) {
+    public static BasicBlock createBlock(final JavaController controller, BlockInterFunction func, String interfaceFunction, long uid) {
         BasicBlock block = null;
         try {
             block = func.getKlass().getConstructor(Long.TYPE).newInstance(uid);
-        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException        | NoSuchMethodException | SecurityException e) {
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
+                     | SecurityException e) {
             // Something goes wrong, print it.
             e.printStackTrace();
             return block;
@@ -418,54 +347,131 @@ public final class XcosCellFactory {
 
         /*
          * Synchronize model information back to the JGraphX data
+         *
+         * Annotations have no inputs/outputs
          */
-        insertPortChildren(controller, block);
+        EnumMap<ObjectProperties, Integer> properties = new EnumMap<>(ObjectProperties.class);
+        properties.put(ObjectProperties.INPUTS, 0);
+        properties.put(ObjectProperties.OUTPUTS, 0);
+        properties.put(ObjectProperties.EVENT_INPUTS, 0);
+        properties.put(ObjectProperties.EVENT_OUTPUTS, 0);
+        if (block.getKind() == Kind.BLOCK) {
+            insertPortChildren(controller, properties, block);
+        }
 
-        block.setStyle(interfaceFunction);
+        String[] strUID = new String[1];
+        controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.UID, strUID);
+
+        String[] style = new String[1];
+        controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.STYLE, style);
+        if (style[0].isEmpty()) {
+            block.setStyle(interfaceFunction);
+        } else {
+            block.setStyle(style[0]);
+        }
+
+        String value;
+        if (block.getKind() == Kind.ANNOTATION) {
+            String[] description = new String[1];
+            controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.DESCRIPTION, description);
+            value = description[0];
+        } else { // BLOCK
+            String[] label = new String[1];
+            controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.LABEL, label);
+            value = label[0];
+        }
+        block.setValue(value);
 
         VectorOfDouble geom = new VectorOfDouble(4);
         controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.GEOMETRY, geom);
-        block.setGeometry(new mxGeometry(geom.get(0), geom.get(1), geom.get(2), geom.get(3)));
 
-        // FIXME find a way to reuse the Scicos compat handler from org.scilab.modules.xcos.io.scicos
+        double x = geom.get(0);
+        double y = geom.get(1);
+        double w = geom.get(2);
+        double h = geom.get(3);
+
+        /*
+         * Compatibility to ease user definition :
+         *   * split are not updated as they have an hard-coded fixed-size
+         *   * round blocks : ports globally layout around the block
+         *   * generic case : layout the ports per kind per block-side
+         */
+        boolean convertGeometry;
+        if ((block instanceof SplitBlock)) {
+            convertGeometry = false;
+        } else if (block instanceof RoundBlock) {
+            int numberOfPorts = properties.get(ObjectProperties.INPUTS) +
+                                properties.get(ObjectProperties.OUTPUTS) +
+                                properties.get(ObjectProperties.EVENT_INPUTS) +
+                                properties.get(ObjectProperties.EVENT_OUTPUTS);
+            convertGeometry = (2 * w + 2 * h) < (numberOfPorts * BasicPort.DEFAULT_PORTSIZE);
+        } else {
+            convertGeometry = w < (properties.get(ObjectProperties.INPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              w < (properties.get(ObjectProperties.OUTPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              h < (properties.get(ObjectProperties.EVENT_INPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              h < (properties.get(ObjectProperties.EVENT_OUTPUTS) * BasicPort.DEFAULT_PORTSIZE);
+        }
+
+        if (convertGeometry) {
+            w = w * DEFAULT_SIZE_FACTOR;
+            h = h * DEFAULT_SIZE_FACTOR;
+
+            /*
+             * Invert the y-axis value and translate it.
+             */
+            y = -y - h;
+        }
+
+
+        block.setGeometry(new mxGeometry(x, y, w, h));
 
         return block;
     }
 
-    /*
-     * Port management
-     */
-
     /**
-     * Helper used to create port children on a parent block.
-     *
-     * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
+     * Instantiate a new block for an already created MVC object
      *
-     * @param controller is the shared controller instance
-     * @param parent is the parent {@link mxCell} to modify
+     * @param lastCreated the owned MVC object
+     * @return a block or null
      */
-    private static void insertPortChildren(final JavaController controller, final XcosCell parent) {
-        final EnumSet<ObjectProperties> properties = EnumSet.of(ObjectProperties.INPUTS, ObjectProperties.OUTPUTS, ObjectProperties.EVENT_INPUTS, ObjectProperties.EVENT_OUTPUTS);
-        insertPortChildren(controller, properties, parent);
+    public static BasicBlock createBlock(final JavaController controller, ScicosObjectOwner lastCreated) {
+        // pre-condition
+        if (lastCreated.getKind() != Kind.ANNOTATION && lastCreated.getKind() != Kind.BLOCK) {
+            return null;
+        }
+
+        String[] interfaceFunction = new String[1];
+        BlockInterFunction func = lookForInterfunction(interfaceFunction[0]);
+
+        return createBlock(controller, func, interfaceFunction[0], lastCreated.getUID());
     }
 
+    /*
+     * Port management
+     */
 
     /**
      * Helper used to create port children on a parent block.
      *
      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
      *
-     * @param controller is the shared controller instance
-     * @param properties specify the kind of port to insert and should be some of : <UL>
-     *        <LI>{@link ObjectProperties#INPUTS}
-     *        <LI>{@link ObjectProperties#OUTPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_INPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
-     * @param parent is the parent {@link mxCell} to modify
+     * @param controller
+     *            is the shared controller instance
+     * @param properties
+     *            specify the kind of port to insert and should be some of :
+     *            <UL>
+     *            <LI>{@link ObjectProperties#INPUTS}
+     *            <LI>{@link ObjectProperties#OUTPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_INPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
+     *            </UL>
+     *            This method will fill the value with the number of added ports
+     * @param parent
+     *            is the parent {@link mxCell} to modify
      */
-    private static void insertPortChildren(final JavaController controller, final EnumSet<ObjectProperties> properties, final XcosCell parent) {
-        for (ObjectProperties property : properties) {
-            insertPortChildren(controller, property, parent);
+    private static void insertPortChildren(final JavaController controller, final EnumMap<ObjectProperties, Integer> properties, final XcosCell parent) {
+        for (ObjectProperties property : properties.keySet()) {
+            properties.put(property, insertPortChildren(controller, property, parent));
         }
     }
 
@@ -474,66 +480,98 @@ public final class XcosCellFactory {
      *
      * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
      *
-     * @param controller is the shared controller instance
-     * @param property specify the kind of port to insert and should be one of : <UL>
-     *        <LI>{@link ObjectProperties#INPUTS}
-     *        <LI>{@link ObjectProperties#OUTPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_INPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
-     * @param parent is the parent {@link mxCell} to modify
+     * @param controller
+     *            is the shared controller instance
+     * @param property
+     *            specify the kind of port to insert and should be one of :
+     *            <UL>
+     *            <LI>{@link ObjectProperties#INPUTS}
+     *            <LI>{@link ObjectProperties#OUTPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_INPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
+     * @param parent
+     *            is the parent {@link mxCell} to modify
+     * @return the number of inserted children
      */
-    private static void insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
-        if (parent.getKind() != Kind.BLOCK) {
-            return;
-        }
-
+    private static int insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
         VectorOfScicosID modelChildren = new VectorOfScicosID();
         controller.getObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
 
-        mxICell[] children = new mxICell[modelChildren.size()];
+        XcosCell[] children = new XcosCell[modelChildren.size()];
         for (int i = 0; i < children.length; i++) {
-            children[i] = createPort(controller, modelChildren.get(i), property);
+            XcosCell child = createPort(controller, modelChildren.get(i), property);
+            children[i] = child;
         }
-        Arrays.stream(children).forEach(c -> parent.insert(c));
+
+        modelChildren.clear();
+        controller.setObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
+
+        Arrays.stream(children).forEach(c -> {
+            parent.insert(c);
+            controller.deleteObject(c.getUID());
+        });
+
+        return children.length;
     }
 
     /**
      * Create a port for a specific uid
      *
-     * @param controller is the shared controller instance
-     * @param uid represent the allocated UID on the MVC
-     * @param property specify the kind of port to create and should be one of : <UL>
-     *        <LI>{@link ObjectProperties#INPUTS}
-     *        <LI>{@link ObjectProperties#OUTPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_INPUTS}
-     *        <LI>{@link ObjectProperties#EVENT_OUTPUTS}
+     * @param controller
+     *            is the shared controller instance
+     * @param uid
+     *            represent the allocated UID on the MVC
+     * @param property
+     *            specify the kind of port to create and should be one of :
+     *            <UL>
+     *            <LI>{@link ObjectProperties#INPUTS}
+     *            <LI>{@link ObjectProperties#OUTPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_INPUTS}
+     *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
      * @return a newly allocated port
      */
     private static final BasicPort createPort(final JavaController controller, long uid, final ObjectProperties property) {
-        boolean[] isImplicit = {false};
+        BasicPort port;
+        boolean[] isImplicit = { false };
 
         switch (property) {
             case INPUTS:
                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
                 if (isImplicit[0]) {
-                    return new ImplicitInputPort(uid);
+                    port = new ImplicitInputPort(uid);
                 } else {
-                    return new ExplicitInputPort(uid);
+                    port = new ExplicitInputPort(uid);
                 }
+                break;
             case OUTPUTS:
                 controller.getObjectProperty(uid, Kind.PORT, ObjectProperties.IMPLICIT, isImplicit);
                 if (isImplicit[0]) {
-                    return new ImplicitOutputPort(uid);
+                    port = new ImplicitOutputPort(uid);
                 } else {
-                    return new ExplicitOutputPort(uid);
+                    port = new ExplicitOutputPort(uid);
                 }
+                break;
             case EVENT_INPUTS:
-                return new ControlPort(uid);
+                port = new ControlPort(uid);
+                break;
             case EVENT_OUTPUTS:
-                return new CommandPort(uid);
+                port = new CommandPort(uid);
+                break;
             default:
                 return null;
         }
+
+        /*
+         * Setup JGraphX properties
+         */
+
+        String[] childUID = new String[1];
+        controller.getObjectProperty(port.getUID(), port.getKind(), ObjectProperties.UID, childUID);
+        if (!childUID[0].isEmpty()) {
+            port.setId(childUID[0]);
+        }
+
+        return port;
     }
 
     /*
@@ -562,18 +600,32 @@ public final class XcosCellFactory {
         /*
          * Synchronize model information back to the JGraphX data
          */
+
+        String[] strUID = new String[1];
+        controller.getObjectProperty(uid, kind, ObjectProperties.UID, strUID);
+        if (!strUID[0].isEmpty()) {
+            link.setId(strUID[0]);
+        }
+
         VectorOfDouble controlPoints = new VectorOfDouble();
         controller.getObjectProperty(uid, kind, ObjectProperties.CONTROL_POINTS, controlPoints);
-        final int controlPointsLen = controlPoints.size() / 2;
+        final int pointsLen = controlPoints.size() / 2;
+
+        mxGeometry geom = new mxGeometry();
 
-        ArrayList<mxPoint> points = new ArrayList<>(controlPointsLen);
-        for (int i = 0 ; i < controlPointsLen; i++) {
-            points.add(new mxPoint(controlPoints.get(i), controlPoints.get(i + controlPointsLen)));
+        // as the link is supposed to be connected and accordingly to the JGraphX rules : do not add the origin and destination point
+        ArrayList<mxPoint> points = new ArrayList<>();
+        int i = 0;
+        // ignore origin
+        i++;
+        // loop for points
+        for (; i < pointsLen - 1; i++) {
+            points.add(new mxPoint(controlPoints.get(2 * i), controlPoints.get(2 * i + 1)));
         }
+        // ignore destination
+        i++;
 
-        mxGeometry geom = new mxGeometry();
         geom.setPoints(points);
-
         link.setGeometry(geom);
         return link;
     }