001/* $Id: Declaration.java 992060 2010-09-02 19:09:47Z simonetripodi $
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements.  See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.commons.digester.plugins;
019
020import java.util.Properties;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.digester.Digester;
024
025/**
026 * Represents a Class that can be instantiated by a PluginCreateRule, plus
027 * info on how to load custom digester rules for mapping xml into that
028 * plugged-in class.
029 *
030 * @since 1.6
031 */
032public class Declaration {
033   
034    /** The class of the object to be instantiated. */
035    private Class<?> pluginClass;
036
037    /** The name of the class of the object to be instantiated. */
038    private String pluginClassName;
039    
040    /** See {@link #setId}. */ 
041    private String id;
042    
043    /** See {@link #setProperties}. */
044    private Properties properties = new Properties();
045    
046    /** See {@link #init}. */
047    private boolean initialized = false;
048
049    /**
050     * Class which is responsible for dynamically loading this
051     * plugin's rules on demand.
052     */
053    private RuleLoader ruleLoader = null;
054    
055    //---------------------- constructors ----------------------------------
056
057    /**
058     * Constructor.
059     */
060    public Declaration(String pluginClassName) {
061        // We can't load the pluginClass at this time, because we don't
062        // have a digester instance yet to load it through. So just
063        // save the name away, and we'll load the Class object in the
064        // init method.
065        this.pluginClassName = pluginClassName;
066    }
067    
068    /**
069     * Constructor.
070     */
071    public Declaration(Class<?> pluginClass) {
072        this.pluginClass = pluginClass;
073        this.pluginClassName = pluginClass.getName();
074    }
075    
076    /**
077     * Create an instance where a fully-initialised ruleLoader instance
078     * is provided by the caller instead of having the PluginManager
079     * "discover" an appropriate one.
080     */
081    public Declaration(Class<?> pluginClass, RuleLoader ruleLoader) {
082        this.pluginClass = pluginClass;
083        this.pluginClassName = pluginClass.getName();
084        this.ruleLoader = ruleLoader;
085    }
086    
087    //---------------------- properties -----------------------------------
088
089    /** 
090     * The id that the user associated with a particular plugin declaration
091     * in the input xml. This id is later used in the input xml to refer
092     * back to the original declaration.
093     * <p>
094     * For plugins declared "in-line", the id is null.
095     */
096    public void setId(String id) {
097        this.id = id;
098    }
099    
100    /**
101     * Return the id associated with this declaration. For plugins
102     * declared "inline", null will be returned.
103     * 
104     * @return The id value. May be null.
105     */
106    public String getId() {
107        return id;
108    }
109
110    /** 
111     * Copy all (key,value) pairs in the param into the properties member of
112     * this object.
113     * <p>
114     * The declaration properties cannot be explicit member variables,
115     * because the set of useful properties a user can provide on a declaration
116     * depends on what RuleFinder classes are available - and extra RuleFinders
117     * can be added by the user. So here we keep a map of the settings, and
118     * let the RuleFinder objects look for whatever properties they consider
119     * significant.
120     * <p>
121     * The "id" and "class" properties are treated differently.
122     */
123    public void setProperties(Properties p) {
124        properties.putAll(p);
125    }
126    
127    /**
128     * Return plugin class associated with this declaration.
129     * 
130     * @return The pluginClass.
131     */
132    public Class<?> getPluginClass() {
133        return pluginClass;
134    }
135
136    //---------------------- methods -----------------------------------
137    
138    /**
139     * Must be called exactly once, and must be called before any call
140     * to the configure method.
141     */
142    public void init(Digester digester, PluginManager pm) throws PluginException {
143        Log log = digester.getLogger();
144        boolean debug = log.isDebugEnabled();
145        if (debug) {
146            log.debug("init being called!");
147        }
148        
149        if (initialized) {
150            throw new PluginAssertionFailure("Init called multiple times.");
151        }
152
153        if ((pluginClass == null) && (pluginClassName != null)) {
154            try {
155                // load the plugin class object
156                pluginClass = 
157                    digester.getClassLoader().loadClass(pluginClassName);
158            } catch(ClassNotFoundException cnfe) {
159                throw new PluginException(
160                    "Unable to load class " + pluginClassName, cnfe);
161            }
162        }
163
164        if (ruleLoader == null) {
165            // the caller didn't provide a ruleLoader to the constructor,
166            // so get the plugin manager to "discover" one.
167            log.debug("Searching for ruleloader...");
168            ruleLoader = pm.findLoader(digester, id, pluginClass, properties);
169        } else {
170            log.debug("This declaration has an explicit ruleLoader.");
171        }
172        
173        if (debug) {
174            if (ruleLoader == null) {
175                log.debug(
176                    "No ruleLoader found for plugin declaration"
177                    + " id [" + id + "]"
178                    + ", class [" + pluginClass.getClass().getName() + "].");
179            } else {
180                log.debug(
181                    "RuleLoader of type [" + ruleLoader.getClass().getName()
182                    + "] associated with plugin declaration"
183                    + " id [" + id + "]"
184                    + ", class [" + pluginClass.getClass().getName() + "].");
185            }
186        }
187        
188        initialized = true;        
189    }
190    
191    /**
192     * Attempt to load custom rules for the target class at the specified
193     * pattern.
194     * <p>
195     * On return, any custom rules associated with the plugin class have
196     * been loaded into the Rules object currently associated with the
197     * specified digester object.
198     */
199     
200    public void configure(Digester digester, String pattern)
201                          throws PluginException {
202        Log log = digester.getLogger();
203        boolean debug = log.isDebugEnabled();
204        if (debug) {
205            log.debug("configure being called!");
206        }
207        
208        if (!initialized) {
209            throw new PluginAssertionFailure("Not initialized.");
210        }
211
212        if (ruleLoader != null) {
213            ruleLoader.addRules(digester, pattern);
214        }
215    }
216}