1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.test;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  
30  import junit.framework.TestCase;
31  import net.sf.maventaglib.checker.Tag;
32  import net.sf.maventaglib.checker.TagAttribute;
33  import net.sf.maventaglib.checker.Tld;
34  import net.sf.maventaglib.checker.TldParser;
35  
36  import org.apache.commons.beanutils.PropertyUtils;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.w3c.dom.Document;
40  import org.xml.sax.EntityResolver;
41  import org.xml.sax.InputSource;
42  import org.xml.sax.SAXException;
43  
44  /**
45   * Insures the following ...
46   * <ul>
47   * <li> Tag handlers have setters for all tag attributes</li>
48   * <li> All tag handlers are in the classpath</li>
49   * <li> Tag handlers do not appear in the TLD more than once</li>
50   * <li> Tag handler attributes do not occur mare than once</li>
51   * </ul>
52   * 
53   * @author Dennis Byrne
54   * @see http://maven-taglib.sourceforge.net/ for dependency download
55   */
56  
57  public abstract class AbstractTagLibTestCase extends TestCase {
58      private static Log log = LogFactory.getLog(AbstractTagLibTestCase.class);
59  
60      protected Tld[] tlds;
61  
62      protected String[] tldPaths;
63      
64      private ClassLoader classLoader = getClass().getClassLoader();
65  
66      /**
67       * Unmarshall TLDs to an object model. TLDs are supplied by a subclass.
68       */
69  
70      protected void setUp() throws Exception {
71  
72          if (tldPaths == null)
73              throw new NullPointerException(
74                      "tldPaths cannot point to null before setUp() is called");
75  
76          int length = tldPaths.length;
77  
78          if (length == 0)
79              throw new IllegalStateException(
80                      "tldPaths should have at least one resource path");
81  
82          tlds = new Tld[length];
83  
84          for (int t = 0; t < length; t++) {
85              String name = tldPaths[t];
86  
87              InputStream stream = classLoader.getResourceAsStream(name);
88  
89              if (stream == null)
90                  throw new NullPointerException(
91                          "couldn't get an input stream for " + name);
92  
93              tlds[t] = TldTestUtils.getTld(name, stream);
94              stream.close();
95          }
96      }
97  
98      public void testUniqueTagTestCase() throws Exception {
99  
100         for (int lib = 0; lib < tlds.length; lib++) {
101             Tld tld = tlds[lib];
102             List tagNames = new ArrayList();
103             Tag[] tags = tld.getTags();
104 
105             for (int t = 0; t < tags.length; t++) {
106                 Tag tag = tags[t];
107 
108                 if (tag == null)
109                     throw new NullPointerException("tag");
110 
111                 String name = tag.getName();
112                 String msg = name + " found more than once in " + tldPaths[lib];
113                 assertFalse(msg, tagNames.contains(name));
114                 tagNames.add(name);
115             } // end t
116         } // end lib
117     }
118 
119     public void testUniqueTagAttributes() throws Exception {
120 
121         for (int lib = 0; lib < tlds.length; lib++) {
122             Tld tld = tlds[lib];
123             Tag[] tags = tld.getTags();
124 
125             for (int t = 0; t < tags.length; t++) {
126 
127                 Tag tag = tags[t];
128                 assertUniqueTagAttributes(tag, tldPaths[lib]);
129 
130             } // end t
131         } // end lib
132     }
133 
134     private void assertUniqueTagAttributes(final Tag tag, final String path) {
135 
136         List attributeNames = new ArrayList();
137         TagAttribute[] atts = tag.getAttributes();
138         String tagName = tag.getName();
139 
140         for (int a = 0; a < atts.length;) {
141 
142             TagAttribute att = atts[a++];
143             String name = att.getAttributeName();
144             String msg = " @" + name + " of " + path + ":" + tagName
145                     + " is duplicated.";
146             assertFalse(msg, attributeNames.contains(name));
147             attributeNames.add(name);
148 
149         } // end a
150 
151     }
152 
153     /**
154      * Make sure the class exists. Make sure there is a setter for each
155      * attribute.
156      */
157 
158     public void testSetters() throws Exception {
159 
160         for (int t = 0; t < tlds.length; t++) {
161             Tld tld = tlds[t];
162             Tag[] tags = tld.getTags();
163 
164             for (int s = 0; s < tags.length; s++) {
165                 Tag tag = tags[s];
166                 String filename = tld.getFilename();
167 
168                 Object object = TldTestUtils.makeTagClassInstance(tag,
169                         filename, classLoader);
170                 log.debug("filename = " + filename + " ; tag = " + tag.getName());
171                 assertSetters(tag, filename, object);
172 
173             } // end for tag
174         } // end for lib
175 
176     }
177 
178     private void assertSetters(final Tag tag, final String path,
179             final Object object) {
180 
181         TagAttribute[] attributes = tag.getAttributes();
182         String tagName = tag.getName();
183 
184         for (int a = 0; a < attributes.length; a++) {
185             TagAttribute attribute = attributes[a];
186             String name = attribute.getAttributeName();
187 
188             if (name == null || "".equals(name.trim()))
189                 throw new IllegalStateException(path + ":" + tagName
190                         + " has a nameless attribute ");
191 
192             String msg = path + ":" + tagName + "@" + name
193                     + " exists, but " + object.getClass().getName()
194                     + " has no setter.";
195             
196             assertTrue(msg, PropertyUtils.isWriteable(object, name));
197         } // end for attributes
198 
199     }
200     
201     private static class DelegateEntityResolver implements EntityResolver
202     {
203         private EntityResolver _delegate;
204 
205         public DelegateEntityResolver(EntityResolver delegate)
206         {
207             this._delegate = delegate;
208         }
209 
210         public InputSource resolveEntity(String publicId, String systemId)
211                 throws SAXException, IOException
212         {
213             if (publicId.equals("-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"))
214             {
215                 return new InputSource(Thread.currentThread()
216                         .getContextClassLoader().getResourceAsStream(
217                                 "META-INF/dtd/web-jsptaglibrary_1_2.dtd"));
218             }
219             else
220             {
221                 return _delegate.resolveEntity(publicId, systemId);
222             }
223         }
224     }    
225 
226     private static class TldTestUtils {
227         private static Log log = LogFactory.getLog(TldTestUtils.class);
228 
229         private static DocumentBuilderFactory dbf = DocumentBuilderFactory
230                 .newInstance();
231         static
232         {
233             dbf.setNamespaceAware(false);
234             dbf.setValidating(false);
235         }
236 
237         public static Tld getTld(String name, InputStream stream)
238                 throws Exception {
239             if (stream == null)
240                 log.error(" input stream is null ");
241 
242             DocumentBuilder db = dbf.newDocumentBuilder();
243             db.setEntityResolver(new DelegateEntityResolver(null));
244             Document doc = db.parse(stream);
245 
246             return TldParser.parse(doc, name);
247         }
248 
249         public static Object makeTagClassInstance(Tag tag, String filename,
250                 ClassLoader classLoader) throws Exception {
251 
252             String clazzName = tag.getTagClass();
253 
254             if (clazzName == null || "".equals(clazzName.trim()))
255                 throw new NullPointerException(tag.getName()
256                         + " is missing a tag class.");
257 
258             try {
259 
260                 Class clazz = classLoader.loadClass(clazzName);
261                 return clazz.newInstance();
262 
263             } catch (ClassNotFoundException e) {
264                 throw new ClassNotFoundException(clazzName);
265             } catch (IllegalAccessException ie) {
266                 throw new IllegalAccessException(clazzName);
267             } catch (InstantiationException iste) {
268                 throw new InstantiationException(clazzName);
269             }
270 
271         }
272 
273     }
274 
275 }