47dfb3f510a7b4ad723ecb51fce4d6ad35ecee95
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / scicos / BlockGraphicElement.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 - 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.io.scicos;
18
19 import static java.util.Arrays.asList;
20
21 import java.util.List;
22
23 import org.scilab.modules.graph.utils.StyleMap;
24 import org.scilab.modules.types.ScilabBoolean;
25 import org.scilab.modules.types.ScilabDouble;
26 import org.scilab.modules.types.ScilabList;
27 import org.scilab.modules.types.ScilabMList;
28 import org.scilab.modules.types.ScilabString;
29 import org.scilab.modules.types.ScilabTList;
30 import org.scilab.modules.types.ScilabType;
31 import org.scilab.modules.xcos.JavaController;
32 import org.scilab.modules.xcos.ObjectProperties;
33 import org.scilab.modules.xcos.block.BasicBlock;
34 import org.scilab.modules.xcos.graph.XcosDiagram;
35 import org.scilab.modules.xcos.io.ScilabTypeCoder;
36 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongElementException;
37 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongStructureException;
38 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongTypeException;
39 import org.scilab.modules.xcos.utils.BlockPositioning;
40
41 import com.mxgraph.model.mxCell;
42 import com.mxgraph.util.mxConstants;
43 import org.scilab.modules.xcos.utils.XcosConstants;
44
45 /**
46  * Protected class which decode graphic fields of a block.
47  *
48  * This class is intentionally package-protected to prevent external use.
49  */
50 // CSOFF: ClassDataAbstractionCoupling
51 final class BlockGraphicElement extends BlockPartsElement {
52     /*
53      * "in_style", "out_style" and style have been added on the 5.3-5.4 dev. cycle they are not checked to be compatible with older versions.
54      */
55     protected static final List<String> DATA_FIELD_NAMES = asList("graphics", "orig", "sz", "flip", "theta", "exprs", "pin", "pout", "pein", "peout", "gr_i",
56             "id", "in_implicit", "out_implicit");
57     protected static final List<String> DATA_FIELD_NAMES_FULL = asList("graphics", "orig", "sz", "flip", "theta", "exprs", "pin", "pout", "pein", "peout",
58             "gr_i", "id", "in_implicit", "out_implicit", "in_style", "out_style", "in_label", "out_label", "style");
59
60     private static final int ORIGIN_INDEX = DATA_FIELD_NAMES_FULL.indexOf("orig");
61     private static final int DIMS_INDEX = DATA_FIELD_NAMES_FULL.indexOf("sz");
62     private static final int FLIP_INDEX = DATA_FIELD_NAMES_FULL.indexOf("flip");
63     private static final int EXPRS_INDEX = DATA_FIELD_NAMES_FULL.indexOf("exprs");
64     private static final int ID_INDEX = DATA_FIELD_NAMES_FULL.indexOf("id");
65     private static final int STYLE_INDEX = DATA_FIELD_NAMES_FULL.indexOf("style");
66
67     private static final int GRAPHICS_INSTRUCTION_SIZE = 8;
68     private static final double DEFAULT_SIZE_FACTOR = 20.0;
69
70     /** Mutable field to easily get the data through methods */
71     private ScilabMList data;
72
73     /** In-progress decoded diagram */
74     private final XcosDiagram diag;
75
76     /** Size factor use to scale Xcos-Scicos dimensions */
77     private final double sizeFactor;
78
79     /**
80      * Default constructor
81      */
82     public BlockGraphicElement(final JavaController controller) {
83         this(controller, null);
84     }
85
86     /**
87      * Default constructor with diagram
88      *
89      * @param diag
90      *            the diagram
91      */
92     public BlockGraphicElement(final JavaController controller, final XcosDiagram diag) {
93         super(controller);
94
95         this.diag = diag;
96
97         /*
98          * Out of a diagram update, use the DEFAULT_SIZE_FACTOR.
99          */
100         if (diag == null) {
101             sizeFactor = DEFAULT_SIZE_FACTOR;
102         } else {
103             sizeFactor = 1.0;
104         }
105     }
106
107     /**
108      * Default constructor with diagram
109      *
110      * @param diag
111      *            the diagram
112      * @param sizeFactor
113      *            the size factor
114      */
115     public BlockGraphicElement(final JavaController controller, final XcosDiagram diag, final double sizeFactor) {
116         super(controller);
117
118         this.diag = diag;
119         this.sizeFactor = sizeFactor;
120     }
121
122     /**
123      * Decode Scicos element into the block.
124      *
125      * This decode method doesn't coverage Port management because we need model information to handle it.
126      *
127      * @param element
128      *            the scicos element
129      * @param into
130      *            the previously instantiated block.
131      * @return the modified into block.
132      * @throws ScicosFormatException
133      *             on error.
134      * @see org.scilab.modules.xcos.io.scicos.Element#decode(org.scilab.modules.types.ScilabType, java.lang.Object)
135      */
136     @Override
137     public BasicBlock decode(ScilabType element, final BasicBlock into) throws ScicosFormatException {
138
139         if (into == null) {
140             throw new IllegalArgumentException();
141         }
142         BasicBlock block = into;
143
144         data = (ScilabMList) element;
145
146         validate();
147
148         block = beforeDecode(element, block);
149
150         /*
151          * fill the data
152          */
153         decodeDimension(block);
154         decodeOrigin(block);
155         decodeFlipAndRotation(block);
156         decodeIdCell(block);
157
158         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.EXPRS, new ScilabTypeCoder().var2vec(data.get(EXPRS_INDEX)));
159
160         if (data.size() > STYLE_INDEX && !isEmptyField(data.get(STYLE_INDEX))) {
161             final ScilabString style = (ScilabString) data.get(STYLE_INDEX);
162             final String s = style.getData()[0][0];
163
164             if (s != null && !s.isEmpty()) {
165                 block.setStyle(s);
166             }
167         }
168
169         block = afterDecode(element, block);
170
171         return block;
172     }
173
174     /**
175      * Validate the current data.
176      *
177      * This method doesn't pass the metrics because it perform many test. Therefore all these tests are trivial and the conditioned action only throw an exception.
178      *
179      * @throws ScicosFormatException
180      *             when there is a validation error.
181      */
182     // CSOFF: CyclomaticComplexity
183     // CSOFF: NPathComplexity
184     // CSOFF: JavaNCSS
185     private void validate() throws ScicosFormatException {
186         if (!canDecode(data)) {
187             throw new WrongElementException();
188         }
189
190         int field = 0;
191
192         // we test if the structure as enough field
193         if (data.size() < DATA_FIELD_NAMES.size()) {
194             throw new WrongStructureException(DATA_FIELD_NAMES);
195         }
196
197         /*
198          * Checking the MList header
199          */
200
201         // Check the first field
202         if (!(data.get(field) instanceof ScilabString)) {
203             throw new WrongTypeException(DATA_FIELD_NAMES, field);
204         }
205         final String[] header = ((ScilabString) data.get(field)).getData()[0];
206
207         // Checking for the field names
208         if (header.length < DATA_FIELD_NAMES.size()) {
209             throw new WrongStructureException(DATA_FIELD_NAMES);
210         }
211         for (int i = 0; i < DATA_FIELD_NAMES.size(); i++) {
212             if (!header[i].equals(DATA_FIELD_NAMES.get(i))) {
213                 throw new WrongStructureException(DATA_FIELD_NAMES);
214             }
215         }
216
217         /*
218          * Checking the data
219          */
220
221         // orig : must contain the coord of the block
222         field++;
223         if (!(data.get(field) instanceof ScilabDouble)) {
224             throw new WrongTypeException(DATA_FIELD_NAMES, field);
225         }
226
227         // sz : must contains the size of the block
228         field++;
229         if (!(data.get(field) instanceof ScilabDouble)) {
230             throw new WrongTypeException(DATA_FIELD_NAMES, field);
231         }
232
233         // flip
234         field++;
235         if (!(data.get(field) instanceof ScilabBoolean)) {
236             throw new WrongTypeException(DATA_FIELD_NAMES, field);
237         }
238
239         // theta
240         field++;
241         if (!(data.get(field) instanceof ScilabDouble)) {
242             throw new WrongTypeException(DATA_FIELD_NAMES, field);
243         }
244
245         // exprs
246         field++;
247         if (!(data.get(field) instanceof ScilabString) && !(data.get(field) instanceof ScilabList) && !(data.get(field) instanceof ScilabTList)
248                 && !isEmptyField(data.get(field))) {
249             throw new WrongTypeException(DATA_FIELD_NAMES, field);
250         }
251
252         // pin
253         field++;
254         if (!(data.get(field) instanceof ScilabDouble)) {
255             throw new WrongTypeException(DATA_FIELD_NAMES, field);
256         }
257
258         // pout
259         field++;
260         if (!(data.get(field) instanceof ScilabDouble)) {
261             throw new WrongTypeException(DATA_FIELD_NAMES, field);
262         }
263
264         // pein
265         field++;
266         if (!(data.get(field) instanceof ScilabDouble)) {
267             throw new WrongTypeException(DATA_FIELD_NAMES, field);
268         }
269
270         // peout
271         field++;
272         if (!(data.get(field) instanceof ScilabDouble)) {
273             throw new WrongTypeException(DATA_FIELD_NAMES, field);
274         }
275
276         // gr_i
277         // !! WARNING !! we do not care about gr_i because there are only
278         // block look related.
279         field++;
280
281         // id
282         field++;
283         if (!(data.get(field) instanceof ScilabString)) {
284             throw new WrongTypeException(DATA_FIELD_NAMES, field);
285         }
286
287         // in_implicit
288         field++;
289         if (!(data.get(field) instanceof ScilabString) && !isEmptyField(data.get(field))) {
290             throw new WrongTypeException(DATA_FIELD_NAMES, field);
291         }
292
293         // out_implicit
294         field++;
295         if (!(data.get(field) instanceof ScilabString) && !isEmptyField(data.get(field))) {
296             throw new WrongTypeException(DATA_FIELD_NAMES, field);
297         }
298
299         // field added on the 5.3-5.4 dev. cycle
300         // not checked due to compatibility
301         // in_style
302         // out_style
303         // style
304     }
305
306     // CSON: CyclomaticComplexity
307     // CSON: NPathComplexity
308     // CSON: JavaNCSS
309
310     /**
311      * Fill the block with the origin parameters
312      *
313      * @param into
314      *            the target instance
315      */
316     private void decodeOrigin(final BasicBlock into) {
317         /*
318          * Getting the values
319          */
320         double x;
321         double y;
322
323         final double[][] real = ((ScilabDouble) data.get(ORIGIN_INDEX)).getRealPart();
324         x = real[0][0];
325         final double[] vector = real[real.length - 1];
326         y = vector[vector.length - 1];
327
328         /*
329          * Apply compatibility patterns
330          */
331         x *= sizeFactor;
332         y *= sizeFactor;
333
334         /*
335          * Invert the y-axis value and translate it.
336          */
337         y = -y - into.getGeometry().getHeight();
338
339         /*
340          * fill parameter
341          */
342         into.getGeometry().setX(x);
343         into.getGeometry().setY(y);
344     }
345
346     /**
347      * Fill the block with the dimension parameters
348      *
349      * @param into
350      *            the target instance
351      */
352     private void decodeDimension(final BasicBlock into) {
353         /*
354          * Getting the values
355          */
356         double w;
357         double h;
358
359         final double[][] real = ((ScilabDouble) data.get(DIMS_INDEX)).getRealPart();
360         w = real[0][0];
361         final double[] vector = real[real.length - 1];
362         h = vector[vector.length - 1];
363
364         /*
365          * When a block has no parent diagram, the size should be updated. On a diagram decode, size is right.
366          */
367         h *= sizeFactor;
368         w *= sizeFactor;
369
370         /*
371          * fill parameter
372          */
373         into.getGeometry().setWidth(w);
374         into.getGeometry().setHeight(h);
375     }
376
377     /**
378      * Fill the block with the flip and theta parameters
379      *
380      * @param into
381      *            the target instance
382      */
383     private void decodeFlipAndRotation(final BasicBlock into) {
384         String[] interfaceFunction = new String[1];
385         controller.getObjectProperty(into.getUID(), into.getKind(), ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
386
387         StyleMap styleMap = new StyleMap(interfaceFunction[0]);
388
389         /*
390          * Flip management
391          */
392         if (!((ScilabBoolean) data.get(FLIP_INDEX)).getData()[0][0]) {
393             styleMap.put(XcosConstants.STYLE_FLIP, Boolean.TRUE.toString());
394         }
395
396         /*
397          * Rotation management
398          */
399         double theta = ((ScilabDouble) data.get(FLIP_INDEX + 1)).getRealPart()[0][0];
400         if (theta != 0) {
401             // convert to a valid value
402             theta = BlockPositioning.roundAngle(-theta);
403             styleMap.put(mxConstants.STYLE_ROTATION, Double.toString(theta));
404         }
405
406         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.STYLE, styleMap.toString());
407     }
408
409     /**
410      * Preserve the id if applicable
411      *
412      * @param into
413      *            the target instance
414      */
415     private void decodeIdCell(final BasicBlock into) {
416         if (diag == null) {
417             return;
418         }
419
420         final String[][] id = ((ScilabString) data.get(ID_INDEX)).getData();
421         if (id.length == 0 || id[0].length == 0 || id[0][0].isEmpty()) {
422             return;
423         }
424
425         /*
426          * Create the local identifier
427          */
428         final mxCell identifier = diag.createCellIdentifier(into);
429         identifier.setValue(id[0][0]);
430
431         into.insert(identifier);
432     }
433
434     /**
435      * Check if the element can be decoded.
436      *
437      * @param element
438      *            the Scicos element
439      * @return true, if the Scicos types match.
440      * @see org.scilab.modules.xcos.io.scicos.Element#canDecode(org.scilab.modules.types.ScilabType)
441      */
442     @Override
443     public boolean canDecode(ScilabType element) {
444         data = (ScilabMList) element;
445
446         final String type = ((ScilabString) data.get(0)).getData()[0][0];
447         return type.equals(DATA_FIELD_NAMES.get(0));
448     }
449 }
450 // CSON: ClassDataAbstractionCoupling