~ubuntu-branches/ubuntu/maverick/libcommons-digester-java/maverick

« back to all changes in this revision

Viewing changes to src/java/org/apache/commons/digester/xmlrules/DigesterRuleParser.java

  • Committer: Bazaar Package Importer
  • Author(s): Takashi Okamoto
  • Date: 2004-08-13 01:59:24 UTC
  • Revision ID: james.westby@ubuntu.com-20040813015924-kxkbfi0a1u5cbxbr
Tags: 1.5.0.1-3
rebuild with J2SDK1.3 comparible mode.(closes: #265253)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ====================================================================
 
3
 *
 
4
 * The Apache Software License, Version 1.1
 
5
 *
 
6
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 
7
 * reserved.
 
8
 *
 
9
 * Redistribution and use in source and binary forms, with or without
 
10
 * modification, are permitted provided that the following conditions
 
11
 * are met:
 
12
 *
 
13
 * 1. Redistributions of source code must retain the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer.
 
15
 *
 
16
 * 2. Redistributions in binary form must reproduce the above copyright
 
17
 *    notice, this list of conditions and the following disclaimer in
 
18
 *    the documentation and/or other materials provided with the
 
19
 *    distribution.
 
20
 *
 
21
 * 3. The end-user documentation included with the redistribution, if
 
22
 *    any, must include the following acknowlegement:
 
23
 *       "This product includes software developed by the
 
24
 *        Apache Software Foundation (http://www.apache.org/)."
 
25
 *    Alternately, this acknowlegement may appear in the software itself,
 
26
 *    if and wherever such third-party acknowlegements normally appear.
 
27
 *
 
28
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 
29
 *    Foundation" must not be used to endorse or promote products derived
 
30
 *    from this software without prior written permission. For written
 
31
 *    permission, please contact apache@apache.org.
 
32
 *
 
33
 * 5. Products derived from this software may not be called "Apache"
 
34
 *    nor may "Apache" appear in their names without prior written
 
35
 *    permission of the Apache Group.
 
36
 *
 
37
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 
38
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
39
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
40
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 
41
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
42
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
43
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 
44
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
45
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 
46
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 
47
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
48
 * SUCH DAMAGE.
 
49
 * ====================================================================
 
50
 *
 
51
 * This software consists of voluntary contributions made by many
 
52
 * individuals on behalf of the Apache Software Foundation.  For more
 
53
 * information on the Apache Software Foundation, please see
 
54
 * <http://www.apache.org/>.
 
55
 *
 
56
 */
 
57
 
 
58
 
 
59
package org.apache.commons.digester.xmlrules;
 
60
 
 
61
 
 
62
import java.io.FileNotFoundException;
 
63
import java.io.IOException;
 
64
import java.net.URL;
 
65
import java.util.ArrayList;
 
66
import java.util.HashSet;
 
67
import java.util.List;
 
68
import java.util.Set;
 
69
import java.util.StringTokenizer;
 
70
 
 
71
import org.apache.commons.collections.ArrayStack;
 
72
import org.apache.commons.digester.AbstractObjectCreationFactory;
 
73
import org.apache.commons.digester.BeanPropertySetterRule;
 
74
import org.apache.commons.digester.CallMethodRule;
 
75
import org.apache.commons.digester.CallParamRule;
 
76
import org.apache.commons.digester.Digester;
 
77
import org.apache.commons.digester.FactoryCreateRule;
 
78
import org.apache.commons.digester.ObjectCreateRule;
 
79
import org.apache.commons.digester.Rule;
 
80
import org.apache.commons.digester.RuleSetBase;
 
81
import org.apache.commons.digester.Rules;
 
82
import org.apache.commons.digester.SetNextRule;
 
83
import org.apache.commons.digester.SetPropertiesRule;
 
84
import org.apache.commons.digester.SetPropertyRule;
 
85
import org.apache.commons.digester.SetTopRule;
 
86
import org.xml.sax.Attributes;
 
87
import org.xml.sax.SAXException;
 
88
 
 
89
 
 
90
/**
 
91
 * This is a RuleSet that parses XML into Digester rules, and then
 
92
 * adds those rules to a 'target' Digester.
 
93
 *
 
94
 * @author David H. Martin - Initial Contribution
 
95
 * @author Scott Sanders   - Added ASL, removed external dependencies
 
96
 * @author Bradley M. Handy - Bean Property Setter Rule addition
 
97
 * 
 
98
 */
 
99
 
 
100
public class DigesterRuleParser extends RuleSetBase {
 
101
    
 
102
    public static final String DIGESTER_PUBLIC_ID = "-//Jakarta Apache //DTD digester-rules XML V1.0//EN";
 
103
    
 
104
    /**
 
105
     * path to the DTD
 
106
     */
 
107
    private String digesterDtdUrl;
 
108
    
 
109
    /**
 
110
     * This is the digester to which we are adding the rules that we parse
 
111
     * from the Rules XML document.
 
112
     */
 
113
    protected Digester targetDigester;
 
114
    
 
115
    
 
116
    /**
 
117
     * A stack whose toString method returns a '/'-separated concatenation
 
118
     * of all the elements in the stack.
 
119
     */
 
120
    protected class PatternStack extends ArrayStack {
 
121
        public String toString() {
 
122
            StringBuffer str = new StringBuffer();
 
123
            for (int i = 0; i < size(); i++) {
 
124
                String elem = get(i).toString();
 
125
                if (elem.length() > 0) {
 
126
                    if (str.length() > 0) {
 
127
                        str.append('/');
 
128
                    }
 
129
                    str.append(elem);
 
130
                }
 
131
            }
 
132
            return str.toString();
 
133
        }
 
134
    }
 
135
    
 
136
    /**
 
137
     * A stack used to maintain the current pattern. The Rules XML document
 
138
     * type allows nesting of patterns. If an element defines a matching
 
139
     * pattern, the resulting pattern is a concatenation of that pattern with
 
140
     * all the ancestor elements' patterns. Hence the need for a stack.
 
141
     */
 
142
    protected PatternStack patternStack;
 
143
    
 
144
    /**
 
145
     * Used to detect circular includes
 
146
     */
 
147
    private Set includedFiles = new HashSet();
 
148
    
 
149
    /**
 
150
     * Constructs a DigesterRuleParser. This object will be inoperable
 
151
     * until the target digester is set, via <code>setTarget(Digester)</code>
 
152
     */
 
153
    public DigesterRuleParser() {
 
154
        patternStack = new PatternStack();
 
155
    }
 
156
    
 
157
    /**
 
158
     * Constructs a rule set for converting XML digester rule descriptions
 
159
     * into Rule objects, and adding them to the given Digester
 
160
     * @param targetDigester the Digester to add the rules to
 
161
     */
 
162
    public DigesterRuleParser(Digester targetDigester) {
 
163
        this.targetDigester = targetDigester;
 
164
        patternStack = new PatternStack();
 
165
    }
 
166
    
 
167
    /**
 
168
     * Constructs a rule set for parsing an XML digester rule file that
 
169
     * has been included within an outer XML digester rule file. In this
 
170
     * case, we must pass the pattern stack and the target digester
 
171
     * to the rule set, as well as the list of files that have already
 
172
     * been included, for cycle detection.
 
173
     * @param targetDigester the Digester to add the rules to
 
174
     * @param stack Stack containing the prefix pattern string to be prepended
 
175
     * to any pattern parsed by this rule set.
 
176
     */
 
177
    private DigesterRuleParser(Digester targetDigester,
 
178
                                PatternStack stack, Set includedFiles) {
 
179
        this.targetDigester = targetDigester;
 
180
        patternStack = stack;
 
181
        this.includedFiles = includedFiles;
 
182
    }
 
183
    
 
184
    /**
 
185
     * Sets the digester into which to add the parsed rules
 
186
     * @param d the Digester to add the rules to
 
187
     */
 
188
    public void setTarget(Digester d) {
 
189
        targetDigester = d;
 
190
    }
 
191
    
 
192
    /**
 
193
     * Sets the location of the digester rules DTD. This is the DTD used
 
194
     * to validate the rules XML file.
 
195
     */
 
196
    public void setDigesterRulesDTD(String dtdURL) {
 
197
        digesterDtdUrl = dtdURL;
 
198
    }
 
199
    
 
200
    /**
 
201
     * Returns the location of the DTD used to validate the digester rules
 
202
     * XML document.
 
203
     */
 
204
    protected String getDigesterRulesDTD() {
 
205
        //ClassLoader classLoader = getClass().getClassLoader();
 
206
        //URL url = classLoader.getResource(DIGESTER_DTD_PATH);
 
207
        //return url.toString();
 
208
        return digesterDtdUrl;
 
209
    }
 
210
    
 
211
    /**
 
212
     * Adds a rule the the target digester. After a rule has been created by
 
213
     * parsing the XML, it is added to the digester by calling this method.
 
214
     * Typically, this method is called via reflection, when executing
 
215
     * a SetNextRule, from the Digester that is parsing the rules XML.
 
216
     * @param rule a Rule to add to the target digester.
 
217
     */
 
218
    public void add(Rule rule) {
 
219
        targetDigester.addRule(patternStack.toString(), rule);
 
220
    }
 
221
    
 
222
    
 
223
    /**
 
224
     * Add to the given digester the set of Rule instances used to parse an XML
 
225
     * document defining Digester rules. When the digester parses an XML file,
 
226
     * it will add the resulting rules & patterns to the 'target digester'
 
227
     * that was passed in this RuleSet's constructor.<P>
 
228
     * If you extend this class to support additional rules, your implementation
 
229
     * should of this method should call this implementation first: i.e.
 
230
     * <code>super.addRuleInstances(digester);</code>
 
231
     */
 
232
    public void addRuleInstances(Digester digester) {
 
233
        final String ruleClassName = Rule.class.getName();
 
234
        digester.register(DIGESTER_PUBLIC_ID, getDigesterRulesDTD());
 
235
        
 
236
        digester.addRule("*/pattern", new PatternRule("value"));
 
237
        
 
238
        digester.addRule("*/include", new IncludeRule());
 
239
        
 
240
        digester.addFactoryCreate("*/bean-property-setter-rule", new BeanPropertySetterRuleFactory());
 
241
        digester.addRule("*/bean-property-setter-rule", new PatternRule("pattern"));
 
242
        digester.addSetNext("*/bean-property-setter-rule", "add", ruleClassName);
 
243
        
 
244
        digester.addFactoryCreate("*/call-method-rule", new CallMethodRuleFactory());
 
245
        digester.addRule("*/call-method-rule", new PatternRule("pattern"));
 
246
        digester.addSetNext("*/call-method-rule", "add", ruleClassName);
 
247
        
 
248
        digester.addFactoryCreate("*/call-param-rule", new CallParamRuleFactory());
 
249
        digester.addRule("*/call-param-rule", new PatternRule("pattern"));
 
250
        digester.addSetNext("*/call-param-rule", "add", ruleClassName);
 
251
        
 
252
        digester.addFactoryCreate("*/factory-create-rule", new FactoryCreateRuleFactory());
 
253
        digester.addRule("*/factory-create-rule", new PatternRule("pattern"));
 
254
        digester.addSetNext("*/factory-create-rule", "add", ruleClassName);
 
255
        
 
256
        digester.addFactoryCreate("*/object-create-rule", new ObjectCreateRuleFactory());
 
257
        digester.addRule("*/object-create-rule", new PatternRule("pattern"));
 
258
        digester.addSetNext("*/object-create-rule", "add", ruleClassName);
 
259
        
 
260
        digester.addFactoryCreate("*/set-properties-rule", new SetPropertiesRuleFactory());
 
261
        digester.addRule("*/set-properties-rule", new PatternRule("pattern"));
 
262
        digester.addSetNext("*/set-properties-rule", "add", ruleClassName);
 
263
        
 
264
        digester.addRule("*/set-properties-rule/alias", new SetPropertiesAliasRule());
 
265
        
 
266
        digester.addFactoryCreate("*/set-property-rule", new SetPropertyRuleFactory());
 
267
        digester.addRule("*/set-property-rule", new PatternRule("pattern"));
 
268
        digester.addSetNext("*/set-property-rule", "add", ruleClassName);
 
269
        
 
270
        digester.addFactoryCreate("*/set-top-rule", new SetTopRuleFactory());
 
271
        digester.addRule("*/set-top-rule", new PatternRule("pattern"));
 
272
        digester.addSetNext("*/set-top-rule", "add", ruleClassName);
 
273
        
 
274
        digester.addFactoryCreate("*/set-next-rule", new SetNextRuleFactory());
 
275
        digester.addRule("*/set-next-rule", new PatternRule("pattern"));
 
276
        digester.addSetNext("*/set-next-rule", "add", ruleClassName);
 
277
    }
 
278
    
 
279
    
 
280
    /**
 
281
     * A rule for extracting the pattern matching strings from the rules XML.
 
282
     * In the digester-rules document type, a pattern can either be declared
 
283
     * in the 'value' attribute of a <pattern> element (in which case the pattern
 
284
     * applies to all rules elements contained within the <pattern> element),
 
285
     * or it can be declared in the optional 'pattern' attribute of a rule
 
286
     * element.
 
287
     */
 
288
    private class PatternRule extends Rule {
 
289
        
 
290
        private String attrName;
 
291
        private String pattern = null;
 
292
        
 
293
        /**
 
294
         * @param digester the Digester used to parse the rules XML file
 
295
         * @param attrName The name of the attribute containing the pattern
 
296
         */
 
297
        public PatternRule(String attrName) {
 
298
            super();
 
299
            this.attrName = attrName;
 
300
        }
 
301
        
 
302
        /**
 
303
         * If a pattern is defined for the attribute, push it onto the
 
304
         * pattern stack.
 
305
         */
 
306
        public void begin(Attributes attributes) {
 
307
            pattern = attributes.getValue(attrName);
 
308
            if (pattern != null) {
 
309
                patternStack.push(pattern);
 
310
            }
 
311
        }
 
312
        
 
313
        /**
 
314
         * If there was a pattern for this element, pop it off the pattern
 
315
         * stack.
 
316
         */
 
317
        public void end() {
 
318
            if (pattern != null) {
 
319
                patternStack.pop();
 
320
            }
 
321
        }
 
322
    }
 
323
    
 
324
    /**
 
325
     * A rule for including one rules XML file within another. Included files
 
326
     * behave as if they are 'macro-expanded' within the includer. This means
 
327
     * that the values of the pattern stack are prefixed to every pattern
 
328
     * in the included rules. <p>This rule will detect 'circular' includes,
 
329
     * which would result in infinite recursion. It throws a
 
330
     * CircularIncludeException when a cycle is detected, which will terminate
 
331
     * the parse.
 
332
     */
 
333
    private class IncludeRule extends Rule {
 
334
        public IncludeRule() {
 
335
            super();
 
336
        }
 
337
        
 
338
        /**
 
339
         * To include a rules xml file, we instantiate another Digester, and
 
340
         * another DigesterRulesRuleSet. We pass the
 
341
         * pattern stack and the target Digester to the new rule set, and
 
342
         * tell the Digester to parse the file.
 
343
         */
 
344
        public void begin(Attributes attributes) throws Exception {
 
345
            // The path attribute gives the URI to another digester rules xml file
 
346
            String fileName = attributes.getValue("path");
 
347
            if (fileName != null && fileName.length() > 0) {
 
348
                includeXMLRules(fileName);
 
349
            }
 
350
            
 
351
            // The class attribute gives the name of a class that implements
 
352
            // the DigesterRulesSource interface
 
353
            String className = attributes.getValue("class");
 
354
            if (className != null && className.length() > 0) {
 
355
                includeProgrammaticRules(className);
 
356
            }
 
357
        }
 
358
        
 
359
        /**
 
360
         * Creates another DigesterRuleParser, and uses it to extract the rules
 
361
         * out of the give XML file. The contents of the current pattern stack
 
362
         * will be prepended to all of the pattern strings parsed from the file.
 
363
         */
 
364
        private void includeXMLRules(String fileName)
 
365
                        throws IOException, SAXException, CircularIncludeException {
 
366
            URL fileURL = DigesterRuleParser.this.getClass().getClassLoader().getResource(fileName);
 
367
            if (fileURL == null) {
 
368
                throw new FileNotFoundException("File \"" + fileName + "\" not found.");
 
369
            }
 
370
            fileName = fileURL.toExternalForm();
 
371
            if (includedFiles.add(fileName) == false) {
 
372
                // circular include detected
 
373
                throw new CircularIncludeException(fileName);
 
374
            }
 
375
            // parse the included xml file
 
376
            DigesterRuleParser includedSet =
 
377
                        new DigesterRuleParser(targetDigester, patternStack, includedFiles);
 
378
            includedSet.setDigesterRulesDTD(getDigesterRulesDTD());
 
379
            Digester digester = new Digester();
 
380
            digester.addRuleSet(includedSet);
 
381
            digester.push(DigesterRuleParser.this);
 
382
            digester.parse(fileName);
 
383
            includedFiles.remove(fileName);
 
384
        }
 
385
        
 
386
        /**
 
387
         * Creates an instance of the indicated class. The class must implement
 
388
         * the DigesterRulesSource interface. Passes the target digester to
 
389
         * that instance. The DigesterRulesSource instance is supposed to add
 
390
         * rules into the digester. The contents of the current pattern stack
 
391
         * will be automatically prepended to all of the pattern strings added
 
392
         * by the DigesterRulesSource instance.
 
393
         */
 
394
        private void includeProgrammaticRules(String className)
 
395
                        throws ClassNotFoundException, ClassCastException,
 
396
                        InstantiationException, IllegalAccessException {
 
397
            
 
398
            Class cls = Class.forName(className);
 
399
            DigesterRulesSource rulesSource = (DigesterRulesSource) cls.newInstance();
 
400
            
 
401
            // wrap the digester's Rules object, to prepend pattern
 
402
            Rules digesterRules = targetDigester.getRules();
 
403
            Rules prefixWrapper =
 
404
                    new RulesPrefixAdapter(patternStack.toString(), digesterRules);
 
405
            
 
406
            targetDigester.setRules(prefixWrapper);
 
407
            try {
 
408
                rulesSource.getRules(targetDigester);
 
409
            } finally {
 
410
                // Put the unwrapped rules back
 
411
                targetDigester.setRules(digesterRules);
 
412
            }
 
413
        }
 
414
    }
 
415
    
 
416
    
 
417
    /**
 
418
     * Wraps a Rules object. Delegates all the Rules interface methods
 
419
     * to the underlying Rules object. Overrides the add method to prepend
 
420
     * a prefix to the pattern string.
 
421
     */
 
422
    private class RulesPrefixAdapter implements Rules {
 
423
        
 
424
        private Rules delegate;
 
425
        private String prefix;
 
426
        
 
427
        /**
 
428
         * @param patternPrefix the pattern string to prepend to the pattern
 
429
         * passed to the add method.
 
430
         * @param rules The wrapped Rules object. All of this class's methods
 
431
         * pass through to this object.
 
432
         */
 
433
        public RulesPrefixAdapter(String patternPrefix, Rules rules) {
 
434
            prefix = patternPrefix;
 
435
            delegate = rules;
 
436
        }
 
437
        
 
438
        /**
 
439
         * Register a new Rule instance matching a pattern which is constructed
 
440
         * by concatenating the pattern prefix with the given pattern.
 
441
         */
 
442
        public void add(String pattern, Rule rule) {
 
443
            delegate.add(prefix + pattern, rule);
 
444
        }
 
445
        
 
446
        /**
 
447
         * This method passes through to the underlying Rules object.
 
448
         */
 
449
        public void clear() {
 
450
            delegate.clear();
 
451
        }
 
452
        
 
453
        /**
 
454
         * This method passes through to the underlying Rules object.
 
455
         */
 
456
        public Digester getDigester() {
 
457
            return delegate.getDigester();
 
458
        }
 
459
        
 
460
        /**
 
461
         * This method passes through to the underlying Rules object.
 
462
         */
 
463
        public String getNamespaceURI() {
 
464
            return delegate.getNamespaceURI();
 
465
        }
 
466
        
 
467
        /**
 
468
         * @deprecated Call match(namespaceURI,pattern) instead.
 
469
         */
 
470
        public List match(String pattern) {
 
471
            return delegate.match(pattern);
 
472
        }
 
473
        
 
474
        /**
 
475
         * This method passes through to the underlying Rules object.
 
476
         */
 
477
        public List match(String namespaceURI, String pattern) {
 
478
            return delegate.match(namespaceURI, pattern);
 
479
        }
 
480
        
 
481
        /**
 
482
         * This method passes through to the underlying Rules object.
 
483
         */
 
484
        public List rules() {
 
485
            return delegate.rules();
 
486
        }
 
487
        
 
488
        /**
 
489
         * This method passes through to the underlying Rules object.
 
490
         */
 
491
        public void setDigester(Digester digester) {
 
492
            delegate.setDigester(digester);
 
493
        }
 
494
        
 
495
        /**
 
496
         * This method passes through to the underlying Rules object.
 
497
         */
 
498
        public void setNamespaceURI(String namespaceURI) {
 
499
            delegate.setNamespaceURI(namespaceURI);
 
500
        }
 
501
    }
 
502
    
 
503
    
 
504
    ///////////////////////////////////////////////////////////////////////
 
505
    // Classes beyond this point are ObjectCreationFactory implementations,
 
506
    // used to create Rule objects and initialize them from SAX attributes.
 
507
    ///////////////////////////////////////////////////////////////////////
 
508
    
 
509
    /**
 
510
     * Factory for creating a BeanPropertySetterRule.
 
511
     */
 
512
    private class BeanPropertySetterRuleFactory extends AbstractObjectCreationFactory {
 
513
        public Object createObject(Attributes attributes) throws Exception {
 
514
            Rule beanPropertySetterRule = null;
 
515
            String propertyname = attributes.getValue("propertyname");
 
516
                
 
517
            if (propertyname == null) {
 
518
                // call the setter method corresponding to the element name.
 
519
                beanPropertySetterRule = new BeanPropertySetterRule();
 
520
            } else {
 
521
                beanPropertySetterRule = new BeanPropertySetterRule(propertyname);
 
522
            }
 
523
            
 
524
            return beanPropertySetterRule;
 
525
        }
 
526
        
 
527
    }
 
528
 
 
529
    /**
 
530
     * Factory for creating a CallMethodRule.
 
531
     */
 
532
    protected class CallMethodRuleFactory extends AbstractObjectCreationFactory {
 
533
        public Object createObject(Attributes attributes) {
 
534
            Rule callMethodRule = null;
 
535
            String methodName = attributes.getValue("methodname");
 
536
            if (attributes.getValue("paramcount") == null) {
 
537
                // call against empty method
 
538
                callMethodRule = new CallMethodRule(methodName);
 
539
            
 
540
            } else {
 
541
                int paramCount = Integer.parseInt(attributes.getValue("paramcount"));
 
542
                
 
543
                String paramTypesAttr = attributes.getValue("paramtypes");
 
544
                if (paramTypesAttr == null || paramTypesAttr.length() == 0) {
 
545
                    callMethodRule = new CallMethodRule(methodName, paramCount);
 
546
                } else {
 
547
                    // Process the comma separated list or paramTypes
 
548
                    // into an array of String class names
 
549
                    ArrayList paramTypes = new ArrayList();
 
550
                    StringTokenizer tokens = new StringTokenizer(paramTypesAttr, " \t\n\r,");
 
551
                    while (tokens.hasMoreTokens()) {
 
552
                            paramTypes.add(tokens.nextToken());
 
553
                    }
 
554
                    callMethodRule = new CallMethodRule( methodName,
 
555
                                                        paramCount,
 
556
                                                        (String[])paramTypes.toArray(new String[0]));
 
557
                }
 
558
            }
 
559
            return callMethodRule;
 
560
        }
 
561
    }
 
562
    
 
563
    /**
 
564
     * Factory for creating a CallParamRule.
 
565
     */
 
566
    protected class CallParamRuleFactory extends AbstractObjectCreationFactory {
 
567
    
 
568
        public Object createObject(Attributes attributes) {
 
569
            // create callparamrule
 
570
            int paramIndex = Integer.parseInt(attributes.getValue("paramnumber"));
 
571
            String attributeName = attributes.getValue("attrname");
 
572
            String fromStack = attributes.getValue("from-stack");
 
573
            Rule callParamRule = null;
 
574
            if (attributeName == null) {
 
575
                if (fromStack == null) {
 
576
                
 
577
                    callParamRule = new CallParamRule( paramIndex );
 
578
                
 
579
                } else {
 
580
 
 
581
                    callParamRule = new CallParamRule( paramIndex, Boolean.valueOf(fromStack).booleanValue());
 
582
                    
 
583
                }
 
584
            } else {
 
585
                if (fromStack == null) {
 
586
                    
 
587
                    callParamRule = new CallParamRule( paramIndex, attributeName );
 
588
                    
 
589
                    
 
590
                } else {
 
591
                    // specifying both from-stack and attribute name is not allowed
 
592
                    throw new RuntimeException("Attributes from-stack and attrname cannot both be present.");
 
593
                }
 
594
            }
 
595
            return callParamRule;
 
596
        }
 
597
    }
 
598
    
 
599
    /**
 
600
     * Factory for creating a FactoryCreateRule
 
601
     */
 
602
    protected class FactoryCreateRuleFactory extends AbstractObjectCreationFactory {
 
603
        public Object createObject(Attributes attributes) {
 
604
            String className = attributes.getValue("classname");
 
605
            String attrName = attributes.getValue("attrname");
 
606
            boolean ignoreExceptions = 
 
607
                "true".equalsIgnoreCase(attributes.getValue("ignore-exceptions"));
 
608
            return (attrName == null || attrName.length() == 0) ?
 
609
                new FactoryCreateRule( className, ignoreExceptions)
 
610
                :
 
611
                new FactoryCreateRule( className, attrName, ignoreExceptions);
 
612
        }
 
613
    }
 
614
    
 
615
    /**
 
616
     * Factory for creating a ObjectCreateRule
 
617
     */
 
618
    protected class ObjectCreateRuleFactory extends AbstractObjectCreationFactory {
 
619
        public Object createObject(Attributes attributes) {
 
620
            String className = attributes.getValue("classname");
 
621
            String attrName = attributes.getValue("attrname");
 
622
            return (attrName == null || attrName.length() == 0) ?
 
623
                new ObjectCreateRule( className)
 
624
                :
 
625
                new ObjectCreateRule( className, attrName);
 
626
        }
 
627
    }
 
628
    
 
629
    /**
 
630
     * Factory for creating a SetPropertiesRule
 
631
     */
 
632
    protected class SetPropertiesRuleFactory extends AbstractObjectCreationFactory {
 
633
        public Object createObject(Attributes attributes) {
 
634
                return new SetPropertiesRule();
 
635
        }
 
636
    }
 
637
    
 
638
    /**
 
639
     * Factory for creating a SetPropertyRule
 
640
     */
 
641
    protected class SetPropertyRuleFactory extends AbstractObjectCreationFactory {
 
642
        public Object createObject(Attributes attributes) {
 
643
            String name = attributes.getValue("name");
 
644
            String value = attributes.getValue("value");
 
645
            return new SetPropertyRule( name, value);
 
646
        }
 
647
    }
 
648
    
 
649
    /**
 
650
     * Factory for creating a SetTopRuleFactory
 
651
     */
 
652
    protected class SetTopRuleFactory extends AbstractObjectCreationFactory {
 
653
        public Object createObject(Attributes attributes) {
 
654
            String methodName = attributes.getValue("methodname");
 
655
            String paramType = attributes.getValue("paramtype");
 
656
            return (paramType == null || paramType.length() == 0) ?
 
657
                new SetTopRule( methodName)
 
658
                :
 
659
                new SetTopRule( methodName, paramType);
 
660
        }
 
661
    }
 
662
    
 
663
    /**
 
664
     * Factory for creating a SetNextRuleFactory
 
665
     */
 
666
    protected class SetNextRuleFactory extends AbstractObjectCreationFactory {
 
667
        public Object createObject(Attributes attributes) {
 
668
            String methodName = attributes.getValue("methodname");
 
669
            String paramType = attributes.getValue("paramtype");
 
670
            return (paramType == null || paramType.length() == 0) ?
 
671
                new SetNextRule( methodName)
 
672
                :
 
673
                new SetNextRule( methodName, paramType);
 
674
        }
 
675
    }
 
676
    
 
677
    /**
 
678
     * A rule for adding a attribute-property alias to the custom alias mappings of
 
679
     * the containing SetPropertiesRule rule.
 
680
     */
 
681
    protected class SetPropertiesAliasRule extends Rule {
 
682
        
 
683
        /**
 
684
         * <p>Base constructor.
 
685
         *
 
686
         * @param digester the Digester used to parse the rules XML file
 
687
         */
 
688
        public SetPropertiesAliasRule() {
 
689
            super();
 
690
        }
 
691
        
 
692
        /**
 
693
         * Add the alias to the SetPropertiesRule object created by the
 
694
         * enclosing <set-properties-rule> tag.
 
695
         */
 
696
        public void begin(Attributes attributes) {
 
697
            String attrName = attributes.getValue("attr-name");
 
698
            String propName = attributes.getValue("prop-name");
 
699
    
 
700
            SetPropertiesRule rule = (SetPropertiesRule) digester.peek();
 
701
            rule.addAlias(attrName, propName);
 
702
        }
 
703
    }
 
704
}