d9a818a0034453df71127080251fcc16d01bba71
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / block / actions / RegionToSuperblockAction.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Clement DAVID
4  * Copyright (C) 2011-2016 - Scilab Enterprises - Clement DAVID
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 package org.scilab.modules.xcos.block.actions;
18
19 import java.awt.event.ActionEvent;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.LinkedList;
23 import java.util.Queue;
24 import java.util.TreeSet;
25 import java.util.logging.Logger;
26
27 import org.scilab.modules.graph.ScilabComponent;
28 import org.scilab.modules.graph.ScilabGraph;
29 import org.scilab.modules.graph.actions.base.VertexSelectionDependantAction;
30 import org.scilab.modules.gui.menuitem.MenuItem;
31 import org.scilab.modules.xcos.block.BasicBlock;
32 import org.scilab.modules.xcos.block.SuperBlock;
33 import org.scilab.modules.xcos.block.io.ContextUpdate;
34 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
35 import org.scilab.modules.xcos.graph.XcosDiagram;
36 import org.scilab.modules.xcos.link.BasicLink;
37 import org.scilab.modules.xcos.port.BasicPort;
38 import org.scilab.modules.xcos.port.control.ControlPort;
39 import org.scilab.modules.xcos.port.input.InputPort;
40 import org.scilab.modules.xcos.port.output.OutputPort;
41 import org.scilab.modules.xcos.utils.BlockPositioning;
42 import org.scilab.modules.xcos.utils.XcosMessages;
43
44 import com.mxgraph.model.mxGeometry;
45 import com.mxgraph.model.mxGraphModel;
46 import com.mxgraph.model.mxICell;
47 import com.mxgraph.util.mxRectangle;
48 import java.nio.LongBuffer;
49 import java.rmi.server.UID;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.logging.Level;
53 import static java.util.stream.Collectors.toList;
54 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
55 import org.scilab.modules.graph.utils.StyleMap;
56 import org.scilab.modules.types.ScilabString;
57 import org.scilab.modules.xcos.JavaController;
58 import org.scilab.modules.xcos.Kind;
59 import org.scilab.modules.xcos.ObjectProperties;
60 import org.scilab.modules.xcos.VectorOfDouble;
61 import org.scilab.modules.xcos.VectorOfInt;
62 import org.scilab.modules.xcos.VectorOfScicosID;
63 import org.scilab.modules.xcos.graph.model.XcosCell;
64 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
65 import org.scilab.modules.xcos.io.ScilabTypeCoder;
66 import org.scilab.modules.xcos.utils.XcosConstants;
67
68 /**
69  * Move the Selected cells to a new SuperBlock diagram
70  */
71 // CSOFF: ClassFanOutComplexity
72 @SuppressWarnings(value = { "serial" })
73 public class RegionToSuperblockAction extends VertexSelectionDependantAction {
74     /** Name of the action */
75     public static final String NAME = XcosMessages.REGION_TO_SUPERBLOCK;
76     /** Icon name of the action */
77     public static final String SMALL_ICON = "object-group";
78     /** Mnemonic key of the action */
79     public static final int MNEMONIC_KEY = 0;
80     /** Accelerator key for the action */
81     public static final int ACCELERATOR_KEY = 0;
82
83     private static final String INTERFUNCTION_NAME = "SUPER_f";
84
85     /**
86      * Default Constructor
87      *
88      * @param scilabGraph
89      *            the graph
90      */
91     public RegionToSuperblockAction(ScilabGraph scilabGraph) {
92         super(scilabGraph);
93     }
94
95     /**
96      * @param scilabGraph
97      *            graph
98      * @return menu item
99      */
100     public static MenuItem createMenu(ScilabGraph scilabGraph) {
101         return createMenu(scilabGraph, RegionToSuperblockAction.class);
102     }
103
104     /**
105      * Represent a broken link at selection time
106      */
107     private static final class Broken implements Comparable<Broken> {
108         private final mxGraphModel parentModel;
109
110         private final BasicPort source;
111         private final BasicPort target;
112
113         private final BasicLink parentLink;
114         private final boolean containsSource;
115
116         /*
117          * Out of selection port position
118          */
119         private final double x;
120         private final double y;
121
122         /*
123          * lazy allocated fields
124          */
125         private BasicLink childLink;
126         private BasicPort parentPort;
127         private ContextUpdate childBlock;
128
129         /**
130          * Default Constructor
131          *
132          * @param parentGraph
133          *            the parent graph
134          * @param parentModel
135          *            the parent model
136          * @param source
137          *            the source
138          * @param target
139          *            the target
140          * @param link
141          *            the link
142          * @param containsSource
143          *            is the source in selection
144          */
145         public Broken(XcosDiagram parentGraph, mxGraphModel parentModel, BasicPort source, BasicPort target, BasicLink link, boolean containsSource) {
146             super();
147             this.parentModel = parentModel;
148
149             this.source = source;
150             this.target = target;
151             this.parentLink = link;
152             this.containsSource = containsSource;
153
154             final BasicPort terminal;
155             if (containsSource) {
156                 terminal = target;
157             } else {
158                 terminal = source;
159             }
160
161             /*
162              * Update position
163              */
164             final mxGeometry pos = parentModel.getGeometry(terminal);
165             final mxGeometry parent = parentModel.getGeometry(parentModel.getParent(terminal));
166             if (pos != null && parent != null) {
167                 this.x = parentGraph.snap(pos.getX() + parent.getX() - (pos.getHeight() / 2));
168                 this.y = parentGraph.snap(pos.getY() + parent.getY() - (pos.getWidth() / 2));
169             } else {
170                 this.x = 0.0;
171                 this.y = 0.0;
172             }
173         }
174
175         /**
176          * @return the link on the parent diagram
177          */
178         public BasicLink getParentLink() {
179             return parentLink;
180         }
181
182         /**
183          * @param isSource
184          *            is a source port needed
185          * @return the parent terminal (source or not)
186          */
187         public mxICell getParentTerminal(boolean isSource) {
188             final mxICell ret;
189
190             if (containsSource) {
191                 if (isSource) {
192                     ret = getParentPort();
193                 } else {
194                     ret = target;
195                 }
196             } else {
197                 if (isSource) {
198                     ret = source;
199                 } else {
200                     ret = getParentPort();
201                 }
202             }
203
204             return ret;
205         }
206
207         /**
208          * @param isSource
209          *            is a source port needed
210          * @return the child terminal (source or not)
211          */
212         public mxICell getChildTerminal(boolean isSource) {
213             final mxICell ret;
214
215             if (containsSource) {
216                 if (isSource) {
217                     ret = source;
218                 } else {
219                     ret = getChildBlock().getChildAt(0);
220                 }
221             } else {
222                 if (isSource) {
223                     ret = getChildBlock().getChildAt(0);
224                 } else {
225                     ret = target;
226                 }
227             }
228
229             return ret;
230         }
231
232         /**
233          * @return the terminal in the selection
234          */
235         public BasicPort getTerminal() {
236             if (containsSource) {
237                 return source;
238             } else {
239                 return target;
240             }
241         }
242
243         /**
244          * @return the link on the child diagram
245          */
246         public BasicLink getChildLink() {
247             if (childLink == null) {
248                 childLink = (BasicLink) parentModel.cloneCells(new Object[] { parentLink }, true)[0];
249             }
250             return childLink;
251         }
252
253         /**
254          * @return the port on the parent diagram
255          */
256         public BasicPort getParentPort() {
257             if (parentPort == null) {
258                 JavaController controller = new JavaController();
259                 long uid = controller.createObject(Kind.PORT);
260
261                 try {
262                     if (containsSource) {
263                         parentPort = IOBlocks.getOpposite(target.getClass())
264                                      .getConstructor(JavaController.class, Long.TYPE, Kind.class, Object.class, String.class, String.class)
265                                      .newInstance(controller, uid, Kind.PORT, null, null, new UID().toString());
266                     } else {
267                         parentPort = IOBlocks.getOpposite(source.getClass())
268                                      .getConstructor(JavaController.class, Long.TYPE, Kind.class, Object.class, String.class, String.class)
269                                      .newInstance(controller, uid, Kind.PORT, null, null, new UID().toString());;
270                     }
271                 } catch (ReflectiveOperationException e) {
272                     Logger.getLogger(RegionToSuperblockAction.class.getName()).severe(e.toString());
273                 } catch (IllegalArgumentException ex) {
274                     Logger.getLogger(RegionToSuperblockAction.class.getName()).log(Level.SEVERE, null, ex);
275                 }
276             }
277
278             return parentPort;
279         }
280
281         /**
282          * @return the child block to put on the child diagram
283          */
284         public ContextUpdate getChildBlock() {
285             if (childBlock == null) {
286                 final BasicPort terminal = getTerminal();
287                 childBlock = IOBlocks.createBlock(terminal);
288
289                 /*
290                  * Set the child position
291                  */
292                 mxGeometry geom = childBlock.getGeometry();
293                 geom.setX(x);
294                 geom.setY(y);
295                 childBlock.setGeometry(geom);
296             }
297
298             return childBlock;
299         }
300
301         /**
302          * Set the ordering on the I/O block and port
303
304          * @param ordering
305          *            the ordering to set
306          */
307         public void setOrdering(JavaController controller, int ordering) {
308             // update the child ordering
309             VectorOfInt ipar = new VectorOfInt(1);
310             ipar.set(0, ordering);
311             controller.setObjectProperty(getChildBlock().getUID(), getChildBlock().getKind(), ObjectProperties.IPAR, ipar);
312
313             VectorOfDouble exprs = new ScilabTypeCoder().var2vec(new ScilabString(Integer.toString(ordering)));
314             controller.setObjectProperty(getChildBlock().getUID(), getChildBlock().getKind(), ObjectProperties.EXPRS, exprs);
315         }
316
317         /**
318          * {@inheritDoc}
319          *
320          * This function is used to sort a {@link TreeSet} of {@link Broken}.
321          */
322         @Override
323         public int compareTo(Broken o) {
324             final int xdiff = (int) (x - o.x);
325             final int ydiff = (int) (y - o.y);
326
327             if (xdiff == 0 && ydiff == 0) {
328                 // same position, sort by hashcode
329                 return hashCode() - o.hashCode();
330             } else {
331                 return (ydiff << (Integer.SIZE / 2)) + xdiff;
332             }
333         }
334
335         /**
336          * {@inheritDoc}
337          */
338         @Override
339         public String toString() {
340             final String labelSep = ": ";
341             final String link = " -> ";
342
343             final StringBuilder str = new StringBuilder();
344             if (getParentLink().getChildCount() > 0) {
345                 // append the label
346                 str.append(getParentLink().getChildAt(0).getValue());
347                 str.append(labelSep);
348             }
349
350             str.append(getParentTerminal(true));
351             str.append(link);
352             str.append(getParentTerminal(false));
353
354             str.append('\n');
355
356             if (getChildLink().getChildCount() > 0) {
357                 // append the label
358                 str.append(getChildLink().getChildAt(0).getValue());
359                 str.append(labelSep);
360             }
361
362             str.append(getChildTerminal(true));
363             str.append(link);
364             str.append(getChildTerminal(false));
365
366             return str.toString();
367         }
368     }
369
370     /**
371      * {@inheritDoc}
372      */
373     @Override
374     public void actionPerformed(ActionEvent e) {
375         final XcosDiagram parentGraph = (XcosDiagram) getGraph(e);
376
377         // action disabled when the cell is edited
378         final ScilabComponent comp = ((ScilabComponent) parentGraph.getAsComponent());
379         if (comp.isEditing()) {
380             return;
381         }
382
383         parentGraph.info(XcosMessages.GENERATE_SUPERBLOCK);
384         parentGraph.getModel().beginUpdate();
385         try {
386             final JavaController controller = new JavaController();
387             final SuperBlock superBlock;
388             final Collection<Broken> brokenLinks;
389
390             /*
391              * Allocate superBlock
392              */
393             final Object[] selection = parentGraph.getSelectionCells();
394             List<XcosCell> toBeMoved = Arrays.stream(selection)
395                                        .filter(o -> o instanceof XcosCell)
396                                        .map(o -> (XcosCell) o)
397                                        .collect(toList());
398
399             superBlock = allocateSuperBlock(controller, parentGraph, selection);
400
401             /*
402              * First perform all modification on the parent diagram to handle
403              * well undo/redo operations.
404              */
405             brokenLinks = updateParent(controller, parentGraph, superBlock, toBeMoved);
406
407             /*
408              * Then move some cells to the child diagram
409              */
410             moveToChild(controller, parentGraph, superBlock, brokenLinks, toBeMoved);
411
412             BlockPositioning.updateBlockView(parentGraph, superBlock);
413         } catch (ScilabInterpreterManagement.InterpreterException ex) {
414             // Scilab seems to be blocked, just consume the exception at this point
415         } finally {
416             parentGraph.getModel().endUpdate();
417             parentGraph.info(XcosMessages.EMPTY_INFO);
418         }
419     }
420
421     /**
422      * Allocate a superBlock
423      *
424      * @param parentGraph
425      *            the base graph
426      * @param selection
427      *            the selected blocks
428      * @return the allocated super block (without specific listeners)
429      */
430     private SuperBlock allocateSuperBlock(final JavaController controller, final XcosDiagram parentGraph, final Object[] selection) throws ScilabInterpreterManagement.InterpreterException {
431         final SuperBlock superBlock = (SuperBlock) XcosCellFactory.createBlock(INTERFUNCTION_NAME);
432
433         /*
434          * Remove the default allocated ports
435          */
436         while (superBlock.getChildCount() > 0) {
437             superBlock.remove(superBlock.getChildCount() - 1);
438         }
439         VectorOfScicosID children = new VectorOfScicosID();
440         controller.setObjectProperty(superBlock.getUID(), superBlock.getKind(), ObjectProperties.CHILDREN, children);
441
442         /*
443          * Place the super block
444          */
445         final mxRectangle dims = parentGraph.getBoundingBoxFromGeometry(selection);
446         final double minX = dims.getX();
447         final double maxX = minX + dims.getWidth();
448
449         final double minY = dims.getY();
450         final double maxY = minY + dims.getHeight();
451
452         mxGeometry geom = superBlock.getGeometry();
453         geom.setX((maxX + minX - geom.getWidth()) / 2.0);
454         geom.setY((maxY + minY - geom.getHeight()) / 2.0);
455         superBlock.setGeometry(geom);
456
457         /*
458          * get statistics to flip and rotate
459          */
460         VectorOfDouble mvcAngle = new VectorOfDouble();
461
462         int angleCounter = 0;
463         int flipCounter = 0;
464         int mirrorCounter = 0;
465         for (Object object : selection) {
466             if (object instanceof BasicBlock) {
467                 final BasicBlock b = (BasicBlock) object;
468
469                 String[] style = new String[1];
470                 controller.getObjectProperty(b.getUID(), Kind.BLOCK, ObjectProperties.STYLE, style);
471                 StyleMap styleMap = new StyleMap(style[0]);
472
473                 final boolean mirrored = Boolean.TRUE.toString().equals(styleMap.get(XcosConstants.STYLE_MIRROR));
474                 final boolean flipped = Boolean.TRUE.toString().equals(styleMap.get(XcosConstants.STYLE_FLIP));
475                 final int intRotation = Double.valueOf(styleMap.getOrDefault(XcosConstants.STYLE_ROTATION, "0")).intValue();
476
477                 angleCounter += intRotation;
478                 if (flipped) {
479                     flipCounter++;
480                 }
481                 if (mirrored) {
482                     mirrorCounter++;
483                 }
484             }
485         }
486
487         /*
488          * apply statistics to flip and rotate
489          */
490         final int halfSize = selection.length / 2;
491         String[] style = new String[1];
492         controller.getObjectProperty(superBlock.getUID(), superBlock.getKind(), ObjectProperties.STYLE, style);
493         StyleMap styleMap = new StyleMap(style[0]);
494
495         styleMap.put(XcosConstants.STYLE_ROTATION, Integer.toString(BlockPositioning.roundAngle(angleCounter / selection.length)));
496         if (flipCounter > halfSize) {
497             styleMap.put(XcosConstants.STYLE_FLIP, Boolean.toString(true));
498         }
499         if (mirrorCounter > halfSize) {
500             styleMap.put(XcosConstants.STYLE_MIRROR, Boolean.toString(true));
501         }
502
503         controller.setObjectProperty(superBlock.getUID(), superBlock.getKind(), ObjectProperties.STYLE, styleMap.toString());
504
505         return superBlock;
506     }
507
508     /**
509      * Create child cells and add them to the parent diagram. All links are also
510      * reconnected
511      *
512      * @param parentGraph
513      *            the parent diagram
514      * @param superBlock
515      *            the superblock
516      * @param inSelectionCells
517      *            the cells in the selection
518      * @return the broken descriptor set
519      */
520     private Collection<Broken> updateParent(final JavaController controller, final XcosDiagram parentGraph, final SuperBlock superBlock, final List<XcosCell> inSelectionCells) {
521         final Collection<Broken> brokenLinks;
522         final mxGraphModel parentModel = (mxGraphModel) parentGraph.getModel();
523
524         parentModel.beginUpdate();
525         try {
526             /*
527              * Add the internal links and fill border links Sort the broken
528              * links by position (to perform a good numbering order) and keep
529              * only one occurrence of a broken link.
530              */
531             brokenLinks = new TreeSet<>();
532             fillLinks(parentGraph, parentModel, inSelectionCells, brokenLinks);
533
534             /*
535              * Disconnect the broken links
536              */
537             for (Broken broken : brokenLinks) {
538                 mxGraphModel.setTerminals(parentModel, broken.getParentLink(), null, null);
539             }
540
541             /*
542              * Add the super block
543              */
544             parentGraph.addCell(superBlock);
545
546             /*
547              * Main broken loop
548              */
549             // ordering access is : IN, OUT, e_IN, e_OUT
550             final int[] ordering = { 0, 0, 0, 0 };
551             for (Broken broken : brokenLinks) {
552
553                 // set the ordering
554                 incrementOrdering(controller, ordering, broken);
555
556                 connectParent(parentGraph, parentModel, superBlock, broken);
557                 connectChild(parentGraph, parentModel, broken);
558
559                 /*
560                  * Update the view
561                  */
562                 BlockPositioning.updateBlockView(parentGraph, broken.getChildBlock());
563             }
564         } finally {
565             parentModel.endUpdate();
566         }
567
568         return brokenLinks;
569     }
570
571     /**
572      * Fill the internalLinks links from a selected block to a selected block
573      * and broken links with a broken object. Also disconnect all the broken
574      * links.
575      *
576      * @param parentGraph
577      *            the graph
578      * @param parentModel
579      *            the model
580      * @param inSelectionCells
581      *            the selected blocks (with fast contains operation)
582      * @param brokenLinks
583      *            the broken links to find.
584      */
585     private void fillLinks(final XcosDiagram parentGraph, final mxGraphModel parentModel, final Collection<XcosCell> inSelectionCells, final Collection<Broken> brokenLinks) {
586         final Queue<Object> loopQueue = new LinkedList<>(inSelectionCells);
587
588         while (!loopQueue.isEmpty()) {
589             final Object cell = loopQueue.remove();
590
591             final int childCount = parentModel.getChildCount(cell);
592             for (int i = 0; i < childCount; i++) {
593                 final Object port = parentModel.getChildAt(cell, i);
594
595                 final int edgeCount = parentModel.getEdgeCount(port);
596                 for (int j = 0; j < edgeCount; j++) {
597                     final Object edge = parentModel.getEdgeAt(port, j);
598
599                     final Object source = parentModel.getTerminal(edge, true);
600                     final Object target = parentModel.getTerminal(edge, false);
601
602                     /*
603                      * Add the links
604                      */
605                     final boolean containsSource = inSelectionCells.contains(parentModel.getParent(source));
606                     final boolean containsTarget = inSelectionCells.contains(parentModel.getParent(target));
607
608                     if (containsSource && containsTarget && edge instanceof XcosCell) {
609                         inSelectionCells.add((XcosCell) edge);
610                         continue;
611                     }
612
613                     /*
614                      * Handle a broken link case
615                      */
616
617                     if (containsSource) {
618                         brokenLinks.add(new Broken(parentGraph, parentModel, (BasicPort) source, (BasicPort) target, (BasicLink) edge, true));
619                         continue;
620                     }
621
622                     if (containsTarget) {
623                         brokenLinks.add(new Broken(parentGraph, parentModel, (BasicPort) source, (BasicPort) target, (BasicLink) edge, false));
624                         continue;
625                     }
626                 }
627             }
628         }
629     }
630
631     /**
632      * Increment and set the ordering to the broken entry.
633      *
634      * @param controller the controller
635      * @param ordering
636      *            4x1 array of ordering
637      * @param broken
638      *            the current broken entry.
639      */
640     // CSOFF: MagicNumber
641     private void incrementOrdering(JavaController controller, final int[] ordering, Broken broken) {
642         if (broken.getTerminal() instanceof InputPort) {
643             broken.setOrdering(controller, ++ordering[0]);
644         } else if (broken.getTerminal() instanceof OutputPort) {
645             broken.setOrdering(controller, ++ordering[1]);
646         } else if (broken.getTerminal() instanceof ControlPort) {
647             broken.setOrdering(controller, ++ordering[2]);
648         } else { // if (broken.getTerminal() instanceof CommandPort)
649             broken.setOrdering(controller, ++ordering[3]);
650         }
651     }
652
653     // CSON: MagicNumber
654
655     /**
656      * Add I/O port and reconnect them
657      *
658      * @param parentGraph
659      *            the parent graph
660      * @param parentModel
661      *            the parent graph model
662      * @param superBlock
663      *            the super block
664      * @param broken
665      *            the broken entry
666      */
667     private void connectParent(final XcosDiagram parentGraph, final mxGraphModel parentModel, final SuperBlock superBlock, Broken broken) {
668         parentGraph.addCell(broken.getParentPort(), superBlock);
669         parentGraph.addCell(broken.getParentLink());
670
671         final mxICell source = broken.getParentTerminal(true);
672         final mxICell target = broken.getParentTerminal(false);
673
674         // then connect the link
675         mxGraphModel.setTerminals(parentModel, broken.getParentLink(), source, target);
676     }
677
678     /**
679      * Add I/O blocks and reconnect them
680      *
681      * @param childGraph
682      *            the child graph
683      * @param childModel
684      *            the child graph model
685      * @param broken
686      *            the broken entry
687      */
688     private void connectChild(final XcosDiagram childGraph, final mxGraphModel childModel, Broken broken) {
689         childGraph.addCell(broken.getChildBlock());
690         childGraph.addCell(broken.getChildLink());
691
692         final mxICell source = broken.getChildTerminal(true);
693         final mxICell target = broken.getChildTerminal(false);
694
695         // then connect the link
696         mxGraphModel.setTerminals(childModel, broken.getChildLink(), source, target);
697     }
698
699     /**
700      * Move the cells to the child graph
701      *
702      * @param parentGraph
703      *            the parent graph
704      * @param superBlock
705      *            the superBlock
706      * @param brokenLinks
707      *            the broken links set
708      * @param inSelectionCells
709      *            the cells in selection
710      * @return the superblock child diagram
711      */
712     private void moveToChild(final JavaController controller, final XcosDiagram parentGraph, final SuperBlock superBlock, final Collection<Broken> brokenLinks, final List<XcosCell> inSelectionCells) {
713         final Collection<XcosCell> cellsToCopy = new ArrayList<>();
714
715         /*
716          * create a collection with all the cells to move
717          */
718         cellsToCopy.addAll(inSelectionCells);
719         for (Broken b : brokenLinks) {
720             cellsToCopy.add(b.getChildBlock());
721             cellsToCopy.add(b.getChildLink());
722         }
723
724         /*
725          * Really move the cells
726          */
727         parentGraph.removeCells(cellsToCopy.toArray(), false);
728
729         VectorOfScicosID children = new VectorOfScicosID(cellsToCopy.size());
730         LongBuffer childrenUIDs = children.asByteBuffer(0, cellsToCopy.size()).asLongBuffer();
731
732         final long parentBlock = superBlock.getUID();
733         final long parentDiagram;
734         if (parentGraph.getKind() == Kind.DIAGRAM) {
735             parentDiagram = parentGraph.getUID();
736         } else {
737             parentDiagram = parentGraph.getRootDiagram().getUID();
738         }
739
740         cellsToCopy.stream().forEach(c -> {
741             childrenUIDs.put(c.getUID());
742             controller.referenceObject(c.getUID());
743
744             controller.setObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PARENT_BLOCK, parentBlock);
745             controller.setObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PARENT_DIAGRAM, parentDiagram);
746         });
747
748         controller.setObjectProperty(superBlock.getUID(), superBlock.getKind(), ObjectProperties.CHILDREN, children);
749     }
750 }
751 // CSON: ClassFanOutComplexity