KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > jsp > TagAnalyzer


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.jsp;
31
32 import com.caucho.bytecode.ByteCodeParser;
33 import com.caucho.bytecode.CodeAttribute;
34 import com.caucho.bytecode.CodeVisitor;
35 import com.caucho.bytecode.JavaClass;
36 import com.caucho.bytecode.JavaMethod;
37 import com.caucho.log.Log;
38 import com.caucho.util.L10N;
39
40 import javax.annotation.Resource;
41 import javax.ejb.EJB JavaDoc;
42 import javax.servlet.jsp.tagext.*;
43 import javax.xml.ws.WebServiceRef;
44 import java.io.InputStream JavaDoc;
45 import java.lang.reflect.Field JavaDoc;
46 import java.lang.reflect.Method JavaDoc;
47 import java.util.HashMap JavaDoc;
48 import java.util.logging.Level JavaDoc;
49 import java.util.logging.Logger JavaDoc;
50
51 /**
52  * Analyzes the class for tag.
53  *
54  * Resin performs optimizations in the java code it produces from a jsp
55  * depending on the nature of the taglib's that are used. For example, if a
56  * taglib class does not use doAfterBody() then Resin can optimize the code it
57  * produces for the jsp that uses that tag.
58  *
59  * In order to determine the nature of a certain tag, and thus the
60  * optimizations that can be performed, Resin analyzes the tag's class.
61  * It does this in two stages: first it uses reflection to look at the class
62  * and then it uses bytecode analysis to look at the class.
63  *
64  * @see com.caucho.jsp.AnalyzedTag
65  */

66 public class TagAnalyzer {
67   private static final Logger JavaDoc log = Log.open(TagAnalyzer.class);
68   static final L10N L = new L10N(TagAnalyzer.class);
69
70   private HashMap JavaDoc<Class JavaDoc,AnalyzedTag> _analyzedTags =
71     new HashMap JavaDoc<Class JavaDoc,AnalyzedTag>();
72
73   /**
74    * Analyzes a tag.
75    */

76   public AnalyzedTag analyze(Class JavaDoc tagClass)
77   {
78     if (tagClass == null)
79       return null;
80     
81     AnalyzedTag analyzedTag = _analyzedTags.get(tagClass);
82     if (analyzedTag != null)
83       return analyzedTag;
84
85     if (! JspTag.class.isAssignableFrom(tagClass)) {
86       return null;
87     }
88
89     if (tagClass.isInterface()) {
90       return null;
91     }
92
93     AnalyzedTag parent = analyze(tagClass.getSuperclass());
94     
95     String JavaDoc name = tagClass.getName().replace('.', '/') + ".class";
96     ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
97
98     AnalyzedTag tag = new AnalyzedTag();
99
100     try {
101       analyzeByReflection(tagClass, tag, parent);
102
103       InputStream JavaDoc is = loader.getResourceAsStream(name);
104
105       if (is == null)
106     return tag;
107
108       try {
109     JavaClass javaClass = new ByteCodeParser().parse(is);
110
111     analyze(javaClass, "doStartTag", "()I", new StartAnalyzer(), tag);
112     analyze(javaClass, "doEndTag", "()I", new EndAnalyzer(), tag);
113
114     if (IterationTag.class.isAssignableFrom(tagClass)) {
115       analyze(javaClass, "doAfterBody", "()I", new AfterAnalyzer(), tag);
116     }
117     
118     if (BodyTag.class.isAssignableFrom(tagClass)) {
119       analyze(javaClass, "doInitBody", "()V",
120           new InitAnalyzer(), tag);
121     }
122     
123     if (TryCatchFinally.class.isAssignableFrom(tagClass)) {
124       analyze(javaClass, "doCatch", "(Ljava/lang/Throwable;)V",
125           new CatchAnalyzer(), tag);
126       analyze(javaClass, "doFinally", "()V",
127           new FinallyAnalyzer(), tag);
128     }
129       } finally {
130     is.close();
131       }
132     } catch (Exception JavaDoc e) {
133       log.log(Level.WARNING, e.toString(), e);
134     }
135
136     return tag;
137   }
138
139   /**
140    * Analyzes the tag by reflection.
141    */

142   public void analyzeByReflection(Class JavaDoc tagClass,
143                   AnalyzedTag tag, AnalyzedTag parent)
144   {
145     tag.setBodyTag(BodyTag.class.isAssignableFrom(tagClass));
146            
147     Method doStartMethod = getMethod(tagClass, "doStartTag", new Class JavaDoc[0]);
148
149     if (doStartMethod != null &&
150     doStartMethod.getDeclaringClass().equals(tagClass)) {
151       if (TagSupport.class.equals(tagClass)) {
152     tag.setDoStart(false);
153     tag.setStartReturnsSkip(false);
154     tag.setStartReturnsInclude(true);
155     tag.setStartReturnsBuffered(false);
156       }
157       else if (BodyTagSupport.class.equals(tagClass)) {
158     tag.setDoStart(false);
159     tag.setStartReturnsSkip(false);
160     tag.setStartReturnsInclude(false);
161     tag.setStartReturnsBuffered(true);
162       }
163       else if (BodyTag.class.isAssignableFrom(tagClass)) {
164     tag.setDoStart(true);
165     tag.setStartReturnsSkip(true);
166     tag.setStartReturnsInclude(true);
167     tag.setStartReturnsBuffered(true);
168       }
169       else {
170     tag.setDoStart(true);
171     tag.setStartReturnsSkip(true);
172     tag.setStartReturnsInclude(true);
173     tag.setStartReturnsBuffered(false);
174       }
175     }
176     else if (parent != null) {
177       tag.setDoStart(parent.getDoStart());
178       tag.setStartReturnsSkip(parent.getStartReturnsSkip());
179       tag.setStartReturnsInclude(parent.getStartReturnsInclude());
180       tag.setStartReturnsBuffered(parent.getStartReturnsBufferedAsParent());
181     }
182     
183     Method doEndMethod = getMethod(tagClass, "doEndTag", new Class JavaDoc[0]);
184
185     if (doEndMethod != null &&
186     doEndMethod.getDeclaringClass().equals(tagClass)) {
187       if (TagSupport.class.equals(tagClass) ||
188       BodyTagSupport.class.equals(tagClass)) {
189     tag.setDoEnd(false);
190     tag.setEndReturnsSkip(false);
191     tag.setEndReturnsEval(true);
192       }
193       else {
194     tag.setDoEnd(true);
195     tag.setEndReturnsSkip(true);
196     tag.setEndReturnsEval(true);
197       }
198     }
199     else if (parent != null) {
200       tag.setDoEnd(parent.getDoEnd());
201       tag.setEndReturnsSkip(parent.getEndReturnsSkip());
202       tag.setEndReturnsEval(parent.getEndReturnsEval());
203     }
204     
205     Method doAfterBody = getMethod(tagClass, "doAfterBody", new Class JavaDoc[0]);
206
207     if (doAfterBody != null &&
208     doAfterBody.getDeclaringClass().equals(tagClass)) {
209       if (TagSupport.class.equals(tagClass) ||
210       BodyTagSupport.class.equals(tagClass)) {
211     tag.setDoAfter(false);
212     tag.setAfterReturnsAgain(false);
213       }
214       else if (! IterationTag.class.isAssignableFrom(tagClass)) {
215     tag.setDoAfter(false);
216     tag.setAfterReturnsAgain(false);
217       }
218       else {
219     tag.setDoAfter(true);
220     tag.setAfterReturnsAgain(true);
221       }
222     }
223     else if (parent != null) {
224       tag.setDoAfter(parent.getDoAfter());
225       tag.setAfterReturnsAgain(parent.getAfterReturnsAgain());
226     }
227     
228     Method doInitBody = getMethod(tagClass, "doInitBody", new Class JavaDoc[0]);
229
230     if (doInitBody != null &&
231     doInitBody.getDeclaringClass().equals(tagClass)) {
232       if (BodyTagSupport.class.equals(tagClass)) {
233     tag.setDoInit(false);
234       }
235       else if (! BodyTag.class.isAssignableFrom(tagClass)) {
236     tag.setDoInit(false);
237       }
238       else {
239     tag.setDoInit(true);
240       }
241     }
242     else if (parent != null) {
243       tag.setDoInit(parent.getDoInit());
244     }
245     
246     Method doCatch = getMethod(tagClass, "doCatch",
247                    new Class JavaDoc[] { Throwable JavaDoc.class });
248
249     if (doCatch != null &&
250     doCatch.getDeclaringClass().equals(tagClass)) {
251       if (! TryCatchFinally.class.isAssignableFrom(tagClass)) {
252     tag.setDoCatch(false);
253       }
254       else {
255     tag.setDoCatch(true);
256       }
257     }
258     else if (parent != null) {
259       tag.setDoCatch(parent.getDoCatch());
260     }
261     
262     Method doFinally = getMethod(tagClass, "doFinally", new Class JavaDoc[0]);
263
264     if (doFinally != null &&
265     doFinally.getDeclaringClass().equals(tagClass)) {
266       if (! TryCatchFinally.class.isAssignableFrom(tagClass)) {
267     tag.setDoFinally(false);
268       }
269       else {
270     tag.setDoFinally(true);
271       }
272     }
273     else if (parent != null) {
274       tag.setDoFinally(parent.getDoFinally());
275     }
276
277     // check for @Resource injection
278
for (Method method : tagClass.getDeclaredMethods()) {
279       if (method.getName().startsWith("set")
280       && (method.isAnnotationPresent(Resource.class)
281           || method.isAnnotationPresent(EJB JavaDoc.class)
282           || method.isAnnotationPresent(WebServiceRef.class))) {
283     tag.setHasInjection(true);
284       }
285     }
286     
287     for (Field JavaDoc field : tagClass.getDeclaredFields()) {
288       if (field.isAnnotationPresent(Resource.class)
289       || field.isAnnotationPresent(EJB JavaDoc.class)
290       || field.isAnnotationPresent(WebServiceRef.class)) {
291     tag.setHasInjection(true);
292       }
293     }
294   }
295
296   private Method getMethod(Class JavaDoc tagClass, String JavaDoc name, Class JavaDoc []args)
297   {
298     try {
299       return tagClass.getMethod(name, args);
300     } catch (Throwable JavaDoc e) {
301       return null;
302     }
303   }
304
305   /**
306    * Analyzes the code for a method
307    */

308   private void analyze(JavaClass javaClass,
309                String JavaDoc name,
310                String JavaDoc signature,
311                Analyzer analyzer,
312                AnalyzedTag tag)
313   {
314     JavaMethod method = javaClass.findMethod(name, signature);
315     if (method == null)
316       return;
317     
318     CodeAttribute codeAttribute = method.getCode();
319
320     if (codeAttribute == null)
321       return;
322
323     CodeVisitor visitor = new CodeVisitor(javaClass, codeAttribute);
324     try {
325       visitor.analyze(analyzer);
326     } catch (Exception JavaDoc e) {
327       log.log(Level.WARNING, e.toString(), e);
328     }
329
330     analyzer.complete(tag);
331   }
332
333   static class Analyzer extends com.caucho.bytecode.Analyzer {
334     public void analyze(CodeVisitor visitor)
335     {
336     }
337     
338     public void complete(AnalyzedTag tag)
339     {
340     }
341   }
342   
343   /**
344    * Callback analyzing the doStartTag method.
345    */

346   static class StartAnalyzer extends Analyzer {
347     private boolean _hasSkip;
348     private boolean _hasInclude;
349     private boolean _hasBuffered;
350     private boolean _hasStart;
351
352     private int _count = 0;
353     private int _value = -1;
354
355     public void analyze(CodeVisitor visitor)
356     {
357       int count = _count++;
358       
359       switch (visitor.getOpcode()) {
360       case CodeVisitor.IRETURN:
361     if (count != 1)
362       _hasStart = true;
363
364     if (_value == Tag.SKIP_BODY)
365       _hasSkip = true;
366     else if (_value == Tag.EVAL_BODY_INCLUDE)
367       _hasInclude = true;
368     else if (_value == BodyTag.EVAL_BODY_BUFFERED)
369       _hasBuffered = true;
370     else {
371       _hasSkip = true;
372       _hasInclude = true;
373       _hasBuffered = true;
374     }
375     break;
376
377       case CodeVisitor.ICONST_M1:
378       case CodeVisitor.ICONST_0:
379       case CodeVisitor.ICONST_1:
380       case CodeVisitor.ICONST_2:
381       case CodeVisitor.ICONST_3:
382       case CodeVisitor.ICONST_4:
383       case CodeVisitor.ICONST_5:
384     if (count != 0)
385       _hasStart = true;
386         
387     _value = visitor.getOpcode() - CodeVisitor.ICONST_0;
388     break;
389
390       case CodeVisitor.BIPUSH:
391     if (count != 0)
392       _hasStart = true;
393     _value = visitor.getByteArg();
394     break;
395
396       case CodeVisitor.SIPUSH:
397     if (count != 0)
398       _hasStart = true;
399     _value = visitor.getShortArg();
400     break;
401       
402       default:
403     _hasStart = true;
404     _value = -1;
405     break;
406       }
407     }
408
409     public void complete(AnalyzedTag tag)
410     {
411       tag.setDoStart(_hasStart);
412       tag.setStartReturnsSkip(_hasSkip);
413       tag.setStartReturnsInclude(_hasInclude);
414       tag.setStartReturnsBuffered(_hasBuffered);
415     }
416   }
417   
418   /**
419    * Callback analyzing the doEndTag method.
420    */

421   static class EndAnalyzer extends Analyzer {
422     private boolean _hasSkip;
423     private boolean _hasEval;
424     private boolean _hasEnd;
425
426     private int _count = 0;
427     private int _value = -1;
428
429     public void analyze(CodeVisitor visitor)
430     {
431       int count = _count++;
432       
433       switch (visitor.getOpcode()) {
434       case CodeVisitor.IRETURN:
435     if (count != 1)
436       _hasEnd = true;
437
438     if (_value == Tag.SKIP_PAGE)
439       _hasSkip = true;
440     else if (_value == Tag.EVAL_PAGE)
441       _hasEval = true;
442     else {
443       _hasEnd = true;
444       _hasSkip = true;
445       _hasEval = true;
446     }
447     break;
448
449       case CodeVisitor.ICONST_M1:
450       case CodeVisitor.ICONST_0:
451       case CodeVisitor.ICONST_1:
452       case CodeVisitor.ICONST_2:
453       case CodeVisitor.ICONST_3:
454       case CodeVisitor.ICONST_4:
455       case CodeVisitor.ICONST_5:
456     if (count != 0)
457       _hasEnd = true;
458         
459     _value = visitor.getOpcode() - CodeVisitor.ICONST_0;
460     break;
461
462       case CodeVisitor.BIPUSH:
463     if (count != 0)
464       _hasEnd = true;
465     _value = visitor.getByteArg();
466     break;
467
468       case CodeVisitor.SIPUSH:
469     if (count != 0)
470       _hasEnd = true;
471     _value = visitor.getShortArg();
472     break;
473
474       default:
475     _hasEnd = true;
476     _value = -1;
477     break;
478       }
479     }
480
481     public void complete(AnalyzedTag tag)
482     {
483       tag.setDoEnd(_hasEnd);
484       tag.setEndReturnsSkip(_hasSkip);
485       tag.setEndReturnsEval(_hasEval);
486     }
487   }
488
489   /**
490    * Callback analyzing the doAfterBody method.
491    */

492   static class AfterAnalyzer extends Analyzer {
493     private boolean _hasAfter;
494     private boolean _hasAgain;
495
496     private int _count = 0;
497     private int _value = -1;
498
499     public void analyze(CodeVisitor visitor)
500     {
501       int count = _count++;
502       
503       switch (visitor.getOpcode()) {
504       case CodeVisitor.IRETURN:
505     if (count != 1)
506       _hasAfter = true;
507
508     if (_value == IterationTag.EVAL_BODY_AGAIN)
509       _hasAgain = true;
510     else if (_value == IterationTag.SKIP_BODY) {
511     }
512     else {
513       _hasAgain = true;
514     }
515     break;
516
517       case CodeVisitor.ICONST_M1:
518       case CodeVisitor.ICONST_0:
519       case CodeVisitor.ICONST_1:
520       case CodeVisitor.ICONST_2:
521       case CodeVisitor.ICONST_3:
522       case CodeVisitor.ICONST_4:
523       case CodeVisitor.ICONST_5:
524     if (count != 0)
525       _hasAfter = true;
526         
527     _value = visitor.getOpcode() - CodeVisitor.ICONST_0;
528     break;
529
530       case CodeVisitor.BIPUSH:
531     if (count != 0)
532       _hasAfter = true;
533     _value = visitor.getByteArg();
534     break;
535
536       case CodeVisitor.SIPUSH:
537     if (count != 0)
538       _hasAfter = true;
539     _value = visitor.getShortArg();
540     break;
541
542       default:
543     _hasAfter = true;
544     _value = -1;
545     break;
546       }
547     }
548
549     public void complete(AnalyzedTag tag)
550     {
551       tag.setDoAfter(_hasAfter);
552       tag.setAfterReturnsAgain(_hasAgain);
553     }
554   }
555
556   /**
557    * Callback analyzing the doInitBody method.
558    */

559   static class InitAnalyzer extends Analyzer {
560     private boolean _hasCode;
561
562     private int _count = 0;
563
564     public void analyze(CodeVisitor visitor)
565     {
566       int count = _count++;
567       
568       switch (visitor.getOpcode()) {
569       case CodeVisitor.RETURN:
570     if (count != 0)
571       _hasCode = true;
572     break;
573
574       default:
575     _hasCode = true;
576     break;
577       }
578     }
579
580     public void complete(AnalyzedTag tag)
581     {
582       tag.setDoInit(_hasCode);
583     }
584   }
585
586   /**
587    * Callback analyzing the doCatch method.
588    */

589   static class CatchAnalyzer extends Analyzer {
590     private boolean _hasCode;
591
592     private int _count = 0;
593
594     public void analyze(CodeVisitor visitor)
595     {
596       int count = _count++;
597       
598       switch (visitor.getOpcode()) {
599       case CodeVisitor.RETURN:
600     if (count != 0)
601       _hasCode = true;
602     break;
603
604       default:
605     _hasCode = true;
606     break;
607       }
608     }
609
610     public void complete(AnalyzedTag tag)
611     {
612       tag.setDoCatch(_hasCode);
613     }
614   }
615
616   /**
617    * Callback analyzing the doFinally method.
618    */

619   static class FinallyAnalyzer extends Analyzer {
620     private boolean _hasCode;
621
622     private int _count = 0;
623
624     public void analyze(CodeVisitor visitor)
625     {
626       int count = _count++;
627       
628       switch (visitor.getOpcode()) {
629       case CodeVisitor.RETURN:
630     if (count != 0)
631       _hasCode = true;
632     break;
633
634       default:
635     _hasCode = true;
636     break;
637       }
638     }
639
640     public void complete(AnalyzedTag tag)
641     {
642       tag.setDoFinally(_hasCode);
643     }
644   }
645 }
646
Popular Tags