1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jasper.compiler;
19
20 import org.apache.struts2.jasper.JasperException;
21 import org.apache.struts2.jasper.JspCompilationContext;
22 import org.apache.struts2.jasper.runtime.JspSourceDependent;
23 import org.apache.struts2.jasper.servlet.JspServletWrapper;
24
25 import javax.servlet.jsp.tagext.*;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.net.URL;
29 import java.net.URLClassLoader;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Vector;
34
35 /***
36 * 1. Processes and extracts the directive info in a tag file.
37 * 2. Compiles and loads tag files used in a JSP file.
38 *
39 * @author Kin-man Chung
40 */
41
42 class TagFileProcessor {
43
44 private Vector tempVector;
45
46 /***
47 * A visitor the tag file
48 */
49 private static class TagFileDirectiveVisitor extends Node.Visitor {
50
51 private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = {
52 new JspUtil.ValidAttribute("display-name"),
53 new JspUtil.ValidAttribute("body-content"),
54 new JspUtil.ValidAttribute("dynamic-attributes"),
55 new JspUtil.ValidAttribute("small-icon"),
56 new JspUtil.ValidAttribute("large-icon"),
57 new JspUtil.ValidAttribute("description"),
58 new JspUtil.ValidAttribute("example"),
59 new JspUtil.ValidAttribute("pageEncoding"),
60 new JspUtil.ValidAttribute("language"),
61 new JspUtil.ValidAttribute("import"),
62 new JspUtil.ValidAttribute("isELIgnored")};
63
64 private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = {
65 new JspUtil.ValidAttribute("name", true),
66 new JspUtil.ValidAttribute("required"),
67 new JspUtil.ValidAttribute("fragment"),
68 new JspUtil.ValidAttribute("rtexprvalue"),
69 new JspUtil.ValidAttribute("type"),
70 new JspUtil.ValidAttribute("description")
71 };
72
73 private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = {
74 new JspUtil.ValidAttribute("name-given"),
75 new JspUtil.ValidAttribute("name-from-attribute"),
76 new JspUtil.ValidAttribute("alias"),
77 new JspUtil.ValidAttribute("variable-class"),
78 new JspUtil.ValidAttribute("scope"),
79 new JspUtil.ValidAttribute("declare"),
80 new JspUtil.ValidAttribute("description")
81 };
82
83 private ErrorDispatcher err;
84 private TagLibraryInfo tagLibInfo;
85
86 private String name = null;
87 private String path = null;
88 private TagExtraInfo tei = null;
89 private String bodycontent = null;
90 private String description = null;
91 private String displayName = null;
92 private String smallIcon = null;
93 private String largeIcon = null;
94 private String dynamicAttrsMapName;
95 private String example = null;
96
97 private Vector attributeVector;
98 private Vector variableVector;
99
100 private static final String ATTR_NAME =
101 "the name attribute of the attribute directive";
102 private static final String VAR_NAME_GIVEN =
103 "the name-given attribute of the variable directive";
104 private static final String VAR_NAME_FROM =
105 "the name-from-attribute attribute of the variable directive";
106 private static final String VAR_ALIAS =
107 "the alias attribute of the variable directive";
108 private static final String TAG_DYNAMIC =
109 "the dynamic-attributes attribute of the tag directive";
110 private HashMap nameTable = new HashMap();
111 private HashMap nameFromTable = new HashMap();
112
113 public TagFileDirectiveVisitor(Compiler compiler,
114 TagLibraryInfo tagLibInfo,
115 String name,
116 String path) {
117 err = compiler.getErrorDispatcher();
118 this.tagLibInfo = tagLibInfo;
119 this.name = name;
120 this.path = path;
121 attributeVector = new Vector();
122 variableVector = new Vector();
123 }
124
125 public void visit(Node.TagDirective n) throws JasperException {
126
127 JspUtil.checkAttributes("Tag directive", n, tagDirectiveAttrs,
128 err);
129
130 bodycontent = checkConflict(n, bodycontent, "body-content");
131 if (bodycontent != null &&
132 !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY) &&
133 !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT) &&
134 !bodycontent.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
135 err.jspError(n, "jsp.error.tagdirective.badbodycontent",
136 bodycontent);
137 }
138 dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName,
139 "dynamic-attributes");
140 if (dynamicAttrsMapName != null) {
141 checkUniqueName(dynamicAttrsMapName, TAG_DYNAMIC, n);
142 }
143 smallIcon = checkConflict(n, smallIcon, "small-icon");
144 largeIcon = checkConflict(n, largeIcon, "large-icon");
145 description = checkConflict(n, description, "description");
146 displayName = checkConflict(n, displayName, "display-name");
147 example = checkConflict(n, example, "example");
148 }
149
150 private String checkConflict(Node n, String oldAttrValue, String attr)
151 throws JasperException {
152
153 String result = oldAttrValue;
154 String attrValue = n.getAttributeValue(attr);
155 if (attrValue != null) {
156 if (oldAttrValue != null && !oldAttrValue.equals(attrValue)) {
157 err.jspError(n, "jsp.error.tag.conflict.attr", attr,
158 oldAttrValue, attrValue);
159 }
160 result = attrValue;
161 }
162 return result;
163 }
164
165
166 public void visit(Node.AttributeDirective n) throws JasperException {
167
168 JspUtil.checkAttributes("Attribute directive", n,
169 attributeDirectiveAttrs, err);
170
171 String attrName = n.getAttributeValue("name");
172 boolean required = JspUtil.booleanValue(
173 n.getAttributeValue("required"));
174 boolean rtexprvalue = true;
175 String rtexprvalueString = n.getAttributeValue("rtexprvalue");
176 if (rtexprvalueString != null) {
177 rtexprvalue = JspUtil.booleanValue(rtexprvalueString);
178 }
179 boolean fragment = JspUtil.booleanValue(
180 n.getAttributeValue("fragment"));
181 String type = n.getAttributeValue("type");
182 if (fragment) {
183
184
185 if (type != null) {
186 err.jspError(n, "jsp.error.fragmentwithtype");
187 }
188
189
190 rtexprvalue = true;
191 if (rtexprvalueString != null) {
192 err.jspError(n, "jsp.error.frgmentwithrtexprvalue");
193 }
194 } else {
195 if (type == null)
196 type = "java.lang.String";
197 }
198
199 TagAttributeInfo tagAttributeInfo =
200 new TagAttributeInfo(attrName, required, type, rtexprvalue,
201 fragment);
202 attributeVector.addElement(tagAttributeInfo);
203 checkUniqueName(attrName, ATTR_NAME, n, tagAttributeInfo);
204 }
205
206 public void visit(Node.VariableDirective n) throws JasperException {
207
208 JspUtil.checkAttributes("Variable directive", n,
209 variableDirectiveAttrs, err);
210
211 String nameGiven = n.getAttributeValue("name-given");
212 String nameFromAttribute = n.getAttributeValue("name-from-attribute");
213 if (nameGiven == null && nameFromAttribute == null) {
214 err.jspError("jsp.error.variable.either.name");
215 }
216
217 if (nameGiven != null && nameFromAttribute != null) {
218 err.jspError("jsp.error.variable.both.name");
219 }
220
221 String alias = n.getAttributeValue("alias");
222 if (nameFromAttribute != null && alias == null ||
223 nameFromAttribute == null && alias != null) {
224 err.jspError("jsp.error.variable.alias");
225 }
226
227 String className = n.getAttributeValue("variable-class");
228 if (className == null)
229 className = "java.lang.String";
230
231 String declareStr = n.getAttributeValue("declare");
232 boolean declare = true;
233 if (declareStr != null)
234 declare = JspUtil.booleanValue(declareStr);
235
236 int scope = VariableInfo.NESTED;
237 String scopeStr = n.getAttributeValue("scope");
238 if (scopeStr != null) {
239 if ("NESTED".equals(scopeStr)) {
240
241 } else if ("AT_BEGIN".equals(scopeStr)) {
242 scope = VariableInfo.AT_BEGIN;
243 } else if ("AT_END".equals(scopeStr)) {
244 scope = VariableInfo.AT_END;
245 }
246 }
247
248 if (nameFromAttribute != null) {
249
250
251
252
253
254
255 nameGiven = alias;
256 checkUniqueName(nameFromAttribute, VAR_NAME_FROM, n);
257 checkUniqueName(alias, VAR_ALIAS, n);
258 } else {
259
260 checkUniqueName(nameGiven, VAR_NAME_GIVEN, n);
261 }
262
263 variableVector.addElement(new TagVariableInfo(
264 nameGiven,
265 nameFromAttribute,
266 className,
267 declare,
268 scope));
269 }
270
271
272
273
274
275 public Vector getAttributesVector() {
276 return attributeVector;
277 }
278
279
280
281
282
283 public Vector getVariablesVector() {
284 return variableVector;
285 }
286
287
288
289
290
291 public String getDynamicAttributesMapName() {
292 return dynamicAttrsMapName;
293 }
294
295 public TagInfo getTagInfo() throws JasperException {
296
297 if (name == null) {
298
299 }
300
301 if (bodycontent == null) {
302 bodycontent = TagInfo.BODY_CONTENT_SCRIPTLESS;
303 }
304
305 String tagClassName = JspUtil.getTagHandlerClassName(path, err);
306
307 TagVariableInfo[] tagVariableInfos
308 = new TagVariableInfo[variableVector.size()];
309 variableVector.copyInto(tagVariableInfos);
310
311 TagAttributeInfo[] tagAttributeInfo
312 = new TagAttributeInfo[attributeVector.size()];
313 attributeVector.copyInto(tagAttributeInfo);
314
315 return new JasperTagInfo(name,
316 tagClassName,
317 bodycontent,
318 description,
319 tagLibInfo,
320 tei,
321 tagAttributeInfo,
322 displayName,
323 smallIcon,
324 largeIcon,
325 tagVariableInfos,
326 dynamicAttrsMapName);
327 }
328
329 static class NameEntry {
330 private String type;
331 private Node node;
332 private TagAttributeInfo attr;
333
334 NameEntry(String type, Node node, TagAttributeInfo attr) {
335 this.type = type;
336 this.node = node;
337 this.attr = attr;
338 }
339
340 String getType() {
341 return type;
342 }
343
344 Node getNode() {
345 return node;
346 }
347
348 TagAttributeInfo getTagAttributeInfo() {
349 return attr;
350 }
351 }
352
353 /***
354 * Reports a translation error if names specified in attributes of
355 * directives are not unique in this translation unit.
356 * <p/>
357 * The value of the following attributes must be unique.
358 * 1. 'name' attribute of an attribute directive
359 * 2. 'name-given' attribute of a variable directive
360 * 3. 'alias' attribute of variable directive
361 * 4. 'dynamic-attributes' of a tag directive
362 * except that 'dynamic-attributes' can (and must) have the same
363 * value when it appears in multiple tag directives.
364 * <p/>
365 * Also, 'name-from' attribute of a variable directive cannot have
366 * the same value as that from another variable directive.
367 */
368 private void checkUniqueName(String name, String type, Node n)
369 throws JasperException {
370 checkUniqueName(name, type, n, null);
371 }
372
373 private void checkUniqueName(String name, String type, Node n,
374 TagAttributeInfo attr)
375 throws JasperException {
376
377 HashMap table = (type == VAR_NAME_FROM) ? nameFromTable : nameTable;
378 NameEntry nameEntry = (NameEntry) table.get(name);
379 if (nameEntry != null) {
380 if (type != TAG_DYNAMIC || nameEntry.getType() != TAG_DYNAMIC) {
381 int line = nameEntry.getNode().getStart().getLineNumber();
382 err.jspError(n, "jsp.error.tagfile.nameNotUnique",
383 type, nameEntry.getType(), Integer.toString(line));
384 }
385 } else {
386 table.put(name, new NameEntry(type, n, attr));
387 }
388 }
389
390 /***
391 * Perform miscellean checks after the nodes are visited.
392 */
393 void postCheck() throws JasperException {
394
395 Iterator iter = nameFromTable.keySet().iterator();
396 while (iter.hasNext()) {
397 String nameFrom = (String) iter.next();
398 NameEntry nameEntry = (NameEntry) nameTable.get(nameFrom);
399 NameEntry nameFromEntry =
400 (NameEntry) nameFromTable.get(nameFrom);
401 Node nameFromNode = nameFromEntry.getNode();
402 if (nameEntry == null) {
403 err.jspError(nameFromNode,
404 "jsp.error.tagfile.nameFrom.noAttribute",
405 nameFrom);
406 } else {
407 Node node = nameEntry.getNode();
408 TagAttributeInfo tagAttr = nameEntry.getTagAttributeInfo();
409 if (!"java.lang.String".equals(tagAttr.getTypeName())
410 || !tagAttr.isRequired()
411 || tagAttr.canBeRequestTime()) {
412 err.jspError(nameFromNode,
413 "jsp.error.tagfile.nameFrom.badAttribute",
414 nameFrom,
415 Integer.toString(node.getStart().getLineNumber()));
416 }
417 }
418 }
419 }
420 }
421
422 /***
423 * Parses the tag file, and collects information on the directives included
424 * in it. The method is used to obtain the info on the tag file, when the
425 * handler that it represents is referenced. The tag file is not compiled
426 * here.
427 *
428 * @param pc the current ParserController used in this compilation
429 * @param name the tag name as specified in the TLD
430 * @param tagfile the path for the tagfile
431 * @param tagLibInfo the TagLibraryInfo object associated with this TagInfo
432 * @return a TagInfo object assembled from the directives in the tag file.
433 */
434 public static TagInfo parseTagFileDirectives(ParserController pc,
435 String name,
436 String path,
437 TagLibraryInfo tagLibInfo)
438 throws JasperException {
439
440 ErrorDispatcher err = pc.getCompiler().getErrorDispatcher();
441
442 Node.Nodes page = null;
443 try {
444 page = pc.parseTagFileDirectives(path);
445 } catch (FileNotFoundException e) {
446 err.jspError("jsp.error.file.not.found", path);
447 } catch (IOException e) {
448 err.jspError("jsp.error.file.not.found", path);
449 }
450
451 TagFileDirectiveVisitor tagFileVisitor
452 = new TagFileDirectiveVisitor(pc.getCompiler(), tagLibInfo, name,
453 path);
454 page.visit(tagFileVisitor);
455 tagFileVisitor.postCheck();
456
457 return tagFileVisitor.getTagInfo();
458 }
459
460 /***
461 * Compiles and loads a tagfile.
462 */
463 private Class loadTagFile(Compiler compiler,
464 String tagFilePath, TagInfo tagInfo,
465 PageInfo parentPageInfo)
466 throws JasperException {
467
468 JspCompilationContext ctxt = compiler.getCompilationContext();
469 JspRuntimeContext rctxt = ctxt.getRuntimeContext();
470 JspServletWrapper wrapper =
471 (JspServletWrapper) rctxt.getWrapper(tagFilePath);
472
473 synchronized (rctxt) {
474 if (wrapper == null) {
475 wrapper = new JspServletWrapper(ctxt.getServletContext(),
476 ctxt.getOptions(),
477 tagFilePath,
478 tagInfo,
479 ctxt.getRuntimeContext(),
480 (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
481 rctxt.addWrapper(tagFilePath, wrapper);
482
483
484 wrapper.getJspEngineContext().setClassLoader(
485 (URLClassLoader) ctxt.getClassLoader());
486 wrapper.getJspEngineContext().setClassPath(ctxt.getClassPath());
487 } else {
488
489
490
491
492 wrapper.getJspEngineContext().setTagInfo(tagInfo);
493 }
494
495 Class tagClazz;
496 int tripCount = wrapper.incTripCount();
497 try {
498 if (tripCount > 0) {
499
500
501
502
503
504 JspServletWrapper tempWrapper
505 = new JspServletWrapper(ctxt.getServletContext(),
506 ctxt.getOptions(),
507 tagFilePath,
508 tagInfo,
509 ctxt.getRuntimeContext(),
510 (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
511 tagClazz = tempWrapper.loadTagFilePrototype();
512 tempVector.add(
513 tempWrapper.getJspEngineContext().getCompiler());
514 } else {
515 tagClazz = wrapper.loadTagFile();
516 }
517 } finally {
518 wrapper.decTripCount();
519 }
520
521
522
523
524 try {
525 Object tagIns = tagClazz.newInstance();
526 if (tagIns instanceof JspSourceDependent) {
527 Iterator iter =
528 ((List) ((JspSourceDependent) tagIns).getDependants()).iterator();
529 while (iter.hasNext()) {
530 parentPageInfo.addDependant((String) iter.next());
531 }
532 }
533 } catch (Exception e) {
534
535 }
536
537 return tagClazz;
538 }
539 }
540
541
542
543
544
545
546 private class TagFileLoaderVisitor extends Node.Visitor {
547
548 private Compiler compiler;
549 private PageInfo pageInfo;
550
551 TagFileLoaderVisitor(Compiler compiler) {
552
553 this.compiler = compiler;
554 this.pageInfo = compiler.getPageInfo();
555 }
556
557 public void visit(Node.CustomTag n) throws JasperException {
558 TagFileInfo tagFileInfo = n.getTagFileInfo();
559 if (tagFileInfo != null) {
560 String tagFilePath = tagFileInfo.getPath();
561 JspCompilationContext ctxt = compiler.getCompilationContext();
562 if (ctxt.getTagFileJarUrls().get(tagFilePath) == null) {
563
564 pageInfo.addDependant(tagFilePath);
565 }
566 Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
567 pageInfo);
568 n.setTagHandlerClass(c);
569 }
570 visitBody(n);
571 }
572 }
573
574 /***
575 * Implements a phase of the translation that compiles (if necessary)
576 * the tag files used in a JSP files. The directives in the tag files
577 * are assumed to have been proccessed and encapsulated as TagFileInfo
578 * in the CustomTag nodes.
579 */
580 public void loadTagFiles(Compiler compiler, Node.Nodes page)
581 throws JasperException {
582
583 tempVector = new Vector();
584 page.visit(new TagFileLoaderVisitor(compiler));
585 }
586
587 /***
588 * Removed the java and class files for the tag prototype
589 * generated from the current compilation.
590 *
591 * @param classFileName If non-null, remove only the class file with
592 * with this name.
593 */
594 public void removeProtoTypeFiles(String classFileName) {
595 Iterator iter = tempVector.iterator();
596 while (iter.hasNext()) {
597 Compiler c = (Compiler) iter.next();
598 if (classFileName == null) {
599 c.removeGeneratedClassFiles();
600 } else if (classFileName.equals(
601 c.getCompilationContext().getClassFileName())) {
602 c.removeGeneratedClassFiles();
603 tempVector.remove(c);
604 return;
605 }
606 }
607 }
608 }
609