001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.xsl;
031:
032: import com.caucho.java.LineMap;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.Encoding;
035: import com.caucho.vfs.IOExceptionWrapper;
036: import com.caucho.vfs.Path;
037: import com.caucho.vfs.Vfs;
038: import com.caucho.vfs.WriteStream;
039: import com.caucho.xml.*;
040: import com.caucho.xpath.XPathFun;
041:
042: import org.w3c.dom.Document;
043: import org.w3c.dom.Node;
044: import org.xml.sax.ContentHandler;
045: import org.xml.sax.InputSource;
046: import org.xml.sax.SAXException;
047: import org.xml.sax.XMLReader;
048: import org.xml.sax.ext.LexicalHandler;
049:
050: import javax.xml.transform.ErrorListener;
051: import javax.xml.transform.OutputKeys;
052: import javax.xml.transform.Result;
053: import javax.xml.transform.Source;
054: import javax.xml.transform.TransformerException;
055: import javax.xml.transform.URIResolver;
056: import javax.xml.transform.dom.DOMResult;
057: import javax.xml.transform.dom.DOMSource;
058: import javax.xml.transform.sax.SAXResult;
059: import javax.xml.transform.sax.SAXSource;
060: import javax.xml.transform.stream.StreamResult;
061: import javax.xml.transform.stream.StreamSource;
062: import java.io.IOException;
063: import java.io.InputStream;
064: import java.io.OutputStream;
065: import java.io.OutputStreamWriter;
066: import java.io.Writer;
067: import java.util.ArrayList;
068: import java.util.HashMap;
069: import java.util.Properties;
070:
071: public class TransformerImpl extends javax.xml.transform.Transformer {
072: protected static L10N L = new L10N(TransformerImpl.class);
073:
074: public final static String LINE_MAP = "caucho.line-map";
075: public final static String CACHE_DEPENDS = "caucho.cache.depends";
076: public final static String GENERATE_LOCATION = "caucho.generate.location";
077:
078: protected StylesheetImpl _stylesheet;
079: protected HashMap<String, Object> _properties = new HashMap<String, Object>();
080: protected HashMap<String, Object> _parameters;
081:
082: private URIResolver _uriResolver;
083: private ErrorListener _errorListener;
084:
085: private Properties _output;
086:
087: protected LineMap _lineMap;
088:
089: protected ArrayList<Path> _cacheDepends = new ArrayList<Path>();
090:
091: protected TransformerImpl(StylesheetImpl stylesheet) {
092: _stylesheet = stylesheet;
093: _uriResolver = stylesheet.getURIResolver();
094: }
095:
096: /**
097: * Returns the URI to filename resolver.
098: */
099: public URIResolver getURIResolver() {
100: return _uriResolver;
101: }
102:
103: /**
104: * Sets the URI to filename resolver.
105: */
106: public void setURIResolver(URIResolver uriResolver) {
107: _uriResolver = uriResolver;
108: }
109:
110: /**
111: * Returns the error listener.
112: */
113: public ErrorListener getErrorListener() {
114: return _errorListener;
115: }
116:
117: /**
118: * Sets the error listener.
119: */
120: public void setErrorListener(ErrorListener errorListener) {
121: _errorListener = errorListener;
122: }
123:
124: public boolean getFeature(String name) {
125: if (name.equals(DOMResult.FEATURE))
126: return true;
127: else if (name.equals(DOMSource.FEATURE))
128: return true;
129: else if (name.equals(StreamSource.FEATURE))
130: return true;
131: else if (name.equals(StreamResult.FEATURE))
132: return true;
133: else if (name.equals(SAXSource.FEATURE))
134: return true;
135: else if (name.equals(SAXResult.FEATURE))
136: return true;
137: else
138: return false;
139: }
140:
141: public void setFeature(String name, boolean enable) {
142: if (name.equals(GENERATE_LOCATION))
143: _stylesheet.setGenerateLocation(enable);
144: }
145:
146: public StylesheetImpl getStylesheet() {
147: return _stylesheet;
148: }
149:
150: public Object getProperty(String name) {
151: Object property = _properties.get(name);
152:
153: if (property != null)
154: return property;
155:
156: if (name.equals(CACHE_DEPENDS))
157: return _cacheDepends;
158: else if (name.equals(LINE_MAP))
159: return _lineMap;
160: else
161: return _stylesheet.getProperty(name);
162: }
163:
164: public void setProperty(String name, Object value) {
165: _properties.put(name, value);
166: }
167:
168: /**
169: * Sets a parameter that XPath expressions in the stylesheet can
170: * use as $name.
171: *
172: * @param name the name of the XPath variable.
173: * @param value the value for the variable.
174: */
175: public void setParameter(String name, Object value) {
176: if (_parameters == null)
177: _parameters = new HashMap<String, Object>();
178:
179: _parameters.put(name, value);
180: }
181:
182: /**
183: * Returns a copy of the xsl:output properties.
184: *
185: * @return a copy of the properties.
186: */
187: public Properties getOutputProperties() {
188: if (_output == null)
189: _output = (Properties) _stylesheet.getOutputProperties()
190: .clone();
191:
192: return (Properties) _output.clone();
193: }
194:
195: /**
196: * Sets the output properties.
197: *
198: * @param properties the new output properties.
199: */
200: public void setOutputProperties(Properties properties) {
201: _output = properties;
202: }
203:
204: /**
205: * Sets a single xsl:output property.
206: *
207: * @param name the name of the property.
208: * @param value the value of the property.
209: */
210: public void setOutputProperty(String name, String value) {
211: if (_output == null)
212: _output = (Properties) _stylesheet.getOutputProperties()
213: .clone();
214:
215: _output.put(name, value);
216: }
217:
218: /**
219: * Returns the value of a single named xsl:output property.
220: *
221: * @param name the name of the property.
222: */
223: public String getOutputProperty(String name) {
224: if (_output == null)
225: _output = (Properties) _stylesheet.getOutputProperties()
226: .clone();
227:
228: return (String) _output.get(name);
229: }
230:
231: /**
232: * Returns the named stylesheet parameter.
233: *
234: * @param name the name of the parameter.
235: *
236: * @ return the value of the named parameter.
237: */
238: public Object getParameter(String name) {
239: if (_parameters == null)
240: return null;
241: else
242: return _parameters.get(name);
243: }
244:
245: /**
246: * Clears all the external stylesheet parameters.
247: */
248: public void clearParameters() {
249: if (_parameters != null)
250: _parameters.clear();
251:
252: if (_cacheDepends != null)
253: _cacheDepends.clear();
254: }
255:
256: /**
257: * Adds a new custom function.
258: *
259: * @param name the name of the function.
260: * @param fun the new function.
261: */
262: public void addFunction(String name, XPathFun fun) {
263: _stylesheet.addFunction(name, fun);
264: }
265:
266: /**
267: * Transforms the source into the result.
268: *
269: * @param source descriptor specifying the input source.
270: * @param result descriptor specifying the output result.
271: */
272: public void transform(Source source, Result result)
273: throws TransformerException {
274: try {
275: Node node = parseDocument(source);
276:
277: if (result instanceof StreamResult) {
278: StreamResult stream = (StreamResult) result;
279:
280: if (stream.getOutputStream() != null)
281: transform(node, stream.getOutputStream(), null,
282: result.getSystemId());
283: else if (stream.getWriter() != null) {
284: Writer writer = stream.getWriter();
285: WriteStream os = Vfs.openWrite(writer);
286:
287: if (writer instanceof OutputStreamWriter) {
288: String javaEncoding = ((OutputStreamWriter) writer)
289: .getEncoding();
290: String mimeEncoding = Encoding
291: .getMimeName(javaEncoding);
292: transform(node, os, mimeEncoding, result
293: .getSystemId());
294: } else
295: transform(node, os, null, result.getSystemId());
296:
297: os.flush();
298: os.free();
299: } else {
300: WriteStream os = Vfs.lookup(result.getSystemId())
301: .openWrite();
302:
303: try {
304: transform(node, os, null, result.getSystemId());
305: } finally {
306: os.close();
307: }
308: }
309: } else if (result instanceof DOMResult) {
310: DOMResult domResult = (DOMResult) result;
311:
312: Node resultNode = domResult.getNode();
313:
314: domResult.setNode(transform(node, resultNode));
315: } else if (result instanceof SAXResult) {
316: SAXResult sax = (SAXResult) result;
317:
318: transform(node, sax.getHandler(), sax
319: .getLexicalHandler());
320: } else
321: throw new TransformerException(String.valueOf(result));
322: } catch (TransformerException e) {
323: throw e;
324: } catch (Exception e) {
325: throw new TransformerExceptionWrapper(e);
326: }
327: }
328:
329: public void transform(Node node, OutputStream os)
330: throws TransformerException {
331: if (os instanceof WriteStream) {
332: String encoding = ((WriteStream) os).getEncoding();
333: if (encoding == null)
334: encoding = "ISO-8859-1";
335:
336: transform(node, os, encoding, null);
337: } else
338: transform(node, os, null, null);
339: }
340:
341: /**
342: * Transforms from a DOM node to an output stream.
343: *
344: * @param node the source node
345: * @param os the destination stream
346: */
347: public void transform(Node node, OutputStream os, String encoding,
348: String systemId) throws TransformerException {
349: if (node == null)
350: throw new IllegalArgumentException(
351: "can't transform null node");
352:
353: try {
354: _lineMap = null;
355: Properties output = getOutputProperties();
356:
357: WriteStream ws;
358:
359: if (os instanceof WriteStream)
360: ws = (WriteStream) os;
361: else {
362: ws = Vfs.openWrite(os);
363:
364: if (systemId != null)
365: ws.setPath(Vfs.lookup(systemId));
366: else if (node instanceof QNode) {
367: String baseURI = ((QNode) node).getBaseURI();
368:
369: if (baseURI != null)
370: ws.setPath(Vfs.lookup(baseURI));
371: }
372: }
373:
374: XmlPrinter out = new XmlPrinter(ws);
375:
376: String method = (String) output.get(OutputKeys.METHOD);
377: out.setMethod(method);
378: if (encoding == null)
379: encoding = (String) output.get(OutputKeys.ENCODING);
380: if (encoding == null && !(os instanceof WriteStream)
381: && !"html".equals(method))
382: encoding = "UTF-8";
383: if (encoding != null)
384: out.setEncoding(encoding);
385: out.setMimeType((String) output.get(OutputKeys.MEDIA_TYPE));
386: String omit = (String) output
387: .get(OutputKeys.OMIT_XML_DECLARATION);
388:
389: if (omit == null || omit.equals("false")
390: || omit.equals("no"))
391: out.setPrintDeclaration(true);
392:
393: out.setStandalone((String) output
394: .get(OutputKeys.STANDALONE));
395: out.setSystemId((String) output
396: .get(OutputKeys.DOCTYPE_SYSTEM));
397: out.setPublicId((String) output
398: .get(OutputKeys.DOCTYPE_PUBLIC));
399:
400: String indent = (String) output.get(OutputKeys.INDENT);
401: if (indent != null)
402: out.setPretty(indent.equals("true")
403: || indent.equals("yes"));
404:
405: String jsp = (String) output.get("caucho.jsp");
406: if (jsp != null)
407: out.setJSP(jsp.equals("true") || jsp.equals("yes"));
408:
409: out.setVersion((String) output.get(OutputKeys.VERSION));
410:
411: String includeContentType = (String) output
412: .get("include-content-type");
413: if (includeContentType != null)
414: out.setIncludeContentType(includeContentType
415: .equals("true")
416: || includeContentType.equals("yes"));
417:
418: if (!_stylesheet.getGenerateLocation()) {
419: } else if (node instanceof CauchoNode) {
420: String filename = ((CauchoNode) node).getFilename();
421: if (filename != null)
422: out.setLineMap(filename);
423: else
424: out.setLineMap("anonymous.xsl");
425: } else
426: out.setLineMap("anonymous.xsl");
427:
428: //out.beginDocument();
429: _stylesheet.transform(node, out, this );
430: //out.endDocument();
431: _lineMap = out.getLineMap();
432: if (os != ws) {
433: ws.flush();
434: ws.free();
435: }
436: } catch (TransformerException e) {
437: throw e;
438: } catch (Exception e) {
439: throw new TransformerExceptionWrapper(e);
440: }
441: }
442:
443: /**
444: * Transforms from the source node to the destination node, returning
445: * the destination node.
446: */
447: public Node transform(Node sourceNode, Node destNode)
448: throws SAXException, IOException {
449: _lineMap = null;
450:
451: if (destNode == null)
452: destNode = Xml.createDocument();
453:
454: DOMBuilder out = new DOMBuilder();
455: out.init(destNode);
456:
457: try {
458: out.startDocument();
459: _stylesheet.transform(sourceNode, out, this );
460: //out.endDocument();
461: } catch (Exception e) {
462: throw new IOExceptionWrapper(e);
463: }
464:
465: return destNode;
466: }
467:
468: /**
469: * Transforms from the source node to the sax handlers.
470: */
471: public void transform(Node sourceNode,
472: ContentHandler contentHandler, LexicalHandler lexicalHandler)
473: throws SAXException, IOException, TransformerException {
474: if (contentHandler == null)
475: throw new IllegalArgumentException(L
476: .l("null content handler"));
477:
478: _lineMap = null;
479:
480: SAXBuilder out = new SAXBuilder();
481: out.setContentHandler(contentHandler);
482:
483: out.startDocument();
484: _stylesheet.transform(sourceNode, out, this );
485: //out.endDocument();
486: }
487:
488: /**
489: * Parses the source XML document from the source.
490: *
491: * @param source the JAXP source.
492: *
493: * @return the parsed document.
494: */
495: protected Node parseDocument(Source source) throws IOException,
496: SAXException, TransformerException {
497: if (source instanceof StreamSource) {
498: StreamSource stream = (StreamSource) source;
499:
500: InputSource in = new InputSource();
501: in.setSystemId(stream.getSystemId());
502: in.setByteStream(stream.getInputStream());
503: in.setCharacterStream(stream.getReader());
504:
505: XmlParser parser = Xml.create();
506:
507: Node node = parser.parseDocument(in);
508:
509: parser.free();
510:
511: return node;
512:
513: // return new QDocument();
514: } else if (source instanceof DOMSource) {
515: Node node = ((DOMSource) source).getNode();
516:
517: return node != null ? node : new QDocument();
518: } else if (source instanceof StringSource) {
519: String string = ((StringSource) source).getString();
520:
521: if (string != null)
522: return parseStringDocument(string, source.getSystemId());
523: else
524: return new QDocument();
525: } else if (source instanceof SAXSource) {
526: SAXSource saxSource = (SAXSource) source;
527:
528: XMLReader reader = saxSource.getXMLReader();
529:
530: if (reader == null)
531: return new QDocument();
532:
533: InputSource inputSource = saxSource.getInputSource();
534:
535: Document doc = new QDocument();
536: DOMBuilder builder = new DOMBuilder();
537: builder.init(doc);
538: reader.setContentHandler(builder);
539:
540: reader.parse(inputSource);
541:
542: return doc;
543: }
544:
545: else
546: throw new TransformerException(L.l("unknown source {0}",
547: source));
548: }
549:
550: /**
551: * Parses the source XML document from the input stream.
552: *
553: * @param is the source input stream.
554: * @param systemId the path of the source
555: *
556: * @return document DOM node for the parsed XML.
557: */
558: protected Node parseDocument(InputStream is, String systemId)
559: throws IOException, SAXException {
560: XmlParser parser = Xml.create();
561:
562: Node node = parser.parseDocument(is);
563:
564: parser.free();
565:
566: return node;
567: }
568:
569: /**
570: * Parses the source document specified by a URL
571: *
572: * @param url path to the document to be parsed.
573: *
574: * @return the parsed document.
575: */
576: protected Node parseDocument(String url) throws IOException,
577: SAXException {
578: XmlParser parser = Xml.create();
579:
580: Node node = parser.parseDocument(url);
581:
582: parser.free();
583:
584: return node;
585: }
586:
587: /**
588: * Parses a string as an XML document.
589: *
590: * @param source the string to use as the XML source
591: * @param systemId the URL for the string document.
592: *
593: * @return the parsed document.
594: */
595: protected Node parseStringDocument(String source, String systemId)
596: throws IOException, SAXException {
597: XmlParser parser = Xml.create();
598:
599: Node node = parser.parseDocumentString(source);
600:
601: parser.free();
602:
603: return node;
604: }
605:
606: public void addCacheDepend(Path path) {
607: _cacheDepends.add(path);
608: }
609:
610: protected void addCacheDepend(String path) {
611: _cacheDepends.add(Vfs.lookup(path));
612: }
613:
614: public ArrayList<Path> getCacheDepends() {
615: return _cacheDepends;
616: }
617: }
|