001 /* $Id: SetRootRule.java 729106 2008-12-23 20:48:09Z rahul $
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
020 package org.apache.commons.digester;
021
022
023 import 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
037 public 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 }