* Bug #15456 fixed: Xcos 5.5.x files did not load properly
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / scicos / BlockElement.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-2015 - 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.HashMap;
22 import java.util.List;
23
24 import org.scilab.modules.types.ScilabDouble;
25 import org.scilab.modules.types.ScilabList;
26 import org.scilab.modules.types.ScilabMList;
27 import org.scilab.modules.types.ScilabString;
28 import org.scilab.modules.types.ScilabType;
29 import org.scilab.modules.xcos.JavaController;
30 import org.scilab.modules.xcos.Kind;
31 import org.scilab.modules.xcos.ObjectProperties;
32 import org.scilab.modules.xcos.block.BasicBlock;
33 import org.scilab.modules.xcos.graph.XcosDiagram;
34 import org.scilab.modules.xcos.graph.model.BlockInterFunction;
35 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
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.port.BasicPort;
40
41 /**
42  * Perform a block transformation between Scicos and Xcos.
43  */
44 // CSOFF: ClassDataAbstractionCoupling
45 // CSOFF: ClassFanOutComplexity
46 public final class BlockElement extends AbstractElement<BasicBlock> {
47     protected static final List<String> DATA_FIELD_NAMES = asList("Block", "graphics", "model", "gui", "doc");
48
49     private static final int INTERFUNCTION_INDEX = DATA_FIELD_NAMES.indexOf("gui");
50
51     /** Mutable field to easily get the data through methods */
52     private ScilabMList data;
53
54     /** In-progress decoded diagram */
55     private final XcosDiagram diag;
56
57     /** Element used to decode/encode Scicos model part into a BasicBlock */
58     private final BlockModelElement modelElement;
59
60     /** Element used to decode/encode Scicos model part into a BasicBlock */
61     private final BlockGraphicElement graphicElement;
62
63     /*
64      * Decoder state
65      */
66
67     /**
68      * Current block ordering, the ordering change on each {@link BlockElement} instance so be careful when allocated a new {@link BlockElement}.
69      */
70     private int ordering;
71     private HashMap<Long, Integer> orderingMap;
72
73     /**
74      * Default constructor.
75      *
76      * The state change on each {@link BlockElement} instance so be careful when allocated a new {@link BlockElement}.
77      */
78     public BlockElement(final JavaController controller, final XcosDiagram diag) {
79         super(controller);
80         this.diag = diag;
81
82         modelElement = new BlockModelElement(controller, diag);
83         graphicElement = new BlockGraphicElement(controller, diag);
84
85         ordering = 0;
86         orderingMap = new HashMap<>();
87     }
88
89     /**
90      * Decode the element into the block.
91      *
92      * @param element
93      *            The current Scilab data
94      * @param into
95      *            the target, if null a new instance is allocated and returned.
96      * @return the decoded block.
97      * @throws ScicosFormatException
98      *             when e decoding error occurred.
99      * @see org.scilab.modules.xcos.io.scicos.Element#decode(org.scilab.modules.types.ScilabType, java.lang.Object)
100      */
101     @Override
102     public BasicBlock decode(ScilabType element, BasicBlock into) throws ScicosFormatException {
103         data = (ScilabMList) element;
104         BasicBlock block = into;
105
106         validate();
107
108         /*
109          * Instantiate the block if it doesn't exist. Do not invoke scilab here on purpose to avoid threading issues
110          */
111         final String interfunction = ((ScilabString) data.get(INTERFUNCTION_INDEX)).getData()[0][0];
112         if (block == null) {
113             BlockInterFunction func = XcosCellFactory.lookForInterfunction(interfunction);
114             block = XcosCellFactory.createBlock(controller, func, interfunction, controller.createObject(Kind.BLOCK), Kind.BLOCK);
115         }
116
117         // associate the parent - children relation before decoding it.
118         long[] parentDiagram = {0};
119         if (diag.getKind() == Kind.BLOCK) {
120             controller.getObjectProperty(diag.getUID(), diag.getKind(), ObjectProperties.PARENT_DIAGRAM, parentDiagram);
121             controller.setObjectProperty(block.getUID(), block.getKind(), ObjectProperties.PARENT_BLOCK, diag.getUID());
122         }
123         controller.setObjectProperty(block.getUID(), block.getKind(), ObjectProperties.PARENT_DIAGRAM, parentDiagram[0]);
124
125         block = beforeDecode(element, block);
126
127         /*
128          * Allocate and setup ports
129          */
130         InputPortElement inElement = new InputPortElement(controller, data);
131         final int numberOfInputPorts = inElement.getNumberOfInputPort();
132         for (int i = 0; i < numberOfInputPorts; i++) {
133             final BasicPort port = inElement.decode(data, null);
134
135             // do not use BasicPort#addPort() to avoid the view update
136             block.insert(port, i);
137         }
138
139         OutputPortElement outElement = new OutputPortElement(controller, data);
140         final int numberOfOutputPorts = outElement.getNumberOfOutputPort();
141         for (int i = 0; i < numberOfOutputPorts; i++) {
142             final BasicPort port = outElement.decode(data, null);
143
144             // do not use BasicPort#addPort() to avoid the view update
145             block.insert(port, numberOfInputPorts + i);
146         }
147
148         /*
149          * Fill block with the data structure
150          */
151         int field = 1;
152         graphicElement.decode(data.get(field), block);
153
154         field++;
155         modelElement.decode(data.get(field), block);
156
157         field++;
158         controller.setObjectProperty(block.getUID(), block.getKind(), ObjectProperties.INTERFACE_FUNCTION, interfunction);
159
160         field++;
161         fillDocStructure(data.get(field), block);
162
163         /*
164          * Set state dependent information.
165          */
166         orderingMap.put(block.getUID(), ordering++);
167
168         block = afterDecode(element, block);
169
170         return block;
171     }
172
173     /**
174      * Use the Scicos documentation structure to get the previous Xcos IDs.
175      *
176      * @param scilabType
177      *            the scicos documentation field.
178      * @param into
179      *            the target instance.
180      */
181     private void fillDocStructure(ScilabType scilabType, BasicBlock into) {
182         /*
183          * The double type is used as the default one, generate on empty field.
184          */
185         if (scilabType instanceof ScilabDouble) {
186             return;
187         }
188
189         /*
190          * Classical behavior
191          */
192         ScilabList list = (ScilabList) scilabType;
193
194         if (list.size() > 0 && list.get(0) instanceof ScilabString) {
195             String uid = ((ScilabString) list.get(0)).getData()[0][0];
196             if (isValidUid(uid)) {
197                 into.setId(uid);
198                 return;
199             }
200         }
201     }
202
203     /**
204      * @param uid
205      *            The uid to check
206      * @return true if the uid is valid, false otherwise
207      */
208     private boolean isValidUid(String uid) {
209         final String[] components = uid.split(":");
210
211         boolean valid = components.length == 3;
212         if (valid) {
213             try {
214                 Integer.parseInt(components[0], 16);
215                 Long.parseLong(components[1], 16);
216                 Integer.parseInt(components[2], 16);
217             } catch (IllegalArgumentException e) {
218                 valid = false;
219             }
220         }
221         return valid;
222     }
223
224     /**
225      * Validate the current data.
226      *
227      * 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.
228      *
229      * @throws ScicosFormatException
230      *             when there is a validation error.
231      */
232     // CSOFF: CyclomaticComplexity
233     // CSOFF: NPathComplexity
234     private void validate() throws ScicosFormatException {
235         if (!canDecode(data)) {
236             throw new WrongElementException();
237         }
238
239         int field = 0;
240
241         // we test if the structure as enough field
242         if (data.size() != DATA_FIELD_NAMES.size()) {
243             throw new WrongStructureException(DATA_FIELD_NAMES);
244         }
245
246         /*
247          * Checking the MList header
248          */
249
250         // Check the first field
251         if (!(data.get(field) instanceof ScilabString)) {
252             throw new WrongTypeException(DATA_FIELD_NAMES, field);
253         }
254         final String[] header = ((ScilabString) data.get(field)).getData()[0];
255
256         // Checking for the field names
257         if (header.length != DATA_FIELD_NAMES.size()) {
258             throw new WrongStructureException(DATA_FIELD_NAMES);
259         }
260         for (int i = 0; i < header.length; i++) {
261             if (!header[i].equals(DATA_FIELD_NAMES.get(i))) {
262                 throw new WrongStructureException(DATA_FIELD_NAMES);
263             }
264         }
265
266         /*
267          * Checking the data
268          */
269
270         // the second field must contain list of all graphic property (how the
271         // block will be displayed )
272         field++;
273         if (!(data.get(field) instanceof ScilabMList)) {
274             throw new WrongTypeException(DATA_FIELD_NAMES, field);
275         }
276
277         // the third field must contains all the information needed to compile
278         // the block
279         field++;
280         if (!(data.get(field) instanceof ScilabMList)) {
281             throw new WrongTypeException(DATA_FIELD_NAMES, field);
282         }
283
284         // the fourth field must contains all the information needed to
285         // represent
286         // the block
287         field++;
288         if (!(data.get(field) instanceof ScilabString)) {
289             throw new WrongTypeException(DATA_FIELD_NAMES, field);
290         }
291
292         // the last field must contain a list of nothing aka scicos doc
293         field++;
294         if (!(data.get(field) instanceof ScilabList) && !isEmptyField(data.get(field))) {
295             throw new WrongTypeException(DATA_FIELD_NAMES, field);
296         }
297     }
298
299     // CSON: CyclomaticComplexity
300     // CSON: NPathComplexity
301
302     /**
303      * Test if the current instance can be used to decode the element
304      *
305      * @param element
306      *            the current element
307      * @return true, if the element can be decoded, false otherwise
308      * @see org.scilab.modules.xcos.io.scicos.Element#canDecode(org.scilab.modules.types.ScilabType)
309      */
310     @Override
311     public boolean canDecode(ScilabType element) {
312         data = (ScilabMList) element;
313
314         final String type = ((ScilabString) data.get(0)).getData()[0][0];
315         return type.equals(DATA_FIELD_NAMES.get(0));
316     }
317 }
318 // CSON: ClassFanOutComplexity
319 // CSON: ClassDataAbstractionCoupling