001: package org.apache.turbine.services.xslt;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.Reader;
025: import java.io.StringWriter;
026: import java.io.Writer;
027: import java.net.URL;
028: import java.util.Iterator;
029: import java.util.Map;
030:
031: import javax.xml.transform.Result;
032: import javax.xml.transform.Source;
033: import javax.xml.transform.Templates;
034: import javax.xml.transform.Transformer;
035: import javax.xml.transform.TransformerException;
036: import javax.xml.transform.TransformerFactory;
037: import javax.xml.transform.dom.DOMSource;
038: import javax.xml.transform.stream.StreamResult;
039: import javax.xml.transform.stream.StreamSource;
040:
041: import org.apache.commons.collections.map.LRUMap;
042: import org.apache.commons.configuration.Configuration;
043: import org.apache.commons.lang.StringUtils;
044: import org.apache.turbine.services.InitializationException;
045: import org.apache.turbine.services.TurbineBaseService;
046: import org.apache.turbine.services.servlet.TurbineServlet;
047: import org.w3c.dom.Node;
048:
049: /**
050: * Implementation of the Turbine XSLT Service. It transforms xml with a given
051: * xsl file. XSL stylesheets are compiled and cached (if the property in
052: * TurbineResources.properties is set) to improve speeds.
053: *
054: * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
055: * @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
056: * @author <a href="thomas.vandahl@tewisoft.de">Thomas Vandahl</a>
057: * @version $Id: TurbineXSLTService.java 567398 2007-08-19 13:11:33Z tv $
058: */
059: public class TurbineXSLTService extends TurbineBaseService implements
060: XSLTService {
061: /**
062: * Property to control the caching of StyleSheetRoots.
063: */
064: protected boolean caching = false;
065:
066: /**
067: * Path to style sheets used for tranforming well-formed
068: * XML documents. The path is relative to the webapp context.
069: */
070: protected String path;
071:
072: /**
073: * Cache of compiled StyleSheetRoots.
074: */
075: private LRUMap cache = new LRUMap(20);
076:
077: /**
078: * Factory for producing templates and null transformers
079: */
080: private static TransformerFactory tfactory;
081:
082: /**
083: * Initialize the TurbineXSLT Service. Load the path to search for
084: * xsl files and initiates the cache.
085: */
086: public void init() throws InitializationException {
087: Configuration conf = getConfiguration();
088:
089: path = conf.getString(STYLESHEET_PATH, STYLESHEET_PATH_DEFAULT);
090: caching = conf.getBoolean(STYLESHEET_CACHING,
091: STYLESHEET_CACHING_DEFAULT);
092:
093: tfactory = TransformerFactory.newInstance();
094:
095: setInit(true);
096: }
097:
098: /**
099: * Try to create a valid url object from the style parameter.
100: *
101: * @param style the xsl-Style
102: * @return a <code>URL</code> object or null if the style sheet could not be found
103: */
104: private URL getStyleURL(String style) {
105: StringBuffer sb = new StringBuffer(128);
106:
107: if (StringUtils.isNotEmpty(path)) {
108: if (path.charAt(0) != '/') {
109: sb.append('/');
110: }
111:
112: sb.append(path);
113:
114: if (path.charAt(path.length() - 1) != '/') {
115: sb.append('/');
116: }
117: } else {
118: sb.append('/');
119: }
120:
121: // we chop off the existing extension
122: int colon = style.lastIndexOf(".");
123:
124: if (colon > 0) {
125: sb.append(style.substring(0, colon));
126: } else {
127: sb.append(style);
128: }
129:
130: sb.append(".xsl");
131:
132: return TurbineServlet.getResource(sb.toString());
133: }
134:
135: /**
136: * Compile Templates from an input URL.
137: */
138: protected Templates compileTemplates(URL source) throws Exception {
139: InputStream in = source.openStream();
140: StreamSource xslin = new StreamSource(in, source.toString());
141: Templates root = TransformerFactory.newInstance().newTemplates(
142: xslin);
143: in.close();
144:
145: return root;
146: }
147:
148: /**
149: * Retrieves Templates. If caching is switched on the
150: * first attempt is to load the Templates from the cache.
151: * If caching is switched off or if the Stylesheet is not found
152: * in the cache a new StyleSheetRoot is compiled from an input
153: * file.
154: */
155: protected Templates getTemplates(String xslName) throws Exception {
156: synchronized (cache) {
157: if (caching && cache.containsKey(xslName)) {
158: return (Templates) cache.get(xslName);
159: }
160: }
161:
162: URL url = getStyleURL(xslName);
163:
164: if (url == null) {
165: return null;
166: }
167:
168: Templates sr = compileTemplates(url);
169:
170: synchronized (cache) {
171: if (caching) {
172: cache.put(xslName, sr);
173: }
174: }
175:
176: return sr;
177: }
178:
179: /**
180: * Transform the input source into the output source using the given style
181: *
182: * @param style the stylesheet parameter
183: * @param in the input source
184: * @param out the output source
185: * @param params XSLT parameter for the style sheet
186: *
187: * @throws TransformerException
188: */
189: protected void transform(String style, Source in, Result out,
190: Map params) throws TransformerException, IOException,
191: Exception {
192: Templates styleTemplate = getTemplates(style);
193:
194: Transformer transformer = (styleTemplate != null) ? styleTemplate
195: .newTransformer()
196: : tfactory.newTransformer();
197:
198: if (params != null) {
199: for (Iterator it = params.entrySet().iterator(); it
200: .hasNext();) {
201: Map.Entry entry = (Map.Entry) it.next();
202: transformer.setParameter(
203: String.valueOf(entry.getKey()), entry
204: .getValue());
205: }
206: }
207:
208: // Start the transformation and rendering process
209: transformer.transform(in, out);
210: }
211:
212: /**
213: * Execute an xslt
214: */
215: public void transform(String xslName, Reader in, Writer out)
216: throws Exception {
217: Source xmlin = new StreamSource(in);
218: Result xmlout = new StreamResult(out);
219:
220: transform(xslName, xmlin, xmlout, null);
221: }
222:
223: /**
224: * Execute an xslt
225: */
226: public String transform(String xslName, Reader in) throws Exception {
227: StringWriter sw = new StringWriter();
228: transform(xslName, in, sw);
229: return sw.toString();
230: }
231:
232: /**
233: * Execute an xslt
234: */
235: public void transform(String xslName, Node in, Writer out)
236: throws Exception {
237: Source xmlin = new DOMSource(in);
238: Result xmlout = new StreamResult(out);
239:
240: transform(xslName, xmlin, xmlout, null);
241: }
242:
243: /**
244: * Execute an xslt
245: */
246: public String transform(String xslName, Node in) throws Exception {
247: StringWriter sw = new StringWriter();
248: transform(xslName, in, sw);
249: return sw.toString();
250: }
251:
252: /**
253: * Execute an xslt
254: */
255: public void transform(String xslName, Reader in, Writer out,
256: Map params) throws Exception {
257: Source xmlin = new StreamSource(in);
258: Result xmlout = new StreamResult(out);
259:
260: transform(xslName, xmlin, xmlout, params);
261: }
262:
263: /**
264: * Execute an xslt
265: */
266: public String transform(String xslName, Reader in, Map params)
267: throws Exception {
268: StringWriter sw = new StringWriter();
269: transform(xslName, in, sw, params);
270: return sw.toString();
271: }
272:
273: /**
274: * Execute an xslt
275: */
276: public void transform(String xslName, Node in, Writer out,
277: Map params) throws Exception {
278: Source xmlin = new DOMSource(in);
279: Result xmlout = new StreamResult(out);
280:
281: transform(xslName, xmlin, xmlout, params);
282: }
283:
284: /**
285: * Execute an xslt
286: */
287: public String transform(String xslName, Node in, Map params)
288: throws Exception {
289: StringWriter sw = new StringWriter();
290: transform(xslName, in, sw, params);
291: return sw.toString();
292: }
293:
294: }
|