2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.test.config;
19  
20  import java.io.IOException;
21  import java.net.URL;
22  import javax.faces.FactoryFinder;
23  import javax.faces.application.Application;
24  import javax.faces.application.ApplicationFactory;
25  import javax.faces.render.RenderKit;
26  import javax.faces.render.RenderKitFactory;
27  import javax.faces.render.Renderer;
28  import org.apache.commons.digester.Digester;
29  import org.apache.commons.digester.Rule;
30  import org.apache.shale.test.mock.MockRenderKit;
31  import org.xml.sax.Attributes;
32  import org.xml.sax.SAXException;
33  
34  /***
35   * <p>Utility class to parse JavaServer Faces configuration resources, and
36   * register JSF artifacts with the mock object hierarchy.</p>
37   *
38   * <p>The following artifacts are registered:</p>
39   * <ul>
40   *     <li><code>Converter</code> (by-id and by-class)</li>
41   *     <li><code>RenderKit</code> and <code>Renderer</code></li>
42   *     <li><code>UIComponent</code></li>
43   *     <li><code>Validator</code></li>
44   * </ul>
45   *
46   * <p>Note that any declared <em>factory</em> instances are explicitly
47   * <strong>NOT</strong> registered, allowing the mock object hierarchy
48   * of the Shale Test Framework to manage these APIs.</p>
49   *
50   * <p><strong>USAGE NOTE</strong> - If you are using an instance of this
51   * class within a subclass of <code>AbstractJsfTestCase</code> or
52   * <code>AbstractJmockJsfTestCase</code>, be sure you have completed the
53   * <code>setUp()</code> processing in this base class before calling one
54   * of the <code>parse()</code> methods.</p>
55   *
56   * @since 1.1
57   */
58  public final class ConfigParser {
59      
60  
61      // ------------------------------------------------------------ Constructors
62  
63  
64      /*** Creates a new instance of ConfigParser */
65      public ConfigParser() {
66      }
67      
68  
69      // ------------------------------------------------------ Manifest Constants
70  
71  
72      /***
73       * <p>Configuration resource URLs for the JSF RI.</p>
74       */
75      private static final String[] JSFRI_RESOURCES =
76      { "/com/sun/faces/jsf-ri-runtime.xml",
77      };
78  
79  
80      /***
81       * <p>Configuration resource URLs for Apache MyFaces.</p>
82       */
83      private static final String[] MYFACES_RESOURCES =
84      { "/org/apache/myfaces/resource/standard-faces-config.xml",
85      };
86  
87  
88      // ------------------------------------------------------ Instance Variables
89  
90  
91      /***
92       * <p>The <code>Digester</code> instance we will use for parsing.</p>
93       */
94      private Digester digester = null;
95  
96  
97      // ------------------------------------------------------- Public Properties
98  
99  
100     /***
101      * <p>Return the URLs of the platform configuration resources for this
102      * application.  The following platforms are currently supported:</p>
103      * <ul>
104      * <li>JavaServer Faces Reference Implementation (version 1.0 - 1.2)</li>
105      * <li>MyFaces (version 1.1)</li>
106      * </ul>
107      *
108      * <p>If MyFaces (version 1.2), currently under development, does not change
109      * the name of the configuration resource, it will be supported as well.</p>
110      */
111     public URL[] getPlatformURLs() {
112 
113         URL[] urls = translate(JSFRI_RESOURCES);
114         if (urls[0] == null) {
115             urls = translate(MYFACES_RESOURCES);
116         }
117         return urls;
118 
119     }
120 
121 
122     // ---------------------------------------------------------- Public Methods
123 
124 
125     /***
126      * <p>Parse the specified JavaServer Faces configuration resource, causing
127      * the appropriate JSF artifacts to be registered with the mock object
128      * hierarchy.</p>
129      *
130      * @param url <code>URL</code> of the configuration resource to parse
131      *
132      * @exception IOException if an input/output error occurs
133      * @exception SAXException if a parsing error occurs
134      */
135     public void parse(URL url) throws IOException, SAXException {
136 
137         // Acquire and configure the Digester instance we will use
138         Digester digester = digester();
139         ApplicationFactory factory = (ApplicationFactory)
140           FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
141         Application application = factory.getApplication();
142         digester.push(application);
143 
144         // Perform the required parsing
145         try {
146             digester.parse(url);
147         } finally {
148             digester.clear();
149         }
150 
151     }
152 
153 
154     /***
155      * <p>Parse the specified set of JavaServer Faces configuration resources,
156      * in the listed order, causing the appropriate JSF artifacts to be registered
157      * with the mock object hierarchy.</p>
158      *
159      * @param urls <code>URL</code>s of the configuration resources to parse
160      *
161      * @exception IOException if an input/output error occurs
162      * @exception SAXException if a parsing error occurs
163      */
164     public void parse(URL[] urls) throws IOException, SAXException {
165 
166         for (int i = 0; i < urls.length; i++) {
167             parse(urls[i]);
168         }
169 
170     }
171 
172 
173     // --------------------------------------------------------- Private Methods
174 
175 
176     /***
177      * <p>Return the <code>Digester</code> instance we will use for parsing,
178      * creating and configuring a new instance if necessary.</p>
179      */
180     private Digester digester() {
181 
182         if (this.digester == null) {
183             this.digester = new Digester();
184             digester.addRule("faces-config/component", new ComponentRule());
185             digester.addCallMethod
186               ("faces-config/component/component-type", "setComponentType", 0);
187             digester.addCallMethod
188               ("faces-config/component/component-class", "setComponentClass", 0);
189             digester.addRule("faces-config/converter", new ConverterRule());
190             digester.addCallMethod
191               ("faces-config/converter/converter-id", "setConverterId", 0);
192             digester.addCallMethod
193               ("faces-config/converter/converter-class", "setConverterClass", 0);
194             digester.addCallMethod
195               ("faces-config/converter/converter-for-class", "setConverterForClass", 0);
196             digester.addRule("faces-config/render-kit", new RenderKitRule());
197             digester.addRule("faces-config/render-kit/render-kit-id", new RenderKitIdRule());
198             digester.addRule("faces-config/render-kit/renderer", new RendererRule());
199             digester.addCallMethod
200               ("faces-config/render-kit/renderer/component-family", "setComponentFamily", 0);
201             digester.addCallMethod
202               ("faces-config/render-kit/renderer/renderer-class", "setRendererClass", 0);
203             digester.addCallMethod
204               ("faces-config/render-kit/renderer/renderer-type", "setRendererType", 0);
205             digester.addRule("faces-config/validator", new ValidatorRule());
206             digester.addCallMethod
207               ("faces-config/validator/validator-id", "setValidatorId", 0);
208             digester.addCallMethod
209               ("faces-config/validator/validator-class", "setValidatorClass", 0);
210         }
211         return this.digester;
212 
213     }
214 
215 
216     /***
217      * <p>Translate an array of resource names into an array of resource URLs.</p>
218      *
219      * @param names Resource names to translate
220      */
221     private URL[] translate(String[] names) {
222 
223         URL[] results = new URL[names.length];
224         for (int i = 0; i < names.length; i++) {
225             results[i] = this.getClass().getResource(names[i]);
226         }
227         return results;
228         
229     }
230 
231 
232     // --------------------------------------------------------- Private Classes
233 
234 
235     /***
236      * <p>Data bean that stores information related to a component.</p>
237      */
238     class ComponentBean {
239 
240         private String componentClass;
241         public String getComponentClass() {
242             return this.componentClass;
243         }
244         public void setComponentClass(String componentClass) {
245             this.componentClass = componentClass;
246         }
247 
248         private String componentType;
249         public String getComponentType() {
250             return this.componentType;
251         }
252         public void setComponentType(String componentType) {
253             this.componentType = componentType;
254         }
255 
256     }
257 
258 
259     /***
260      * <p>Digester <code>Rule</code> for processing components.</p>
261      */
262     class ComponentRule extends Rule {
263 
264         public void begin(String namespace, String name, Attributes attributes) {
265             getDigester().push(new ComponentBean());
266         }
267 
268         public void end(String namespace, String name) {
269             ComponentBean bean = (ComponentBean) getDigester().pop();
270             Application application = (Application) getDigester().peek();
271             application.addComponent(bean.getComponentType(), bean.getComponentClass());
272         }
273 
274     }
275 
276 
277     /***
278      * <p>Data bean that stores information related to a converter.</p>
279      */
280     class ConverterBean {
281 
282         private String converterClass;
283         public String getConverterClass() {
284             return this.converterClass;
285         }
286         public void setConverterClass(String converterClass) {
287             this.converterClass = converterClass;
288         }
289 
290         private String converterForClass;
291         public String getConverterForClass() {
292             return this.converterForClass;
293         }
294         public void setConverterForClass(String converterForClass) {
295             this.converterForClass = converterForClass;
296         }
297 
298         private String converterId;
299         public String getConverterId() {
300             return this.converterId;
301         }
302         public void setConverterId(String converterId) {
303             this.converterId = converterId;
304         }
305 
306     }
307 
308 
309     /***
310      * <p>Digester <code>Rule</code> for processing converers.</p>
311      */
312     class ConverterRule extends Rule {
313 
314         public void begin(String namespace, String name, Attributes attributes) {
315             getDigester().push(new ConverterBean());
316         }
317 
318         public void end(String namespace, String name) {
319             ConverterBean bean = (ConverterBean) getDigester().pop();
320             Application application = (Application) getDigester().peek();
321             if (bean.getConverterId() != null) {
322                 application.addConverter(bean.getConverterId(), bean.getConverterClass());
323             } else {
324                 Class clazz = null;
325                 try {
326                     clazz = this.getClass().getClassLoader().loadClass(bean.getConverterForClass());
327                 } catch (ClassNotFoundException e) {
328                     throw new IllegalArgumentException("java.lang.ClassNotFoundException: "
329                         + bean.getConverterForClass());
330                 }
331                 application.addConverter(clazz, bean.getConverterClass());
332             }
333         }
334 
335     }
336 
337 
338     /***
339      * <p>Digester <code>Rule</code> for processing render kits.</p>
340      */
341     class RenderKitRule extends Rule {
342 
343         public void begin(String namespace, String name, Attributes attributes) {
344             RenderKitFactory factory = (RenderKitFactory)
345               FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
346             getDigester().push(factory.getRenderKit(null, RenderKitFactory.HTML_BASIC_RENDER_KIT));
347         }
348 
349         public void end(String namespace, String name) {
350             getDigester().pop();
351         }
352 
353     }
354 
355 
356     /***
357      * <p>Digester <code>Rule</code> for processing render kit identifiers.</p>
358      */
359     class RenderKitIdRule extends Rule {
360 
361         public void body(String namespace, String name, String text) {
362             String renderKitId = text.trim();
363             RenderKitFactory factory = (RenderKitFactory)
364               FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
365             RenderKit renderKit = factory.getRenderKit(null, renderKitId);
366             if (renderKit == null) {
367                 renderKit = new MockRenderKit();
368                 factory.addRenderKit(renderKitId, renderKit);
369             }
370             digester.pop();
371             digester.push(renderKit);
372         }
373 
374     }
375 
376 
377     /***
378      * <p>Data bean that stores information related to a renderer.</p>
379      */
380     class RendererBean {
381 
382         private String componentFamily;
383         public String getComponentFamily() {
384             return this.componentFamily;
385         }
386         public void setComponentFamily(String componentFamily) {
387             this.componentFamily = componentFamily;
388         }
389 
390         private String rendererClass;
391         public String getRendererClass() {
392             return this.rendererClass;
393         }
394         public void setRendererClass(String rendererClass) {
395             this.rendererClass = rendererClass;
396         }
397 
398         private String rendererType;
399         public String getRendererType() {
400             return this.rendererType;
401         }
402         public void setRendererType(String rendererType) {
403             this.rendererType = rendererType;
404         }
405 
406     }
407 
408 
409     /***
410      * <p>Digester <code>Rule</code> for processing renderers.</p>
411      */
412     class RendererRule extends Rule {
413 
414         public void begin(String namespace, String name, Attributes attributes) {
415             getDigester().push(new RendererBean());
416         }
417 
418         public void end(String namespace, String name) {
419             RendererBean bean = (RendererBean) getDigester().pop();
420             RenderKit kit = (RenderKit) getDigester().peek();
421             Renderer renderer = null;
422             Class clazz = null;
423             try {
424                 clazz = this.getClass().getClassLoader().loadClass(bean.getRendererClass());
425                 renderer = (Renderer) clazz.newInstance();
426             } catch (Exception e) {
427                 throw new IllegalArgumentException("Exception while trying to instantiate"
428                     + " renderer class '" + bean.getRendererClass() + "' : "
429                     + e.getMessage());
430             }
431             kit.addRenderer(bean.getComponentFamily(), bean.getRendererType(),
432                             renderer);
433         }
434 
435     }
436 
437 
438     /***
439      * <p>Data bean that stores information related to a validator.</p>
440      */
441     class ValidatorBean {
442 
443         private String validatorClass;
444         public String getValidatorClass() {
445             return this.validatorClass;
446         }
447         public void setValidatorClass(String validatorClass) {
448             this.validatorClass = validatorClass;
449         }
450 
451         private String validatorId;
452         public String getValidatorId() {
453             return this.validatorId;
454         }
455         public void setValidatorId(String validatorId) {
456             this.validatorId = validatorId;
457         }
458 
459     }
460 
461 
462     /***
463      * <p>Digester <code>Rule</code> for processing validators.</p>
464      */
465     class ValidatorRule extends Rule {
466 
467         public void begin(String namespace, String name, Attributes attributes) {
468             getDigester().push(new ValidatorBean());
469         }
470 
471         public void end(String namespace, String name) {
472             ValidatorBean bean = (ValidatorBean) getDigester().pop();
473             Application application = (Application) getDigester().peek();
474             application.addValidator(bean.getValidatorId(), bean.getValidatorClass());
475         }
476 
477     }
478 
479 
480 }