001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.portal.layout.renderer.aspect.impl;
018:
019: import java.io.IOException;
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.HashMap;
025:
026: import javax.xml.transform.Transformer;
027: import javax.xml.transform.sax.SAXResult;
028: import javax.xml.transform.sax.TransformerHandler;
029:
030: import org.apache.avalon.framework.activity.Disposable;
031: import org.apache.avalon.framework.parameters.ParameterException;
032: import org.apache.avalon.framework.parameters.Parameters;
033: import org.apache.avalon.framework.service.ServiceException;
034: import org.apache.avalon.framework.service.ServiceManager;
035: import org.apache.avalon.framework.configuration.Configuration;
036: import org.apache.avalon.framework.configuration.ConfigurationException;
037: import org.apache.avalon.framework.configuration.Configurable;
038: import org.apache.cocoon.portal.PortalService;
039: import org.apache.cocoon.portal.layout.Layout;
040: import org.apache.cocoon.portal.layout.renderer.aspect.RendererAspectContext;
041: import org.apache.cocoon.sitemap.PatternException;
042: import org.apache.cocoon.xml.IncludeXMLConsumer;
043: import org.apache.cocoon.components.variables.VariableResolverFactory;
044: import org.apache.cocoon.components.variables.VariableResolver;
045: import org.apache.excalibur.source.Source;
046: import org.apache.excalibur.source.SourceResolver;
047: import org.apache.excalibur.xml.xslt.XSLTProcessor;
048: import org.apache.excalibur.xml.xslt.XSLTProcessorException;
049: import org.xml.sax.ContentHandler;
050: import org.xml.sax.SAXException;
051: import org.xml.sax.ext.LexicalHandler;
052:
053: /**
054: * Apply a XSLT stylesheet to the contained layout. All following renderer aspects
055: * are applied first before the XML is fed into the XSLT. All configuration and layout
056: * parameters are made available to the stylesheet.
057: *
058: * <h2>Example XML:</h2>
059: * <pre>
060: * <-- result from output of following renderers transformed by stylesheet -->
061: * </pre>
062: *
063: *
064: * The parameter values may contain Strings and/or references to input modules which will be resolved each
065: * time the aspect is rendered.
066: * <h2>Applicable to:</h2>
067: * {@link org.apache.cocoon.portal.layout.Layout}
068: *
069: * <h2>Configuration</h2>
070: * <h3>cocoon.xconf</h3>
071: *
072: * <pre>
073: * <aspect name="xslt" class="org.apache.cocoon.portal.layout.renderer.aspect.impl.XSLTAspect">
074: * <parameters>
075: * <parameter name="<i>name1</i>" value="<i>parameter value</i>"/>
076: * <parameter name="<i>name2</i>" value="<i>parameter value</i>"/>
077: * <parameter>
078: * </aspect>
079: * </pre>
080: *
081: * <h2>Parameters</h2>
082: * <table><tbody>
083: * <tr><th>style</th><td></td><td>req</td><td>String</td><td><code>null</code></td></tr>
084: * <tr><th>xslt-processor-role</th><td></td><td>req</td><td>String</td><td><code>null</code></td></tr>
085: * </tbody></table>
086: *
087: * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
088: * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
089: *
090: * @version CVS $Id: XSLTAspect.java 433543 2006-08-22 06:22:54Z crossley $
091: */
092: public class XSLTAspect extends AbstractAspect implements Disposable,
093: Configurable {
094:
095: protected List variables = new ArrayList();
096:
097: protected Parameters parameters;
098:
099: protected VariableResolverFactory variableFactory;
100:
101: /* (non-Javadoc)
102: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
103: */
104: public void service(ServiceManager manager) throws ServiceException {
105: super .service(manager);
106: this .variableFactory = (VariableResolverFactory) this .manager
107: .lookup(VariableResolverFactory.ROLE);
108: }
109:
110: /* (non-Javadoc)
111: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
112: */
113: public void configure(Configuration config)
114: throws ConfigurationException {
115: Configuration parameterItems = config.getChild("parameters",
116: false);
117:
118: if (parameterItems != null) {
119: this .parameters = Parameters
120: .fromConfiguration(parameterItems);
121: }
122: }
123:
124: /* (non-Javadoc)
125: * @see org.apache.cocoon.portal.layout.renderer.RendererAspect#toSAX(org.apache.cocoon.portal.layout.renderer.RendererAspectContext, org.apache.cocoon.portal.layout.Layout, org.apache.cocoon.portal.PortalService, org.xml.sax.ContentHandler)
126: */
127: public void toSAX(RendererAspectContext context, Layout layout,
128: PortalService service, ContentHandler handler)
129: throws SAXException {
130: PreparedConfiguration config = (PreparedConfiguration) context
131: .getAspectConfiguration();
132:
133: XSLTProcessor processor = null;
134: Source stylesheet = null;
135: SourceResolver resolver = null;
136: try {
137: resolver = (SourceResolver) this .manager
138: .lookup(SourceResolver.ROLE);
139: stylesheet = resolver.resolveURI(this .getStylesheetURI(
140: config, layout));
141: processor = (XSLTProcessor) this .manager
142: .lookup(config.xsltRole);
143: TransformerHandler transformer = processor
144: .getTransformerHandler(stylesheet);
145: // Pass configured parameters to the stylesheet.
146: if (config.parameters.size() > 0) {
147: Map.Entry entry;
148: Transformer theTransformer = transformer
149: .getTransformer();
150: Iterator iter = config.parameters.entrySet().iterator();
151: while (iter.hasNext()) {
152: entry = (Map.Entry) iter.next();
153: String value = getParameterValue(entry);
154: theTransformer.setParameter(
155: (String) entry.getKey(), value);
156: }
157: }
158:
159: Map parameter = layout.getParameters();
160: if (parameter.size() > 0) {
161: Map.Entry entry;
162: Transformer theTransformer = transformer
163: .getTransformer();
164: for (Iterator iter = parameter.entrySet().iterator(); iter
165: .hasNext();) {
166: entry = (Map.Entry) iter.next();
167: theTransformer.setParameter(
168: (String) entry.getKey(), entry.getValue());
169: }
170: }
171: SAXResult result = new SAXResult(new IncludeXMLConsumer(
172: (handler)));
173: if (handler instanceof LexicalHandler) {
174: result.setLexicalHandler((LexicalHandler) handler);
175: }
176: transformer.setResult(result);
177: transformer.startDocument();
178: context.invokeNext(layout, service, transformer);
179:
180: transformer.endDocument();
181: } catch (XSLTProcessorException xpe) {
182: throw new SAXException("XSLT Exception.", xpe);
183: } catch (IOException io) {
184: throw new SAXException("Error in resolving.", io);
185: } catch (ServiceException ce) {
186: throw new SAXException("Unable to lookup component.", ce);
187: } finally {
188: if (null != resolver) {
189: resolver.release(stylesheet);
190: this .manager.release(resolver);
191: }
192: this .manager.release(processor);
193: }
194: }
195:
196: protected String getStylesheetURI(PreparedConfiguration config,
197: Layout layout) throws SAXException {
198: // FIXME Get the stylesheet either from a layout attribute or another aspect
199: try {
200: String stylesheet = config.stylesheet.resolve();
201: return stylesheet;
202: } catch (PatternException pe) {
203: throw new SAXException(
204: "Pattern exception during variable resolving.", pe);
205: }
206: }
207:
208: protected String getParameterValue(Map.Entry entry)
209: throws SAXException {
210: try {
211: return ((VariableResolver) entry.getValue()).resolve();
212: } catch (PatternException pe) {
213: throw new SAXException("Unable to get value for parameter "
214: + entry.getKey(), pe);
215: }
216: }
217:
218: protected static class PreparedConfiguration {
219: public VariableResolver stylesheet;
220: public String xsltRole;
221: public Map parameters = new HashMap();
222:
223: public void takeValues(PreparedConfiguration from) {
224: this .stylesheet = from.stylesheet;
225: this .xsltRole = from.xsltRole;
226: this .parameters = from.parameters;
227: }
228: }
229:
230: /* (non-Javadoc)
231: * @see org.apache.cocoon.portal.layout.renderer.aspect.RendererAspect#prepareConfiguration(org.apache.avalon.framework.parameters.Parameters)
232: */
233: public Object prepareConfiguration(Parameters configuration)
234: throws ParameterException {
235: PreparedConfiguration pc = new PreparedConfiguration();
236: pc.xsltRole = configuration.getParameter("xslt-processor-role",
237: XSLTProcessor.ROLE);
238: String stylesheet = configuration.getParameter("style");
239: try {
240: pc.stylesheet = this .variableFactory.lookup(stylesheet);
241: } catch (PatternException pe) {
242: throw new ParameterException(
243: "Unknown pattern for stylesheet " + stylesheet, pe);
244: }
245: this .variables.add(pc.stylesheet);
246: if (this .parameters != null) {
247: String[] name = this .parameters.getNames();
248: for (int i = 0; i < name.length; ++i) {
249: try {
250: VariableResolver resolver = this .variableFactory
251: .lookup(this .parameters
252: .getParameter(name[i]));
253: this .variables.add(resolver);
254: pc.parameters.put(name[i], resolver);
255: } catch (PatternException e) {
256: throw new ParameterException(
257: "Invalid value for parameter " + name[i], e);
258: }
259: }
260: }
261: return pc;
262: }
263:
264: /* (non-Javadoc)
265: * @see org.apache.avalon.framework.activity.Disposable#dispose()
266: */
267: public void dispose() {
268: if (this .manager != null) {
269: Iterator vars = this .variables.iterator();
270: while (vars.hasNext()) {
271: this .variableFactory.release((VariableResolver) vars
272: .next());
273: }
274: this.variables.clear();
275: this.manager.release(this.variableFactory);
276: this.manager = null;
277: this.variableFactory = null;
278: }
279: }
280: }
|