bbd42ce52d961d6c0461a3e2ec3ab1703a14e59a
[scilab.git] / scilab / modules / graph / src / java / org / scilab / modules / graph / utils / ScilabGraphUtils.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Clement DAVID
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 package org.scilab.modules.graph.utils;
17
18 import java.awt.geom.Dimension2D;
19 import java.io.IOException;
20 import java.lang.ref.WeakReference;
21 import java.net.URL;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.WeakHashMap;
25 import java.util.logging.Logger;
26
27 import javax.swing.Icon;
28
29 import org.apache.batik.bridge.BridgeContext;
30 import org.apache.batik.bridge.DocumentLoader;
31 import org.apache.batik.bridge.GVTBuilder;
32 import org.apache.batik.bridge.UserAgent;
33 import org.apache.batik.bridge.UserAgentAdapter;
34 import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
35 import org.apache.batik.gvt.GraphicsNode;
36 import org.apache.batik.util.XMLResourceDescriptor;
37 import org.scilab.forge.jlatexmath.ParseException;
38 import org.scilab.forge.jlatexmath.TeXConstants;
39 import org.scilab.forge.jlatexmath.TeXFormula;
40 import org.scilab.forge.jlatexmath.TeXIcon;
41 import org.scilab.modules.graph.view.SupportedLabelType;
42 import org.w3c.dom.Document;
43
44 import com.mxgraph.util.mxUtils;
45
46 /**
47  * Utilities functions for ScilabGraph
48  */
49 public final class ScilabGraphUtils extends mxUtils {
50     /**
51      * Cache for the generated SVG components
52      */
53     private static Map<URL, WeakReference<GraphicsNode>> generatedSVGComponents = new HashMap<URL, WeakReference<GraphicsNode>>();
54     /**
55      * Cache for the generated SVG document sizes
56      */
57     private static Map<URL, Dimension2D> generatedSVGSizes = new HashMap<URL, Dimension2D>();
58
59     /**
60      * Cache for the generated latex icons
61      */
62     private static Map<Float, Map<String, TeXIcon>> generatedLatexIcons = new WeakHashMap<Float, Map<String, TeXIcon>>();
63
64     /**
65      * Table conversion between escaped/unescaped HTML symbols
66      */
67     private static final String[][] HTML_ESCAPE_TABLE = { { "&lt;", "<" }, { "&gt;", ">" }, { "&amp;", "&" }, { "&quot;", "\"" }, { "&agrave;", "\u00e0" },
68         { "&Agrave;", "\u00c0" }, { "&acirc;", "\u00e2" }, { "&auml;", "\u00e4" }, { "&Auml;", "\u00c4" }, { "&Acirc;", "\u00c2" },
69         { "&aring;", "\u00e5" }, { "&Aring;", "\u00c5" }, { "&aelig;", "\u00e6" }, { "&AElig;", "\u00c6" }, { "&ccedil;", "\u00e7" },
70         { "&Ccedil;", "\u00c7" }, { "&eacute;", "\u00e9" }, { "&Eacute;", "\u00c9" }, { "&egrave;", "\u00e8" }, { "&Egrave;", "\u00c8" },
71         { "&ecirc;", "\u00ea" }, { "&Ecirc;", "\u00ca" }, { "&euml;", "\u00eb" }, { "&Euml;", "\u00cb" }, { "&iuml;", "\u00ef" }, { "&Iuml;", "\u00cf" },
72         { "&ocirc;", "\u00f4" }, { "&Ocirc;", "\u00d4" }, { "&ouml;", "\u00f6" }, { "&Ouml;", "\u00d6" }, { "&oslash;", "\u00f8" },
73         { "&Oslash;", "\u00d8" }, { "&szlig;", "\u00df" }, { "&ugrave;", "\u00f9" }, { "&Ugrave;", "\u00d9" }, { "&ucirc;", "\u00fb" },
74         { "&Ucirc;", "\u00db" }, { "&uuml;", "\u00fc" }, { "&Uuml;", "\u00dc" }, { "&nbsp;", " " }, { "&reg;", "\u00a9" }, { "&copy;", "\u00ae" },
75         { "&euro;", "\u20a0" }
76     };
77
78     /**
79      * Return a cached or a new instance of a {@link GraphicsNode} generated
80      * from the SVG file.
81      *
82      * @param filename
83      *            the file to parse
84      * @return the corresponding graphic node
85      */
86     public static GraphicsNode getSVGComponent(URL filename) {
87         WeakReference<GraphicsNode> nodeRef;
88         GraphicsNode node;
89
90         nodeRef = generatedSVGComponents.get(filename);
91         if (nodeRef != null) {
92             node = nodeRef.get();
93         } else {
94             node = null;
95         }
96
97         if (node == null) {
98             try {
99                 String xmlParser = XMLResourceDescriptor.getXMLParserClassName();
100                 SAXSVGDocumentFactory df = new SAXSVGDocumentFactory(xmlParser);
101                 Document doc = df.createDocument(filename.toString());
102                 UserAgent userAgent = new UserAgentAdapter();
103                 DocumentLoader loader = new DocumentLoader(userAgent);
104                 BridgeContext ctx = new BridgeContext(userAgent, loader);
105                 ctx.setDynamicState(BridgeContext.STATIC);
106                 GVTBuilder builder = new GVTBuilder();
107
108                 node = builder.build(ctx, doc);
109
110                 generatedSVGComponents.put(filename, node.getWeakReference());
111                 generatedSVGSizes.put(filename, ctx.getDocumentSize());
112             } catch (IOException e) {
113                 Logger.getLogger(ScilabGraphUtils.class.getName()).severe(e.toString());
114             }
115         }
116         return node;
117     }
118
119     /**
120      * Get the document size for a given URL.
121      *
122      * This method use the Document size cache to get the svg element dimension
123      * and not the real size of the graphical tree.
124      *
125      * @param filename
126      *            the file
127      * @return the dimension of the file
128      */
129     public static Dimension2D getSVGDocumentSizes(URL filename) {
130         Dimension2D ret = generatedSVGSizes.get(filename);
131
132         // Generate the GraphicsNode if not available
133         if (ret == null) {
134             getSVGComponent(filename);
135             ret = generatedSVGSizes.get(filename);
136         }
137         return ret;
138     }
139
140     /**
141      * Return a cached or a new instance of a TexIcon generated from the text.
142      *
143      * @param text
144      *            the latex formula
145      * @return the TeXIcon
146      * @throws ParseException
147      *             when the text is not a valid formula
148      */
149     public static Icon getTexIcon(String text, float size) throws ParseException {
150         TeXIcon icon;
151         String escapedText = SupportedLabelType.Latex.escape(text);
152
153         Map<String, TeXIcon> iconMap = generatedLatexIcons.get(size);
154         if (iconMap == null) {
155             iconMap = new WeakHashMap<String, TeXIcon>();
156             generatedLatexIcons.put(size, iconMap);
157         }
158
159         icon = iconMap.get(escapedText);
160         if (icon == null) {
161             TeXFormula tex = new TeXFormula(escapedText);
162             icon = tex.createTeXIcon(TeXConstants.STYLE_DISPLAY, size);
163             iconMap.put(escapedText, icon);
164         }
165         return icon;
166     }
167
168     /**
169      * Remove the blank char on the beginning of the sequence. This method
170      * modify the {@link StringBuilder} in place.
171      *
172      * @param seq
173      *            the sequence
174      */
175     public static void removeBlanks(StringBuilder seq) {
176         int i;
177         for (i = 0; i < seq.length(); i++) {
178             char car = seq.charAt(i);
179             if (car != ' ' && car != '\n') {
180                 break;
181             }
182         }
183
184         seq.delete(0, i);
185     }
186
187     /**
188      * Unescape html
189      *
190      * This method modify the {@link StringBuilder} in place.
191      *
192      * @param escapedText
193      *            the working text
194      * @param index
195      *            the beginning index
196      */
197     public static void unescape(StringBuilder escapedText, int index) {
198         int start, end, table_index;
199
200         start = escapedText.indexOf("&", index);
201         if (start > -1) {
202             end = escapedText.indexOf(";", start);
203             // we don't start from the beginning
204             // the next time, to handle the case of
205             // the &
206             index = start + 1;
207
208             if (end > start) {
209                 String temp = escapedText.substring(start, end + 1);
210                 // search in HTML_ESCAPE_TABLE[][] if temp is there
211                 table_index = 0;
212                 while (table_index < HTML_ESCAPE_TABLE.length) {
213                     if (HTML_ESCAPE_TABLE[table_index][0].equals(temp)) {
214                         break;
215                     } else {
216                         table_index++;
217                     }
218                 }
219                 if (table_index < HTML_ESCAPE_TABLE.length) {
220                     escapedText.replace(start, end + 1, HTML_ESCAPE_TABLE[table_index][1]);
221                     unescape(escapedText, index); // recursive call
222                 }
223             }
224         }
225         return;
226     }
227
228     /**
229      * This class is a static singleton thus it must not be instantiated
230      */
231     private ScilabGraphUtils() {
232     }
233 }