001/* $Id: SetTopRule.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 "set parent" method on the top (child)
028 * object, passing the (top-1) (parent) object as an argument.</p>
029 *
030 * <p>This rule now supports more flexible method matching by default.
031 * It is possible that this may break (some) code 
032 * written against release 1.1.1 or earlier.
033 * See {@link #isExactMatch()} for more details.</p>
034 */
035
036public class SetTopRule extends Rule {
037
038
039    // ----------------------------------------------------------- Constructors
040
041
042    /**
043     * Construct a "set parent" rule with the specified method name.  The
044     * "set parent" method's argument type is assumed to be the class of the
045     * parent object.
046     *
047     * @param digester The associated Digester
048     * @param methodName Method name of the "set parent" method to call
049     *
050     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
051     * Use {@link #SetTopRule(String methodName)} instead.
052     */
053    @Deprecated
054    public SetTopRule(Digester digester, String methodName) {
055
056        this(methodName);
057
058    }
059
060
061    /**
062     * Construct a "set parent" rule with the specified method name.
063     *
064     * @param digester The associated Digester
065     * @param methodName Method name of the "set parent" method to call
066     * @param paramType Java class of the "set 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 #SetTopRule(String methodName, String paramType)} instead.
073     */
074    @Deprecated
075    public SetTopRule(Digester digester, String methodName,
076                      String paramType) {
077
078        this(methodName, paramType);
079
080    }
081
082    /**
083     * Construct a "set parent" rule with the specified method name.  The
084     * "set parent" method's argument type is assumed to be the class of the
085     * parent object.
086     *
087     * @param methodName Method name of the "set parent" method to call
088     */
089    public SetTopRule(String methodName) {
090
091        this(methodName, null);
092
093    }
094
095
096    /**
097     * Construct a "set parent" rule with the specified method name.
098     *
099     * @param methodName Method name of the "set parent" method to call
100     * @param paramType Java class of the "set parent" method's argument
101     *  (if you wish to use a primitive type, specify the corresonding
102     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
103     *  for a <code>boolean</code> parameter)
104     */
105    public SetTopRule(String methodName,
106                      String paramType) {
107
108        this.methodName = methodName;
109        this.paramType = paramType;
110
111    }
112
113
114    // ----------------------------------------------------- Instance Variables
115
116
117    /**
118     * The method name to call on the child 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     * <p>Is exact matching being used.</p>
138     *
139     * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
140     * to introspect the relevent objects so that the right method can be called.
141     * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
142     * This matches methods very strictly 
143     * and so may not find a matching method when one exists.
144     * This is still the behaviour when exact matching is enabled.</p>
145     *
146     * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
147     * This method finds more methods but is less precise when there are several methods 
148     * with correct signatures.
149     * So, if you want to choose an exact signature you might need to enable this property.</p>
150     *
151     * <p>The default setting is to disable exact matches.</p>
152     *
153     * @return true iff exact matching is enabled
154     * @since Digester Release 1.1.1
155     */
156    public boolean isExactMatch() {
157    
158        return useExactMatch;
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    @Override
178    public void end() throws Exception {
179
180        // Identify the objects to be used
181        Object child = digester.peek(0);
182        Object parent = digester.peek(1);
183        
184        if (digester.log.isDebugEnabled()) {
185            if (child == null) {
186                digester.log.debug("[SetTopRule]{" + digester.match +
187                        "} Call [NULL CHILD]." +
188                        methodName + "(" + parent + ")");
189            } else {
190                digester.log.debug("[SetTopRule]{" + digester.match +
191                        "} Call " + child.getClass().getName() + "." +
192                        methodName + "(" + parent + ")");
193            }
194        }
195
196        // Call the specified method
197        Class<?> paramTypes[] = new Class<?>[1];
198        if (paramType != null) {
199            paramTypes[0] =
200                    digester.getClassLoader().loadClass(paramType);
201        } else {
202            paramTypes[0] = parent.getClass();
203        }
204
205        if (useExactMatch) {
206        
207            MethodUtils.invokeExactMethod(child, methodName,
208                new Object[]{ parent }, paramTypes);
209                
210        } else {
211        
212            MethodUtils.invokeMethod(child, methodName,
213                new Object[]{ parent }, paramTypes);
214        
215        }
216    }
217
218
219    /**
220     * Render a printable version of this Rule.
221     */
222    @Override
223    public String toString() {
224
225        StringBuffer sb = new StringBuffer("SetTopRule[");
226        sb.append("methodName=");
227        sb.append(methodName);
228        sb.append(", paramType=");
229        sb.append(paramType);
230        sb.append("]");
231        return (sb.toString());
232
233    }
234
235
236}