001    package 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    
022    import java.util.Properties;
023    
024    import org.apache.commons.logging.Log;
025    import 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     */
033    public 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    }