/* $Id: FromXmlRuleSet.java 471661 2006-11-06 08:09:25Z skitching $
 *
 * 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.xmlrules;


import java.net.URL;

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

import org.xml.sax.InputSource;

/**
 * A Digester rule set where the rules come from an XML file.
 *
 * @since 1.2
 */
public class FromXmlRuleSet extends RuleSetBase {

    public static final String DIGESTER_DTD_PATH = "org/apache/commons/digester/xmlrules/digester-rules.dtd";

    /**
     * The file containing the Digester rules, in XML.
     */
    private XMLRulesLoader rulesLoader;

    /**
     * The rule set for parsing the Digester rules
     */
    private DigesterRuleParser parser;

    /**
        * The digester for loading the rules xml.
        */
    private Digester rulesDigester;

    /**
     * Constructs a FromXmlRuleSet using the default DigesterRuleParser and
     * rulesDigester.
     * @param rulesXml the path to the XML document defining the Digester rules
     */
    public FromXmlRuleSet(URL rulesXml) {
        this(rulesXml, new DigesterRuleParser(), new Digester());
    }

    /**
     * Constructs a FromXmlRuleSet using the default DigesterRuleParser and
     * a ruleDigester for loading the rules xml.
     * @param rulesXml the path to the XML document defining the Digester rules
     * @param rulesDigester the digester to read the rules xml.
     */
    public FromXmlRuleSet(URL rulesXml, Digester rulesDigester) {
        this(rulesXml, new DigesterRuleParser(), rulesDigester);
    }

    /**
     * @param rulesXml the path to the XML document defining the Digester rules
     * @param parser an instance of DigesterRuleParser, for parsing the rules from XML
     */
    public FromXmlRuleSet(URL rulesXml, DigesterRuleParser parser) {
        this(rulesXml, parser, new Digester());
    }

    /**
     * @param rulesXml the path to the XML document defining the Digester rules
     * @param parser an instance of DigesterRuleParser, for parsing the rules from XML
     * @param rulesDigester the digester used to load the Xml rules.
     */
    public FromXmlRuleSet(URL rulesXml, DigesterRuleParser parser, Digester rulesDigester) {
        init(new URLXMLRulesLoader(rulesXml), parser, rulesDigester);
    }

    /**
     * Constructs a FromXmlRuleSet using the default DigesterRuleParser and
     * rulesDigester.
     * @param inputSource load the xml rules from this InputSource
     */
    public FromXmlRuleSet(InputSource inputSource) {
        this(inputSource, new DigesterRuleParser(), new Digester());
    }
    
    /**
     * Constructs a FromXmlRuleSet using the default DigesterRuleParser and
     * a ruleDigester for loading the rules xml.
     * @param inputSource load the xml rules from this InputSource
     * @param rulesDigester the digester to read the rules xml.
     */
    public FromXmlRuleSet(InputSource inputSource, Digester rulesDigester) {
        this(inputSource, new DigesterRuleParser(), rulesDigester);
    }

    /**
     * @param inputSource load the xml rules from this InputSource
     * @param parser an instance of DigesterRuleParser, for parsing the rules from XML
     */
    public FromXmlRuleSet(InputSource inputSource, DigesterRuleParser parser) {
        this(inputSource, parser, new Digester());
    }

    /**
     * @param inputSource load the xml rules from this InputSource
     * @param parser an instance of DigesterRuleParser, for parsing the rules from XML
     * @param rulesDigester the digester used to load the Xml rules.
     */
    public FromXmlRuleSet(InputSource inputSource, DigesterRuleParser parser, Digester rulesDigester) {
        init(new InputSourceXMLRulesLoader(inputSource), parser, rulesDigester);
    }
    
    /**
     * Base constructor
     */
    private void init(XMLRulesLoader rulesLoader, DigesterRuleParser parser, Digester rulesDigester) {
        this.rulesLoader = rulesLoader;
        this.parser = parser;
        this.rulesDigester = rulesDigester;
    }
    
    /**
     * Adds to the digester the set of Rule instances defined in the
     * XML file for this rule set.
     * @see org.apache.commons.digester.RuleSetBase
     */
    public void addRuleInstances(org.apache.commons.digester.Digester digester) throws XmlLoadException {
        addRuleInstances(digester, null);
    }
    
    /**
     * Adds to the digester the set of Rule instances defined in the
     * XML file for this rule set.
     * <p>
     * Note that this method doesn't have a matching one on the DigesterLoader
     * class, because it is not expected to be widely used, and DigesterLoader's
     * load method is already heavily overloaded.
     *
     * @param digester is the digester that rules will be added to.
     * @param basePath is a path that will be prefixed to every
     * pattern string defined in the xmlrules input file.
     *
     * @see org.apache.commons.digester.RuleSetBase
     * @since 1.6
     */
    public void addRuleInstances(
    org.apache.commons.digester.Digester digester,
    String basePath) 
    throws XmlLoadException {
        
        URL dtdURL = getClass().getClassLoader().getResource(DIGESTER_DTD_PATH);
        if (dtdURL == null) {
            throw new XmlLoadException("Cannot find resource \"" +
                    DIGESTER_DTD_PATH + "\"");
        }
        parser.setDigesterRulesDTD(dtdURL.toString());
        parser.setTarget(digester);
        parser.setBasePath(basePath);

        rulesDigester.addRuleSet(parser);
        rulesDigester.push(parser);

        rulesLoader.loadRules();
    }
    
    /** 
     * Worker class encapsulates loading mechanisms.
     * Private until some reason is found to make it public.
     */
    private abstract static class XMLRulesLoader {
        /** Load rules now */
        public abstract void loadRules()  throws XmlLoadException;
    }
    
    /** Loads XMLRules from an URL */
    private class URLXMLRulesLoader extends XMLRulesLoader {
        private URL url;
        public URLXMLRulesLoader(URL url) {
            this.url = url;
        }
        
        public void loadRules() throws XmlLoadException {
            try {
                rulesDigester.parse(url.openStream());
            } catch (Exception ex) {
                throw new XmlLoadException(ex);
            }
        }
    }

    /** Loads XMLRules from an InputSource */
    private class InputSourceXMLRulesLoader extends XMLRulesLoader {
        private InputSource inputSource;
        public InputSourceXMLRulesLoader(InputSource inputSource) {
            this.inputSource = inputSource;
        }
        
        public void loadRules() throws XmlLoadException {
            try {
                rulesDigester.parse(inputSource);
            } catch (Exception ex) {
                throw new XmlLoadException(ex);
            }
        }
    }
}

