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