001/* $Id: SetRootRule.java 471661 2006-11-06 08:09:25Z skitching $
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 */ 
018
019
020package org.apache.commons.digester;
021
022
023import org.apache.commons.beanutils.MethodUtils;
024
025
026/**
027 * <p>Rule implementation that calls a method on the root object on the stack,
028 * passing the top object (child) as an argument.
029 * It is important to remember that this rule acts on <code>end</code>.</p>
030 *
031 * <p>This rule now supports more flexible method matching by default.
032 * It is possible that this may break (some) code 
033 * written against release 1.1.1 or earlier.
034 * See {@link #isExactMatch()} for more details.</p>
035 */
036
037public class SetRootRule extends Rule {
038
039
040    // ----------------------------------------------------------- Constructors
041
042
043    /**
044     * Construct a "set root" rule with the specified method name.  The
045     * method's argument type is assumed to be the class of the
046     * child object.
047     *
048     * @param digester The associated Digester
049     * @param methodName Method name of the parent method to call
050     *
051     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
052     * Use {@link #SetRootRule(String methodName)} instead.
053     */
054    public SetRootRule(Digester digester, String methodName) {
055
056        this(methodName);
057
058    }
059
060
061    /**
062     * Construct a "set root" rule with the specified method name.
063     *
064     * @param digester The associated Digester
065     * @param methodName Method name of the parent method to call
066     * @param paramType Java class of the parent method's argument
067     *  (if you wish to use a primitive type, specify the corresonding
068     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
069     *  for a <code>boolean</code> parameter)
070     *
071     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
072     * Use {@link #SetRootRule(String methodName,String paramType)} instead.
073     */
074    public SetRootRule(Digester digester, String methodName,
075                       String paramType) {
076
077        this(methodName, paramType);
078
079    }
080
081    /**
082     * Construct a "set root" rule with the specified method name.  The
083     * method's argument type is assumed to be the class of the
084     * child object.
085     *
086     * @param methodName Method name of the parent method to call
087     */
088    public SetRootRule(String methodName) {
089
090        this(methodName, null);
091
092    }
093
094
095    /**
096     * Construct a "set root" rule with the specified method name.
097     *
098     * @param methodName Method name of the parent method to call
099     * @param paramType Java class of the parent method's argument
100     *  (if you wish to use a primitive type, specify the corresonding
101     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
102     *  for a <code>boolean</code> parameter)
103     */
104    public SetRootRule(String methodName,
105                       String paramType) {
106
107        this.methodName = methodName;
108        this.paramType = paramType;
109
110    }
111
112    // ----------------------------------------------------- Instance Variables
113
114
115    /**
116     * The method name to call on the parent object.
117     */
118    protected String methodName = null;
119
120
121    /**
122     * The Java class name of the parameter type expected by the method.
123     */
124    protected String paramType = null;
125    
126    /**
127     * Should we use exact matching. Default is no.
128     */
129    protected boolean useExactMatch = false;
130
131
132    // --------------------------------------------------------- Public Methods
133
134
135    /**
136     * <p>Is exact matching being used.</p>
137     *
138     * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
139     * to introspect the relevent objects so that the right method can be called.
140     * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
141     * This matches methods very strictly 
142     * and so may not find a matching method when one exists.
143     * This is still the behaviour when exact matching is enabled.</p>
144     *
145     * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
146     * This method finds more methods but is less precise when there are several methods 
147     * with correct signatures.
148     * So, if you want to choose an exact signature you might need to enable this property.</p>
149     *
150     * <p>The default setting is to disable exact matches.</p>
151     *
152     * @return true iff exact matching is enabled
153     * @since Digester Release 1.1.1
154     */
155    public boolean isExactMatch() {
156    
157        return useExactMatch;
158    }
159    
160    
161    /**
162     * <p>Set whether exact matching is enabled.</p>
163     *
164     * <p>See {@link #isExactMatch()}.</p>
165     *
166     * @param useExactMatch should this rule use exact method matching
167     * @since Digester Release 1.1.1
168     */
169    public void setExactMatch(boolean useExactMatch) {
170
171        this.useExactMatch = useExactMatch;
172    }
173
174    /**
175     * Process the end of this element.
176     */
177    public void end() throws Exception {
178
179        // Identify the objects to be used
180        Object child = digester.peek(0);
181        Object parent = digester.root;
182        if (digester.log.isDebugEnabled()) {
183            if (parent == null) {
184                digester.log.debug("[SetRootRule]{" + digester.match +
185                        "} Call [NULL ROOT]." +
186                        methodName + "(" + child + ")");
187            } else {
188                digester.log.debug("[SetRootRule]{" + digester.match +
189                        "} Call " + parent.getClass().getName() + "." +
190                        methodName + "(" + child + ")");
191            }
192        }
193
194        // Call the specified method
195        Class paramTypes[] = new Class[1];
196        if (paramType != null) {
197            paramTypes[0] =
198                    digester.getClassLoader().loadClass(paramType);
199        } else {
200            paramTypes[0] = child.getClass();
201        }
202        
203        if (useExactMatch) {
204        
205            MethodUtils.invokeExactMethod(parent, methodName,
206                new Object[]{ child }, paramTypes);
207                
208        } else {
209        
210            MethodUtils.invokeMethod(parent, methodName,
211                new Object[]{ child }, paramTypes);
212        
213        }
214    }
215
216
217    /**
218     * Render a printable version of this Rule.
219     */
220    public String toString() {
221
222        StringBuffer sb = new StringBuffer("SetRootRule[");
223        sb.append("methodName=");
224        sb.append(methodName);
225        sb.append(", paramType=");
226        sb.append(paramType);
227        sb.append("]");
228        return (sb.toString());
229
230    }
231
232
233}