4860d7e164b1b67f63751fc57f37c5d95b763657
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / model / XcosCell.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 java.util.Collections;
19 import java.util.List;
20
21 import org.scilab.modules.xcos.JavaController;
22 import org.scilab.modules.xcos.Kind;
23 import org.scilab.modules.xcos.ObjectProperties;
24 import org.scilab.modules.xcos.PortKind;
25 import org.scilab.modules.xcos.VectorOfDouble;
26 import org.scilab.modules.xcos.VectorOfScicosID;
27
28 import com.mxgraph.model.mxCell;
29 import com.mxgraph.model.mxGeometry;
30 import com.mxgraph.model.mxICell;
31 import com.mxgraph.util.mxPoint;
32 import java.rmi.server.UID;
33 import java.util.regex.Pattern;
34
35 /**
36  * An Xcos cell is a JGraphX cell that store most of its information into the
37  * Scicos MVC.
38  *
39  * The reference to the Scicos MVC object is taken at construction time and
40  * released by the destructor. There is thus no need to manage parent /
41  * children, block / port or port / link association referencing as the JVM will
42  * GC the object if needed.
43  */
44 public class XcosCell extends mxCell {
45     private static final long serialVersionUID = 1L;
46     private static Pattern validCIdentifier = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]+");
47
48     private transient ScicosObjectOwner owner;
49
50     /**
51      * Construct an Xcos graphical object.
52      *
53      * @param controller
54      *            the shared controller
55      * @param uid
56      *            the associated MVC identifier
57      * @param kind
58      *            the associated MVC kind
59      */
60     public XcosCell(final JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id) {
61         super();
62
63         // defensive programming
64         if (uid == 0l) {
65             throw new IllegalArgumentException();
66         }
67
68         owner = new ScicosObjectOwner(controller, uid, kind);
69         setValue(controller, value);
70         setGeometry(controller, geometry);
71         setStyle(controller, style);
72         setId(controller, id);
73     }
74
75     /**
76      * @return the MVC unique identifier
77      */
78     public long getUID() {
79         return owner.getUID();
80     }
81
82     /**
83      * @return the MVC kind of object
84      */
85     public Kind getKind() {
86         return owner.getKind();
87     }
88
89     /*
90      * Override setters and hierarchy modifiers to propagate changes to the model
91      */
92
93     /*
94      * (non-Javadoc)
95      *
96      * @see com.mxgraph.model.mxCell#setValue(java.lang.Object)
97      */
98     @Override
99     public void setValue(Object value) {
100         setValue(new JavaController(), value);
101     }
102
103     public final void setValue(JavaController controller, Object value) {
104         super.setValue(value);
105         setMVCValue(controller, value);
106     }
107
108     private void setMVCValue(JavaController controller, Object value) {
109         if (value == null) {
110             return;
111         }
112
113         switch (getKind()) {
114             case BLOCK:
115                 if (validCIdentifier.matcher(String.valueOf(value)).matches()) {
116                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.LABEL, String.valueOf(value));
117                 }
118             // no break on purpose
119             case ANNOTATION:
120                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DESCRIPTION, String.valueOf(value));
121                 break;
122             case LINK:
123             case PORT:
124                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.LABEL, String.valueOf(value));
125                 break;
126             default:
127                 break;
128         }
129     }
130
131     /*
132      * (non-Javadoc)
133      *
134      * @see com.mxgraph.model.mxCell#setId(java.lang.String)
135      */
136     @Override
137     public void setId(String id) {
138         setId(new JavaController(), id);
139     }
140
141     public final void setId(JavaController controller, String id) {
142         if (id == null || id.isEmpty()) {
143             id = new UID().toString();
144         }
145
146         super.setId(id);
147         setMVCId(controller, id);
148     }
149
150     private void setMVCId(JavaController controller, String id) {
151         if (id == null) {
152             return;
153         }
154
155         switch (getKind()) {
156             case ANNOTATION:
157             case BLOCK:
158             case LINK:
159             case PORT:
160                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.UID, id);
161                 break;
162             default:
163                 break;
164         }
165     }
166
167     /*
168      * (non-Javadoc)
169      *
170      * @see com.mxgraph.model.mxCell#setGeometry(com.mxgraph.model.mxGeometry)
171      */
172     @Override
173     public void setGeometry(mxGeometry geometry) {
174         setGeometry(new JavaController(), geometry);
175     }
176
177     public final void setGeometry(JavaController controller, mxGeometry geometry) {
178         super.setGeometry(geometry);
179         setMVCGeometry(controller, geometry);
180     }
181
182     private void setMVCGeometry(JavaController controller, mxGeometry geometry) {
183         if (geometry == null) {
184             return;
185         }
186
187         switch (getKind()) {
188             case ANNOTATION:
189             case BLOCK: {
190                 VectorOfDouble v = new VectorOfDouble(4);
191                 v.set(0, geometry.getX());
192                 v.set(1, geometry.getY());
193                 v.set(2, geometry.getWidth());
194                 v.set(3, geometry.getHeight());
195                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.GEOMETRY, v);
196                 break;
197             }
198             case LINK: {
199                 /*
200                  * try to find the origin of the source and target accordingly to the JGraphX implementation
201                  */
202                 mxPoint sourcePoint = null;
203                 mxPoint targetPoint = null;
204                 mxICell sourceCell = getSource();
205                 mxICell targetCell = getTarget();
206                 if (sourceCell != null && sourceCell.getGeometry() != null) {
207                     sourcePoint = new mxPoint(sourceCell.getGeometry().getCenterX(), sourceCell.getGeometry().getCenterY());
208                 }
209                 if (targetCell != null && targetCell.getGeometry() != null) {
210                     targetPoint = new mxPoint(targetCell.getGeometry().getCenterX(), targetCell.getGeometry().getCenterY());
211                 }
212                 if (sourcePoint == null) {
213                     sourcePoint = geometry.getSourcePoint();
214                 }
215                 if (targetPoint == null) {
216                     targetPoint = geometry.getTargetPoint();
217                 }
218                 if (sourcePoint == null) {
219                     sourcePoint = new mxPoint();
220                 }
221                 if (targetPoint == null) {
222                     targetPoint = new mxPoint();
223                 }
224                 List<mxPoint> points = geometry.getPoints();
225                 if (points == null) {
226                     points = Collections.emptyList();
227                 }
228
229                 /*
230                  * At that point, the sourcePoint, targetPoint and points are valid values (but may be unknown) encode them to the the CONTROL_POINTS
231                  */
232
233                 // Allocate some space to contains them all
234                 int nbOfPoints = 2 + points.size();
235                 VectorOfDouble v = new VectorOfDouble(2 * nbOfPoints);
236                 int i = 0;
237                 // then fill the allocation space
238                 v.set(2 * i, sourcePoint.getX());
239                 v.set(2 * i + 1, sourcePoint.getY());
240                 i++;
241                 for (; i < nbOfPoints - 1; i++) {
242                     v.set(2 * i, points.get(i - 1).getX());
243                     v.set(2 * i + 1, points.get(i - 1).getY());
244                 }
245                 v.set(2 * i, targetPoint.getX());
246                 v.set(2 * i + 1, targetPoint.getY());
247                 i++;
248                 /*
249                  * Finally push the values to the model
250                  */
251                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CONTROL_POINTS, v);
252                 break;
253             }
254             default:
255                 break;
256         }
257     }
258
259     /*
260      * (non-Javadoc)
261      *
262      * @see com.mxgraph.model.mxCell#setStyle(java.lang.String)
263      */
264     @Override
265     public void setStyle(String style) {
266         setStyle(new JavaController(), style);
267     }
268
269     public final void setStyle(JavaController controller, String style) {
270         super.setStyle(style);
271         setMVCStyle(controller, style);
272     }
273
274     private void setMVCStyle(JavaController controller, String style) {
275         if (style == null) {
276             return;
277         }
278
279         switch (getKind()) {
280             case BLOCK:
281             case LINK:
282             case ANNOTATION:
283             case PORT:
284                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.STYLE, style);
285                 break;
286             default:
287                 break;
288         }
289     }
290
291     /*
292      * (non-Javadoc)
293      *
294      * @see com.mxgraph.model.mxCell#removeFromParent()
295      */
296     @Override
297     public void removeFromParent() {
298         super.removeFromParent();
299
300         // do not remove from parent on SUPER_f diagram creation : there is an MVC parent but no JGraphX one
301         if (parent == null) {
302             return;
303         }
304
305         JavaController controller = new JavaController();
306         switch (getKind()) {
307             case ANNOTATION:
308             case BLOCK:
309             case LINK: {
310                 /*
311                  * Retrieve the parent
312                  */
313                 long[] parent = new long[1];
314                 Kind parentKind = Kind.BLOCK;
315                 ObjectProperties prop = ObjectProperties.PARENT_BLOCK;
316                 controller.getObjectProperty(getUID(), getKind(), prop, parent);
317                 if (parent[0] == 0l) {
318                     parentKind = Kind.DIAGRAM;
319                     prop = ObjectProperties.PARENT_DIAGRAM;
320                     controller.getObjectProperty(getUID(), getKind(), prop, parent);
321                 }
322
323                 /*
324                  * If there is a parent, clear it
325                  */
326                 if (parent[0] != 0l) {
327                     VectorOfScicosID children = new VectorOfScicosID();
328                     controller.getObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
329                     children.remove(getUID());
330                     controller.setObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
331
332                     controller.setObjectProperty(getUID(), getKind(), prop, 0l);
333                 }
334                 break;
335             }
336             case PORT: {
337                 long[] parent = new long[1];
338                 Kind parentKind = Kind.BLOCK;
339                 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, parent);
340
341                 int[] portKind = new int[1];
342                 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PORT_KIND, portKind);
343                 ObjectProperties property = relatedPortKindProperty(portKind[0]);
344
345                 VectorOfScicosID ports = new VectorOfScicosID();
346                 controller.getObjectProperty(parent[0], parentKind, property, ports);
347                 ports.remove(getUID());
348                 controller.setObjectProperty(parent[0], parentKind, property, ports);
349
350                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, 0l);
351                 break;
352             }
353             default:
354                 break;
355         }
356     }
357
358     private ObjectProperties relatedPortKindProperty(int portKind) {
359         ObjectProperties property;
360         switch (PortKind.values()[portKind]) {
361             case PORT_IN:
362                 property = ObjectProperties.INPUTS;
363                 break;
364             case PORT_OUT:
365                 property = ObjectProperties.OUTPUTS;
366                 break;
367             case PORT_EIN:
368                 property = ObjectProperties.EVENT_INPUTS;
369                 break;
370             case PORT_EOUT:
371                 property = ObjectProperties.EVENT_OUTPUTS;
372                 break;
373             default:
374                 property = null;
375                 break;
376         }
377         return property;
378     }
379
380     /*
381      * (non-Javadoc)
382      *
383      * @see com.mxgraph.model.mxCell#setParent(com.mxgraph.model.mxICell)
384      */
385     @Override
386     public void setParent(mxICell parent) {
387         super.setParent(parent);
388
389         if (parent instanceof XcosCell) {
390             XcosCell p = (XcosCell) parent;
391             JavaController controller = new JavaController();
392             switch (getKind()) {
393                 case ANNOTATION:
394                 case BLOCK:
395                 case LINK:
396                     if (p.getKind() == Kind.DIAGRAM) {
397                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, p.getUID());
398                     } else {
399                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_BLOCK, p.getUID());
400
401                         long[] root = new long[1];
402                         controller.getObjectProperty(p.getUID(), p.getKind(), ObjectProperties.PARENT_DIAGRAM, root);
403                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, root[0]);
404                     }
405                     break;
406                 case PORT:
407                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, p.getUID());
408                     break;
409                 default:
410                     break;
411             }
412         }
413     }
414
415     @Override
416     public mxICell setTerminal(mxICell terminal, boolean isSource) {
417         mxICell cell = super.setTerminal(terminal, isSource);
418         if (cell == null) {
419             return cell;
420         }
421
422         // a terminal of an XcosCell is always another XcosCell
423         final long uid = ((XcosCell) cell).getUID();
424         final Kind kind = ((XcosCell) cell).getKind();
425
426         JavaController controller = new JavaController();
427         switch (getKind()) {
428             case LINK:
429                 if (isSource) {
430                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_PORT, uid);
431                 } else {
432                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DESTINATION_PORT, uid);
433                 }
434                 if (uid != 0l) {
435                     controller.setObjectProperty(uid, kind, ObjectProperties.CONNECTED_SIGNALS, getUID());
436                 }
437                 break;
438             default:
439                 break;
440         }
441
442         return cell;
443     }
444
445     /*
446      * (non-Javadoc)
447      *
448      * @see com.mxgraph.model.mxCell#insert(com.mxgraph.model.mxICell, int)
449      */
450     @Override
451     public mxICell insert(mxICell child, int index) {
452         mxICell inserted = super.insert(child, index);
453
454         // the child might not be an XcosCell but just an mxCell label
455         if (child instanceof XcosCell) {
456             XcosCell c = (XcosCell) child;
457             switch (getKind()) {
458                 case BLOCK:
459                     if (c.getKind() == Kind.PORT) {
460                         insertPort(c, index);
461                     } else {
462                         insertChild(c, index);
463                     }
464                     break;
465                 case DIAGRAM:
466                     insertChild(c, index);
467                     break;
468                 default:
469                     break;
470             }
471         }
472
473         return inserted;
474     }
475
476     private void insertPort(XcosCell c, int index) {
477         JavaController controller = new JavaController();
478         int[] v = new int[1];
479         controller.getObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PORT_KIND, v);
480
481         VectorOfScicosID children = new VectorOfScicosID();
482         final ObjectProperties property = relatedPortKindProperty(v[0]);
483
484         if (property != null) {
485             controller.getObjectProperty(getUID(), getKind(), property, children);
486             // do no use the index argument as it is not possible to insert out of order on the MVC (as we have kind of port)
487             children.add(c.getUID());
488             controller.setObjectProperty(getUID(), getKind(), property, children);
489         }
490     }
491
492     private void insertChild(XcosCell c, int index) {
493         JavaController controller = new JavaController();
494         VectorOfScicosID children = new VectorOfScicosID();
495
496         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
497         children.add(index, c.getUID());
498         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
499     }
500
501     /*
502      * (non-Javadoc)
503      *
504      * @see com.mxgraph.model.mxCell#remove(com.mxgraph.model.mxICell)
505      */
506     @Override
507     public mxICell remove(mxICell child) {
508         mxICell removed = super.remove(child);
509
510         // the child might not be an XcosCell but just an mxCell label
511         if (child instanceof XcosCell) {
512             XcosCell c = (XcosCell) child;
513             switch (getKind()) {
514                 case BLOCK:
515                     if (c.getKind() == Kind.PORT) {
516                         removePort(c);
517                     } else {
518                         removeChild(c);
519                     }
520                     break;
521                 case DIAGRAM:
522                     removeChild(c);
523                     break;
524                 default:
525                     break;
526             }
527         }
528         return removed;
529     }
530
531     private void removePort(XcosCell c) {
532         JavaController controller = new JavaController();
533         int[] v = new int[1];
534         controller.getObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PORT_KIND, v);
535
536         VectorOfScicosID children = new VectorOfScicosID();
537         final ObjectProperties property;
538         switch (PortKind.values()[v[0]]) {
539             case PORT_IN:
540                 property = ObjectProperties.INPUTS;
541                 break;
542             case PORT_OUT:
543                 property = ObjectProperties.OUTPUTS;
544                 break;
545             case PORT_EIN:
546                 property = ObjectProperties.EVENT_INPUTS;
547                 break;
548             case PORT_EOUT:
549                 property = ObjectProperties.EVENT_OUTPUTS;
550                 break;
551             default:
552                 property = null;
553                 break;
554         }
555
556         controller.getObjectProperty(getUID(), getKind(), property, children);
557         children.remove(c.getUID());
558         controller.setObjectProperty(getUID(), getKind(), property, children);
559     }
560
561     private void removeChild(XcosCell c) {
562         JavaController controller = new JavaController();
563         VectorOfScicosID children = new VectorOfScicosID();
564
565         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
566         children.remove(c.getUID());
567         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
568     }
569
570     /*
571      * Override methods from Object
572      */
573
574     @Override
575     public Object clone() throws CloneNotSupportedException {
576         JavaController controller = new JavaController();
577         XcosCell c = (XcosCell) super.clone();
578
579         c.owner = new ScicosObjectOwner(controller.cloneObject(getUID(), true, false), getKind());
580         return c;
581     }
582 }