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