496c0e6ab982a4176e7edb754fc847c29d40a512
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / codec / XcosObjectCodec.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
4  * Copyright (C) 2014 - 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.codec;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Method;
22 import java.util.Map;
23 import java.util.WeakHashMap;
24
25 import org.scilab.modules.graph.utils.ScilabGraphConstants;
26 import org.scilab.modules.graph.utils.StyleMap;
27 import org.scilab.modules.xcos.JavaController;
28 import org.scilab.modules.xcos.Kind;
29 import org.scilab.modules.xcos.graph.model.XcosCell;
30 import org.w3c.dom.Node;
31
32 import com.mxgraph.io.mxCellCodec;
33 import com.mxgraph.io.mxCodec;
34 import com.mxgraph.model.mxICell;
35 import com.mxgraph.util.mxConstants;
36 import org.scilab.modules.xcos.utils.XcosConstants;
37
38 /**
39  * Codec for any xcos object
40  */
41 public class XcosObjectCodec extends mxCellCodec {
42     /**
43      * Refs field for codecs
44      */
45     protected static final String[] REFS = { "parent", "source", "target" };
46
47     /*
48      * Cache fields and accessors
49      */
50     protected Map<Class<?>, Map<String, Field>> fields = new WeakHashMap<>();
51     protected Map<Class<?>, Map<Field, Method>> getters = new WeakHashMap<>();
52     protected Map<Class<?>, Map<Field, Method>> setters = new WeakHashMap<>();
53
54     /**
55      * Attribute name containing {@link com.mxgraph.model.mxCell} style.
56      */
57     protected static final String STYLE = "style";
58     private static final String DIRECTION = "direction";
59     private static final String WEST = "west";
60     private static final String SOUTH = "south";
61     private static final String EAST = "east";
62
63     private static final int DIRECTION_STEP = 90;
64
65     /**
66      * The constructor used on for configuration
67      *
68      * @param template
69      *            Prototypical instance of the object to be encoded/decoded.
70      * @param exclude
71      *            Optional array of fieldnames to be ignored.
72      * @param idrefs
73      *            Optional array of fieldnames to be converted to/from
74      *            references.
75      * @param mapping
76      *            Optional mapping from field- to attributenames.
77      */
78     public XcosObjectCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) {
79         super(template, exclude, idrefs, mapping);
80
81     }
82
83     /**
84      * {@inheritDoc}
85      *
86      * Overridden for performance issues.
87      */
88     @Override
89     protected Method getAccessor(Object obj, Field field, boolean isGetter) {
90         /*
91          * Cache the object
92          */
93         final Class<?> type = obj.getClass();
94
95         Map<Field, Method> map;
96         if (isGetter) {
97             map = getters.get(type);
98         } else {
99             map = setters.get(type);
100         }
101         if (map == null) {
102             map = new WeakHashMap<Field, Method>();
103             if (isGetter) {
104                 getters.put(type, map);
105             } else {
106                 setters.put(type, map);
107             }
108         }
109
110         Method m = map.get(field);
111         if (m != null) {
112             return m;
113         }
114
115         /*
116          * Cache is empty, look for the accessor
117          */
118         m = super.getAccessor(obj, field, isGetter);
119         if (m != null) {
120             map.put(field, m);
121         }
122
123         return m;
124     }
125
126     /**
127      * {@inheritDoc}
128      *
129      * Overridden for performance issues.
130      */
131     @Override
132     protected Field getField(Object obj, String fieldname) {
133         /*
134          * Cache the object
135          */
136         final Class<?> type = obj.getClass();
137
138         Map<String, Field> map = fields.get(type);
139         if (map == null) {
140             map = new WeakHashMap<String, Field>();
141             fields.put(type, map);
142         }
143
144         Field f = map.get(fieldname);
145         if (f != null) {
146             return f;
147         }
148
149         /*
150          * Cache is empty, look for the field
151          */
152         f = super.getField(obj, fieldname);
153         if (f != null) {
154             map.put(fieldname, f);
155         }
156
157         return f;
158     }
159
160     /**
161      * {@inheritDoc}
162      *
163      * Clone the object while preserving UID / Kind allocation
164      *
165      */
166     @Override
167     protected Object cloneTemplate(Node node) {
168         Object obj = null;
169
170         try {
171             if (template.getClass().isEnum()) {
172                 obj = template.getClass().getEnumConstants()[0];
173             } else {
174                 if (XcosCell.class.isAssignableFrom(template.getClass())) {
175                     JavaController controller = new JavaController();
176                     Kind kind = ((XcosCell) template).getKind();
177
178                     Constructor<? extends Object> cstrs = template.getClass().getConstructor(Long.TYPE);
179                     obj = cstrs.newInstance(controller.createObject(kind));
180                 } else {
181                     obj = template.getClass().newInstance();
182                 }
183             }
184
185         } catch (ReflectiveOperationException e) {
186             // ignore
187             e.printStackTrace();
188         } catch (SecurityException e) {
189             // ignore
190             e.printStackTrace();
191         }
192
193         return obj;
194
195     }
196
197     /**
198      * Apply compatibility pattern to the decoded object
199      *
200      * @param dec
201      *            Codec that controls the decoding process.
202      * @param node
203      *            XML node to decode the object from.
204      * @param obj
205      *            Object decoded.
206      * @return The Object transformed
207      * @see org.scilab.modules.xcos.io.codec.XcosObjectCodec#afterDecode(com.mxgraph.io.mxCodec,
208      *      org.w3c.dom.Node, java.lang.Object)
209      */
210     @Override
211     public Object afterDecode(mxCodec dec, Node node, Object obj) {
212         if (obj instanceof mxICell) {
213             final mxICell cell = (mxICell) obj;
214
215             final Node id = node.getAttributes().getNamedItem("id");
216             if (id != null) {
217                 cell.setId(id.getNodeValue());
218             }
219
220         }
221         return super.afterDecode(dec, node, obj);
222     }
223
224     /**
225      * @param style
226      *            the style to be formatted
227      */
228     public void formatStyle(StyleMap style) {
229         if (style.containsKey(DIRECTION)) {
230             String direction = style.get(DIRECTION);
231
232             int angle = 0;
233             do {
234                 if (direction.compareTo(EAST) == 0) {
235                     break;
236                 }
237                 angle += DIRECTION_STEP;
238                 if (direction.compareTo(SOUTH) == 0) {
239                     break;
240                 }
241                 angle += DIRECTION_STEP;
242                 if (direction.compareTo(WEST) == 0) {
243                     break;
244                 }
245                 angle += DIRECTION_STEP;
246             } while (false);
247
248             style.remove(DIRECTION);
249             style.put(XcosConstants.STYLE_ROTATION, Integer.toString(angle));
250
251         }
252
253         if (!style.containsKey(ScilabGraphConstants.STYLE_FLIP)) {
254             style.put(XcosConstants.STYLE_FLIP, Boolean.FALSE.toString());
255         }
256
257         if (!style.containsKey(ScilabGraphConstants.STYLE_MIRROR)) {
258             style.put(XcosConstants.STYLE_MIRROR, Boolean.FALSE.toString());
259         }
260     }
261
262     /**
263      * Trace any msg to the xml document.
264      *
265      * @param enc
266      *            the current encoder
267      * @param node
268      *            the current node
269      * @param msg
270      *            the message
271      * @param format
272      *            the format
273      */
274     protected void trace(mxCodec enc, Node node, String msg, Object... format) {
275         node.appendChild(enc.getDocument().createComment(String.format(msg, format)));
276     }
277 }