alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Commons Digester example source code file (PluginRules.java)

This example Commons Digester source code file (PluginRules.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Commons Digester tags/keywords

digester, list, list, plugincontext, pluginmanager, pluginrules, pluginrules, rule, rules, rules, rulesbase, rulesfactory, string, string, util

The Commons Digester PluginRules.java source code

/* $Id: PluginRules.java 992104 2010-09-02 20:24:31Z 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.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.digester.Rules;
import org.apache.commons.digester.RulesBase;
import org.apache.commons.logging.Log;

/**
 * A custom digester Rules manager which must be used as the Rules object
 * when using the plugins module functionality.
 * <p>
 * During parsing, a linked list of PluginCreateRule instances develop, and
 * this list also acts like a stack. The original instance that was set before 
 * the Digester started parsing is always at the tail of the list, and the
 * Digester always holds a reference to the instance at the head of the list
 * in the rules member. Initially, this list/stack holds just one instance,
 * ie head and tail are the same object.
 * <p>
 * When the start of an xml element causes a PluginCreateRule to fire, a new 
 * PluginRules instance is created and inserted at the head of the list (ie
 * pushed onto the stack of Rules objects). Digester.getRules() therefore
 * returns this new Rules object, and any custom rules associated with that 
 * plugin are added to that instance. 
 * <p>
 * When the end of the xml element is encountered (and therefore the 
 * PluginCreateRule end method fires), the stack of Rules objects is popped,
 * so that Digester.getRules returns the previous Rules object. 
 *
 * @since 1.6
 */

public class PluginRules implements Rules {
                                               
    /**
     * The Digester instance with which this Rules instance is associated.
     */
    protected Digester digester = null;

    /** 
     * The (optional) object which generates new rules instances.
     */
    private RulesFactory rulesFactory;

    /** 
     * The rules implementation that we are "enhancing" with plugins
     * functionality, as per the Decorator pattern.
     */
    private Rules decoratedRules;
    
    /** Object which contains information about all known plugins. */
    private PluginManager pluginManager;

    /**
     * The path below which this rules object has responsibility.
     * For paths shorter than or equal the mountpoint, the parent's 
     * match is called.
     */
    private String mountPoint = null;
    
    /**
     * The Rules object that holds rules applying "above" the mountpoint,
     * ie the next Rules object down in the stack.
     */
    private PluginRules parent = null;
    
    /**
     * A reference to the object that holds all data which should only
     * exist once per digester instance.
     */
    private PluginContext pluginContext = null;
    
    // ------------------------------------------------------------- Constructor
    
    /**
     * Constructor for top-level Rules objects. Exactly one of these must
     * be created and installed into the Digester instance as the Rules
     * object before parsing starts.
     */
    public PluginRules() {
        this(new RulesBase());
    }

    /**
     * Constructor for top-level Rules object which handles rule-matching
     * using the specified implementation.
     */
    public PluginRules(Rules decoratedRules) {
        this.decoratedRules = decoratedRules;

        pluginContext = new PluginContext();
        pluginManager = new PluginManager(pluginContext);
    }

    /**
     * Constructs a Rules instance which has a parent Rules object 
     * (which is different from having a delegate rules object). 
     * <p>
     * One of these is created each time a PluginCreateRule's begin method 
     * fires, in order to manage the custom rules associated with whatever 
     * concrete plugin class the user has specified.
     *
     * @param digester is the object this rules will be associated with.
     * @param mountPoint is the digester match path for the element 
     * matching a PluginCreateRule which caused this "nested parsing scope"
     * to begin. This is expected to be equal to digester.getMatch().
     * @param parent must be non-null.
     * @param pluginClass is the plugin class whose custom rules will be
     * loaded into this new PluginRules object.
     */
     PluginRules(
     Digester digester, 
     String mountPoint, 
     PluginRules parent, 
     Class<?> pluginClass) 
     throws PluginException {
        // no need to set digester or decoratedRules.digester,
        // because when Digester.setRules is called, the setDigester
        // method on this object will be called.
        
        this.digester = digester;
        this.mountPoint = mountPoint;
        this.parent = parent;
        this.rulesFactory = parent.rulesFactory;
        
        if (rulesFactory == null) {
            decoratedRules = new RulesBase();
        } else {
            decoratedRules = rulesFactory.newRules(digester, pluginClass);
        }
        
        pluginContext = parent.pluginContext;
        pluginManager = new PluginManager(parent.pluginManager);
    }
    
    // ------------------------------------------------------------- Properties

    /**
     * Return the parent Rules object.
     */
    public Rules getParent() {
        return parent;
    }
    
    /**
     * Return the Digester instance with which this instance is associated.
     */
    public Digester getDigester() {
        return digester;
    }

    /**
     * Set the Digester instance with which this Rules instance is associated.
     *
     * @param digester The newly associated Digester instance
     */
    public void setDigester(Digester digester) {
        this.digester = digester;
        decoratedRules.setDigester(digester);
    }

    /**
     * Return the namespace URI that will be applied to all subsequently
     * added <code>Rule objects.
     */
    public String getNamespaceURI() {
        return decoratedRules.getNamespaceURI();
    }

    /**
     * Set the namespace URI that will be applied to all subsequently
     * added <code>Rule objects.
     *
     * @param namespaceURI Namespace URI that must match on all
     *  subsequently added rules, or <code>null for matching
     *  regardless of the current namespace URI
     */
    public void setNamespaceURI(String namespaceURI) {
        decoratedRules.setNamespaceURI(namespaceURI);
    }

    /**
     * Return the object which "knows" about all declared plugins.
     * 
     * @return The pluginManager value
     */
    public PluginManager getPluginManager() {
        return pluginManager;
    }
    
    /**
     * See {@link PluginContext#getRuleFinders}.
     */
    public List<RuleFinder> getRuleFinders() {
        return pluginContext.getRuleFinders();
    }
    
    /**
     * See {@link PluginContext#setRuleFinders}.
     */
    public void setRuleFinders(List<RuleFinder> ruleFinders) {
        pluginContext.setRuleFinders(ruleFinders);
    }
    
    /**
     * Return the rules factory object (or null if one has not been specified).
     */
    public RulesFactory getRulesFactory() {
        return rulesFactory;
    }
    
    /**
     * Set the object which is used to generate the new Rules instances created
     * to hold and process the rules associated with each plugged-in class.
     */
    public void setRulesFactory(RulesFactory factory) {
        rulesFactory = factory;
    }
    
    // --------------------------------------------------------- Public Methods

    /**
     * This package-scope method is used by the PluginCreateRule class to
     * get direct access to the rules that were dynamically added by the
     * plugin. No other class should need access to this object.
     */
    Rules getDecoratedRules() {
        return decoratedRules;
    }
    
    /**
     * Return the list of rules registered with this object, in the order
     * they were registered with this object.
     * <p>
     * Note that Rule objects stored in parent Rules objects are not
     * returned by this method.
     * 
     * @return list of all Rule objects known to this Rules instance.
     */
    public List<Rule> rules() {
        return decoratedRules.rules();
    }

    /**
     * Register a new Rule instance matching the specified pattern.
     * 
     * @param pattern Nesting pattern to be matched for this Rule.
     * This parameter treats equally patterns that begin with and without
     * a leading slash ('/').
     * @param rule Rule instance to be registered
     */
    public void add(String pattern, Rule rule) {
        Log log = LogUtils.getLogger(digester);
        boolean debug = log.isDebugEnabled();
        
        if (debug) {
            log.debug("add entry" + ": mapping pattern [" + pattern + "]" + 
                  " to rule of type [" + rule.getClass().getName() + "]");
        }
        
        // allow patterns with a leading slash character
        if (pattern.startsWith("/"))
        {
            pattern = pattern.substring(1);
        }

        if (mountPoint != null
                && !pattern.equals(mountPoint)
                && !pattern.startsWith(mountPoint + "/")) {
            // This can only occur if a plugin attempts to add a
            // rule with a pattern that doesn't start with the
            // prefix passed to the addRules method. Plugins mustn't
            // add rules outside the scope of the tag they were specified
            // on, so refuse this.
            
            // alas, can't throw exception
            log.warn(
                "An attempt was made to add a rule with a pattern that"
                + "is not at or below the mountpoint of the current"
                + " PluginRules object."
                + " Rule pattern: " + pattern
                + ", mountpoint: " + mountPoint
                + ", rule type: " + rule.getClass().getName());
            return;
        }
        
        decoratedRules.add(pattern, rule);

        if (rule instanceof InitializableRule) {
            try {
                ((InitializableRule)rule).postRegisterInit(pattern);
            } catch (PluginConfigurationException e) {
                // Currently, Digester doesn't handle exceptions well
                // from the add method. The workaround is for the
                // initialisable rule to remember that its initialisation
                // failed, and to throw the exception when begin is
                // called for the first time.
                if (debug) {
                    log.debug("Rule initialisation failed", e);
                }
                // throw e; -- alas, can't do this
                return;
            }
        }
        
        if (debug) {
            log.debug("add exit" + ": mapped pattern [" + pattern + "]" + 
                  " to rule of type [" + rule.getClass().getName() + "]");
        }
    }

    /**
     * Clear all rules.
     */
    public void clear() {
        decoratedRules.clear();
    }
    
    /**
     * Return a List of all registered Rule instances that match the specified
     * nesting pattern, or a zero-length List if there are no matches.  If more
     * than one Rule instance matches, they <strong>must be returned
     * in the order originally registered through the <code>add()
     * method.
     *
     * @param path the path to the xml nodes to be matched.
     *
     * @deprecated Call match(namespaceURI,pattern) instead.
     */
    @Deprecated
    public List<Rule> match(String path) {
        return (match(null, path));
    }

    /**
     * Return a List of all registered Rule instances that match the specified
     * nodepath, or a zero-length List if there are no matches.  If more
     * than one Rule instance matches, they <strong>must be returned
     * in the order originally registered through the <code>add()
     * method.
     * <p>
     * @param namespaceURI Namespace URI for which to select matching rules,
     *  or <code>null to match regardless of namespace URI
     * @param path the path to the xml nodes to be matched.
     */
    public List<Rule> match(String namespaceURI, String path) {
        Log log = LogUtils.getLogger(digester);
        boolean debug = log.isDebugEnabled();
        
        if (debug) {
            log.debug(
                "Matching path [" + path +
                "] on rules object " + this.toString());
        }

        List<Rule> matches;
        if ((mountPoint != null) && 
            (path.length() <= mountPoint.length())) {
            if (debug) {
                log.debug(
                    "Path [" + path + "] delegated to parent.");
            }
            
            matches = parent.match(namespaceURI, path);
            
            // Note that in the case where path equals mountPoint, 
            // we deliberately return only the rules from the parent,
            // even though this object may hold some rules matching
            // this same path. See PluginCreateRule's begin, body and end
            // methods for the reason.
        } else {
                log.debug("delegating to decorated rules.");
            matches = decoratedRules.match(namespaceURI, path); 
        }

        return matches;
    }

    /** See {@link PluginContext#setPluginClassAttribute}. */
    public void setPluginClassAttribute(String namespaceUri, 
                                        String attrName) {
        pluginContext.setPluginClassAttribute(namespaceUri, attrName);
    }

    /** See {@link PluginContext#setPluginIdAttribute}. */
    public void setPluginIdAttribute(String namespaceUri, 
                                     String attrName) {
        pluginContext.setPluginIdAttribute(namespaceUri, attrName);
    }
    
    /** See {@link PluginContext#getPluginClassAttrNs}. */
    public String getPluginClassAttrNs() {
        return pluginContext.getPluginClassAttrNs();
    }
    
    /** See {@link PluginContext#getPluginClassAttr}. */
    public String getPluginClassAttr() {
        return pluginContext.getPluginClassAttr();
    }
    
    /** See {@link PluginContext#getPluginIdAttrNs}. */
    public String getPluginIdAttrNs() {
        return pluginContext.getPluginIdAttrNs();
    }
    
    /** See {@link PluginContext#getPluginIdAttr}. */
    public String getPluginIdAttr() {
        return pluginContext.getPluginIdAttr();
    }
}

Other Commons Digester examples (source code examples)

Here is a short list of links related to this Commons Digester PluginRules.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 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.