|
Commons Digester example source code file (PluginCreateRule.java)
The Commons Digester PluginCreateRule.java source code/* $Id: PluginCreateRule.java 992060 2010-09-02 19:09:47Z simonetripodi $ * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.digester.plugins; import java.util.List; import org.apache.commons.digester.Rule; import org.apache.commons.logging.Log; /** * Allows the original rules for parsing the configuration file to define * points at which plugins are allowed, by configuring a PluginCreateRule * with the appropriate pattern. * * @since 1.6 */ public class PluginCreateRule extends Rule implements InitializableRule { // see setPluginClassAttribute private String pluginClassAttrNs = null; private String pluginClassAttr = null; // see setPluginIdAttribute private String pluginIdAttrNs = null; private String pluginIdAttr = null; /** * In order to invoke the addRules method on the plugin class correctly, * we need to know the pattern which this rule is matched by. */ private String pattern; /** A base class that any plugin must derive from. */ private Class<?> baseClass = null; /** * Info about optional default plugin to be used if no plugin-id is * specified in the input data. This can simplify the syntax where one * particular plugin is usually used. */ private Declaration defaultPlugin; /** * Currently, none of the Rules methods allow exceptions to be thrown. * Therefore if this class cannot initialise itself properly, it cannot * cause the digester to stop. Instead, we cache the exception and throw * it the first time the begin() method is called. */ private PluginConfigurationException initException; //-------------------- constructors ------------------------------------- /** * Create a plugin rule where the user <i>must specify a plugin-class * or plugin-id. * * @param baseClass is the class which any specified plugin <i>must be * descended from. */ public PluginCreateRule(Class<?> baseClass) { this.baseClass = baseClass; } /** * Create a plugin rule where the user <i>may specify a plugin. * If the user doesn't specify a plugin, then the default class specified * in this constructor is used. * * @param baseClass is the class which any specified plugin <i>must be * descended from. * @param dfltPluginClass is the class which will be used if the user * doesn't specify any plugin-class or plugin-id. This class will have * custom rules installed for it just like a declared plugin. */ public PluginCreateRule(Class<?> baseClass, Class> dfltPluginClass) { this.baseClass = baseClass; if (dfltPluginClass != null) { defaultPlugin = new Declaration(dfltPluginClass); } } /** * Create a plugin rule where the user <i>may specify a plugin. * If the user doesn't specify a plugin, then the default class specified * in this constructor is used. * * @param baseClass is the class which any specified plugin <i>must be * descended from. * @param dfltPluginClass is the class which will be used if the user * doesn't specify any plugin-class or plugin-id. This class will have * custom rules installed for it just like a declared plugin. * @param dfltPluginRuleLoader is a RuleLoader instance which knows how * to load the custom rules associated with this default plugin. */ public PluginCreateRule(Class<?> baseClass, Class> dfltPluginClass, RuleLoader dfltPluginRuleLoader) { this.baseClass = baseClass; if (dfltPluginClass != null) { defaultPlugin = new Declaration(dfltPluginClass, dfltPluginRuleLoader); } } //------------------- properties --------------------------------------- /** * Sets the xml attribute which the input xml uses to indicate to a * PluginCreateRule which class should be instantiated. * <p> * See {@link PluginRules#setPluginClassAttribute} for more info. */ public void setPluginClassAttribute(String namespaceUri, String attrName) { pluginClassAttrNs = namespaceUri; pluginClassAttr = attrName; } /** * Sets the xml attribute which the input xml uses to indicate to a * PluginCreateRule which plugin declaration is being referenced. * <p> * See {@link PluginRules#setPluginIdAttribute} for more info. */ public void setPluginIdAttribute(String namespaceUri, String attrName) { pluginIdAttrNs = namespaceUri; pluginIdAttr = attrName; } //------------------- methods -------------------------------------------- /** * Invoked after this rule has been added to the set of digester rules, * associated with the specified pattern. Check all configuration data is * valid and remember the pattern for later. * * @param matchPattern is the digester match pattern that is associated * with this rule instance, eg "root/widget". * @exception PluginConfigurationException */ public void postRegisterInit(String matchPattern) throws PluginConfigurationException { Log log = LogUtils.getLogger(digester); boolean debug = log.isDebugEnabled(); if (debug) { log.debug("PluginCreateRule.postRegisterInit" + ": rule registered for pattern [" + matchPattern + "]"); } if (digester == null) { // We require setDigester to be called before this method. // Note that this means that PluginCreateRule cannot be added // to a Rules object which has not yet been added to a // Digester object. initException = new PluginConfigurationException( "Invalid invocation of postRegisterInit" + ": digester not set."); throw initException; } if (pattern != null) { // We have been called twice, ie a single instance has been // associated with multiple patterns. // // Generally, Digester Rule instances can be associated with // multiple patterns. However for plugins, this creates some // complications. Some day this may be supported; however for // now we just reject this situation. initException = new PluginConfigurationException( "A single PluginCreateRule instance has been mapped to" + " multiple patterns; this is not supported."); throw initException; } if (matchPattern.indexOf('*') != -1) { // having wildcards in patterns is extremely difficult to // deal with. For now, we refuse to allow this. // // TODO: check for any chars not valid in xml element name // rather than just *. // // Reasons include: // (a) handling recursive plugins, and // (b) determining whether one pattern is "below" another, // as done by PluginRules. Without wildcards, "below" // just means startsWith, which is easy to check. initException = new PluginConfigurationException( "A PluginCreateRule instance has been mapped to" + " pattern [" + matchPattern + "]." + " This pattern includes a wildcard character." + " This is not supported by the plugin architecture."); throw initException; } if (baseClass == null) { baseClass = Object.class; } PluginRules rules = (PluginRules) digester.getRules(); PluginManager pm = rules.getPluginManager(); // check default class is valid if (defaultPlugin != null) { if (!baseClass.isAssignableFrom(defaultPlugin.getPluginClass())) { initException = new PluginConfigurationException( "Default class [" + defaultPlugin.getPluginClass().getName() + "] does not inherit from [" + baseClass.getName() + "]."); throw initException; } try { defaultPlugin.init(digester, pm); } catch(PluginException pwe) { throw new PluginConfigurationException( pwe.getMessage(), pwe.getCause()); } } // remember the pattern for later pattern = matchPattern; if (pluginClassAttr == null) { // the user hasn't set explicit xml attr names on this rule, // so fetch the default values pluginClassAttrNs = rules.getPluginClassAttrNs(); pluginClassAttr = rules.getPluginClassAttr(); if (debug) { log.debug( "init: pluginClassAttr set to per-digester values [" + "ns=" + pluginClassAttrNs + ", name=" + pluginClassAttr + "]"); } } else { if (debug) { log.debug( "init: pluginClassAttr set to rule-specific values [" + "ns=" + pluginClassAttrNs + ", name=" + pluginClassAttr + "]"); } } if (pluginIdAttr == null) { // the user hasn't set explicit xml attr names on this rule, // so fetch the default values pluginIdAttrNs = rules.getPluginIdAttrNs(); pluginIdAttr = rules.getPluginIdAttr(); if (debug) { log.debug( "init: pluginIdAttr set to per-digester values [" + "ns=" + pluginIdAttrNs + ", name=" + pluginIdAttr + "]"); } } else { if (debug) { log.debug( "init: pluginIdAttr set to rule-specific values [" + "ns=" + pluginIdAttrNs + ", name=" + pluginIdAttr + "]"); } } } /** * Invoked when the Digester matches this rule against an xml element. * <p> * A new instance of the target class is created, and pushed onto the * stack. A new "private" PluginRules object is then created and set as * the digester's default Rules object. Any custom rules associated with * the plugin class are then loaded into that new Rules object. * Finally, any custom rules that are associated with the current pattern * (such as SetPropertiesRules) have their begin methods executed. * * @param namespace * @param name * @param attributes * * @throws ClassNotFoundException * @throws PluginInvalidInputException * @throws PluginConfigurationException */ @Override public void begin(String namespace, String name, org.xml.sax.Attributes attributes) throws java.lang.Exception { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); if (debug) { log.debug("PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + digester.getMatch() + "]"); } if (initException != null) { // we had a problem during initialisation that we could // not report then; report it now. throw initException; } // load any custom rules associated with the plugin PluginRules oldRules = (PluginRules) digester.getRules(); PluginManager pluginManager = oldRules.getPluginManager(); Declaration currDeclaration = null; String pluginClassName; if (pluginClassAttrNs == null) { // Yep, this is ugly. // // In a namespace-aware parser, the one-param version will // return attributes with no namespace. // // In a non-namespace-aware parser, the two-param version will // never return any attributes, ever. pluginClassName = attributes.getValue(pluginClassAttr); } else { pluginClassName = attributes.getValue(pluginClassAttrNs, pluginClassAttr); } String pluginId; if (pluginIdAttrNs == null) { pluginId = attributes.getValue(pluginIdAttr); } else { pluginId = attributes.getValue(pluginIdAttrNs, pluginIdAttr); } if (pluginClassName != null) { // The user is using a plugin "inline", ie without a previous // explicit declaration. If they have used the same plugin class // before, we have already gone to the effort of creating a // Declaration object, so retrieve it. If there is no existing // declaration object for this class, then create one. currDeclaration = pluginManager.getDeclarationByClass( pluginClassName); if (currDeclaration == null) { currDeclaration = new Declaration(pluginClassName); try { currDeclaration.init(digester, pluginManager); } catch(PluginException pwe) { throw new PluginInvalidInputException( pwe.getMessage(), pwe.getCause()); } pluginManager.addDeclaration(currDeclaration); } } else if (pluginId != null) { currDeclaration = pluginManager.getDeclarationById(pluginId); if (currDeclaration == null) { throw new PluginInvalidInputException( "Plugin id [" + pluginId + "] is not defined."); } } else if (defaultPlugin != null) { currDeclaration = defaultPlugin; } else { throw new PluginInvalidInputException( "No plugin class specified for element " + pattern); } // get the class of the user plugged-in type Class<?> pluginClass = currDeclaration.getPluginClass(); String path = digester.getMatch(); // create a new Rules object and effectively push it onto a stack of // rules objects. The stack is actually a linked list; using the // PluginRules constructor below causes the new instance to link // to the previous head-of-stack, then the Digester.setRules() makes // the new instance the new head-of-stack. PluginRules newRules = new PluginRules(digester, path, oldRules, pluginClass); digester.setRules(newRules); if (debug) { log.debug("PluginCreateRule.begin: installing new plugin: " + "oldrules=" + oldRules.toString() + ", newrules=" + newRules.toString()); } // load up the custom rules currDeclaration.configure(digester, pattern); // create an instance of the plugin class Object instance = pluginClass.newInstance(); getDigester().push(instance); if (debug) { log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + digester.getMatch() + "]" + " pushed instance of plugin [" + pluginClass.getName() + "]"); } // and now we have to fire any custom rules which would have // been matched by the same path that matched this rule, had // they been loaded at that time. List<Rule> rules = newRules.getDecoratedRules().match(namespace, path); fireBeginMethods(rules, namespace, name, attributes); } /** * Process the body text of this element. * * @param text The body text of this element */ @Override public void body(String namespace, String name, String text) throws Exception { // While this class itself has no work to do in the body method, // we do need to fire the body methods of all dynamically-added // rules matching the same path as this rule. During begin, we had // to manually execute the dynamic rules' begin methods because they // didn't exist in the digester's Rules object when the match begin. // So in order to ensure consistent ordering of rule execution, the // PluginRules class deliberately avoids returning any such rules // in later calls to the match method, instead relying on this // object to execute them at the appropriate time. // // Note that this applies only to rules matching exactly the path // which is also matched by this PluginCreateRule. String path = digester.getMatch(); PluginRules newRules = (PluginRules) digester.getRules(); List<Rule> rules = newRules.getDecoratedRules().match(namespace, path); fireBodyMethods(rules, namespace, name, text); } /** * Invoked by the digester when the closing tag matching this Rule's * pattern is encountered. * </p> * * @param namespace Description of the Parameter * @param name Description of the Parameter * @exception Exception Description of the Exception * * @see #begin */ @Override public void end(String namespace, String name) throws Exception { // see body method for more info String path = digester.getMatch(); PluginRules newRules = (PluginRules) digester.getRules(); List<Rule> rules = newRules.getDecoratedRules().match(namespace, path); fireEndMethods(rules, namespace, name); // pop the stack of PluginRules instances, which // discards all custom rules associated with this plugin digester.setRules(newRules.getParent()); // and get rid of the instance of the plugin class from the // digester object stack. digester.pop(); } /** * Return the pattern that this Rule is associated with. * <p> * In general, Rule instances <i>can be associated with multiple * patterns. A PluginCreateRule, however, will only function correctly * when associated with a single pattern. It is possible to fix this, but * I can't be bothered just now because this feature is unlikely to be * used. * </p> * * @return The pattern value */ public String getPattern() { return pattern; } /** * Duplicate the processing that the Digester does when firing the * begin methods of rules. It would be really nice if the Digester * class provided a way for this functionality to just be invoked * directly. */ public void fireBeginMethods(List<Rule> rules, String namespace, String name, org.xml.sax.Attributes list) throws java.lang.Exception { if ((rules != null) && (rules.size() > 0)) { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); for (int i = 0; i < rules.size(); i++) { try { Rule rule = rules.get(i); if (debug) { log.debug(" Fire begin() for " + rule); } rule.begin(namespace, name, list); } catch (Exception e) { throw digester.createSAXException(e); } catch (Error e) { throw e; } } } } /** * Duplicate the processing that the Digester does when firing the * body methods of rules. It would be really nice if the Digester * class provided a way for this functionality to just be invoked * directly. */ private void fireBodyMethods(List<Rule> rules, String namespaceURI, String name, String text) throws Exception { if ((rules != null) && (rules.size() > 0)) { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); for (int i = 0; i < rules.size(); i++) { try { Rule rule = rules.get(i); if (debug) { log.debug(" Fire body() for " + rule); } rule.body(namespaceURI, name, text); } catch (Exception e) { throw digester.createSAXException(e); } catch (Error e) { throw e; } } } } /** * Duplicate the processing that the Digester does when firing the * end methods of rules. It would be really nice if the Digester * class provided a way for this functionality to just be invoked * directly. */ public void fireEndMethods(List<Rule> rules, String namespaceURI, String name) throws Exception { // Fire "end" events for all relevant rules in reverse order if (rules != null) { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); for (int i = 0; i < rules.size(); i++) { int j = (rules.size() - i) - 1; try { Rule rule = rules.get(j); if (debug) { log.debug(" Fire end() for " + rule); } rule.end(namespaceURI, name); } catch (Exception e) { throw digester.createSAXException(e); } catch (Error e) { throw e; } } } } } Other Commons Digester examples (source code examples)Here is a short list of links related to this Commons Digester PluginCreateRule.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.