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