3728d5bca895cbd47a996491a5fc35955ea366a5
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / scicos / BlockModelElement.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Clement DAVID
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 package org.scilab.modules.xcos.io.scicos;
14
15 import static java.util.Arrays.asList;
16
17 import java.util.List;
18
19 import org.flexdock.util.UUID;
20 import org.scilab.modules.types.ScilabBoolean;
21 import org.scilab.modules.types.ScilabDouble;
22 import org.scilab.modules.types.ScilabList;
23 import org.scilab.modules.types.ScilabMList;
24 import org.scilab.modules.types.ScilabString;
25 import org.scilab.modules.types.ScilabTList;
26 import org.scilab.modules.types.ScilabType;
27 import org.scilab.modules.xcos.JavaController;
28 import org.scilab.modules.xcos.Kind;
29 import org.scilab.modules.xcos.ObjectProperties;
30 import org.scilab.modules.xcos.VectorOfDouble;
31 import org.scilab.modules.xcos.VectorOfInt;
32 import org.scilab.modules.xcos.block.BasicBlock;
33 import org.scilab.modules.xcos.graph.XcosDiagram;
34 import org.scilab.modules.xcos.io.ScilabTypeCoder;
35 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongElementException;
36 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongStructureException;
37 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongTypeException;
38 import org.scilab.modules.xcos.port.BasicPort;
39 import org.scilab.modules.xcos.port.command.CommandPort;
40 import org.scilab.modules.xcos.port.control.ControlPort;
41
42 /**
43  * Protected class which decode model fields of a block.
44  *
45  * This class is intentionally package-protected to prevent external use.
46  */
47 // CSOFF: ClassDataAbstractionCoupling
48 final class BlockModelElement extends BlockPartsElement {
49     /*
50      * "uid" have been added on the 5.5.0 cycle. It is not checked to be compatible with older versions.
51      */
52     protected static final List<String> DATA_FIELD_NAMES = asList("model", "sim", "in", "in2", "intyp", "out", "out2", "outtyp", "evtin", "evtout", "state",
53             "dstate", "odstate", "rpar", "ipar", "opar", "blocktype", "firing", "dep_ut", "label", "nzcross", "nmode", "equations");
54     protected static final List<String> DATA_FIELD_NAMES_FULL = asList("model", "sim", "in", "in2", "intyp", "out", "out2", "outtyp", "evtin", "evtout",
55             "state", "dstate", "odstate", "rpar", "ipar", "opar", "blocktype", "firing", "dep_ut", "label", "nzcross", "nmode", "equations", "uid");
56
57     private static final int CTRL_PORT_INDEX = DATA_FIELD_NAMES.indexOf("evtin");
58     private static final int CMD_PORT_INDEX = DATA_FIELD_NAMES.indexOf("evtout");
59     private static final int STATE_INDEX = DATA_FIELD_NAMES.indexOf("state");
60     private static final int FIRING_INDEX = DATA_FIELD_NAMES.indexOf("firing");
61     private static final int DEPENDU_INDEX = DATA_FIELD_NAMES.indexOf("dep_ut");
62
63     /** Mutable field to easily get the data through methods */
64     private ScilabMList data;
65
66     /** In-progress decoded diagram */
67     private final XcosDiagram diag;
68
69     /**
70      * Default constructor
71      */
72     public BlockModelElement(final JavaController controller, final XcosDiagram diag) {
73         super(controller);
74
75         this.diag = diag;
76     }
77
78     /**
79      * Decode Scicos element into the block.
80      *
81      * This decode method doesn't coverage Port management because we need graphics information to handle it.
82      *
83      * @param element
84      *            the scicos element
85      * @param into
86      *            the previously instantiated block.
87      * @return the modified into block.
88      * @throws ScicosFormatException
89      *             on error.
90      * @see org.scilab.modules.xcos.io.scicos.Element#decode(org.scilab.modules.types.ScilabType, java.lang.Object)
91      */
92     @Override
93     public BasicBlock decode(ScilabType element, BasicBlock into) throws ScicosFormatException {
94
95         if (into == null) {
96             throw new IllegalArgumentException();
97         }
98
99         data = (ScilabMList) element;
100         BasicBlock local = into;
101
102         validate();
103
104         local = beforeDecode(element, local);
105
106         /*
107          * fill the data
108          */
109         fillSimulationFunction(local);
110         fillControlCommandPorts(local);
111         fillFirstRawParameters(local);
112         fillFiringParameters(local);
113         fillSecondRawParameters(local);
114
115         local = afterDecode(element, local);
116
117         return local;
118     }
119
120     /**
121      * Fill the simulation data into the block
122      *
123      * @param into
124      *            the target instance
125      */
126     private void fillSimulationFunction(BasicBlock into) {
127         String[] functionName = new String[1];
128         controller.getObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_FUNCTION_NAME, functionName);
129
130         int[] functionType = new int[1];
131         controller.getObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_FUNCTION_API, functionType);
132
133         if (data.get(1) instanceof ScilabString) {
134             functionName[0] = ((ScilabString) data.get(1)).getData()[0][0];
135         } else if ((data.get(1) instanceof ScilabList)) {
136             functionName[0] = ((ScilabString) ((ScilabList) data.get(1)).get(0)).getData()[0][0];
137             functionType[0] = (int) ((ScilabDouble) ((ScilabList) data.get(1)).get(1)).getRealPart()[0][0];
138         }
139
140         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_FUNCTION_NAME, functionName[0]);
141         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_FUNCTION_API, functionType[0]);
142     }
143
144     /**
145      * Fill the block with the control and command ports
146      *
147      * @param into
148      *            the target instance
149      */
150     private void fillControlCommandPorts(BasicBlock into) {
151         ScilabDouble dataNbControlPort = (ScilabDouble) data.get(CTRL_PORT_INDEX);
152         ScilabDouble dataNbCommandPort = (ScilabDouble) data.get(CMD_PORT_INDEX);
153
154         if (dataNbControlPort.getRealPart() != null) {
155             final int baseIndex = into.getChildCount();
156
157             int nbControlPort = dataNbControlPort.getHeight();
158             for (int i = 0; i < nbControlPort; i++) {
159                 final BasicPort port = new ControlPort(controller.createObject(Kind.PORT));
160                 port.setId(UUID.randomUUID().toString());
161
162                 // do not use BasicPort#addPort() to avoid the view update
163                 into.insert(port, baseIndex + i);
164             }
165         }
166
167         if (dataNbCommandPort.getRealPart() != null) {
168             final int baseIndex = into.getChildCount();
169
170             int nbCommandPort = dataNbCommandPort.getHeight();
171             for (int i = 0; i < nbCommandPort; i++) {
172                 final BasicPort port = new CommandPort(controller.createObject(Kind.PORT));
173                 port.setId(UUID.randomUUID().toString());
174
175                 // do not use BasicPort#addPort() to avoid the view update
176                 into.insert(port, baseIndex + i);
177             }
178         }
179     }
180
181     /**
182      * Fill the block with the first raw parameters
183      *
184      * @param into
185      *            the target instance
186      */
187     private void fillFirstRawParameters(BasicBlock into) {
188         // state
189         int field = STATE_INDEX;
190         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.STATE, toVectorOfDouble((ScilabDouble) data.get(field)));
191
192         // dstate
193         field++;
194         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.DSTATE, toVectorOfDouble((ScilabDouble) data.get(field)));
195
196         // odstate
197         field++;
198         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.ODSTATE, new ScilabTypeCoder().var2vec(data.get(field)));
199
200         // rpar
201         field++;
202         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.RPAR, toVectorOfDouble((ScilabDouble) data.get(field)));
203
204         // ipar
205         field++;
206         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.IPAR, toVectorOfInt((ScilabDouble) data.get(field)));
207
208         // opar
209         field++;
210         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.OPAR, new ScilabTypeCoder().var2vec(data.get(field)));
211
212         // blocktype
213         field++;
214         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_BLOCKTYPE, ((ScilabString) data.get(field)).getData()[0][0]);
215     }
216
217     /**
218      * Fill the block with the firing parameters
219      *
220      * @param into
221      *            the target instance
222      */
223     private void fillFiringParameters(BasicBlock into) {
224         /*
225          * A boolean can be used to indicate that no initial event will be emitted.
226          */
227         if (data.get(FIRING_INDEX) instanceof ScilabBoolean) {
228             return;
229         }
230
231         /*
232          * Normal behavior
233          */
234         final ScilabDouble firing = (ScilabDouble) data.get(FIRING_INDEX);
235
236         if (!isEmptyField(firing)) {
237             final List<CommandPort> allCommandPorts = BasicBlockInfo.getAllTypedPorts(into, false, CommandPort.class);
238
239             final boolean isColumnDominant = firing.getHeight() >= firing.getWidth();
240             final double[][] values = firing.getRealPart();
241             final int[] indexes = { 0, 0 };
242
243             for (int i = 0; i < allCommandPorts.size(); i++) {
244                 CommandPort port = allCommandPorts.get(i);
245                 controller.setObjectProperty(port.getUID(), port.getKind(), ObjectProperties.FIRING, values[indexes[0]][indexes[1]]);
246
247                 incrementIndexes(indexes, isColumnDominant);
248             }
249         }
250     }
251
252     private VectorOfDouble toVectorOfDouble(ScilabDouble value) {
253         VectorOfDouble ret = new VectorOfDouble(value.getHeight());
254         for (int i = 0; i < value.getWidth(); i++) {
255             ret.set(i, value.getRealElement(i, 0));
256         }
257         return ret;
258     }
259
260     private VectorOfInt toVectorOfInt(ScilabDouble value) {
261         VectorOfInt ret = new VectorOfInt(value.getHeight());
262         for (int i = 0; i < value.getWidth(); i++) {
263             ret.set(i, (int) value.getRealElement(i, 0));
264         }
265         return ret;
266     }
267
268     /**
269      * Fill the block with the second raw parameters
270      *
271      * @param into
272      *            the target instance
273      * @throws WrongStructureException
274      *             on wrong value
275      */
276     private void fillSecondRawParameters(BasicBlock into) throws WrongStructureException {
277         // dep-ut
278         int field = DEPENDU_INDEX;
279         final boolean[][] dependsOn = ((ScilabBoolean) data.get(field)).getData();
280         VectorOfInt v = new VectorOfInt(2);
281
282         if (dependsOn.length == 1 && dependsOn[0].length == 2) {
283             v.set(0, dependsOn[0][0] ? 1 : 0);
284             v.set(1, dependsOn[0][1] ? 1 : 0);
285         } else if (dependsOn.length == 2 && dependsOn[0].length == 1 && dependsOn[1].length == 1) {
286             v.set(0, dependsOn[0][0] ? 1 : 0);
287             v.set(1, dependsOn[1][0] ? 1 : 0);
288         } else {
289             throw new WrongStructureException(((ScilabString) data.get(0)).getData()[0][DEPENDU_INDEX]);
290         }
291         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.SIM_DEP_UT, v);
292
293         // label
294         // do nothing
295         field++;
296
297         // nzcross
298         field++;
299         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.NZCROSS, toVectorOfInt((ScilabDouble) data.get(field)));
300
301         // nmode
302         field++;
303         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.NMODE, toVectorOfInt((ScilabDouble) data.get(field)));
304
305         // equation
306         field++;
307         controller.setObjectProperty(into.getUID(), into.getKind(), ObjectProperties.EQUATIONS, new ScilabTypeCoder().var2vec(data.get(field)));
308
309         // uid
310         // compatibility check, for pre-5.5.0 diagrams
311         field++;
312         if (field >= data.size()) {
313             return;
314         }
315         final ScilabType uid = data.get(field);
316         if (uid instanceof ScilabString) {
317             final String id = ((ScilabString) uid).getData()[0][0];
318             if (id != null && !id.isEmpty()) {
319                 into.setId(id);
320             }
321         }
322     }
323
324     /**
325      * Validate the current data.
326      *
327      * 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.
328      *
329      * @throws ScicosFormatException
330      *             when there is a validation error.
331      */
332     // CSOFF: CyclomaticComplexity
333     // CSOFF: NPathComplexity
334     // CSOFF: JavaNCSS
335     // CSOFF: MethodLength
336     private void validate() throws ScicosFormatException {
337         if (!canDecode(data)) {
338             throw new WrongElementException();
339         }
340
341         int field = 0;
342
343         // we test if the structure as enough field
344         if (data.size() < DATA_FIELD_NAMES.size()) {
345             throw new WrongStructureException(DATA_FIELD_NAMES);
346         }
347
348         /*
349          * Checking the MList header
350          */
351
352         // Check the first field
353         if (!(data.get(field) instanceof ScilabString)) {
354             throw new WrongTypeException(DATA_FIELD_NAMES, field);
355         }
356         final String[] header = ((ScilabString) data.get(field)).getData()[0];
357
358         // Checking for the field names
359         if (header.length < DATA_FIELD_NAMES.size()) {
360             throw new WrongStructureException(DATA_FIELD_NAMES);
361         }
362         for (int i = 0; i < DATA_FIELD_NAMES.size(); i++) {
363             if (!header[i].equals(DATA_FIELD_NAMES.get(i))) {
364                 throw new WrongStructureException(DATA_FIELD_NAMES);
365             }
366         }
367
368         /*
369          * Checking the data
370          */
371
372         // sim : String or list(String, int)
373         field++;
374         if (!(data.get(field) instanceof ScilabString) && !(data.get(field) instanceof ScilabList)) {
375             throw new WrongTypeException(DATA_FIELD_NAMES, field);
376         }
377
378         // in
379         field++;
380         if (!(data.get(field) instanceof ScilabDouble)) {
381             throw new WrongTypeException(DATA_FIELD_NAMES, field);
382         }
383
384         // in2
385         field++;
386         if (!(data.get(field) instanceof ScilabDouble)) {
387             throw new WrongTypeException(DATA_FIELD_NAMES, field);
388         }
389
390         // intyp
391         field++;
392         if (!(data.get(field) instanceof ScilabDouble)) {
393             throw new WrongTypeException(DATA_FIELD_NAMES, field);
394         }
395
396         // out
397         field++;
398         if (!(data.get(field) instanceof ScilabDouble)) {
399             throw new WrongTypeException(DATA_FIELD_NAMES, field);
400         }
401
402         // out2
403         field++;
404         if (!(data.get(field) instanceof ScilabDouble)) {
405             throw new WrongTypeException(DATA_FIELD_NAMES, field);
406         }
407
408         // outtyp
409         field++;
410         if (!(data.get(field) instanceof ScilabDouble)) {
411             throw new WrongTypeException(DATA_FIELD_NAMES, field);
412         }
413
414         // evtin
415         field++;
416         if (!(data.get(field) instanceof ScilabDouble)) {
417             throw new WrongTypeException(DATA_FIELD_NAMES, field);
418         }
419
420         // evtout
421         field++;
422         if (!(data.get(field) instanceof ScilabDouble)) {
423             throw new WrongTypeException(DATA_FIELD_NAMES, field);
424         }
425
426         // state
427         field++;
428         if (!(data.get(field) instanceof ScilabDouble)) {
429             throw new WrongTypeException(DATA_FIELD_NAMES, field);
430         }
431
432         // dstate
433         // TODO: the ScilabString value is undocumented
434         field++;
435         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabString)) {
436             throw new WrongTypeException(DATA_FIELD_NAMES, field);
437         }
438
439         // odstate
440         field++;
441         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabList)) {
442             throw new WrongTypeException(DATA_FIELD_NAMES, field);
443         }
444
445         // rpar
446         // SuperBlocks store all "included" data in rpar field.
447         field++;
448         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabMList) && !(data.get(field) instanceof ScilabString)) {
449             throw new WrongTypeException(DATA_FIELD_NAMES, field);
450         }
451
452         // ipar
453         // !! WARNING !! scifunc_block_m ipar = list(...)
454         field++;
455         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabList)) {
456             throw new WrongTypeException(DATA_FIELD_NAMES, field);
457         }
458
459         // opar
460         field++;
461         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabList)) {
462             throw new WrongTypeException(DATA_FIELD_NAMES, field);
463         }
464
465         // blocktype
466         field++;
467         if (!(data.get(field) instanceof ScilabString)) {
468             throw new WrongTypeException(DATA_FIELD_NAMES, field);
469         }
470
471         // firing
472         field++;
473         if (!(data.get(field) instanceof ScilabDouble) && !(data.get(field) instanceof ScilabBoolean)) {
474             throw new WrongTypeException(DATA_FIELD_NAMES, field);
475         }
476
477         // dep-ut
478         field++;
479         if (!(data.get(field) instanceof ScilabBoolean)) {
480             throw new WrongTypeException(DATA_FIELD_NAMES, field);
481         }
482
483         // label
484         field++;
485         if (!(data.get(field) instanceof ScilabString)) {
486             throw new WrongTypeException(DATA_FIELD_NAMES, field);
487         }
488
489         // nzcross
490         field++;
491         if (!(data.get(field) instanceof ScilabDouble)) {
492             throw new WrongTypeException(DATA_FIELD_NAMES, field);
493         }
494
495         // nmode
496         field++;
497         if (!(data.get(field) instanceof ScilabDouble)) {
498             throw new WrongTypeException(DATA_FIELD_NAMES, field);
499         }
500
501         // equations
502         field++;
503         if (!(data.get(field) instanceof ScilabTList) && !isEmptyField(data.get(field))) {
504             throw new WrongTypeException(DATA_FIELD_NAMES, field);
505         }
506
507         // uid not checked, introduced in Scilab 5.5.0
508     }
509
510     // CSON: CyclomaticComplexity
511     // CSON: NPathComplexity
512     // CSON: JavaNCSS
513     // CSON: MethodLength
514
515     /**
516      * Check if the element can be decoded.
517      *
518      * @param element
519      *            the Scicos element
520      * @return true, if the Scicos types match.
521      * @see org.scilab.modules.xcos.io.scicos.Element#canDecode(org.scilab.modules.types.ScilabType)
522      */
523     @Override
524     public boolean canDecode(ScilabType element) {
525         data = (ScilabMList) element;
526
527         final String type = ((ScilabString) data.get(0)).getData()[0][0];
528         return type.equals(DATA_FIELD_NAMES.get(0));
529     }
530 }
531 // CSON: ClassDataAbstractionCoupling