/*
 * 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.chain2.config.xml;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.RuleSetBase;

/**
 * <p>Digester <code>RuleSet</code> for configuring <em>Chain of
 * Responsibility</em> command chains, and adding them to an appropriate
 * {@link org.apache.commons.chain2.Catalog}.  The following properties
 * may be configured prior to executing the <code>addRuleInstance()</code>
 * method in order to influence the rules that get added, with default
 * values in square brackets:</p>
 * <ul>
 * <li><strong>catalogClass</strong> -- Fully qualified name of the
 *     implementation class used to create new
 *     {@link org.apache.commons.chain2.Catalog} instances.
 *     If not specified, the default value is
 *     <code>org.apache.commons.chain2.impl.CatalogBase</code>.</li>
 * <li><strong>catalogElement</strong> -- Name of the XML element representing
 *     the addition of a {@link org.apache.commons.chain2.Catalog}.
 *     Any such catalog that is created will be registered with the
 *     {@link org.apache.commons.chain2.impl.CatalogFactoryBase} instance for our
 *     application, under the name specified by the <code>nameAttribute</code>
 *     attribute (if present), or as the default {@link org.apache.commons.chain2.Catalog}.
 *     If not specified, the default value is <code>catalog</code>.</li>
 * <li><strong>chainClass</strong> -- Fully qualified name of the implementation
 *     class used to create new {@link org.apache.commons.chain2.Chain} instances.
 *     If not specified, the default value is
 *     <code>org.apache.commons.chain2.impl.ChainBase</code>.
 *     </li>
 * <li><strong>chainElement</strong> -- Name of the XML element representing
 *     the addition of a {@link org.apache.commons.chain2.Chain}.  A chain
 *     element has the same functionality as a command element, except that
 *     it defaults the implementation class to
 *     <code>org.apache.commons.chain2.impl.ChainBase</code>.  [chain]</li>
 * <li><strong>classAttribute</strong> -- Attribute on a chain (optional) or
 *     command (required) element that specifies the fully qualified class
 *     name of the implementation class that should be instantiated.
 *     [className]</li>
 * <li><strong>commandElement</strong> -- Name of the XML element
 *     representing the addition of a {@link org.apache.commons.chain2.Command}.
 *     An implementation class name must be provided on the attribute named by the
 *     <code>classAttribute</code> property.  [command]</li>
 * <li><strong>defineElement</strong> -- Name of the XML element
 *     that associates the element specified by the <code>nameAttribute</code>
 *     attributes with a {@link org.apache.commons.chain2.Command} or
 *     {@link org.apache.commons.chain2.Chain} implementation class
 *     named by the <code>classAttribute</code> attribute.  [define]</li>
 * <li><strong>nameAttribute</strong> -- Attribute on an outermost chain or
 *     command element that will be used to register this command with the
 *     associated {@link org.apache.commons.chain2.Catalog} instance on the stack.
 *     [name]</li>
 * <li><strong>namespaceURI</strong> -- The XML namespace URI with which these
 *     rules will be associated, or <code>null</code> for no namespace.
 *     [null]</li>
 * </ul>
 *
 * @version $Id$
 */
public class ConfigRuleSet extends RuleSetBase {

    // ----------------------------------------------------- Instance Variables

    /** catalogClass */
    private String catalogClass = "org.apache.commons.chain2.impl.CatalogBase";
    /** catalogElement */
    private String catalogElement = "catalog";
    /** chainClass */
    private String chainClass = "org.apache.commons.chain2.impl.ChainBase";
    /** chainElement */
    private String chainElement = "chain";
    /** classAttribute */
    private String classAttribute = "className";
    /** commandElement */
    private String commandElement = "command";
    /** defineElement */
    private String defineElement = "define";
    /** nameAttribute */
    private String nameAttribute = "name";

    // ------------------------------------------------------------- Properties

    /**
     * <p>Return the fully qualified {@link org.apache.commons.chain2.Catalog}
     *  implementation class.</p>
     * @return The Catalog's class name.
     */
    public String getCatalogClass() {
        return this.catalogClass;
    }

    /**
     * <p>Set the fully qualified {@link org.apache.commons.chain2.Catalog}
     * implementation class.</p>
     *
     * @param val The new {@link org.apache.commons.chain2.Catalog}
     *  implementation class
     */
    public void setCatalogClass(final String val) {
        this.catalogClass = val;
    }

    /**
     * <p>Return the element name of a catalog element.</p>
     * @return The element name of a catalog element.
     */
    public String getCatalogElement() {
        return this.catalogElement;
    }

    /**
     * <p>Set the element name of a catalog element.</p>
     *
     * @param val The new element name
     */
    public void setCatalogElement(final String val) {
        this.catalogElement = val;
    }

    /**
     * <p>Return the fully qualified {@link org.apache.commons.chain2.Chain}
     * implementation class.</p>
     * @return The Chain's class name.
     */
    public String getChainClass() {
        return this.chainClass;
    }

    /**
     * <p>Set the fully qualified {@link org.apache.commons.chain2.Chain}
     * implementation class.</p>
     *
     * @param val The new {@link org.apache.commons.chain2.Chain}
     * implementation class
     */
    public void setChainClass(final String val) {
        this.chainClass = val;
    }

    /**
     * <p>Return the element name of a chain element.</p>
     * @return The element name of a catalog element.
     */
    public String getChainElement() {
        return this.chainElement;
    }

    /**
     * <p>Set the element name of a chain element.</p>
     *
     * @param val The new element name
     */
    public void setChainElement(final String val) {
        this.chainElement = val;
    }

    /**
     * <p>Return the attribute name of a class attribute.</p>
     * @return The attribute name of a class attribute.
     */
    public String getClassAttribute() {
        return this.classAttribute;
    }

    /**
     * <p>Set the attribute name of a class attribute.</p>
     *
     * @param val The new attribute name
     */
    public void setClassAttribute(final String val) {
        this.classAttribute = val;
    }

    /**
     * <p>Return the element name of a command element.</p>
     * @return The element name of a command element.
     */
    public String getCommandElement() {
        return this.commandElement;
    }

    /**
     * <p>Set the element name of a command element.</p>
     *
     * @param val The new element name
     */
    public void setCommandElement(final String val) {
        this.commandElement = val;
    }

    /**
     * <p>Return the element name of a define element.</p>
     * @return The element name of a define element.
     */
    public String getDefineElement() {
        return this.defineElement;
    }

    /**
     * <p>Set the element name of a define element.</p>
     *
     * @param val The new element name
     */
    public void setDefineElement(final String val) {
        this.defineElement = val;
    }

    /**
     * <p>Return the attribute name of a name attribute.</p>
     * @return The attribute name of an attribute element.
     */
    public String getNameAttribute() {
        return this.nameAttribute;
    }

    /**
     * <p>Set the attribute name of a name attribute.</p>
     *
     * @param val The new attribute name
     */
    public void setNameAttribute(final String val) {
        this.nameAttribute = val;
    }

    // --------------------------------------------------------- Public Methods

    /**
     * <p>Add the set of Rule instances defined in this RuleSet to the
     * specified <code>Digester</code> instance, associating them with
     * our namespace URI (if any).  This method should only be called
     * by a Digester instance.</p>
     *
     * @param digester Digester instance to which the new Rule instances
     *  should be added.
     */
    @Override
    public void addRuleInstances(final Digester digester) {
        addCatalogRules(digester);
        addChainRules(digester);
        addCommandRules(digester);
        addDefineElementRule(digester);
    }

    /**
     * addCatalogRules
     * @param digester Digester
     */
    private void addCatalogRules(final Digester digester) {
        digester.addRule("*/" + getCatalogElement(),
                new ConfigCatalogRule(getNameAttribute(), getCatalogClass()));
        digester.addSetProperties("*/" + getCatalogElement());
    }

    /**
     * addChainRules
     * @param digester Digester
     */
    private void addChainRules(final Digester digester) {
        digester.addObjectCreate("*/" + getChainElement(), getChainClass(), getClassAttribute());
        digester.addSetProperties("*/" + getChainElement());
        digester.addRule("*/" + getChainElement(), new ConfigRegisterRule(getNameAttribute()));
    }

    /**
     * addCommandRules
     * @param digester Digester
     */
    private void addCommandRules(final Digester digester) {
        digester.addObjectCreate("*/" + getCommandElement(), null, getClassAttribute());
        digester.addSetProperties("*/" + getCommandElement());
        digester.addRule("*/" + getCommandElement(), new ConfigRegisterRule(getNameAttribute()));
    }

    /**
     * addDefineElementRule
     * @param digester Digester
     */
    private void addDefineElementRule(final Digester digester) {
        digester.addRule("*/" + getDefineElement(),
                new ConfigDefineRule(getNameAttribute(), getClassAttribute()));
    }

}
