KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jasper > compiler > PageDataImpl


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

17 package org.apache.jasper.compiler;
18
19 import java.io.InputStream JavaDoc;
20 import java.io.ByteArrayInputStream JavaDoc;
21 import java.io.CharArrayWriter JavaDoc;
22 import java.io.UnsupportedEncodingException JavaDoc;
23 import java.util.ListIterator JavaDoc;
24 import javax.servlet.jsp.tagext.PageData JavaDoc;
25 import org.xml.sax.Attributes JavaDoc;
26 import org.xml.sax.helpers.AttributesImpl JavaDoc;
27 import org.apache.jasper.JasperException;
28
29 /**
30  * An implementation of <tt>javax.servlet.jsp.tagext.PageData</tt> which
31  * builds the XML view of a given page.
32  *
33  * The XML view is built in two passes:
34  *
35  * During the first pass, the FirstPassVisitor collects the attributes of the
36  * top-level jsp:root and those of the jsp:root elements of any included
37  * pages, and adds them to the jsp:root element of the XML view.
38  * In addition, any taglib directives are converted into xmlns: attributes and
39  * added to the jsp:root element of the XML view.
40  * This pass ignores any nodes other than JspRoot and TaglibDirective.
41  *
42  * During the second pass, the SecondPassVisitor produces the XML view, using
43  * the combined jsp:root attributes determined in the first pass and any
44  * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective
45  * nodes).
46  *
47  * @author Jan Luehe
48  */

49 class PageDataImpl extends PageData JavaDoc implements TagConstants {
50
51     private static final String JavaDoc JSP_VERSION = "2.0";
52     private static final String JavaDoc CDATA_START_SECTION = "<![CDATA[\n";
53     private static final String JavaDoc CDATA_END_SECTION = "]]>\n";
54
55     // string buffer used to build XML view
56
private StringBuffer JavaDoc buf;
57
58     /**
59      * Constructor.
60      *
61      * @param page the page nodes from which to generate the XML view
62      */

63     public PageDataImpl(Node.Nodes page, Compiler JavaDoc compiler)
64             throws JasperException {
65
66     // First pass
67
FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(),
68                               compiler.getPageInfo());
69     page.visit(firstPass);
70
71     // Second pass
72
buf = new StringBuffer JavaDoc();
73     SecondPassVisitor secondPass
74         = new SecondPassVisitor(page.getRoot(), buf, compiler,
75                     firstPass.getJspIdPrefix());
76     page.visit(secondPass);
77     }
78
79     /**
80      * Returns the input stream of the XML view.
81      *
82      * @return the input stream of the XML view
83      */

84     public InputStream JavaDoc getInputStream() {
85     // Turn StringBuffer into InputStream
86
try {
87             return new ByteArrayInputStream JavaDoc(buf.toString().getBytes("UTF-8"));
88         } catch (UnsupportedEncodingException JavaDoc uee) {
89         // should never happen
90
throw new RuntimeException JavaDoc(uee.toString());
91         }
92     }
93
94     /*
95      * First-pass Visitor for JspRoot nodes (representing jsp:root elements)
96      * and TablibDirective nodes, ignoring any other nodes.
97      *
98      * The purpose of this Visitor is to collect the attributes of the
99      * top-level jsp:root and those of the jsp:root elements of any included
100      * pages, and add them to the jsp:root element of the XML view.
101      * In addition, this Visitor converts any taglib directives into xmlns:
102      * attributes and adds them to the jsp:root element of the XML view.
103      */

104     static class FirstPassVisitor
105             extends Node.Visitor implements TagConstants {
106
107     private Node.Root root;
108     private AttributesImpl JavaDoc rootAttrs;
109     private PageInfo pageInfo;
110
111     // Prefix for the 'id' attribute
112
private String JavaDoc jspIdPrefix;
113
114     /*
115      * Constructor
116      */

117     public FirstPassVisitor(Node.Root root, PageInfo pageInfo) {
118         this.root = root;
119         this.pageInfo = pageInfo;
120         this.rootAttrs = new AttributesImpl JavaDoc();
121         this.rootAttrs.addAttribute("", "", "version", "CDATA",
122                     JSP_VERSION);
123         this.jspIdPrefix = "jsp";
124     }
125
126     public void visit(Node.Root n) throws JasperException {
127         visitBody(n);
128         if (n == root) {
129         /*
130          * Top-level page.
131          *
132          * Add
133          * xmlns:jsp="http://java.sun.com/JSP/Page"
134          * attribute only if not already present.
135          */

136         if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) {
137             rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA",
138                        JSP_URI);
139         }
140
141         if (pageInfo.isJspPrefixHijacked()) {
142             /*
143              * 'jsp' prefix has been hijacked, that is, bound to a
144              * namespace other than the JSP namespace. This means that
145              * when adding an 'id' attribute to each element, we can't
146              * use the 'jsp' prefix. Therefore, create a new prefix
147              * (one that is unique across the translation unit) for use
148              * by the 'id' attribute, and bind it to the JSP namespace
149              */

150             jspIdPrefix += "jsp";
151             while (pageInfo.containsPrefix(jspIdPrefix)) {
152             jspIdPrefix += "jsp";
153             }
154             rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix,
155                        "CDATA", JSP_URI);
156         }
157
158         root.setAttributes(rootAttrs);
159         }
160     }
161
162     public void visit(Node.JspRoot n) throws JasperException {
163         addAttributes(n.getTaglibAttributes());
164             addAttributes(n.getNonTaglibXmlnsAttributes());
165         addAttributes(n.getAttributes());
166
167         visitBody(n);
168     }
169
170     /*
171      * Converts taglib directive into "xmlns:..." attribute of jsp:root
172      * element.
173      */

174     public void visit(Node.TaglibDirective n) throws JasperException {
175         Attributes JavaDoc attrs = n.getAttributes();
176         if (attrs != null) {
177         String JavaDoc qName = "xmlns:" + attrs.getValue("prefix");
178         /*
179          * According to javadocs of org.xml.sax.helpers.AttributesImpl,
180          * the addAttribute method does not check to see if the
181          * specified attribute is already contained in the list: This
182          * is the application's responsibility!
183          */

184         if (rootAttrs.getIndex(qName) == -1) {
185             String JavaDoc location = attrs.getValue("uri");
186             if (location != null) {
187                         if (location.startsWith("/")) {
188                             location = URN_JSPTLD + location;
189                         }
190             rootAttrs.addAttribute("", "", qName, "CDATA",
191                            location);
192             } else {
193             location = attrs.getValue("tagdir");
194             rootAttrs.addAttribute("", "", qName, "CDATA",
195                            URN_JSPTAGDIR + location);
196             }
197         }
198         }
199     }
200
201     public String JavaDoc getJspIdPrefix() {
202         return jspIdPrefix;
203     }
204
205     private void addAttributes(Attributes JavaDoc attrs) {
206         if (attrs != null) {
207         int len = attrs.getLength();
208
209         for (int i=0; i<len; i++) {
210                     String JavaDoc qName = attrs.getQName(i);
211             if ("version".equals(qName)) {
212             continue;
213             }
214
215                     // Bugzilla 35252: http://issues.apache.org/bugzilla/show_bug.cgi?id=35252
216
if(rootAttrs.getIndex(qName) == -1) {
217                         rootAttrs.addAttribute(attrs.getURI(i),
218                                                attrs.getLocalName(i),
219                                                qName,
220                                                attrs.getType(i),
221                                                attrs.getValue(i));
222                     }
223         }
224         }
225     }
226     }
227
228
229     /*
230      * Second-pass Visitor responsible for producing XML view and assigning
231      * each element a unique jsp:id attribute.
232      */

233     static class SecondPassVisitor extends Node.Visitor
234             implements TagConstants {
235
236     private Node.Root root;
237     private StringBuffer JavaDoc buf;
238     private Compiler JavaDoc compiler;
239     private String JavaDoc jspIdPrefix;
240     private boolean resetDefaultNS = false;
241
242     // Current value of jsp:id attribute
243
private int jspId;
244
245     /*
246      * Constructor
247      */

248     public SecondPassVisitor(Node.Root root, StringBuffer JavaDoc buf,
249                  Compiler JavaDoc compiler, String JavaDoc jspIdPrefix) {
250         this.root = root;
251         this.buf = buf;
252         this.compiler = compiler;
253         this.jspIdPrefix = jspIdPrefix;
254     }
255
256     /*
257      * Visits root node.
258      */

259     public void visit(Node.Root n) throws JasperException {
260         if (n == this.root) {
261         // top-level page
262
appendXmlProlog();
263         appendTag(n);
264         } else {
265         boolean resetDefaultNSSave = resetDefaultNS;
266         if (n.isXmlSyntax()) {
267             resetDefaultNS = true;
268         }
269         visitBody(n);
270         resetDefaultNS = resetDefaultNSSave;
271         }
272     }
273
274     /*
275      * Visits jsp:root element of JSP page in XML syntax.
276      *
277      * Any nested jsp:root elements (from pages included via an
278      * include directive) are ignored.
279      */

280     public void visit(Node.JspRoot n) throws JasperException {
281         visitBody(n);
282     }
283
284     public void visit(Node.PageDirective n) throws JasperException {
285         appendPageDirective(n);
286     }
287
288     public void visit(Node.IncludeDirective n) throws JasperException {
289         // expand in place
290
visitBody(n);
291     }
292
293     public void visit(Node.Comment n) throws JasperException {
294         // Comments are ignored in XML view
295
}
296
297     public void visit(Node.Declaration n) throws JasperException {
298         appendTag(n);
299     }
300
301     public void visit(Node.Expression n) throws JasperException {
302         appendTag(n);
303     }
304
305     public void visit(Node.Scriptlet n) throws JasperException {
306         appendTag(n);
307     }
308
309     public void visit(Node.JspElement n) throws JasperException {
310         appendTag(n);
311     }
312
313     public void visit(Node.ELExpression n) throws JasperException {
314         if (!n.getRoot().isXmlSyntax()) {
315         buf.append("<").append(JSP_TEXT_ACTION);
316         buf.append(" ");
317             buf.append(jspIdPrefix);
318         buf.append(":id=\"");
319         buf.append(jspId++).append("\">");
320         }
321         buf.append("${");
322             buf.append(JspUtil.escapeXml(n.getText()));
323         buf.append("}");
324         if (!n.getRoot().isXmlSyntax()) {
325         buf.append(JSP_TEXT_ACTION_END);
326         }
327         buf.append("\n");
328     }
329
330     public void visit(Node.IncludeAction n) throws JasperException {
331         appendTag(n);
332     }
333     
334     public void visit(Node.ForwardAction n) throws JasperException {
335         appendTag(n);
336     }
337
338     public void visit(Node.GetProperty n) throws JasperException {
339         appendTag(n);
340     }
341
342     public void visit(Node.SetProperty n) throws JasperException {
343         appendTag(n);
344     }
345
346     public void visit(Node.ParamAction n) throws JasperException {
347         appendTag(n);
348     }
349
350     public void visit(Node.ParamsAction n) throws JasperException {
351         appendTag(n);
352     }
353
354     public void visit(Node.FallBackAction n) throws JasperException {
355         appendTag(n);
356     }
357
358     public void visit(Node.UseBean n) throws JasperException {
359         appendTag(n);
360     }
361     
362     public void visit(Node.PlugIn n) throws JasperException {
363         appendTag(n);
364     }
365
366         public void visit(Node.NamedAttribute n) throws JasperException {
367             appendTag(n);
368         }
369         
370         public void visit(Node.JspBody n) throws JasperException {
371             appendTag(n);
372         }
373
374     public void visit(Node.CustomTag n) throws JasperException {
375         boolean resetDefaultNSSave = resetDefaultNS;
376         appendTag(n, resetDefaultNS);
377         resetDefaultNS = resetDefaultNSSave;
378     }
379
380     public void visit(Node.UninterpretedTag n) throws JasperException {
381         boolean resetDefaultNSSave = resetDefaultNS;
382         appendTag(n, resetDefaultNS);
383         resetDefaultNS = resetDefaultNSSave;
384     }
385
386     public void visit(Node.JspText n) throws JasperException {
387         appendTag(n);
388     }
389
390     public void visit(Node.DoBodyAction n) throws JasperException {
391         appendTag(n);
392     }
393
394         public void visit(Node.InvokeAction n) throws JasperException {
395         appendTag(n);
396     }
397
398     public void visit(Node.TagDirective n) throws JasperException {
399         appendTagDirective(n);
400     }
401
402     public void visit(Node.AttributeDirective n) throws JasperException {
403         appendTag(n);
404     }
405
406     public void visit(Node.VariableDirective n) throws JasperException {
407         appendTag(n);
408     }
409         
410     public void visit(Node.TemplateText n) throws JasperException {
411         /*
412          * If the template text came from a JSP page written in JSP syntax,
413          * create a jsp:text element for it (JSP 5.3.2).
414          */

415         appendText(n.getText(), !n.getRoot().isXmlSyntax());
416     }
417
418     /*
419      * Appends the given tag, including its body, to the XML view.
420      */

421     private void appendTag(Node n) throws JasperException {
422         appendTag(n, false);
423     }
424
425     /*
426      * Appends the given tag, including its body, to the XML view,
427      * and optionally reset default namespace to "", if none specified.
428      */

429     private void appendTag(Node n, boolean addDefaultNS)
430         throws JasperException {
431
432         Node.Nodes body = n.getBody();
433         String JavaDoc text = n.getText();
434
435         buf.append("<").append(n.getQName());
436         buf.append("\n");
437
438         printAttributes(n, addDefaultNS);
439         buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
440         buf.append(jspId++).append("\"\n");
441
442         if (ROOT_ACTION.equals(n.getLocalName()) || body != null
443                 || text != null) {
444         buf.append(">\n");
445         if (ROOT_ACTION.equals(n.getLocalName())) {
446             if (compiler.getCompilationContext().isTagFile()) {
447             appendTagDirective();
448             } else {
449             appendPageDirective();
450             }
451         }
452         if (body != null) {
453             body.visit(this);
454         } else {
455             appendText(text, false);
456         }
457         buf.append("</" + n.getQName() + ">\n");
458         } else {
459         buf.append("/>\n");
460         }
461     }
462
463     /*
464      * Appends the page directive with the given attributes to the XML
465      * view.
466      *
467      * Since the import attribute of the page directive is the only page
468      * attribute that is allowed to appear multiple times within the same
469      * document, and since XML allows only single-value attributes,
470      * the values of multiple import attributes must be combined into one,
471      * separated by comma.
472      *
473      * If the given page directive contains just 'contentType' and/or
474      * 'pageEncoding' attributes, we ignore it, as we've already appended
475      * a page directive containing just these two attributes.
476      */

477     private void appendPageDirective(Node.PageDirective n) {
478         boolean append = false;
479         Attributes JavaDoc attrs = n.getAttributes();
480         int len = (attrs == null) ? 0 : attrs.getLength();
481         for (int i=0; i<len; i++) {
482         String JavaDoc attrName = attrs.getQName(i);
483         if (!"pageEncoding".equals(attrName)
484                 && !"contentType".equals(attrName)) {
485             append = true;
486             break;
487         }
488         }
489         if (!append) {
490         return;
491         }
492
493         buf.append("<").append(n.getQName());
494         buf.append("\n");
495
496         // append jsp:id
497
buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
498         buf.append(jspId++).append("\"\n");
499
500         // append remaining attributes
501
for (int i=0; i<len; i++) {
502         String JavaDoc attrName = attrs.getQName(i);
503         if ("import".equals(attrName) || "contentType".equals(attrName)
504                 || "pageEncoding".equals(attrName)) {
505             /*
506              * Page directive's 'import' attribute is considered
507              * further down, and its 'pageEncoding' and 'contentType'
508              * attributes are ignored, since we've already appended
509              * a new page directive containing just these two
510              * attributes
511              */

512             continue;
513         }
514         String JavaDoc value = attrs.getValue(i);
515         buf.append(" ").append(attrName).append("=\"");
516         buf.append(JspUtil.getExprInXml(value)).append("\"\n");
517         }
518         if (n.getImports().size() > 0) {
519         // Concatenate names of imported classes/packages
520
boolean first = true;
521         ListIterator JavaDoc iter = n.getImports().listIterator();
522         while (iter.hasNext()) {
523             if (first) {
524             first = false;
525             buf.append(" import=\"");
526             } else {
527             buf.append(",");
528             }
529             buf.append(JspUtil.getExprInXml((String JavaDoc) iter.next()));
530         }
531         buf.append("\"\n");
532         }
533         buf.append("/>\n");
534     }
535
536     /*
537      * Appends a page directive with 'pageEncoding' and 'contentType'
538      * attributes.
539      *
540      * The value of the 'pageEncoding' attribute is hard-coded
541      * to UTF-8, whereas the value of the 'contentType' attribute, which
542      * is identical to what the container will pass to
543      * ServletResponse.setContentType(), is derived from the pageInfo.
544      */

545     private void appendPageDirective() {
546         buf.append("<").append(JSP_PAGE_DIRECTIVE_ACTION);
547         buf.append("\n");
548
549         // append jsp:id
550
buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
551         buf.append(jspId++).append("\"\n");
552         buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
553         buf.append(" ").append("contentType").append("=\"");
554         buf.append(compiler.getPageInfo().getContentType()).append("\"\n");
555         buf.append("/>\n");
556     }
557
558     /*
559      * Appends the tag directive with the given attributes to the XML
560      * view.
561      *
562      * If the given tag directive contains just a 'pageEncoding'
563      * attributes, we ignore it, as we've already appended
564      * a tag directive containing just this attributes.
565      */

566     private void appendTagDirective(Node.TagDirective n)
567             throws JasperException {
568
569         boolean append = false;
570         Attributes JavaDoc attrs = n.getAttributes();
571         int len = (attrs == null) ? 0 : attrs.getLength();
572         for (int i=0; i<len; i++) {
573         String JavaDoc attrName = attrs.getQName(i);
574         if (!"pageEncoding".equals(attrName)) {
575             append = true;
576             break;
577         }
578         }
579         if (!append) {
580         return;
581         }
582
583         appendTag(n);
584     }
585
586     /*
587      * Appends a tag directive containing a single 'pageEncoding'
588      * attribute whose value is hard-coded to UTF-8.
589      */

590     private void appendTagDirective() {
591         buf.append("<").append(JSP_TAG_DIRECTIVE_ACTION);
592         buf.append("\n");
593
594         // append jsp:id
595
buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
596         buf.append(jspId++).append("\"\n");
597         buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
598         buf.append("/>\n");
599     }
600
601     private void appendText(String JavaDoc text, boolean createJspTextElement) {
602         if (createJspTextElement) {
603         buf.append("<").append(JSP_TEXT_ACTION);
604         buf.append("\n");
605
606         // append jsp:id
607
buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
608         buf.append(jspId++).append("\"\n");
609         buf.append(">\n");
610
611         appendCDATA(text);
612         buf.append(JSP_TEXT_ACTION_END);
613         buf.append("\n");
614         } else {
615         appendCDATA(text);
616         }
617     }
618     
619     /*
620      * Appends the given text as a CDATA section to the XML view, unless
621      * the text has already been marked as CDATA.
622      */

623     private void appendCDATA(String JavaDoc text) {
624         buf.append(CDATA_START_SECTION);
625         buf.append(escapeCDATA(text));
626         buf.append(CDATA_END_SECTION);
627     }
628
629     /*
630      * Escapes any occurrences of "]]>" (by replacing them with "]]&gt;")
631      * within the given text, so it can be included in a CDATA section.
632      */

633     private String JavaDoc escapeCDATA(String JavaDoc text) {
634             if( text==null ) return "";
635         int len = text.length();
636         CharArrayWriter JavaDoc result = new CharArrayWriter JavaDoc(len);
637         for (int i=0; i<len; i++) {
638         if (((i+2) < len)
639                 && (text.charAt(i) == ']')
640                 && (text.charAt(i+1) == ']')
641                 && (text.charAt(i+2) == '>')) {
642             // match found
643
result.write(']');
644             result.write(']');
645             result.write('&');
646             result.write('g');
647             result.write('t');
648             result.write(';');
649             i += 2;
650         } else {
651             result.write(text.charAt(i));
652         }
653         }
654         return result.toString();
655     }
656
657     /*
658      * Appends the attributes of the given Node to the XML view.
659      */

660     private void printAttributes(Node n, boolean addDefaultNS) {
661
662         /*
663          * Append "xmlns" attributes that represent tag libraries
664          */

665         Attributes JavaDoc attrs = n.getTaglibAttributes();
666         int len = (attrs == null) ? 0 : attrs.getLength();
667         for (int i=0; i<len; i++) {
668         String JavaDoc name = attrs.getQName(i);
669         String JavaDoc value = attrs.getValue(i);
670         buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
671         }
672
673         /*
674          * Append "xmlns" attributes that do not represent tag libraries
675          */

676         attrs = n.getNonTaglibXmlnsAttributes();
677         len = (attrs == null) ? 0 : attrs.getLength();
678         boolean defaultNSSeen = false;
679         for (int i=0; i<len; i++) {
680         String JavaDoc name = attrs.getQName(i);
681         String JavaDoc value = attrs.getValue(i);
682         buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
683         defaultNSSeen |= "xmlns".equals(name);
684         }
685         if (addDefaultNS && !defaultNSSeen) {
686         buf.append(" xmlns=\"\"\n");
687         }
688         resetDefaultNS = false;
689
690         /*
691          * Append all other attributes
692          */

693         attrs = n.getAttributes();
694         len = (attrs == null) ? 0 : attrs.getLength();
695         for (int i=0; i<len; i++) {
696         String JavaDoc name = attrs.getQName(i);
697         String JavaDoc value = attrs.getValue(i);
698         buf.append(" ").append(name).append("=\"");
699         buf.append(JspUtil.getExprInXml(value)).append("\"\n");
700         }
701     }
702
703     /*
704      * Appends XML prolog with encoding declaration.
705      */

706     private void appendXmlProlog() {
707         buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
708     }
709     }
710 }
711
712
Popular Tags