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