View Javadoc

1   /*
2    * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: DOMBuilder.java,v 2bab6eb875e8 2010/08/11 16:45:50 max $ */
18  
19  package net.sourceforge.jeuclid;
20  
21  import javax.annotation.concurrent.GuardedBy;
22  import javax.annotation.concurrent.ThreadSafe;
23  import javax.xml.transform.Transformer;
24  import javax.xml.transform.TransformerException;
25  import javax.xml.transform.TransformerFactory;
26  import javax.xml.transform.dom.DOMResult;
27  import javax.xml.transform.dom.DOMSource;
28  import javax.xml.transform.stream.StreamSource;
29  
30  import net.sourceforge.jeuclid.elements.generic.DocumentElement;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.w3c.dom.DOMException;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.DocumentFragment;
37  import org.w3c.dom.Element;
38  import org.w3c.dom.Node;
39  
40  /**
41   * Builds a MathML tree from a given DOM tree.
42   * 
43   * @version $Revision: 2bab6eb875e8 $
44   */
45  @ThreadSafe
46  public final class DOMBuilder {
47      /**
48       * Logger for this class
49       */
50      private static final Log LOGGER = LogFactory.getLog(DOMBuilder.class);
51  
52      private static final class SingletonHolder {
53          private static final DOMBuilder INSTANCE = new DOMBuilder();
54  
55          private SingletonHolder() {
56          }
57      }
58  
59      @GuardedBy("itself")
60      private final Transformer contentTransformer;
61  
62      @GuardedBy("itself")
63      private final Transformer identityTransformer;
64  
65      @GuardedBy("itself")
66      private final Transformer namespaceTransformer;
67  
68      /**
69       * Default constructor.
70       */
71      protected DOMBuilder() {
72          this.identityTransformer = this.createIdentityTransformer();
73          this.contentTransformer = this.createTransformer(
74                  "/net/sourceforge/jeuclid/content/mathmlc2p.xsl",
75                  this.identityTransformer);
76          this.namespaceTransformer = this.createTransformer(
77                  "/net/sourceforge/jeuclid/addMathMLNamespace.xsl",
78                  this.identityTransformer);
79      }
80  
81      private Transformer createIdentityTransformer() {
82          Transformer t;
83          try {
84              t = TransformerFactory.newInstance().newTransformer();
85          } catch (final TransformerException e) {
86              DOMBuilder.LOGGER.warn(e.getMessage());
87              t = null;
88              assert false;
89          }
90          return t;
91      }
92  
93      private Transformer createTransformer(final String sourceFile,
94              final Transformer fallback) {
95          Transformer t;
96          try {
97              t = TransformerFactory.newInstance().newTemplates(
98                      new StreamSource(DOMBuilder.class
99                              .getResourceAsStream(sourceFile))).newTransformer();
100         } catch (final TransformerException e) {
101             DOMBuilder.LOGGER.warn(e.getMessage());
102             t = fallback;
103         }
104         return t;
105     }
106 
107     /**
108      * @return the singleton instance of the DOMBuilder
109      */
110     public static DOMBuilder getInstance() {
111         return DOMBuilder.SingletonHolder.INSTANCE;
112     }
113 
114     /**
115      * use {@link #getInstance()} instead.
116      * 
117      * @return see {@link #getInstance()}
118      * @deprecated use {@link #getInstance()} instead.
119      */
120     @Deprecated
121     public static DOMBuilder getDOMBuilder() {
122         return DOMBuilder.getInstance();
123     }
124 
125     /**
126      * Constructs a builder with content math support.
127      * 
128      * @param node
129      *            The MathML document. Can be an instance of Document, Element
130      *            or DocumentFragment with Element child
131      * @return the parsed Document
132      * @see #createJeuclidDom(Node, boolean)
133      */
134     public DocumentElement createJeuclidDom(final Node node) {
135         return this.createJeuclidDom(node, true);
136     }
137 
138     /**
139      * Constructs a builder.
140      * <p>
141      * This constructor needs a valid DOM Tree. To obtain a DOM tree, you may
142      * use {@link MathMLParserSupport}.
143      * 
144      * @param node
145      *            The MathML document. Can be an instance of Document, Element
146      *            or DocumentFragment with Element child
147      * @param supportContent
148      *            if set to true, content Math will be supported. This impacts
149      *            performance.
150      * @return the parsed Document
151      * @see MathMLParserSupport
152      */
153     public DocumentElement createJeuclidDom(final Node node,
154             final boolean supportContent) {
155         return this.createJeuclidDom(node, supportContent, false);
156     }
157 
158     /**
159      * Constructs a builder.
160      * <p>
161      * This constructor needs a valid DOM Tree. To obtain a DOM tree, you may
162      * use {@link MathMLParserSupport}.
163      * 
164      * @param node
165      *            The MathML document. Can be an instance of Document, Element
166      *            or DocumentFragment with Element child
167      * @param supportContent
168      *            if set to true, content Math will be supported. This impacts
169      *            performance.
170      * @param addNamespace
171      *            if set to true, the MathML namespace will be added to all
172      *            elements.
173      * @return the parsed Document
174      * @see MathMLParserSupport
175      */
176     public DocumentElement createJeuclidDom(final Node node,
177             final boolean supportContent, final boolean addNamespace) {
178         Node documentElement;
179         if (node instanceof Document) {
180             documentElement = ((Document) node).getDocumentElement();
181         } else if (node instanceof Element) {
182             documentElement = node;
183         } else if (node instanceof DocumentFragment) {
184             final Node child = node.getFirstChild();
185             if (!(child instanceof Element)) {
186                 throw new IllegalArgumentException(
187                         "Expected DocumentFragment with Element child");
188             }
189             documentElement = child;
190         } else {
191             throw new IllegalArgumentException("Unsupported node: " + node
192                     + ". Expected either Document, Element or DocumentFragment");
193         }
194 
195         if (addNamespace) {
196             documentElement = this.applyTransform(documentElement,
197                     this.namespaceTransformer);
198         }
199 
200         final DocumentElement d;
201         if (supportContent) {
202             d = this.applyTransform(documentElement, this.contentTransformer);
203         } else {
204             d = this.applyTransform(documentElement, this.identityTransformer);
205         }
206         return d;
207     }
208 
209     private DocumentElement applyTransform(final Node src,
210             final Transformer transformer) {
211         DocumentElement d;
212         try {
213             final DOMSource source = new DOMSource(src);
214             d = new DocumentElement();
215             final DOMResult result = new DOMResult(d);
216             synchronized (transformer) {
217                 transformer.transform(source, result);
218             }
219         } catch (final TransformerException e) {
220             d = null;
221             DOMBuilder.LOGGER.warn(e.getMessage());
222         } catch (final NullPointerException e) {
223             d = null;
224             // Happens if the stylesheet was not loaded correctly
225             DOMBuilder.LOGGER.warn(e.getMessage());
226         } catch (final DOMException e) {
227             d = null;
228             DOMBuilder.LOGGER.warn(e.getMessage());
229         }
230         return d;
231     }
232 
233 }