001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.chain.config;
018
019import java.net.URL;
020
021import org.apache.commons.chain.Catalog;
022import org.apache.commons.digester.Digester;
023import org.apache.commons.digester.RuleSet;
024
025/**
026 * Class to parse the contents of an XML configuration file (using
027 * Commons Digester) that defines and configures commands and command chains
028 * to be registered in a {@link Catalog}. Advanced users can configure the
029 * detailed parsing behavior by configuring the properties of an instance
030 * of this class prior to calling the {@code parse()} method. It
031 * is legal to call the {@code parse()} method more than once, in order
032 * to parse more than one configuration document.
033 *
034 * @author Craig R. McClanahan
035 * @version $Revision$ $Date$
036 */
037public class ConfigParser {
038
039    // ----------------------------------------------------- Instance Variables
040
041    /**
042     * The {@code Digester} to be used for parsing.
043     */
044    private Digester digester = null;
045
046    /**
047     * The {@code RuleSet} to be used for configuring our Digester
048     * parsing rules.
049     */
050    private RuleSet ruleSet = null;
051
052    /**
053     * Should Digester use the context class loader?
054     */
055    private boolean useContextClassLoader = true;
056
057    // ----------------------------------------------------------- Constructors
058
059    /**
060     * The Default-Constructor for this class.
061     */
062    public ConfigParser() {
063    }
064
065    // ------------------------------------------------------------- Properties
066
067    /**
068     * Return the {@code Digester} instance to be used for
069     * parsing, creating one if necessary.
070     *
071     * @return A Digester instance.
072     */
073    public Digester getDigester() {
074        if (digester == null) {
075            digester = new Digester();
076            RuleSet ruleSet = getRuleSet();
077            digester.setNamespaceAware(ruleSet.getNamespaceURI() != null);
078            digester.setUseContextClassLoader(getUseContextClassLoader());
079            digester.setValidating(false);
080            digester.addRuleSet(ruleSet);
081        }
082        return digester;
083    }
084
085    /**
086     * Return the {@code RuleSet} to be used for configuring
087     * our {@code Digester} parsing rules, creating one if necessary.
088     *
089     * @return The RuleSet for configuring a Digester instance.
090     */
091    public RuleSet getRuleSet() {
092        if (ruleSet == null) {
093            ruleSet = new ConfigRuleSet();
094        }
095        return ruleSet;
096    }
097
098    /**
099     * Set the {@code RuleSet} to be used for configuring
100     * our {@code Digester} parsing rules.
101     *
102     * @param ruleSet The new RuleSet to use
103     */
104    public void setRuleSet(RuleSet ruleSet) {
105        this.digester = null;
106        this.ruleSet = ruleSet;
107    }
108
109    /**
110     * Return the "use context class loader" flag. If set to
111     * {@code true}, Digester will attempt to instantiate new
112     * command and chain instances from the context class loader.
113     *
114     * @return {@code true} if Digester should use the context class loader.
115     */
116    public boolean getUseContextClassLoader() {
117        return this.useContextClassLoader;
118    }
119
120    /**
121     * Set the "use context class loader" flag.
122     *
123     * @param useContextClassLoader The new flag value
124     */
125    public void setUseContextClassLoader(boolean useContextClassLoader) {
126        this.useContextClassLoader = useContextClassLoader;
127    }
128
129    // --------------------------------------------------------- Public Methods
130
131    /**
132     * Parse the XML document at the specified URL, using the configured
133     * {@code RuleSet}, registering top level commands into the specified
134     * {@link Catalog}. Use this method <strong>only</strong> if you have
135     * <strong>NOT</strong> included any {@code factory} element in your
136     * configuration resource, and wish to supply the catalog explicitly.
137     *
138     * @param catalog {@link Catalog} into which configured chains are
139     *        to be registered
140     * @param url {@code URL} of the XML document to be parsed
141     *
142     * @throws Exception if a parsing error occurs
143     *
144     * @deprecated Use parse(URL) on a configuration resource with "factory"
145     *  element(s) embedded
146     */
147    @Deprecated
148    public void parse(Catalog<?> catalog, URL url) throws Exception {
149        // Prepare our Digester instance
150        Digester digester = getDigester();
151        digester.clear();
152        digester.push(catalog);
153
154        // Parse the configuration document
155        digester.parse(url);
156    }
157
158    /**
159     * Parse the XML document at the specified URL using the configured
160     * {@code RuleSet}, registering catalogs with nested chains and
161     * commands as they are encountered. Use this method <strong>only</strong>
162     * if you have included one or more {@code factory} elements in your
163     * configuration resource.
164     *
165     * @param url {@code URL} of the XML document to be parsed
166     *
167     * @throws Exception if a parsing error occurs
168     */
169    public void parse(URL url) throws Exception {
170        // Prepare our Digester instance
171        Digester digester = getDigester();
172        digester.clear();
173
174        // Parse the configuration document
175        digester.parse(url);
176    }
177}