KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > language > markup > CocoonMarkupLanguage


1 /*
2  * Copyright 1999-2005 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.components.language.markup;
17
18 import org.apache.avalon.framework.logger.LogEnabled;
19 import org.apache.avalon.framework.logger.Logger;
20
21 import org.apache.cocoon.ProcessingException;
22 import org.apache.cocoon.components.language.programming.ProgrammingLanguage;
23 import org.apache.cocoon.xml.AbstractXMLPipe;
24 import org.apache.cocoon.xml.XMLConsumer;
25 import org.apache.cocoon.xml.XMLUtils;
26
27 import org.xml.sax.Attributes JavaDoc;
28 import org.xml.sax.ContentHandler JavaDoc;
29 import org.xml.sax.SAXException JavaDoc;
30 import org.xml.sax.ext.LexicalHandler JavaDoc;
31 import org.xml.sax.helpers.AttributesImpl JavaDoc;
32
33 import java.io.File JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Set JavaDoc;
40
41 /**
42  * Base implementation of <code>MarkupLanguage</code>. This class uses
43  * logicsheets as the only means of code generation. Code generation
44  * should be decoupled from this context!!!
45  *
46  * @author <a HREF="mailto:ricardo@apache.org">Ricardo Rocha</a>
47  * @author <a HREF="mailto:ssahuc@apache.org">Sebastien Sahuc</a>
48  * @author <a HREF="mailto:dims@yahoo.com">Davanum Srinivas</a>
49  * @author <a HREF="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
50  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
51  * @version CVS $Id: CocoonMarkupLanguage.java 219681 2005-07-19 13:27:29Z anathaniel $
52  */

53 public abstract class CocoonMarkupLanguage extends AbstractMarkupLanguage
54 {
55     /**
56      * Store the dependencies of the currently loaded program.
57      */

58     private final Set JavaDoc dependencies = new HashSet JavaDoc();
59
60     /** The default constructor. */
61     public CocoonMarkupLanguage() {
62     }
63
64     /**
65      * Recycle this component: clear logic sheet list and dependencies.
66      */

67     public void recycle() {
68         super.recycle();
69         this.dependencies.clear();
70     }
71
72     /**
73      * Prepare the input source for logicsheet processing and code generation
74      * with a preprocess filter.
75      * The return <code>XMLFilter</code> object is the first filter on the
76      * transformer chain.
77      *
78      * The markup language preprocess filter adds information on the root element
79      * such as creation-date, file-name and file-path, plus it use the the passed
80      * programming language to quote <code>Strings</code> on PCDATA node.
81      *
82      * @param filename The source filename
83      * @param language The target programming language
84      * @return The preprocess filter
85      *
86      * @see PreProcessFilter
87      */

88     protected AbstractXMLPipe getPreprocessFilter(String JavaDoc filename,
89                                                   AbstractXMLPipe filter,
90                                                   ProgrammingLanguage language) {
91         PreProcessFilter prefilter = new PreProcessFilter(filter, filename, language);
92         prefilter.enableLogging(getLogger());
93         return prefilter;
94     }
95
96     /**
97      * Returns a filter that chain on the fly the requested transformers for source
98      * code generation. This method scans the input SAX events for
99      * &lt;?xml-logicsheet?&gt; processing instructions and top-level
100      * &lt;prefix:logicsheet&gt; elements. Logicsheet declarations are removed from
101      * the input document.
102      *
103      * @param logicsheetMarkupGenerator the logicsheet markup generator
104      * @return XMLFilter the filter that build on the fly the transformer chain
105      */

106     protected TransformerChainBuilderFilter getTransformerChainBuilder(
107         LogicsheetCodeGenerator logicsheetMarkupGenerator)
108     {
109         CocoonTransformerChainBuilderFilter filter =
110             new CocoonTransformerChainBuilderFilter(
111                 logicsheetMarkupGenerator);
112         filter.enableLogging(getLogger());
113         return filter;
114     }
115
116     // This is required here to avoid IllegalAccessError when
117
// CocoonTransformerChainBuilderFilter invokes the method.
118
protected void addLogicsheetToList(LanguageDescriptor language,
119                                        String JavaDoc logicsheetLocation)
120         throws IOException JavaDoc, SAXException JavaDoc, ProcessingException
121     {
122         super.addLogicsheetToList(language, logicsheetLocation);
123     }
124
125     /**
126      * Add a dependency on an external file to the document for inclusion in
127      * generated code. This is used to populate a list of <code>File</code>'s
128      * tested for change on each invocation; this information is used to assert
129      * whether regeneration is necessary. XSP uses &lt;xsp:dependency&gt;
130      * elements for this purpose.
131      *
132      * @param location The file path of the dependent file
133      * @see AbstractMarkupLanguage
134      * @see org.apache.cocoon.generation.ServerPagesGenerator
135      * @see org.apache.cocoon.generation.AbstractServerPage
136      */

137     protected void addDependency(String JavaDoc location) {
138         dependencies.add(location);
139     }
140
141     /**
142      * Returns the root element for this language.
143      */

144     public abstract String JavaDoc getRootElement();
145
146 //
147
// Inner classes
148
//
149

150     /**
151      * Preprocess filter for Cocoon Markup languages.
152      * It looks for PI event other that &lt;?xml-logisheet HREF=&quot;...&quot;&gt;
153      * for quoting them;
154      * It adds creation-date, file-name and file-path attributes to the root
155      * Element;
156      * And it quotes the PCDATA based by calling the quote method of the
157      * programming language.
158      *
159      * @see org.xml.sax.ContentHandler
160      */

161     public class PreProcessFilter extends AbstractXMLPipe implements LogEnabled {
162         protected Logger log;
163
164         protected AbstractXMLPipe filter;
165
166         protected String JavaDoc filename;
167
168         protected boolean isRootElem;
169
170         protected ProgrammingLanguage language;
171
172         protected String JavaDoc localPrefix;
173
174         /**
175          * @param filename the filename
176          * @param language the programming language
177          */

178         public PreProcessFilter (AbstractXMLPipe filter, String JavaDoc filename, ProgrammingLanguage language) {
179             super ();
180             this.filename = filename;
181             this.language = language;
182             this.filter = filter;
183             // Put meself in front of filter
184
super.setLexicalHandler(this.filter);
185             super.setContentHandler(this.filter);
186         }
187
188         public void setConsumer(XMLConsumer consumer) {
189             // Add consumer after filter
190
this.filter.setConsumer(consumer);
191         }
192
193         public void setContentHandler(ContentHandler JavaDoc handler) {
194             this.filter.setContentHandler(handler);
195         }
196
197         public void setLexicalHandler(LexicalHandler JavaDoc handler) {
198             this.filter.setLexicalHandler(handler);
199         }
200
201         public void enableLogging(Logger logger) {
202             if (this.log == null) {
203                 this.log = logger;
204             }
205         }
206
207         public void startDocument() throws SAXException JavaDoc {
208             super.startDocument();
209             isRootElem = true;
210         }
211
212         public void processingInstruction(String JavaDoc target, String JavaDoc data) throws SAXException JavaDoc {
213             if (!"xml-logicsheet".equals(target)) {
214                 data = this.language.quoteString(data);
215             }
216             super.processingInstruction(target, data);
217         }
218
219         public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
220             if (CocoonMarkupLanguage.this.getURI().equals(uri)) {
221                 this.localPrefix = prefix;
222             }
223             super.startPrefixMapping(prefix, uri);
224         }
225
226         public void startElement (String JavaDoc namespaceURI, String JavaDoc localName,
227                           String JavaDoc qName, Attributes JavaDoc atts) throws SAXException JavaDoc {
228              if (isRootElem) {
229                  if (!CocoonMarkupLanguage.this.getURI().equals(namespaceURI) ||
230                          !CocoonMarkupLanguage.this.getRootElement().equals(localName))
231                  {
232                      throw new SAXException JavaDoc("This page is not valid page of this markup langugage."
233                              + " Root element is: " + namespaceURI + ":" + localName
234                              + ", must be: " + CocoonMarkupLanguage.this.getURI()
235                              + ":" + CocoonMarkupLanguage.this.getRootElement());
236                  }
237
238                  isRootElem=false;
239                  // Store path and file name
240
int pos = this.filename.lastIndexOf(File.separatorChar);
241                  String JavaDoc name = this.filename.substring(pos + 1);
242                  String JavaDoc path = this.filename.substring(0, pos).replace(File.separatorChar, '/');
243                  // update the attributes
244
AttributesImpl JavaDoc newAtts;
245                  if (atts == null || atts.getLength() == 0) {
246                      newAtts = new AttributesImpl JavaDoc();
247                  } else {
248                      newAtts = new AttributesImpl JavaDoc(atts);
249                  }
250                  newAtts.addAttribute("", "file-name", "file-name", "CDATA", name);
251                  newAtts.addAttribute("", "file-path", "file-path", "CDATA", path);
252                  newAtts.addAttribute("", "creation-date", "creation-date", "CDATA",
253                          String.valueOf(System.currentTimeMillis()));
254                  // forward element with the modified attribute
255
super.startElement(namespaceURI, localName, qName, newAtts);
256             } else {
257                 super.startElement(namespaceURI, localName, qName, atts);
258             }
259         }
260     }
261
262     /**
263      * This filter builds on the fly a chain of transformers. It extends the
264      * <code>AbstractMarkupLanguage.TransformerChainBuilderFilter</code> so
265      * it can add common markup language features such as:
266      * <ul>
267      * <li>Looking for &lt;?xml-logisheet HREF=&quot;...&quot?;&gt; PI and
268      * &lt;xsp:xml-logisheet location=&quot;...&quot;&gt; elements to register
269      * user defined logicsheets;</li>
270      * <li>Adding all the dependencies related to the pages as
271      * &lt;xsp:dependency;&gt;...&lt;/xsp:dependency;&gt;</li>
272      * </ul>
273      *
274      * @see org.xml.sax.ContentHandler
275      */

276     public class CocoonTransformerChainBuilderFilter
277             extends TransformerChainBuilderFilter
278             implements LogEnabled {
279
280         protected Logger log;
281
282         private List JavaDoc startPrefix;
283
284         private Object JavaDoc[] rootElement;
285
286         private StringBuffer JavaDoc rootChars;
287
288         private boolean isRootElem;
289
290         private boolean insideRootElement;
291
292         private boolean finished;
293
294         private String JavaDoc localPrefix;
295
296         /**
297          * @param logicsheetMarkupGenerator the code generator
298          */

299         public CocoonTransformerChainBuilderFilter(
300             LogicsheetCodeGenerator logicsheetMarkupGenerator)
301         {
302             super(logicsheetMarkupGenerator);
303         }
304
305         /**
306          * Provide component with a logger.
307          *
308          * @param logger the logger
309          */

310         public void enableLogging(Logger logger) {
311             if (this.log == null) {
312                 this.log = logger;
313             }
314         }
315
316         public void processingInstruction(String JavaDoc target, String JavaDoc data) throws SAXException JavaDoc {
317             // Retrieve logicsheets declared by processing-instruction
318
if ("xml-logicsheet".equals(target)) {
319                 int start = data.indexOf("href");
320                 if (start >= 0) {
321                     // add 6, for lenght of 'href', plus '=' char, plus '"' char
322
start += 6;
323                     // get the quote char. Can be " or '
324
char quote = data.charAt(start-1);
325                     int end = data.indexOf(quote, start);
326                     String JavaDoc href = data.substring(start, end);
327
328                     try {
329                         CocoonMarkupLanguage.this.addLogicsheetToList(language, href);
330                     } catch (ProcessingException pe) {
331                         log.warn("ProcessingException in SitemapMarkupLanguage", pe);
332                         throw new SAXException JavaDoc (pe);
333                     } catch (IOException JavaDoc ioe) {
334                         log.warn("CocoonMarkupLanguage.processingInstruction", ioe);
335                         throw new SAXException JavaDoc (ioe);
336                     }
337                 }
338                 // Do not forward the PI event.
339
return;
340             }
341
342             // Call super when this is not a logicsheet related PI
343
super.processingInstruction(target,data);
344         }
345
346         public void startDocument () throws SAXException JavaDoc {
347             isRootElem=true;
348             insideRootElement=false;
349             finished=false;
350             startPrefix = new ArrayList JavaDoc();
351             rootChars = new StringBuffer JavaDoc();
352         }
353
354         public void startElement (String JavaDoc namespaceURI, String JavaDoc localName,
355                 String JavaDoc qName, Attributes JavaDoc atts) throws SAXException JavaDoc {
356             if (finished) {
357                 // Call super method
358
super.startElement(namespaceURI, localName, qName, atts);
359             } else {
360                 // Need more work
361
if(isRootElem) {
362                     localPrefix = "";
363                     if (qName.indexOf(':') != -1)
364                        localPrefix = qName.substring(0, qName.indexOf(':'));
365
366                     isRootElem = false;
367                     // Cache the root element and resend the SAX event when
368
// we've finished dealing with <xsp:logicsheet > elements
369
rootElement = new Object JavaDoc[4];
370                     rootElement[0]=namespaceURI;
371                     rootElement[1]=localName;
372                     rootElement[2]=qName;
373                     rootElement[3]=atts;
374                 } else {
375                     insideRootElement = true;
376                     // Retrieve logicsheets declared by top-level elements <xsp:logicsheet ...>
377
// And do not forward the startElement event
378
if (CocoonMarkupLanguage.this.getURI().equals(namespaceURI)
379                             && "logicsheet".equals(localName)) {
380                         String JavaDoc href = atts.getValue("location");
381                         try {
382                             CocoonMarkupLanguage.this.addLogicsheetToList(language, href);
383                         } catch (ProcessingException pe) {
384                             log.warn("CocoonMarkupLanguage.startElement", pe);
385                             throw new SAXException JavaDoc (pe);
386                         } catch (IOException JavaDoc ioe) {
387                             log.warn("CocoonMarkupLanguage.startElement", ioe);
388                             throw new SAXException JavaDoc (ioe);
389                         }
390                     } else {
391                         // This element is not a <xsp:logicsheet> element, so finish
392
// by:
393
// * setting the 'fisnished' flag to true ;
394
// * refiring all the cached events ;
395
// * firing all the necessary event dealing with file dependencies
396
finished = true;
397
398                         // Send SAX events 'startDocument'
399
super.startDocument();
400
401                         // Send all prefix namespace
402
String JavaDoc [] prefixArray;
403                         for (int i=0; i<startPrefix.size(); i++) {
404                             prefixArray = (String JavaDoc []) startPrefix.get(i);
405                             super.startPrefixMapping(prefixArray[0], prefixArray[1]);
406                         }
407
408                         // Send cached RootElement event
409
super.startElement(
410                             (String JavaDoc) rootElement[0],
411                             (String JavaDoc) rootElement[1],
412                             (String JavaDoc) rootElement[2],
413                             (Attributes JavaDoc) rootElement[3]
414                         );
415
416                         // Send cached characters
417
char[] ch = rootChars.toString().toCharArray();
418                         if (ch.length > 0) {
419                             super.characters(ch, 0, ch.length);
420                         }
421
422                         // Send the events dealing with dependencies.
423
// If some dependencies exist, then creates
424
// <xsp:dependency> elements
425
char[] locationChars;
426                         Iterator JavaDoc iter = CocoonMarkupLanguage.this.dependencies.iterator();
427                         while(iter.hasNext()) {
428                             super.startElement(
429                                 (String JavaDoc) rootElement[0], "dependency", localPrefix + ":dependency", XMLUtils.EMPTY_ATTRIBUTES
430                             );
431                             locationChars = ((String JavaDoc) iter.next()).toCharArray();
432                             super.characters(locationChars, 0 , locationChars.length);
433                             super.endElement((String JavaDoc)rootElement[0], "dependency", localPrefix + ":dependency");
434                         }
435
436                         // And finally forward current Element.
437
super.startElement(namespaceURI, localName, qName, atts);
438                     }
439                 }
440             }
441         }
442
443         public void endElement (String JavaDoc namespaceURI, String JavaDoc localName,
444                 String JavaDoc qName) throws SAXException JavaDoc {
445             if (finished) {
446                 // Forward the events
447
super.endElement(namespaceURI, localName, qName);
448             }
449         }
450
451         public void characters(char[] ch, int start, int length) throws SAXException JavaDoc {
452             if (finished) {
453                 super.characters(ch, start, length);
454             } else if(!insideRootElement) {
455                 // Caching the PCDATA for the root element
456
rootChars.append(ch, start, length);
457             }
458         }
459
460         public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
461             if(finished) {
462                 super.startPrefixMapping(prefix, uri);
463             } else {
464                 String JavaDoc[] prefixArray = new String JavaDoc [2];
465                 prefixArray[0]= prefix;
466                 prefixArray[1]= uri;
467                 startPrefix.add(prefixArray);
468             }
469         }
470     }
471 }
472
Popular Tags