View Javadoc

1   /***
2    * Copyright 2004 Fabrizio Giustina.
3    *
4    * Licensed under the Artistic License; you may not use this file
5    * except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://maven-taglib.sourceforge.net/license.html
9    *
10   * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
11   * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
12   * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13   */
14  package net.sf.maventaglib.checker;
15  
16  import javax.servlet.jsp.tagext.TagExtraInfo;
17  import javax.servlet.jsp.tagext.TagSupport;
18  
19  import org.apache.commons.beanutils.PropertyUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.dom4j.Document;
24  import org.dom4j.Element;
25  import org.dom4j.tree.DefaultDocument;
26  import org.dom4j.tree.DefaultElement;
27  
28  
29  /***
30   * Validates tag handler classes fount in tlds.
31   * @author Fabrizio Giustina
32   * @version $Revision $ ($Author $)
33   */
34  public class TldChecker
35  {
36  
37      /***
38       * logger.
39       */
40      private static Log log = LogFactory.getLog(TldChecker.class);
41  
42      /***
43       * Check the given tld. Assure then:
44       * <ul>
45       * <li>Any tag class is loadable</li>
46       * <li>the tag class has a setter for any of the declared attribute</li>
47       * <li>the type declared in the dtd for an attribute (if any) matches the type accepted by the getter</li>
48       * </ul>
49       * @param tlds list of Tld to check.
50       * @return Document containing validation results
51       */
52      public Document check(Tld[] tlds)
53      {
54          Document doc = new DefaultDocument();
55          Element document = new DefaultElement("document");
56  
57          doc.add(document);
58  
59          for (int j = 0; j < tlds.length; j++)
60          {
61              Element tldNode = checkTld(tlds[j]);
62              document.add(tldNode);
63          }
64  
65          return doc;
66      }
67  
68      /***
69       * Checks a single tld and returns validation results.
70       * @param tld Tld
71       * @return tld element
72       */
73      private Element checkTld(Tld tld)
74      {
75          // new section for each tld
76          Element section = new DefaultElement("tld");
77          section.addAttribute("name", tld.getName());
78          section.addAttribute("file", tld.getFilename());
79          Tag[] tags = tld.getTags();
80          if (tags != null)
81          {
82              for (int j = 0; j < tags.length; j++)
83              {
84                  Element tagNode = checkTag(tags[j]);
85                  section.add(tagNode);
86              }
87          }
88  
89          return section;
90      }
91  
92      /***
93       * Checks a single tag and returns validation results.
94       * @param tag Tag
95       * @return tag element
96       */
97      private Element checkTag(Tag tag)
98      {
99          // new subsection for each tag
100         Element subsection = new DefaultElement("tag");
101 
102         String className = tag.getTagClass();
103         subsection.addAttribute("name", tag.getName());
104 
105         Element tagImpl = new DefaultElement("tagclass");
106         tagImpl.addAttribute("name", className);
107 
108         subsection.add(tagImpl);
109         Object tagObject = null;
110 
111         try
112         {
113             Class tagClass = Class.forName(className);
114 
115             if (!TagSupport.class.isAssignableFrom(tagClass))
116             {
117                 tagImpl.addAttribute("extend", "error");
118             }
119 
120             try
121             {
122                 tagObject = tagClass.newInstance();
123             }
124             catch (Throwable e)
125             {
126                 tagImpl.addAttribute("loadable", "error");
127             }
128         }
129         catch (ClassNotFoundException e)
130         {
131             tagImpl.addAttribute("found", "error");
132             tagImpl.addAttribute("extend", "error");
133             tagImpl.addAttribute("loadable", "error");
134         }
135 
136         if (tag.getTeiClass() != null)
137         {
138             Element teiImpl = checkTeiClass(tag.getTeiClass());
139             subsection.add(teiImpl);
140         }
141 
142         TagAttribute[] attributes = tag.getAttributes();
143 
144         if (tagObject != null && attributes.length > 0)
145         {
146             Element attributesNode = new DefaultElement("attributes");
147             subsection.add(attributesNode);
148 
149             for (int j = 0; j < attributes.length; j++)
150             {
151                 Element attributeNode = checkAttribute(tagObject, attributes[j]);
152                 attributesNode.add(attributeNode);
153             }
154         }
155 
156         return subsection;
157     }
158 
159     /***
160      * Check a declared TagExtraInfo class.
161      * @param className TEI class name
162      * @return teiclass Element
163      */
164     private Element checkTeiClass(String className)
165     {
166         Element teiImpl = new DefaultElement("teiclass");
167         teiImpl.addAttribute("name", className);
168 
169         Class teiClass = null;
170         try
171         {
172             teiClass = Class.forName(className);
173 
174             if (!TagExtraInfo.class.isAssignableFrom(teiClass))
175             {
176                 teiImpl.addAttribute("extend", "error");
177             }
178 
179             try
180             {
181                 teiClass.newInstance();
182             }
183             catch (Throwable e)
184             {
185                 teiImpl.addAttribute("loadable", "error");
186             }
187         }
188         catch (ClassNotFoundException e)
189         {
190             teiImpl.addAttribute("found", "error");
191             teiImpl.addAttribute("extend", "error");
192             teiImpl.addAttribute("loadable", "error");
193         }
194         return teiImpl;
195     }
196 
197     /***
198      * Checks a single attribute and returns validation results.
199      * @param tag tag handler instance
200      * @param attribute TagAttribute
201      * @return attribute element
202      */
203     private Element checkAttribute(Object tag, TagAttribute attribute)
204     {
205         String tldType = attribute.getAttributeType();
206         String tldName = attribute.getAttributeName();
207 
208         Element attributeNode = new DefaultElement("attribute");
209         attributeNode.addAttribute("tldname", tldName);
210         attributeNode.addAttribute("tldtype", StringUtils.defaultString(tldType));
211 
212         if (!PropertyUtils.isWriteable(tag, tldName))
213         {
214             Element errorNode = new DefaultElement("error");
215             errorNode.addAttribute("level", "error");
216             errorNode.addText("Setter not found");
217             attributeNode.add(errorNode);
218             return attributeNode;
219         }
220 
221         Class tagType = null;
222         try
223         {
224             tagType = PropertyUtils.getPropertyType(tag, tldName);
225         }
226         catch (Throwable e)
227         {
228             // should never happen, since we already checked the writable property
229         }
230 
231         if (tldType != null && tagType != null)
232         {
233             Class tldTypeClass = getClassFromName(tldType);
234 
235             if (!tagType.isAssignableFrom(tldTypeClass))
236             {
237                 //out.write(this.errorTd);
238 
239                 Element errorNode = new DefaultElement("error");
240                 errorNode.addAttribute("level", "error");
241                 errorNode.addText("Error in attribute type: tld declares ["
242                     + tldType
243                     + "], class declares ["
244                     + tagType.getName()
245                     + "]");
246                 attributeNode.add(errorNode);
247             }
248 
249         }
250 
251         attributeNode.addAttribute("tagtype", tagType == null ? "" : tagType.getName());
252 
253         if (tldType != null && !tldType.equals(tagType.getName()))
254         {
255             Element errorNode = new DefaultElement("error");
256             errorNode.addAttribute("level", "warning");
257             errorNode.addText("Inexact match: tld declares ["
258                 + tldType
259                 + "], class declares ["
260                 + tagType.getName()
261                 + "]");
262             attributeNode.add(errorNode);
263         }
264         else if (tldType == null && !java.lang.String.class.equals(tagType))
265         {
266             Element errorNode = new DefaultElement("error");
267             errorNode.addAttribute("level", "info");
268             errorNode.addText("Attribute type different from String and not declared in tld.");
269             attributeNode.add(errorNode);
270         }
271 
272         return attributeNode;
273 
274     }
275 
276     /***
277      * returns a class from its name, handling primitives.
278      * @param className clss name
279      * @return Class istantiated using Class.forName or the matching primitive.
280      */
281     private Class getClassFromName(String className)
282     {
283 
284         Class tldTypeClass = null;
285 
286         if ("int".equals(className))
287         {
288             tldTypeClass = int.class;
289         }
290         else if ("long".equals(className))
291         {
292             tldTypeClass = long.class;
293         }
294         else if ("double".equals(className))
295         {
296             tldTypeClass = double.class;
297         }
298         else if ("boolean".equals(className))
299         {
300             tldTypeClass = boolean.class;
301         }
302         else if ("char".equals(className))
303         {
304             tldTypeClass = char.class;
305         }
306         else if ("byte".equals(className))
307         {
308             tldTypeClass = byte.class;
309         }
310 
311         if (tldTypeClass == null)
312         {
313             // not a primitive type
314             try
315             {
316                 tldTypeClass = Class.forName(className);
317             }
318             catch (ClassNotFoundException e)
319             {
320                 log.error("unable to find class [" + className + "] declared in 'type' attribute");
321             }
322         }
323         return tldTypeClass;
324     }
325 
326 }