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
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 "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
036 public 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 }