KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > NekoHTMLGenerator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.generation;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Properties JavaDoc;
24
25 import javax.servlet.http.HttpServletRequest JavaDoc;
26
27 import org.apache.avalon.framework.activity.Disposable;
28 import org.apache.avalon.framework.configuration.Configurable;
29 import org.apache.avalon.framework.configuration.Configuration;
30 import org.apache.avalon.framework.configuration.ConfigurationException;
31 import org.apache.avalon.framework.parameters.Parameters;
32 import org.apache.avalon.framework.service.ServiceException;
33 import org.apache.avalon.framework.service.ServiceManager;
34 import org.apache.cocoon.ProcessingException;
35 import org.apache.cocoon.ResourceNotFoundException;
36 import org.apache.cocoon.caching.CacheableProcessingComponent;
37 import org.apache.cocoon.components.source.SourceUtil;
38 import org.apache.cocoon.environment.ObjectModelHelper;
39 import org.apache.cocoon.environment.Request;
40 import org.apache.cocoon.environment.SourceResolver;
41 import org.apache.cocoon.environment.http.HttpEnvironment;
42 import org.apache.cocoon.util.PostInputStream;
43 import org.apache.cocoon.xml.dom.DOMBuilder;
44 import org.apache.cocoon.xml.dom.DOMStreamer;
45 import org.apache.excalibur.source.Source;
46 import org.apache.excalibur.source.SourceException;
47 import org.apache.excalibur.source.SourceValidity;
48 import org.apache.excalibur.xml.xpath.XPathProcessor;
49 import org.apache.xerces.parsers.AbstractSAXParser;
50 import org.cyberneko.html.HTMLConfiguration;
51 import org.w3c.dom.Document JavaDoc;
52 import org.w3c.dom.NodeList JavaDoc;
53 import org.xml.sax.InputSource JavaDoc;
54 import org.xml.sax.SAXException JavaDoc;
55
56 /**
57  * @cocoon.sitemap.component.documentation
58  * The neko html generator reads HTML from a source, converts it to XHTML
59  * and generates SAX Events. It uses the NekoHTML library to do this.
60  *
61  * @cocoon.sitemap.component.name nekohtml
62  * @cocoon.sitemap.component.label content
63  * @cocoon.sitemap.component.logger sitemap.generator.nekohtml
64  * @cocoon.sitemap.component.documentation.caching
65  * Uses the last modification date of the xml document for validation
66  *
67  * @cocoon.sitemap.component.pooling.max 32
68  *
69  * @author <a HREF="mailto:dims@yahoo.com">Davanum Srinivas</a>
70  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
71  * @author <a HREF="mailto:barozzi@nicolaken.com">Nicola Ken Barozzi</a>
72  * @author <a HREF="mailto:gianugo@apache.org">Gianugo Rabellino</a>
73  *
74  * @version CVS $Id: NekoHTMLGenerator.java 153376 2005-02-11 08:50:21Z cziegeler $
75  */

76 public class NekoHTMLGenerator extends ServiceableGenerator
77 implements Configurable, CacheableProcessingComponent, Disposable {
78
79     /** The parameter that specifies what request attribute to use, if any */
80     public static final String JavaDoc FORM_NAME = "form-name";
81
82     /** The source, if coming from a file */
83     private Source inputSource;
84
85     /** The source, if coming from the request */
86     private InputStream JavaDoc requestStream;
87
88     /** XPATH expression */
89     private String JavaDoc xpath = null;
90
91     /** XPath Processor */
92     private XPathProcessor processor = null;
93
94     /** Neko properties */
95     private Properties JavaDoc properties;
96
97     public void service(ServiceManager manager)
98     throws ServiceException {
99         super.service( manager );
100         this.processor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE);
101     }
102
103     public void configure(Configuration config) throws ConfigurationException {
104
105         String JavaDoc configUrl = config.getChild("neko-config").getValue(null);
106
107         if(configUrl != null) {
108             org.apache.excalibur.source.SourceResolver resolver = null;
109             Source configSource = null;
110             try {
111                 resolver = (org.apache.excalibur.source.SourceResolver)this.manager.lookup(org.apache.excalibur.source.SourceResolver.ROLE);
112                 configSource = resolver.resolveURI(configUrl);
113                 if (getLogger().isDebugEnabled()) {
114                     getLogger().debug("Loading configuration from " + configSource.getURI());
115                 }
116
117                 this.properties = new Properties JavaDoc();
118                 this.properties.load(configSource.getInputStream());
119
120             } catch (Exception JavaDoc e) {
121                 getLogger().warn("Cannot load configuration from " + configUrl);
122                 throw new ConfigurationException("Cannot load configuration from " + configUrl, e);
123             } finally {
124                 if ( null != resolver ) {
125                     this.manager.release(resolver);
126                     resolver.release(configSource);
127                 }
128             }
129         }
130     }
131
132     /**
133      * Recycle this component.
134      * All instance variables are set to <code>null</code>.
135      */

136     public void recycle() {
137         if (this.inputSource != null) {
138             this.resolver.release( this.inputSource );
139             this.inputSource = null;
140             this.requestStream = null;
141         }
142         this.xpath = null;
143         super.recycle();
144     }
145
146     /**
147      * Setup the html generator.
148      * Try to get the last modification date of the source for caching.
149      */

150     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters par)
151     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
152         super.setup(resolver, objectModel, src, par);
153
154         Request JavaDoc request = ObjectModelHelper.getRequest(objectModel);
155         
156         if (src == null) {
157             // Handle this request as the StreamGenerator does (from the POST
158
// request or from a request parameter), but try to make sure
159
// that the output will be well-formed
160

161             String JavaDoc contentType = request.getContentType();
162
163             if (contentType == null ) {
164                 throw new IOException JavaDoc("Content-type was not specified for this request");
165             } else if (contentType.startsWith("application/x-www-form-urlencoded") ||
166                 contentType.startsWith("multipart/form-data")) {
167                 String JavaDoc requested = parameters.getParameter(FORM_NAME, null);
168                 if (requested == null) {
169                     throw new ProcessingException(
170                         "NekoHtmlGenerator with no \"src\" parameter expects a sitemap parameter called '" +
171                         FORM_NAME + "' for handling form data"
172                     );
173                 }
174
175                 String JavaDoc sXml = request.getParameter(requested);
176
177                 requestStream = new ByteArrayInputStream JavaDoc(sXml.getBytes());
178
179             } else if (contentType.startsWith("text/plain") ||
180                 contentType.startsWith("text/xml") ||
181                 contentType.startsWith("application/xml")) {
182
183                 HttpServletRequest JavaDoc httpRequest = (HttpServletRequest JavaDoc) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
184                 if ( httpRequest == null ) {
185                     throw new ProcessingException("This functionality only works in an http environment.");
186                 }
187                 int len = request.getContentLength();
188                 if (len > 0) {
189                     requestStream = new PostInputStream(httpRequest.getInputStream(), len);
190                 } else {
191                     throw new IOException JavaDoc("getContentLen() == 0");
192                 }
193             } else {
194                 throw new IOException JavaDoc("Unexpected getContentType(): " + request.getContentType());
195             }
196
197
198         }
199
200         xpath = request.getParameter("xpath");
201         if(xpath == null)
202             xpath = par.getParameter("xpath",null);
203
204         // append the request parameter to the URL if necessary
205
if (par.getParameterAsBoolean("copy-parameters", false)
206                 && request.getQueryString() != null) {
207             StringBuffer JavaDoc query = new StringBuffer JavaDoc(super.source);
208             query.append(super.source.indexOf("?") == -1 ? '?' : '&');
209             query.append(request.getQueryString());
210             super.source = query.toString();
211         }
212
213         try {
214             if (source != null)
215                 this.inputSource = resolver.resolveURI(super.source);
216         } catch (SourceException se) {
217             throw SourceUtil.handle("Unable to resolve " + super.source, se);
218         }
219     }
220
221     /**
222      * Generate the unique key.
223      * This key must be unique inside the space of this component.
224      * This method must be invoked before the generateValidity() method.
225      *
226      * @return The generated key or <code>0</code> if the component
227      * is currently not cacheable.
228      */

229     public java.io.Serializable JavaDoc getKey() {
230         if (this.inputSource == null)
231             return null;
232
233         if (this.xpath != null) {
234             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(this.inputSource.getURI());
235             buffer.append(':').append(this.xpath);
236             return buffer.toString();
237         } else {
238             return this.inputSource.getURI();
239         }
240     }
241
242     /**
243      * Generate the validity object.
244      * Before this method can be invoked the generateKey() method
245      * must be invoked.
246      *
247      * @return The generated validity object or <code>null</code> if the
248      * component is currently not cacheable.
249      */

250     public SourceValidity getValidity() {
251         if (this.inputSource == null)
252             return null;
253         return this.inputSource.getValidity();
254     }
255
256     /**
257      * Generate XML data.
258      */

259     public void generate()
260     throws IOException JavaDoc, SAXException JavaDoc, ProcessingException {
261         try {
262             HtmlSaxParser parser = new HtmlSaxParser(this.properties);
263             
264             if (inputSource != null)
265                 requestStream = this.inputSource.getInputStream();
266
267             if(xpath != null) {
268                 DOMBuilder builder = new DOMBuilder();
269                 parser.setContentHandler(builder);
270                 parser.parse(new InputSource JavaDoc(requestStream));
271                 Document JavaDoc doc = builder.getDocument();
272
273                 DOMStreamer domStreamer = new DOMStreamer(this.contentHandler,
274                                                           this.lexicalHandler);
275                 this.contentHandler.startDocument();
276                 NodeList JavaDoc nl = processor.selectNodeList(doc, xpath);
277                 int length = nl.getLength();
278                 for(int i=0; i < length; i++) {
279                     domStreamer.stream(nl.item(i));
280                 }
281                 this.contentHandler.endDocument();
282             } else {
283                 parser.setContentHandler(this.contentHandler);
284                 parser.parse(new InputSource JavaDoc(requestStream));
285             }
286             requestStream.close();
287         } catch (IOException JavaDoc e){
288             throw new ResourceNotFoundException("Could not get resource "
289                 + this.inputSource.getURI(), e);
290         } catch (SAXException JavaDoc e){
291             throw e;
292         } catch (Exception JavaDoc e){
293             throw new ProcessingException("Exception in NekoHTMLGenerator.generate()",e);
294         }
295     }
296
297
298     public void dispose() {
299         if (this.manager != null) {
300             this.manager.release(this.processor);
301             this.manager = null;
302         }
303         this.processor = null;
304         super.dispose();
305     }
306
307     public static class HtmlSaxParser extends AbstractSAXParser {
308
309         public HtmlSaxParser(Properties JavaDoc properties) {
310             super(getConfig(properties));
311         }
312     
313         private static HTMLConfiguration getConfig(Properties JavaDoc properties) {
314             HTMLConfiguration config = new HTMLConfiguration();
315             config.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
316             if (properties != null) {
317                 for (Iterator JavaDoc i = properties.keySet().iterator();i.hasNext();) {
318                     String JavaDoc name = (String JavaDoc) i.next();
319                     config.setProperty(name, properties.getProperty(name));
320                 }
321             }
322             return config;
323         }
324     }
325 }
326
Popular Tags