2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
62
63
64 /*** Creates a new instance of ConfigParser */
65 public ConfigParser() {
66 }
67
68
69
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
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
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
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
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
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
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
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 }