source: greenstone3/trunk/src/java/org/greenstone/gsdl3/util/XMLTransformer.java@ 18435

Last change on this file since 18435 was 18435, checked in by max, 15 years ago
  1. Added a reusable ErrorListener class for handling warnings, fatal and regular errors that may occur when transforming xml with stylesheets or other xslt files.
  2. Each time a Transformer object is used, we are now registering an ErrorListener on it to deal with any Transformation-related errors.
  • Property svn:keywords set to Author Date Id Revision
File size: 12.3 KB
Line 
1/*
2 * XMLTransformer.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.util;
20
21// XML classes
22import javax.xml.transform.Transformer;
23import javax.xml.transform.TransformerFactory;
24import javax.xml.transform.TransformerConfigurationException;
25import javax.xml.transform.TransformerException;
26import javax.xml.transform.ErrorListener;
27
28import javax.xml.transform.stream.StreamSource;
29import javax.xml.transform.dom.DOMSource;
30import javax.xml.transform.stream.StreamResult;
31import javax.xml.transform.dom.DOMResult;
32
33import javax.xml.parsers.DocumentBuilderFactory;
34import javax.xml.parsers.DocumentBuilder;
35import org.w3c.dom.Element;
36import org.w3c.dom.Document;
37
38import org.w3c.dom.Node;
39import org.w3c.dom.NodeList;
40
41// other java classes
42import java.io.StringReader;
43import java.io.StringWriter;
44import java.io.File;
45import java.util.HashMap;
46import java.util.Set;
47import java.util.Map;
48import java.util.Iterator;
49
50import org.apache.xml.utils.DefaultErrorHandler;
51
52import org.apache.log4j.*;
53
54/** XMLTransformer - utility class for greenstone
55 *
56 * transforms xml using xslt
57 *
58 * @author <a href="mailto:[email protected]">Katherine Don</a>
59 * @version $Revision: 18435 $
60 */
61public class XMLTransformer {
62
63 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XMLTransformer.class.getName());
64
65 /** The transformer factory we're using */
66 TransformerFactory t_factory=null;
67
68 /**
69 * The no-arguments constructor.
70 *
71 * Any exceptions thrown are caught internally
72 *
73 * @see javax.xml.transform.TransformerFactory
74 */
75 public XMLTransformer() {
76
77 // make sure we are using the xalan transformer
78 System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
79 try {
80 this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
81
82 } catch (Exception e) {
83 logger.error("exception "+e.getMessage());
84 }
85 }
86
87
88
89 /**
90 * Transform an XML document using a XSLT stylesheet
91 *
92 * @param stylesheet a filename for an XSLT stylesheet
93 * @param xml_in the XML to be transformed
94 * @return the transformed XML
95 */
96 public String transform(String stylesheet, String xml_in) {
97
98 try {
99 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
100 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
101 transformer.setErrorListener(new TransformErrorListener());
102
103 // Use the Transformer to transform an XML Source and send the output to a Result object.
104 StringWriter output = new StringWriter();
105
106 transformer.transform(new StreamSource(new StringReader(xml_in)), new StreamResult(output));
107 return output.toString();
108 } catch (TransformerConfigurationException e) {
109 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
110 logger.error(e.getLocationAsString());
111 return "";
112 } catch (TransformerException e) {
113 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
114 return "";
115 }
116 }
117
118 public String transformToString(Document stylesheet, Document source) {
119 return transformToString(stylesheet, source, null);
120 }
121
122 public String transformToString(Document stylesheet, Document source, HashMap parameters) {
123
124 try {
125 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
126 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
127 transformer.setErrorListener(new TransformErrorListener());
128 if (parameters != null) {
129 Set params = parameters.entrySet();
130 Iterator i = params.iterator();
131 while (i.hasNext()) {
132 Map.Entry m = (Map.Entry)i.next();
133 transformer.setParameter((String)m.getKey(), m.getValue());
134 }
135 }
136 //transformer.setParameter("page_lang", source.getDocumentElement().getAttribute(GSXML.LANG_ATT));
137
138
139 // Use the Transformer to transform an XML Source and send the output to a Result object.
140 StringWriter output = new StringWriter();
141
142 transformer.transform(new DOMSource(source), new StreamResult(output));
143 return output.toString();
144 } catch (TransformerConfigurationException e) {
145 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
146 logger.error(e.getLocationAsString());
147 return "";
148 } catch (TransformerException e) {
149 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
150 return "";
151 }
152 }
153
154 public Node transform(Document stylesheet, Document source) {
155 return transform(stylesheet, source, null, null);
156 }
157
158 public Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType) {
159 try {
160 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
161 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
162 transformer.setErrorListener(new TransformErrorListener());
163 if (parameters != null) {
164 Set params = parameters.entrySet();
165 Iterator i = params.iterator();
166 while (i.hasNext()) {
167 Map.Entry m = (Map.Entry)i.next();
168 transformer.setParameter((String)m.getKey(), m.getValue());
169 }
170 }
171
172 // When we transform the DOMResult, we need to make sure the result of
173 // the transformation has a DocType. For that to happen, we need to create
174 // the DOMResult using a Document with a predefined docType.
175 // If we don't have a DocType then do the transformation with a DOMResult
176 // that does not contain any doctype (like we use to do before).
177 DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType);
178 transformer.transform(new DOMSource(source), result);
179 return result.getNode(); // pass the entire document
180 }
181 catch (TransformerConfigurationException e) {
182 return transformError("couldn't create transformer object", e);
183 }
184 catch (TransformerException e) {
185 return transformError("couldn't transform the source", e);
186 }
187 }
188
189 public Node transform(File stylesheet, File source) {
190 try {
191 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
192 transformer.setErrorListener(new TransformErrorListener());
193 DOMResult result = new DOMResult();
194 transformer.transform(new StreamSource(source), result);
195 return result.getNode().getFirstChild();
196 } catch (TransformerConfigurationException e) {
197 return transformError("couldn't create transformer object", e);
198 }
199 catch (TransformerException e) {
200 return transformError("couldn't transform the source", e);
201 }
202 }
203
204 public Node transform(File stylesheet, File source, Document docDocType) {
205 try {
206 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
207 transformer.setErrorListener(new TransformErrorListener());
208 DOMResult result = new DOMResult(docDocType);
209 transformer.transform(new StreamSource(source), result);
210 return result.getNode().getFirstChild();
211 } catch (TransformerConfigurationException e) {
212 return transformError("couldn't create transformer object", e);
213 }
214 catch (TransformerException e) {
215 return transformError("couldn't transform the source", e);
216 }
217 }
218
219 // Given a heading string on the sort of transformation error that occurred and the exception object itself,
220 // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns
221 // an xhtml error page that is constructed from it.
222 protected Node transformError(String heading, TransformerException e) {
223 String message = heading + "\n" + e.getMessage();
224 logger.error(heading + ": " + e.getMessage());
225
226 String location = e.getLocationAsString();
227 if(location != null) {
228 logger.error(location);
229 message = message + "\n" + location;
230 }
231 System.err.println("****\n" + message + "\n****");
232 return constructErrorXHTMLPage(message);
233 }
234
235 // Given an error message, splits it into separate lines based on any newlines present and generates an xhtml page
236 // (xml Element) with paragraphs for each line. This is then returned so that it can be displayed in the browser.
237 public static Element constructErrorXHTMLPage(String message) {
238 try{
239 String[] lines = message.split("\n");
240
241 Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
242 // <html></html>
243 Node htmlNode = xhtmlDoc.createElement("html");
244 xhtmlDoc.appendChild(htmlNode);
245 // <head></head>
246 Node headNode = xhtmlDoc.createElement("head");
247 htmlNode.appendChild(headNode);
248 // <title></title>
249 Node titleNode = xhtmlDoc.createElement("title");
250 headNode.appendChild(titleNode);
251 Node titleString = xhtmlDoc.createTextNode("Error occurred");
252 titleNode.appendChild(titleString);
253
254 // <body></body>
255 Node bodyNode = xhtmlDoc.createElement("body");
256 htmlNode.appendChild(bodyNode);
257
258 // finally put the message in the body
259 Node h1Node = xhtmlDoc.createElement("h1");
260 bodyNode.appendChild(h1Node);
261 Node headingString = xhtmlDoc.createTextNode("The following error occurred:");
262 h1Node.appendChild(headingString);
263
264 //Node textNode = xhtmlDoc.createTextNode(message);
265 //bodyNode.appendChild(textNode);
266
267 for (int i = 0; i < lines.length; i++) {
268 Node pNode = xhtmlDoc.createElement("p");
269 Node textNode = xhtmlDoc.createTextNode(lines[i]);
270 pNode.appendChild(textNode);
271 bodyNode.appendChild(pNode);
272 }
273
274 return xhtmlDoc.getDocumentElement();
275
276 }catch(Exception e) {
277 String errmsg = "Exception trying to construct error xhtml page from message: " + message
278 + "\n" + e.getMessage();
279 System.err.println(errmsg);
280 logger.error(errmsg);
281 return null;
282 }
283 }
284
285 // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings that may
286 // occur when transforming an xml file with an xslt stylesheet. The errors are printed both to the greenstone.log and
287 // to the tomcat console (System.err), and the error message is stored in the errorMessage variable so that it can
288 // be retrieved and be used to generate an xhtml error page.
289 static public class TransformErrorListener implements ErrorListener {
290 protected String errorMessage = null;
291
292 // Receive notification of a recoverable error.
293 public void error(TransformerException exception) {
294 handleError("Error:\n", exception);
295 }
296 // Receive notification of a non-recoverable error.
297 public void fatalError(TransformerException exception) {
298 handleError("Fatal Error:\n", exception);
299 }
300 // Receive notification of a warning.
301 public void warning(TransformerException exception) {
302 handleError("Warning:\n", exception);
303 }
304
305 public String toString(TransformerException e) {
306 String location = e.getLocationAsString();
307 if(location == null) {
308 return e.getMessage();
309 }
310 return e.getMessage() + "\n" + location;
311 }
312
313 // clears the errorPage variable after first call to this method
314 public String getErrorMessage() {
315 String errMsg = this.errorMessage;
316 if(this.errorMessage != null) {
317 this.errorMessage = null;
318 }
319 return errMsg;
320 }
321
322 // sets the errorMessage member variable to the data stored in the exception
323 // and writes the errorMessage to the logger and tomcat's System.err
324 protected void handleError(String errorType, TransformerException exception) {
325 this.errorMessage = errorType + toString(exception);
326 System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n");
327 logger.error(this.errorMessage);
328 }
329 }
330}
Note: See TracBrowser for help on using the repository browser.