KickJava   Java API By Example, From Geeks To Geeks.

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


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

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

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

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

159             String JavaDoc contentType = request.getContentType();
160
161             if (contentType == null ) {
162                 throw new IOException JavaDoc("Content-type was not specified for this request");
163             } else if (contentType.startsWith("application/x-www-form-urlencoded") ||
164                 contentType.startsWith("multipart/form-data")) {
165                 String JavaDoc requested = parameters.getParameter(FORM_NAME, null);
166                 if (requested == null) {
167                     throw new ProcessingException(
168                         "HtmlGenerator with no \"src\" parameter expects a sitemap parameter called '" +
169                         FORM_NAME + "' for handling form data"
170                     );
171                 }
172
173                 String JavaDoc sXml = request.getParameter(requested);
174
175                 requestStream = new ByteArrayInputStream JavaDoc(sXml.getBytes());
176
177             } else if (contentType.startsWith("text/plain") ||
178                 contentType.startsWith("text/xml") ||
179                 contentType.startsWith("application/xml")) {
180
181                 HttpServletRequest JavaDoc httpRequest = (HttpServletRequest JavaDoc) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
182                 if ( httpRequest == null ) {
183                     throw new ProcessingException("This functionality only works in an http environment.");
184                 }
185                 int len = request.getContentLength();
186                 if (len > 0) {
187                     requestStream = new PostInputStream(httpRequest.getInputStream(), len);
188                 } else {
189                     throw new IOException JavaDoc("getContentLen() == 0");
190                 }
191             } else {
192                 throw new IOException JavaDoc("Unexpected getContentType(): " + request.getContentType());
193             }
194
195
196         }
197
198         xpath = request.getParameter("xpath");
199         if (xpath == null) {
200             xpath = par.getParameter("xpath",null);
201         }
202
203         // append the request parameter to the URL if necessary
204
if (par.getParameterAsBoolean("copy-parameters", false)
205                 && request.getQueryString() != null) {
206             StringBuffer JavaDoc query = new StringBuffer JavaDoc(super.source);
207             query.append(super.source.indexOf("?") == -1 ? '?' : '&');
208             query.append(request.getQueryString());
209             super.source = query.toString();
210         }
211
212         try {
213             if (source != null) {
214                 this.inputSource = resolver.resolveURI(super.source);
215             }
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
234         if (this.xpath != null) {
235             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(this.inputSource.getURI());
236             buffer.append(':').append(this.xpath);
237             return buffer.toString();
238         } else {
239             return this.inputSource.getURI();
240         }
241     }
242
243     /**
244      * Generate the validity object.
245      * Before this method can be invoked the generateKey() method
246      * must be invoked.
247      *
248      * @return The generated validity object or <code>null</code> if the
249      * component is currently not cacheable.
250      */

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

261     public void generate()
262     throws IOException JavaDoc, SAXException JavaDoc, ProcessingException {
263         try {
264             // Setup an instance of Tidy.
265
Tidy tidy = new Tidy();
266             tidy.setXmlOut(true);
267
268             if (this.properties == null) {
269                 tidy.setXHTML(true);
270             } else {
271                 tidy.setConfigurationFromProps(this.properties);
272             }
273
274             //Set Jtidy warnings on-off
275
tidy.setShowWarnings(getLogger().isWarnEnabled());
276             //Set Jtidy final result summary on-off
277
tidy.setQuiet(!getLogger().isInfoEnabled());
278             //Set Jtidy infos to a String (will be logged) instead of System.out
279
StringWriter JavaDoc stringWriter = new StringWriter JavaDoc();
280             PrintWriter JavaDoc errorWriter = new PrintWriter JavaDoc(stringWriter);
281             tidy.setErrout(errorWriter);
282
283             // Extract the document using JTidy and stream it.
284

285             if (inputSource != null)
286                 requestStream = this.inputSource.getInputStream();
287
288             org.w3c.dom.Document JavaDoc doc = tidy.parseDOM(new BufferedInputStream JavaDoc(requestStream), null);
289
290             // FIXME: Jtidy doesn't warn or strip duplicate attributes in same
291
// tag; stripping.
292
XMLUtils.stripDuplicateAttributes(doc, null);
293
294             errorWriter.flush();
295             errorWriter.close();
296             if(getLogger().isWarnEnabled()) {
297                getLogger().warn(stringWriter.toString());
298             }
299
300             DOMStreamer domStreamer = new DOMStreamer(this.contentHandler,
301                                                       this.lexicalHandler);
302             this.contentHandler.startDocument();
303
304             if(xpath != null) {
305                 NodeList JavaDoc nl = processor.selectNodeList(doc, xpath);
306                 int length = nl.getLength();
307                 for(int i=0; i < length; i++) {
308                     domStreamer.stream(nl.item(i));
309                 }
310             } else {
311                 // If the HTML document contained a <?xml ... declaration, tidy would have recognized
312
// this as a processing instruction (with a 'null' target), giving problems further
313
// on in the pipeline. Therefore we only serialize the document element.
314
domStreamer.stream(doc.getDocumentElement());
315             }
316             this.contentHandler.endDocument();
317         } catch (SAXException JavaDoc e){
318             SourceUtil.handleSAXException(this.inputSource.getURI(), e);
319         }
320     }
321
322
323     public void dispose() {
324         if (this.manager != null) {
325             this.manager.release(this.processor);
326             this.manager = null;
327         }
328         this.processor = null;
329         super.dispose();
330     }
331 }
332
Popular Tags