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