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
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 @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 }