001    package org.apache.commons.digester3.plugins.strategies;
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.digester3.Digester;
025    import org.apache.commons.digester3.plugins.PluginException;
026    import org.apache.commons.digester3.plugins.RuleFinder;
027    import org.apache.commons.digester3.plugins.RuleLoader;
028    
029    /**
030     * A rule-finding algorithm which expects the caller to specify a classname and methodname as plugin properties.
031     * 
032     * @since 1.6
033     */
034    public class FinderFromClass
035        extends RuleFinder
036    {
037        private static final String DFLT_RULECLASS_ATTR = "ruleclass";
038    
039        private static final String DFLT_METHOD_ATTR = "method";
040    
041        private static final String DFLT_METHOD_NAME = "addRules";
042    
043        private final String ruleClassAttr;
044    
045        private final String methodAttr;
046    
047        private final String dfltMethodName;
048    
049        /**
050         * See {@link #findLoader}.
051         */
052        public FinderFromClass()
053        {
054            this( DFLT_RULECLASS_ATTR, DFLT_METHOD_ATTR, DFLT_METHOD_NAME );
055        }
056    
057        /**
058         * Create a rule-finder which invokes a user-specified method on a user-specified class whenever dynamic rules for a
059         * plugin need to be loaded. See the findRules method for more info.
060         * 
061         * @param ruleClassAttr must be non-null.
062         * @param methodAttr may be null.
063         * @param dfltMethodName may be null.
064         */
065        public FinderFromClass( String ruleClassAttr, String methodAttr, String dfltMethodName )
066        {
067            this.ruleClassAttr = ruleClassAttr;
068            this.methodAttr = methodAttr;
069            this.dfltMethodName = dfltMethodName;
070        }
071    
072        /**
073         * If there exists a property with the name matching constructor param ruleClassAttr, then load the specified class,
074         * locate the appropriate rules-adding method on that class, and return an object encapsulating that info.
075         * <p>
076         * If there is no matching property provided, then just return null.
077         * <p>
078         * The returned object (when non-null) will invoke the target method on the selected class whenever its addRules
079         * method is invoked. The target method is expected to have the following prototype:
080         * <code> public static void xxxxx(Digester d, String patternPrefix); </code>
081         * <p>
082         * The target method can be specified in several ways. If this object's constructor was passed a non-null methodAttr
083         * parameter, and the properties defines a value with that key, then that is taken as the target method name. If
084         * there is no matching property, or the constructor was passed null for methodAttr, then the dfltMethodName passed
085         * to the constructor is used as the name of the method on the target class. And if that was null, then
086         * DFLT_METHOD_NAME will be used.
087         * <p>
088         * When the user explicitly declares a plugin in the input xml, the xml attributes on the declaration tag are passed
089         * here as properties, so the user can select any class in the classpath (and any method on that class provided it
090         * has the correct prototype) as the source of dynamic rules for the plugged-in class.
091         *
092         * @param digester The digester instance where locating plugin classes
093         * @param pluginClass The plugin Java class
094         * @param p The properties object that holds any xml attributes the user may have specified on the plugin
095         *          declaration in order to indicate how to locate the plugin rules.
096         * @return a source of digester rules for the specified plugin class.
097         * @throws PluginException if the algorithm finds a source of rules, but there is something invalid
098         *         about that source.
099         */
100        @Override
101        public RuleLoader findLoader( Digester digester, Class<?> pluginClass, Properties p )
102            throws PluginException
103        {
104            String ruleClassName = p.getProperty( ruleClassAttr );
105            if ( ruleClassName == null )
106            {
107                // nope, user hasn't requested dynamic rules to be loaded
108                // from a specific class.
109                return null;
110            }
111    
112            // ok, we are in business
113            String methodName = null;
114            if ( methodAttr != null )
115            {
116                methodName = p.getProperty( methodAttr );
117            }
118            if ( methodName == null )
119            {
120                methodName = dfltMethodName;
121            }
122            if ( methodName == null )
123            {
124                methodName = DFLT_METHOD_NAME;
125            }
126    
127            Class<?> ruleClass;
128            try
129            {
130                // load the plugin class object
131                ruleClass = digester.getClassLoader().loadClass( ruleClassName );
132            }
133            catch ( ClassNotFoundException cnfe )
134            {
135                throw new PluginException( "Unable to load class " + ruleClassName, cnfe );
136            }
137    
138            return new LoaderFromClass( ruleClass, methodName );
139        }
140    
141    }