2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - DIGITEO - Clement DAVID
4 * Copyright (C) 2011-2017 - Scilab Enterprises - Clement DAVID
6 * Copyright (C) 2012 - 2016 - Scilab Enterprises
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.
17 package org.scilab.modules.xcos.io.scicos;
19 import static java.util.Arrays.asList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
26 import org.scilab.modules.types.ScilabBoolean;
27 import org.scilab.modules.types.ScilabDouble;
28 import org.scilab.modules.types.ScilabList;
29 import org.scilab.modules.types.ScilabMList;
30 import org.scilab.modules.types.ScilabString;
31 import org.scilab.modules.types.ScilabTList;
32 import org.scilab.modules.types.ScilabType;
33 import org.scilab.modules.xcos.JavaController;
34 import org.scilab.modules.xcos.block.BasicBlock;
35 import org.scilab.modules.xcos.block.SuperBlock;
36 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
37 import org.scilab.modules.xcos.graph.model.ScicosObjectOwner;
38 import org.scilab.modules.xcos.graph.ScicosParameters;
39 import org.scilab.modules.xcos.graph.XcosDiagram;
40 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.VersionMismatchException;
41 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongStructureException;
42 import org.scilab.modules.xcos.io.scicos.ScicosFormatException.WrongTypeException;
43 import org.scilab.modules.xcos.link.BasicLink;
44 import org.scilab.modules.xcos.port.BasicPort;
45 import org.scilab.modules.xcos.utils.BlockPositioning;
47 import com.mxgraph.model.mxCell;
48 import com.mxgraph.model.mxGeometry;
49 import com.mxgraph.model.mxGraphModel;
50 import com.mxgraph.model.mxICell;
51 import com.mxgraph.model.mxIGraphModel;
52 import com.mxgraph.util.mxPoint;
53 import org.scilab.modules.xcos.Xcos;
56 * Perform a diagram transformation between Scicos and Xcos.
58 // CSOFF: ClassDataAbstractionCoupling
59 // CSOFF: ClassFanOutComplexity
60 public final class DiagramElement extends AbstractElement<XcosDiagram> {
61 protected static final List<String> DATA_FIELD_NAMES = asList("diagram", "props", "objs");
62 protected static final List<String> DATA_FIELD_NAMES_FULL = asList("diagram", "props", "objs", "version", "contrib");
63 private static final List<String> VERSIONS = asList("", "scicos4.2", "scicos4.3", "scicos4.4");
65 private static final int OBJS_INDEX = DATA_FIELD_NAMES_FULL.indexOf("objs");
66 private static final int VERSION_INDEX = DATA_FIELD_NAMES_FULL.indexOf("version");
68 private static final double H_MARGIN = 40.0;
69 private static final double V_MARGIN = 40.0;
71 /** Diagram properties MList header (scs_m.props) */
72 private static final String[] PROPS_FIELDS = { "params", "wpar", "title", "tol", "tf", "context", "void1", "options", "void2", "void3", "doc" };
74 /** Index of the title in the props field */
75 private static final int TITLE_INDEX = 2;
77 /** Diagram options MList header (scs_m.props.options) */
78 private static final String[] OPTS_FIELDS = { "scsopt", "3D", "Background", "Link", "ID", "Cmap" };
80 * Window properties (scs_m.props.wpar).
82 * This property has no impact among simulation
84 private static final double[][] WPAR = { { 600, 450, 0, 0, 600, 450 } };
86 // The window parameters and diagram options are not used in the simulation
87 // thus we set it to default values.
88 // As the values are scicos dependent we avoid using constant references.
90 private static final ScilabTList DIAGRAM_OPTIONS = new ScilabTList(OPTS_FIELDS,
93 Arrays.asList(new ScilabBoolean(true), new ScilabDouble(33))),
94 new ScilabDouble(new double[][] { { 8, 1 } }), // Background
95 new ScilabDouble(new double[][] { { 1, 5 } }), // Link
97 Arrays.asList(new ScilabDouble(new double[][] { { 5, 1 } }), new ScilabDouble(new double[][] { { 4, 1 } }))),
98 new ScilabDouble(new double[][] { { 0.8, 0.8, 0.8 } }) // Cmap
102 private ScilabMList base;
104 private double minimalYaxisValue = Double.POSITIVE_INFINITY;
105 private double minimalXaxisValue = Double.POSITIVE_INFINITY;
108 * Default constructor
110 public DiagramElement(final JavaController controller) {
115 * Decode the diagram with version validation.
118 * the diagram Scicos element
120 * the Xcos instance, if null, a new instance is returned.
121 * @return the modified into parameters
122 * @throws ScicosFormatException
123 * when a decoding error occurs
124 * @see org.scilab.modules.xcos.io.scicos.Element#decode(org.scilab.modules.types.ScilabType, java.lang.Object)
127 public XcosDiagram decode(ScilabType element, XcosDiagram into) throws ScicosFormatException {
128 return decode(element, into, true);
135 * the diagram Scicos element
137 * the Xcos instance, if null, a new instance is returned.
139 * true, if the diagram version will be checked. false otherwise.
140 * @return the modified into parameters
141 * @throws ScicosFormatException
142 * when a decoding error occurs
143 * @see org.scilab.modules.xcos.io.scicos.Element#decode(org.scilab.modules.types.ScilabType, java.lang.Object)
145 public XcosDiagram decode(ScilabType element, XcosDiagram into, boolean validate) throws ScicosFormatException {
146 base = (ScilabMList) element;
148 XcosDiagram diag = into;
150 throw new IllegalArgumentException();
153 diag = beforeDecode(element, diag);
154 diag.getModel().beginUpdate();
156 // Validate the base field
157 String wrongVersion = null;
160 } catch (VersionMismatchException e) {
161 wrongVersion = e.getWrongVersion();
165 decodeDiagram(diag, validate);
167 diag.getModel().endUpdate();
168 diag = afterDecode(element, diag);
170 // Rethrow the version exception after a decode.
171 if (wrongVersion != null) {
172 throw new VersionMismatchException(wrongVersion);
182 * the current diagram
184 * if true, enable graphic updates ; false disable them
185 * @throws ScicosFormatException
188 private void decodeDiagram(XcosDiagram diag, boolean validate) throws ScicosFormatException {
189 // Fill the local parameters
190 // NOTE: the title field is checked on the ScicosParametersElement
191 final String title = ((ScilabString) ((ScilabTList) base.get(1)).get(2)).getData()[0][0];
192 diag.setTitle(title);
194 // Fill the diagram attributes
195 ScicosParametersElement params = new ScicosParametersElement(controller);
196 params.decode(base.get(1), new ScicosParameters(Xcos.findRoot(controller, diag), new ScicosObjectOwner(controller, diag.getUID(), diag.getKind())));
198 // Decode the objs attributes
200 // Update the objs properties if applicable
201 updateObjs(diag, validate);
205 * Decode the objs list into cells
208 * the target instance
209 * @throws ScicosFormatException
212 private void decodeObjs(final XcosDiagram diag) throws ScicosFormatException {
213 final int nbOfObjs = ((ScilabList) base.get(OBJS_INDEX)).size();
214 final HashMap<Integer, BasicBlock> blocks = new HashMap<Integer, BasicBlock>(nbOfObjs, 1.0f);
216 final BlockElement blockElement = new BlockElement(controller, diag);
217 final LinkElement linkElement = new LinkElement(controller, blocks);
218 final LabelElement labelElement = new LabelElement(controller);
223 for (int i = 0; i < nbOfObjs; i++) {
224 final ScilabMList data = (ScilabMList) ((ScilabList) base.get(OBJS_INDEX)).get(i);
227 if (blockElement.canDecode(data)) {
228 BasicBlock block = blockElement.decode(data, null);
229 blocks.put(i, block);
232 BlockPositioning.updateBlockView(diag, block);
234 minimalYaxisValue = Math.min(minimalYaxisValue, ((mxCell) cell).getGeometry().getY());
235 minimalXaxisValue = Math.min(minimalXaxisValue, ((mxCell) cell).getGeometry().getX());
236 } else if (labelElement.canDecode(data)) {
237 cell = labelElement.decode(data, null);
239 minimalYaxisValue = Math.min(minimalYaxisValue, ((mxCell) cell).getGeometry().getY());
240 minimalXaxisValue = Math.min(minimalXaxisValue, ((mxCell) cell).getGeometry().getX());
251 for (int i = nbOfObjs - 1; i >= 0; i--) {
252 final ScilabMList data = (ScilabMList) ((ScilabList) base.get(OBJS_INDEX)).get(i);
255 if (linkElement.canDecode(data)) {
256 BasicLink link = linkElement.decode(data, null);
259 final List<mxPoint> points = ((mxCell) cell).getGeometry().getPoints();
260 for (final mxPoint p : points) {
261 minimalYaxisValue = Math.min(minimalYaxisValue, p.getY());
262 minimalXaxisValue = Math.min(minimalXaxisValue, p.getX());
273 * Update the diagram object after decode
276 * the diagram to update
278 * perform graphic updates, or not
280 private void updateObjs(final XcosDiagram diag, boolean validate) {
281 final mxGraphModel model = (mxGraphModel) diag.getModel();
283 final double minY = -minimalYaxisValue + V_MARGIN;
284 final double minX = -minimalXaxisValue + H_MARGIN;
285 for (final Object cell : model.getCells().values()) {
286 updateMinimalSize(cell, model);
287 translate(cell, model, minX, minY);
289 updateLabels(cell, model);
294 // update the cell size to be at least selectable
295 private static final void updateMinimalSize(final Object cell, final mxIGraphModel model) {
296 if (!(cell instanceof BasicBlock)) {
300 final double min = 7.0;
302 final mxGeometry geom = model.getGeometry(cell);
308 if (geom.getWidth() < min) {
309 dx = (geom.getWidth() - min) / 2;
315 if (geom.getHeight() < min) {
316 dy = (geom.getHeight() - min) / 2;
322 geom.translate(dx, dy);
325 // Translate the y axis for blocks and links
326 private static final void translate(final Object cell, final mxIGraphModel model, final double minX, final double minY) {
327 if (cell instanceof BasicPort) {
331 final mxGeometry geom = model.getGeometry(cell);
333 geom.translate(minX, minY);
337 // update the labels of ports for SuperBlock
338 private static final void updateLabels(final Object cell, final mxIGraphModel model) {
339 if (!(cell instanceof SuperBlock)) {
342 final SuperBlock parent = (SuperBlock) cell;
344 // Assume that the children are sorted after decode
345 final Map<IOBlocks, List<mxICell>> ports = IOBlocks.getAllPorts(parent);
346 // FIXME is it really needed
347 // final Map<IOBlocks, List<BasicBlock>> blocks = IOBlocks.getAllBlocks(parent);
349 // for (final IOBlocks io : IOBlocks.values()) {
350 // final List<mxICell> port = ports.get(io);
351 // final List<BasicBlock> block = blocks.get(io);
353 // final int len = Math.min(port.size(), block.size());
354 // for (int i = 0; i < len; i++) {
355 // final mxICell p = port.get(i);
356 // final mxICell b = block.get(i);
358 // // if the I/O block has a port child and a label child,
360 // if (b.getChildCount() > 1) {
361 // final Object value = b.getChildAt(b.getChildCount() - 1).getValue();
362 // p.setValue(value);
369 * Check that the current ScilabType is a valid Diagram.
371 * 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.
373 * @param checkVersion
374 * true, when the check validate the version
375 * @throws ScicosFormatException
376 * When the diagram is not valid
378 // CSOFF: CyclomaticComplexity
379 // CSOFF: NPathComplexity
380 private void validate(boolean checkVersion) throws ScicosFormatException {
382 // Have we enough fields ?
383 if (base.size() < DATA_FIELD_NAMES.size()) {
384 throw new WrongStructureException(DATA_FIELD_NAMES);
390 * Checking the MList header
393 // Check the first field
394 if (!(base.get(field) instanceof ScilabString)) {
395 throw new WrongTypeException(DATA_FIELD_NAMES, field);
397 String[] header = ((ScilabString) base.get(field)).getData()[0];
399 // Check the number of fields
400 if (header.length < DATA_FIELD_NAMES.size()) {
401 throw new WrongStructureException(DATA_FIELD_NAMES);
404 // Check the first fields values
405 for (int i = 0; i < DATA_FIELD_NAMES.size(); i++) {
406 if (!header[i].equals(DATA_FIELD_NAMES.get(i))) {
407 throw new WrongStructureException(DATA_FIELD_NAMES);
412 * Checking the data types
415 // the second field must contain list of props
417 if (!(base.get(field) instanceof ScilabTList)) {
418 throw new WrongTypeException(DATA_FIELD_NAMES, field);
421 // the third field must contains lists of blocks and links
423 if (!(base.get(field) instanceof ScilabList)) {
424 throw new WrongTypeException(DATA_FIELD_NAMES, field);
427 // the last field must contain the scicos version used
430 // doesn't check version if not present (optional field)
431 if (base.size() <= field) {
435 if (!(base.get(field) instanceof ScilabString)) {
436 throw new WrongTypeException(DATA_FIELD_NAMES, field);
440 * Check the version if applicable
443 String scicosVersion = ((ScilabString) base.get(field)).getData()[0][0];
444 if (!VERSIONS.contains(scicosVersion)) {
445 throw new VersionMismatchException(scicosVersion);
450 // CSON: CyclomaticComplexity
451 // CSON: NPathComplexity
456 * @return true if the header is valid, false otherwise
457 * @see org.scilab.modules.xcos.io.scicos.Element#canDecode(org.scilab.modules.types.ScilabType)
460 public boolean canDecode(ScilabType element) {
461 if (!(element instanceof ScilabMList)) {
465 base = (ScilabMList) element;
470 final String type = ((ScilabString) base.get(0)).getData()[0][0];
471 final boolean typeIsValid = type.equals(DATA_FIELD_NAMES.get(0));
474 * Check the version if applicable
476 final String scicosVersion;
477 if (base.size() > VERSION_INDEX) {
478 scicosVersion = ((ScilabString) base.get(VERSION_INDEX)).getData()[0][0];
482 final boolean versionIsValid = VERSIONS.contains(scicosVersion);
483 return typeIsValid && versionIsValid;
486 // CSON: ClassDataAbstractionCoupling
487 // CSON: ClassFanOutComplexity