KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > digester > plugins > PluginRules


1 /* $Id: PluginRules.java 179714 2005-06-03 03:53:39Z skitching $
2  *
3  * Copyright 2003-2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * 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 package org.apache.commons.digester.plugins;
18
19 import java.util.List JavaDoc;
20
21 import org.apache.commons.digester.Digester;
22 import org.apache.commons.digester.Rule;
23 import org.apache.commons.digester.Rules;
24 import org.apache.commons.digester.RulesBase;
25 import org.apache.commons.logging.Log;
26
27 /**
28  * A custom digester Rules manager which must be used as the Rules object
29  * when using the plugins module functionality.
30  * <p>
31  * During parsing, a linked list of PluginCreateRule instances develop, and
32  * this list also acts like a stack. The original instance that was set before
33  * the Digester started parsing is always at the tail of the list, and the
34  * Digester always holds a reference to the instance at the head of the list
35  * in the rules member. Initially, this list/stack holds just one instance,
36  * ie head and tail are the same object.
37  * <p>
38  * When the start of an xml element causes a PluginCreateRule to fire, a new
39  * PluginRules instance is created and inserted at the head of the list (ie
40  * pushed onto the stack of Rules objects). Digester.getRules() therefore
41  * returns this new Rules object, and any custom rules associated with that
42  * plugin are added to that instance.
43  * <p>
44  * When the end of the xml element is encountered (and therefore the
45  * PluginCreateRule end method fires), the stack of Rules objects is popped,
46  * so that Digester.getRules returns the previous Rules object.
47  *
48  * @since 1.6
49  */

50
51 public class PluginRules implements Rules {
52                                                
53     /**
54      * The Digester instance with which this Rules instance is associated.
55      */

56     protected Digester digester = null;
57
58     /**
59      * The (optional) object which generates new rules instances.
60      */

61     private RulesFactory rulesFactory;
62
63     /**
64      * The rules implementation that we are "enhancing" with plugins
65      * functionality, as per the Decorator pattern.
66      */

67     private Rules decoratedRules;
68     
69     /** Object which contains information about all known plugins. */
70     private PluginManager pluginManager;
71
72     /**
73      * The path below which this rules object has responsibility.
74      * For paths shorter than or equal the mountpoint, the parent's
75      * match is called.
76      */

77     private String JavaDoc mountPoint = null;
78     
79     /**
80      * The Rules object that holds rules applying "above" the mountpoint,
81      * ie the next Rules object down in the stack.
82      */

83     private PluginRules parent = null;
84     
85     /**
86      * A reference to the object that holds all data which should only
87      * exist once per digester instance.
88      */

89     private PluginContext pluginContext = null;
90     
91     // ------------------------------------------------------------- Constructor
92

93     /**
94      * Constructor for top-level Rules objects. Exactly one of these must
95      * be created and installed into the Digester instance as the Rules
96      * object before parsing starts.
97      */

98     public PluginRules() {
99         this(new RulesBase());
100     }
101
102     /**
103      * Constructor for top-level Rules object which handles rule-matching
104      * using the specified implementation.
105      */

106     public PluginRules(Rules decoratedRules) {
107         this.decoratedRules = decoratedRules;
108
109         pluginContext = new PluginContext();
110         pluginManager = new PluginManager(pluginContext);
111     }
112
113     /**
114      * Constructs a Rules instance which has a parent Rules object
115      * (which is different from having a delegate rules object).
116      * <p>
117      * One of these is created each time a PluginCreateRule's begin method
118      * fires, in order to manage the custom rules associated with whatever
119      * concrete plugin class the user has specified.
120      *
121      * @param digester is the object this rules will be associated with.
122      * @param mountPoint is the digester match path for the element
123      * matching a PluginCreateRule which caused this "nested parsing scope"
124      * to begin. This is expected to be equal to digester.getMatch().
125      * @param parent must be non-null.
126      * @param pluginClass is the plugin class whose custom rules will be
127      * loaded into this new PluginRules object.
128      */

129      PluginRules(
130      Digester digester,
131      String JavaDoc mountPoint,
132      PluginRules parent,
133      Class JavaDoc pluginClass)
134      throws PluginException {
135         // no need to set digester or decoratedRules.digester,
136
// because when Digester.setRules is called, the setDigester
137
// method on this object will be called.
138

139         this.digester = digester;
140         this.mountPoint = mountPoint;
141         this.parent = parent;
142         this.rulesFactory = parent.rulesFactory;
143         
144         if (rulesFactory == null) {
145             decoratedRules = new RulesBase();
146         } else {
147             decoratedRules = rulesFactory.newRules(digester, pluginClass);
148         }
149         
150         pluginContext = parent.pluginContext;
151         pluginManager = new PluginManager(parent.pluginManager);
152     }
153     
154     // ------------------------------------------------------------- Properties
155

156     /**
157      * Return the parent Rules object.
158      */

159     public Rules getParent() {
160         return parent;
161     }
162     
163     /**
164      * Return the Digester instance with which this instance is associated.
165      */

166     public Digester getDigester() {
167         return digester;
168     }
169
170     /**
171      * Set the Digester instance with which this Rules instance is associated.
172      *
173      * @param digester The newly associated Digester instance
174      */

175     public void setDigester(Digester digester) {
176         this.digester = digester;
177         decoratedRules.setDigester(digester);
178     }
179
180     /**
181      * Return the namespace URI that will be applied to all subsequently
182      * added <code>Rule</code> objects.
183      */

184     public String JavaDoc getNamespaceURI() {
185         return decoratedRules.getNamespaceURI();
186     }
187
188     /**
189      * Set the namespace URI that will be applied to all subsequently
190      * added <code>Rule</code> objects.
191      *
192      * @param namespaceURI Namespace URI that must match on all
193      * subsequently added rules, or <code>null</code> for matching
194      * regardless of the current namespace URI
195      */

196     public void setNamespaceURI(String JavaDoc namespaceURI) {
197         decoratedRules.setNamespaceURI(namespaceURI);
198     }
199
200     /**
201      * Return the object which "knows" about all declared plugins.
202      *
203      * @return The pluginManager value
204      */

205     public PluginManager getPluginManager() {
206         return pluginManager;
207     }
208     
209     /**
210      * See {@link PluginContext#getRuleFinders}.
211      */

212     public List JavaDoc getRuleFinders() {
213         return pluginContext.getRuleFinders();
214     }
215     
216     /**
217      * See {@link PluginContext#setRuleFinders}.
218      */

219     public void setRuleFinders(List JavaDoc ruleFinders) {
220         pluginContext.setRuleFinders(ruleFinders);
221     }
222     
223     /**
224      * Return the rules factory object (or null if one has not been specified).
225      */

226     public RulesFactory getRulesFactory() {
227         return rulesFactory;
228     }
229     
230     /**
231      * Set the object which is used to generate the new Rules instances created
232      * to hold and process the rules associated with each plugged-in class.
233      */

234     public void setRulesFactory(RulesFactory factory) {
235         rulesFactory = factory;
236     }
237     
238     // --------------------------------------------------------- Public Methods
239

240     /**
241      * This package-scope method is used by the PluginCreateRule class to
242      * get direct access to the rules that were dynamically added by the
243      * plugin. No other class should need access to this object.
244      */

245     Rules getDecoratedRules() {
246         return decoratedRules;
247     }
248     
249     /**
250      * Return the list of rules registered with this object, in the order
251      * they were registered with this object.
252      * <p>
253      * Note that Rule objects stored in parent Rules objects are not
254      * returned by this method.
255      *
256      * @return list of all Rule objects known to this Rules instance.
257      */

258     public List JavaDoc rules() {
259         return decoratedRules.rules();
260     }
261
262     /**
263      * Register a new Rule instance matching the specified pattern.
264      *
265      * @param pattern Nesting pattern to be matched for this Rule.
266      * This parameter treats equally patterns that begin with and without
267      * a leading slash ('/').
268      * @param rule Rule instance to be registered
269      */

270     public void add(String JavaDoc pattern, Rule rule) {
271         Log log = LogUtils.getLogger(digester);
272         boolean debug = log.isDebugEnabled();
273         
274         if (debug) {
275             log.debug("add entry" + ": mapping pattern [" + pattern + "]" +
276                   " to rule of type [" + rule.getClass().getName() + "]");
277         }
278         
279         // allow patterns with a leading slash character
280
if (pattern.startsWith("/"))
281         {
282             pattern = pattern.substring(1);
283         }
284
285         if (mountPoint != null) {
286             if (!pattern.equals(mountPoint)
287               && !pattern.startsWith(mountPoint + "/")) {
288                 // This can only occur if a plugin attempts to add a
289
// rule with a pattern that doesn't start with the
290
// prefix passed to the addRules method. Plugins mustn't
291
// add rules outside the scope of the tag they were specified
292
// on, so refuse this.
293

294                 // alas, can't throw exception
295
log.warn(
296                     "An attempt was made to add a rule with a pattern that"
297                     + "is not at or below the mountpoint of the current"
298                     + " PluginRules object."
299                     + " Rule pattern: " + pattern
300                     + ", mountpoint: " + mountPoint
301                     + ", rule type: " + rule.getClass().getName());
302                 return;
303             }
304         }
305         
306         decoratedRules.add(pattern, rule);
307
308         if (rule instanceof InitializableRule) {
309             try {
310                 ((InitializableRule)rule).postRegisterInit(pattern);
311             } catch (PluginConfigurationException e) {
312                 // Currently, Digester doesn't handle exceptions well
313
// from the add method. The workaround is for the
314
// initialisable rule to remember that its initialisation
315
// failed, and to throw the exception when begin is
316
// called for the first time.
317
if (debug) {
318                     log.debug("Rule initialisation failed", e);
319                 }
320                 // throw e; -- alas, can't do this
321
return;
322             }
323         }
324         
325         if (debug) {
326             log.debug("add exit" + ": mapped pattern [" + pattern + "]" +
327                   " to rule of type [" + rule.getClass().getName() + "]");
328         }
329     }
330
331     /**
332      * Clear all rules.
333      */

334     public void clear() {
335         decoratedRules.clear();
336     }
337     
338     /**
339      * Return a List of all registered Rule instances that match the specified
340      * nesting pattern, or a zero-length List if there are no matches. If more
341      * than one Rule instance matches, they <strong>must</strong> be returned
342      * in the order originally registered through the <code>add()</code>
343      * method.
344      *
345      * @param path the path to the xml nodes to be matched.
346      *
347      * @deprecated Call match(namespaceURI,pattern) instead.
348      */

349     public List JavaDoc match(String JavaDoc path) {
350         return (match(null, path));
351     }
352
353     /**
354      * Return a List of all registered Rule instances that match the specified
355      * nodepath, or a zero-length List if there are no matches. If more
356      * than one Rule instance matches, they <strong>must</strong> be returned
357      * in the order originally registered through the <code>add()</code>
358      * method.
359      * <p>
360      * @param namespaceURI Namespace URI for which to select matching rules,
361      * or <code>null</code> to match regardless of namespace URI
362      * @param path the path to the xml nodes to be matched.
363      */

364     public List JavaDoc match(String JavaDoc namespaceURI, String JavaDoc path) {
365         Log log = LogUtils.getLogger(digester);
366         boolean debug = log.isDebugEnabled();
367         
368         if (debug) {
369             log.debug(
370                 "Matching path [" + path +
371                 "] on rules object " + this.toString());
372         }
373
374         List JavaDoc matches;
375         if ((mountPoint != null) &&
376             (path.length() <= mountPoint.length())) {
377             if (debug) {
378                 log.debug(
379                     "Path [" + path + "] delegated to parent.");
380             }
381             
382             matches = parent.match(namespaceURI, path);
383             
384             // Note that in the case where path equals mountPoint,
385
// we deliberately return only the rules from the parent,
386
// even though this object may hold some rules matching
387
// this same path. See PluginCreateRule's begin, body and end
388
// methods for the reason.
389
} else {
390                 log.debug("delegating to decorated rules.");
391             matches = decoratedRules.match(namespaceURI, path);
392         }
393
394         return matches;
395     }
396
397     /** See {@link PluginContext#setPluginClassAttribute}. */
398     public void setPluginClassAttribute(String JavaDoc namespaceUri,
399                                         String JavaDoc attrName) {
400         pluginContext.setPluginClassAttribute(namespaceUri, attrName);
401     }
402
403     /** See {@link PluginContext#setPluginIdAttribute}. */
404     public void setPluginIdAttribute(String JavaDoc namespaceUri,
405                                      String JavaDoc attrName) {
406         pluginContext.setPluginIdAttribute(namespaceUri, attrName);
407     }
408     
409     /** See {@link PluginContext#getPluginClassAttrNs}. */
410     public String JavaDoc getPluginClassAttrNs() {
411         return pluginContext.getPluginClassAttrNs();
412     }
413     
414     /** See {@link PluginContext#getPluginClassAttr}. */
415     public String JavaDoc getPluginClassAttr() {
416         return pluginContext.getPluginClassAttr();
417     }
418     
419     /** See {@link PluginContext#getPluginIdAttrNs}. */
420     public String JavaDoc getPluginIdAttrNs() {
421         return pluginContext.getPluginIdAttrNs();
422     }
423     
424     /** See {@link PluginContext#getPluginIdAttr}. */
425     public String JavaDoc getPluginIdAttr() {
426         return pluginContext.getPluginIdAttr();
427     }
428 }
429
Popular Tags