001 /* 002 * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 /* $Id: DOMBuilder.java,v 2bab6eb875e8 2010/08/11 16:45:50 max $ */ 018 019 package net.sourceforge.jeuclid; 020 021 import javax.annotation.concurrent.GuardedBy; 022 import javax.annotation.concurrent.ThreadSafe; 023 import javax.xml.transform.Transformer; 024 import javax.xml.transform.TransformerException; 025 import javax.xml.transform.TransformerFactory; 026 import javax.xml.transform.dom.DOMResult; 027 import javax.xml.transform.dom.DOMSource; 028 import javax.xml.transform.stream.StreamSource; 029 030 import net.sourceforge.jeuclid.elements.generic.DocumentElement; 031 032 import org.apache.commons.logging.Log; 033 import org.apache.commons.logging.LogFactory; 034 import org.w3c.dom.DOMException; 035 import org.w3c.dom.Document; 036 import org.w3c.dom.DocumentFragment; 037 import org.w3c.dom.Element; 038 import org.w3c.dom.Node; 039 040 /** 041 * Builds a MathML tree from a given DOM tree. 042 * 043 * @version $Revision: 2bab6eb875e8 $ 044 */ 045 @ThreadSafe 046 public final class DOMBuilder { 047 /** 048 * Logger for this class 049 */ 050 private static final Log LOGGER = LogFactory.getLog(DOMBuilder.class); 051 052 private static final class SingletonHolder { 053 private static final DOMBuilder INSTANCE = new DOMBuilder(); 054 055 private SingletonHolder() { 056 } 057 } 058 059 @GuardedBy("itself") 060 private final Transformer contentTransformer; 061 062 @GuardedBy("itself") 063 private final Transformer identityTransformer; 064 065 @GuardedBy("itself") 066 private final Transformer namespaceTransformer; 067 068 /** 069 * Default constructor. 070 */ 071 protected DOMBuilder() { 072 this.identityTransformer = this.createIdentityTransformer(); 073 this.contentTransformer = this.createTransformer( 074 "/net/sourceforge/jeuclid/content/mathmlc2p.xsl", 075 this.identityTransformer); 076 this.namespaceTransformer = this.createTransformer( 077 "/net/sourceforge/jeuclid/addMathMLNamespace.xsl", 078 this.identityTransformer); 079 } 080 081 private Transformer createIdentityTransformer() { 082 Transformer t; 083 try { 084 t = TransformerFactory.newInstance().newTransformer(); 085 } catch (final TransformerException e) { 086 DOMBuilder.LOGGER.warn(e.getMessage()); 087 t = null; 088 assert false; 089 } 090 return t; 091 } 092 093 private Transformer createTransformer(final String sourceFile, 094 final Transformer fallback) { 095 Transformer t; 096 try { 097 t = TransformerFactory.newInstance().newTemplates( 098 new StreamSource(DOMBuilder.class 099 .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 }