1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.struts2.jasper.compiler;
18
19 import org.apache.struts2.jasper.JasperException;
20 import org.xml.sax.Attributes;
21 import org.xml.sax.helpers.AttributesImpl;
22
23 import javax.servlet.jsp.tagext.PageData;
24 import java.io.ByteArrayInputStream;
25 import java.io.CharArrayWriter;
26 import java.io.InputStream;
27 import java.io.UnsupportedEncodingException;
28 import java.util.ListIterator;
29
30 /***
31 * An implementation of <tt>javax.servlet.jsp.tagext.PageData</tt> which
32 * builds the XML view of a given page.
33 * <p/>
34 * The XML view is built in two passes:
35 * <p/>
36 * During the first pass, the FirstPassVisitor collects the attributes of the
37 * top-level jsp:root and those of the jsp:root elements of any included
38 * pages, and adds them to the jsp:root element of the XML view.
39 * In addition, any taglib directives are converted into xmlns: attributes and
40 * added to the jsp:root element of the XML view.
41 * This pass ignores any nodes other than JspRoot and TaglibDirective.
42 * <p/>
43 * During the second pass, the SecondPassVisitor produces the XML view, using
44 * the combined jsp:root attributes determined in the first pass and any
45 * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective
46 * nodes).
47 *
48 * @author Jan Luehe
49 */
50 class PageDataImpl extends PageData implements TagConstants {
51
52 private static final String JSP_VERSION = "2.0";
53 private static final String CDATA_START_SECTION = "<![CDATA[\n";
54 private static final String CDATA_END_SECTION = "]]>\n";
55
56
57 private StringBuffer buf;
58
59 /***
60 * Constructor.
61 *
62 * @param page the page nodes from which to generate the XML view
63 */
64 public PageDataImpl(Node.Nodes page, Compiler compiler)
65 throws JasperException {
66
67
68 FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(),
69 compiler.getPageInfo());
70 page.visit(firstPass);
71
72
73 buf = new StringBuffer();
74 SecondPassVisitor secondPass
75 = new SecondPassVisitor(page.getRoot(), buf, compiler,
76 firstPass.getJspIdPrefix());
77 page.visit(secondPass);
78 }
79
80 /***
81 * Returns the input stream of the XML view.
82 *
83 * @return the input stream of the XML view
84 */
85 public InputStream getInputStream() {
86
87 try {
88 return new ByteArrayInputStream(buf.toString().getBytes("UTF-8"));
89 } catch (UnsupportedEncodingException uee) {
90
91 throw new RuntimeException(uee.toString());
92 }
93 }
94
95
96
97
98
99
100
101
102
103
104
105 static class FirstPassVisitor
106 extends Node.Visitor implements TagConstants {
107
108 private Node.Root root;
109 private AttributesImpl rootAttrs;
110 private PageInfo pageInfo;
111
112
113 private String jspIdPrefix;
114
115
116
117
118 public FirstPassVisitor(Node.Root root, PageInfo pageInfo) {
119 this.root = root;
120 this.pageInfo = pageInfo;
121 this.rootAttrs = new AttributesImpl();
122 this.rootAttrs.addAttribute("", "", "version", "CDATA",
123 JSP_VERSION);
124 this.jspIdPrefix = "jsp";
125 }
126
127 public void visit(Node.Root n) throws JasperException {
128 visitBody(n);
129 if (n == root) {
130
131
132
133
134
135
136
137 if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) {
138 rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA",
139 JSP_URI);
140 }
141
142 if (pageInfo.isJspPrefixHijacked()) {
143
144
145
146
147
148
149
150
151 jspIdPrefix += "jsp";
152 while (pageInfo.containsPrefix(jspIdPrefix)) {
153 jspIdPrefix += "jsp";
154 }
155 rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix,
156 "CDATA", JSP_URI);
157 }
158
159 root.setAttributes(rootAttrs);
160 }
161 }
162
163 public void visit(Node.JspRoot n) throws JasperException {
164 addAttributes(n.getTaglibAttributes());
165 addAttributes(n.getNonTaglibXmlnsAttributes());
166 addAttributes(n.getAttributes());
167
168 visitBody(n);
169 }
170
171
172
173
174
175 public void visit(Node.TaglibDirective n) throws JasperException {
176 Attributes attrs = n.getAttributes();
177 if (attrs != null) {
178 String qName = "xmlns:" + attrs.getValue("prefix");
179
180
181
182
183
184
185 if (rootAttrs.getIndex(qName) == -1) {
186 String location = attrs.getValue("uri");
187 if (location != null) {
188 if (location.startsWith("/")) {
189 location = URN_JSPTLD + location;
190 }
191 rootAttrs.addAttribute("", "", qName, "CDATA",
192 location);
193 } else {
194 location = attrs.getValue("tagdir");
195 rootAttrs.addAttribute("", "", qName, "CDATA",
196 URN_JSPTAGDIR + location);
197 }
198 }
199 }
200 }
201
202 public String getJspIdPrefix() {
203 return jspIdPrefix;
204 }
205
206 private void addAttributes(Attributes attrs) {
207 if (attrs != null) {
208 int len = attrs.getLength();
209
210 for (int i = 0; i < len; i++) {
211 String qName = attrs.getQName(i);
212 if ("version".equals(qName)) {
213 continue;
214 }
215
216
217 if (rootAttrs.getIndex(qName) == -1) {
218 rootAttrs.addAttribute(attrs.getURI(i),
219 attrs.getLocalName(i),
220 qName,
221 attrs.getType(i),
222 attrs.getValue(i));
223 }
224 }
225 }
226 }
227 }
228
229
230
231
232
233
234 static class SecondPassVisitor extends Node.Visitor
235 implements TagConstants {
236
237 private Node.Root root;
238 private StringBuffer buf;
239 private Compiler compiler;
240 private String jspIdPrefix;
241 private boolean resetDefaultNS = false;
242
243
244 private int jspId;
245
246
247
248
249 public SecondPassVisitor(Node.Root root, StringBuffer buf,
250 Compiler compiler, String jspIdPrefix) {
251 this.root = root;
252 this.buf = buf;
253 this.compiler = compiler;
254 this.jspIdPrefix = jspIdPrefix;
255 }
256
257
258
259
260 public void visit(Node.Root n) throws JasperException {
261 if (n == this.root) {
262
263 appendXmlProlog();
264 appendTag(n);
265 } else {
266 boolean resetDefaultNSSave = resetDefaultNS;
267 if (n.isXmlSyntax()) {
268 resetDefaultNS = true;
269 }
270 visitBody(n);
271 resetDefaultNS = resetDefaultNSSave;
272 }
273 }
274
275
276
277
278
279
280
281 public void visit(Node.JspRoot n) throws JasperException {
282 visitBody(n);
283 }
284
285 public void visit(Node.PageDirective n) throws JasperException {
286 appendPageDirective(n);
287 }
288
289 public void visit(Node.IncludeDirective n) throws JasperException {
290
291 visitBody(n);
292 }
293
294 public void visit(Node.Comment n) throws JasperException {
295
296 }
297
298 public void visit(Node.Declaration n) throws JasperException {
299 appendTag(n);
300 }
301
302 public void visit(Node.Expression n) throws JasperException {
303 appendTag(n);
304 }
305
306 public void visit(Node.Scriptlet n) throws JasperException {
307 appendTag(n);
308 }
309
310 public void visit(Node.JspElement n) throws JasperException {
311 appendTag(n);
312 }
313
314 public void visit(Node.ELExpression n) throws JasperException {
315 if (!n.getRoot().isXmlSyntax()) {
316 buf.append("<").append(JSP_TEXT_ACTION);
317 buf.append(" ");
318 buf.append(jspIdPrefix);
319 buf.append(":id=\"");
320 buf.append(jspId++).append("\">");
321 }
322 buf.append("${");
323 buf.append(JspUtil.escapeXml(n.getText()));
324 buf.append("}");
325 if (!n.getRoot().isXmlSyntax()) {
326 buf.append(JSP_TEXT_ACTION_END);
327 }
328 buf.append("\n");
329 }
330
331 public void visit(Node.IncludeAction n) throws JasperException {
332 appendTag(n);
333 }
334
335 public void visit(Node.ForwardAction n) throws JasperException {
336 appendTag(n);
337 }
338
339 public void visit(Node.GetProperty n) throws JasperException {
340 appendTag(n);
341 }
342
343 public void visit(Node.SetProperty n) throws JasperException {
344 appendTag(n);
345 }
346
347 public void visit(Node.ParamAction n) throws JasperException {
348 appendTag(n);
349 }
350
351 public void visit(Node.ParamsAction n) throws JasperException {
352 appendTag(n);
353 }
354
355 public void visit(Node.FallBackAction n) throws JasperException {
356 appendTag(n);
357 }
358
359 public void visit(Node.UseBean n) throws JasperException {
360 appendTag(n);
361 }
362
363 public void visit(Node.PlugIn n) throws JasperException {
364 appendTag(n);
365 }
366
367 public void visit(Node.NamedAttribute n) throws JasperException {
368 appendTag(n);
369 }
370
371 public void visit(Node.JspBody n) throws JasperException {
372 appendTag(n);
373 }
374
375 public void visit(Node.CustomTag n) throws JasperException {
376 boolean resetDefaultNSSave = resetDefaultNS;
377 appendTag(n, resetDefaultNS);
378 resetDefaultNS = resetDefaultNSSave;
379 }
380
381 public void visit(Node.UninterpretedTag n) throws JasperException {
382 boolean resetDefaultNSSave = resetDefaultNS;
383 appendTag(n, resetDefaultNS);
384 resetDefaultNS = resetDefaultNSSave;
385 }
386
387 public void visit(Node.JspText n) throws JasperException {
388 appendTag(n);
389 }
390
391 public void visit(Node.DoBodyAction n) throws JasperException {
392 appendTag(n);
393 }
394
395 public void visit(Node.InvokeAction n) throws JasperException {
396 appendTag(n);
397 }
398
399 public void visit(Node.TagDirective n) throws JasperException {
400 appendTagDirective(n);
401 }
402
403 public void visit(Node.AttributeDirective n) throws JasperException {
404 appendTag(n);
405 }
406
407 public void visit(Node.VariableDirective n) throws JasperException {
408 appendTag(n);
409 }
410
411 public void visit(Node.TemplateText n) throws JasperException {
412
413
414
415
416 appendText(n.getText(), !n.getRoot().isXmlSyntax());
417 }
418
419
420
421
422 private void appendTag(Node n) throws JasperException {
423 appendTag(n, false);
424 }
425
426
427
428
429
430 private void appendTag(Node n, boolean addDefaultNS)
431 throws JasperException {
432
433 Node.Nodes body = n.getBody();
434 String text = n.getText();
435
436 buf.append("<").append(n.getQName());
437 buf.append("\n");
438
439 printAttributes(n, addDefaultNS);
440 buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
441 buf.append(jspId++).append("\"\n");
442
443 if (ROOT_ACTION.equals(n.getLocalName()) || body != null
444 || text != null) {
445 buf.append(">\n");
446 if (ROOT_ACTION.equals(n.getLocalName())) {
447 if (compiler.getCompilationContext().isTagFile()) {
448 appendTagDirective();
449 } else {
450 appendPageDirective();
451 }
452 }
453 if (body != null) {
454 body.visit(this);
455 } else {
456 appendText(text, false);
457 }
458 buf.append("</" + n.getQName() + ">\n");
459 } else {
460 buf.append("/>\n");
461 }
462 }
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 private void appendPageDirective(Node.PageDirective n) {
479 boolean append = false;
480 Attributes attrs = n.getAttributes();
481 int len = (attrs == null) ? 0 : attrs.getLength();
482 for (int i = 0; i < len; i++) {
483 String attrName = attrs.getQName(i);
484 if (!"pageEncoding".equals(attrName)
485 && !"contentType".equals(attrName)) {
486 append = true;
487 break;
488 }
489 }
490 if (!append) {
491 return;
492 }
493
494 buf.append("<").append(n.getQName());
495 buf.append("\n");
496
497
498 buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
499 buf.append(jspId++).append("\"\n");
500
501
502 for (int i = 0; i < len; i++) {
503 String attrName = attrs.getQName(i);
504 if ("import".equals(attrName) || "contentType".equals(attrName)
505 || "pageEncoding".equals(attrName)) {
506
507
508
509
510
511
512
513 continue;
514 }
515 String value = attrs.getValue(i);
516 buf.append(" ").append(attrName).append("=\"");
517 buf.append(JspUtil.getExprInXml(value)).append("\"\n");
518 }
519 if (n.getImports().size() > 0) {
520
521 boolean first = true;
522 ListIterator iter = n.getImports().listIterator();
523 while (iter.hasNext()) {
524 if (first) {
525 first = false;
526 buf.append(" import=\"");
527 } else {
528 buf.append(",");
529 }
530 buf.append(JspUtil.getExprInXml((String) iter.next()));
531 }
532 buf.append("\"\n");
533 }
534 buf.append("/>\n");
535 }
536
537
538
539
540
541
542
543
544
545
546 private void appendPageDirective() {
547 buf.append("<").append(JSP_PAGE_DIRECTIVE_ACTION);
548 buf.append("\n");
549
550
551 buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
552 buf.append(jspId++).append("\"\n");
553 buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
554 buf.append(" ").append("contentType").append("=\"");
555 buf.append(compiler.getPageInfo().getContentType()).append("\"\n");
556 buf.append("/>\n");
557 }
558
559
560
561
562
563
564
565
566
567 private void appendTagDirective(Node.TagDirective n)
568 throws JasperException {
569
570 boolean append = false;
571 Attributes attrs = n.getAttributes();
572 int len = (attrs == null) ? 0 : attrs.getLength();
573 for (int i = 0; i < len; i++) {
574 String attrName = attrs.getQName(i);
575 if (!"pageEncoding".equals(attrName)) {
576 append = true;
577 break;
578 }
579 }
580 if (!append) {
581 return;
582 }
583
584 appendTag(n);
585 }
586
587
588
589
590
591 private void appendTagDirective() {
592 buf.append("<").append(JSP_TAG_DIRECTIVE_ACTION);
593 buf.append("\n");
594
595
596 buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
597 buf.append(jspId++).append("\"\n");
598 buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
599 buf.append("/>\n");
600 }
601
602 private void appendText(String text, boolean createJspTextElement) {
603 if (createJspTextElement) {
604 buf.append("<").append(JSP_TEXT_ACTION);
605 buf.append("\n");
606
607
608 buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
609 buf.append(jspId++).append("\"\n");
610 buf.append(">\n");
611
612 appendCDATA(text);
613 buf.append(JSP_TEXT_ACTION_END);
614 buf.append("\n");
615 } else {
616 appendCDATA(text);
617 }
618 }
619
620
621
622
623
624 private void appendCDATA(String text) {
625 buf.append(CDATA_START_SECTION);
626 buf.append(escapeCDATA(text));
627 buf.append(CDATA_END_SECTION);
628 }
629
630
631
632
633
634 private String escapeCDATA(String text) {
635 if (text == null) return "";
636 int len = text.length();
637 CharArrayWriter result = new CharArrayWriter(len);
638 for (int i = 0; i < len; i++) {
639 if (((i + 2) < len)
640 && (text.charAt(i) == ']')
641 && (text.charAt(i + 1) == ']')
642 && (text.charAt(i + 2) == '>')) {
643
644 result.write(']');
645 result.write(']');
646 result.write('&');
647 result.write('g');
648 result.write('t');
649 result.write(';');
650 i += 2;
651 } else {
652 result.write(text.charAt(i));
653 }
654 }
655 return result.toString();
656 }
657
658
659
660
661 private void printAttributes(Node n, boolean addDefaultNS) {
662
663
664
665
666 Attributes attrs = n.getTaglibAttributes();
667 int len = (attrs == null) ? 0 : attrs.getLength();
668 for (int i = 0; i < len; i++) {
669 String name = attrs.getQName(i);
670 String value = attrs.getValue(i);
671 buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
672 }
673
674
675
676
677 attrs = n.getNonTaglibXmlnsAttributes();
678 len = (attrs == null) ? 0 : attrs.getLength();
679 boolean defaultNSSeen = false;
680 for (int i = 0; i < len; i++) {
681 String name = attrs.getQName(i);
682 String value = attrs.getValue(i);
683 buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
684 defaultNSSeen |= "xmlns".equals(name);
685 }
686 if (addDefaultNS && !defaultNSSeen) {
687 buf.append(" xmlns=\"\"\n");
688 }
689 resetDefaultNS = false;
690
691
692
693
694 attrs = n.getAttributes();
695 len = (attrs == null) ? 0 : attrs.getLength();
696 for (int i = 0; i < len; i++) {
697 String name = attrs.getQName(i);
698 String value = attrs.getValue(i);
699 buf.append(" ").append(name).append("=\"");
700 buf.append(JspUtil.getExprInXml(value)).append("\"\n");
701 }
702 }
703
704
705
706
707 private void appendXmlProlog() {
708 buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
709 }
710 }
711 }
712