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