001: /* $Id: SetNextRule.java 471661 2006-11-06 08:09:25Z skitching $
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: package org.apache.commons.digester;
020:
021: import org.apache.commons.beanutils.MethodUtils;
022:
023: /**
024: * <p>Rule implementation that calls a method on the (top-1) (parent)
025: * object, passing the top object (child) as an argument. It is
026: * commonly used to establish parent-child relationships.</p>
027: *
028: * <p>This rule now supports more flexible method matching by default.
029: * It is possible that this may break (some) code
030: * written against release 1.1.1 or earlier.
031: * See {@link #isExactMatch()} for more details.</p>
032: *
033: * <p>Note that while CallMethodRule uses commons-beanutils' data-conversion
034: * functionality (ConvertUtils class) to convert parameter values into
035: * the appropriate type for the parameter to the called method, this
036: * rule does not. Needing to use ConvertUtils functionality when building
037: * parent-child relationships is expected to be very rare; however if you
038: * do need this then instead of using this rule, create a CallMethodRule
039: * specifying targetOffset of 1 in the constructor.</p>
040: */
041:
042: public class SetNextRule extends Rule {
043:
044: // ----------------------------------------------------------- Constructors
045:
046: /**
047: * Construct a "set next" rule with the specified method name. The
048: * method's argument type is assumed to be the class of the
049: * child object.
050: *
051: * @param digester The associated Digester
052: * @param methodName Method name of the parent method to call
053: *
054: * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
055: * Use {@link #SetNextRule(String methodName)} instead.
056: */
057: public SetNextRule(Digester digester, String methodName) {
058:
059: this (methodName);
060:
061: }
062:
063: /**
064: * Construct a "set next" rule with the specified method name.
065: *
066: * @param digester The associated Digester
067: * @param methodName Method name of the parent method to call
068: * @param paramType Java class of the parent method's argument
069: * (if you wish to use a primitive type, specify the corresonding
070: * Java wrapper class instead, such as <code>java.lang.Boolean</code>
071: * for a <code>boolean</code> parameter)
072: *
073: * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
074: * Use {@link #SetNextRule(String methodName,String paramType)} instead.
075: */
076: public SetNextRule(Digester digester, String methodName,
077: String paramType) {
078:
079: this (methodName, paramType);
080:
081: }
082:
083: /**
084: * Construct a "set next" 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 SetNextRule(String methodName) {
091:
092: this (methodName, null);
093:
094: }
095:
096: /**
097: * Construct a "set next" rule with the specified method name.
098: *
099: * @param methodName Method name of the parent method to call
100: * @param paramType Java class of the 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 SetNextRule(String methodName, String paramType) {
106:
107: this .methodName = methodName;
108: this .paramType = paramType;
109:
110: }
111:
112: // ----------------------------------------------------- Instance Variables
113:
114: /**
115: * The method name to call on the parent object.
116: */
117: protected String methodName = null;
118:
119: /**
120: * The Java class name of the parameter type expected by the method.
121: */
122: protected String paramType = null;
123:
124: /**
125: * Should we use exact matching. Default is no.
126: */
127: protected boolean useExactMatch = false;
128:
129: // --------------------------------------------------------- Public Methods
130:
131: /**
132: * <p>Is exact matching being used.</p>
133: *
134: * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code>
135: * to introspect the relevent objects so that the right method can be called.
136: * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
137: * This matches methods very strictly
138: * and so may not find a matching method when one exists.
139: * This is still the behaviour when exact matching is enabled.</p>
140: *
141: * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
142: * This method finds more methods but is less precise when there are several methods
143: * with correct signatures.
144: * So, if you want to choose an exact signature you might need to enable this property.</p>
145: *
146: * <p>The default setting is to disable exact matches.</p>
147: *
148: * @return true iff exact matching is enabled
149: * @since Digester Release 1.1.1
150: */
151: public boolean isExactMatch() {
152:
153: return useExactMatch;
154: }
155:
156: /**
157: * <p>Set whether exact matching is enabled.</p>
158: *
159: * <p>See {@link #isExactMatch()}.</p>
160: *
161: * @param useExactMatch should this rule use exact method matching
162: * @since Digester Release 1.1.1
163: */
164: public void setExactMatch(boolean useExactMatch) {
165:
166: this .useExactMatch = useExactMatch;
167: }
168:
169: /**
170: * Process the end of this element.
171: */
172: public void end() throws Exception {
173:
174: // Identify the objects to be used
175: Object child = digester.peek(0);
176: Object parent = digester.peek(1);
177: if (digester.log.isDebugEnabled()) {
178: if (parent == null) {
179: digester.log.debug("[SetNextRule]{" + digester.match
180: + "} Call [NULL PARENT]." + methodName + "("
181: + child + ")");
182: } else {
183: digester.log.debug("[SetNextRule]{" + digester.match
184: + "} Call " + parent.getClass().getName() + "."
185: + methodName + "(" + child + ")");
186: }
187: }
188:
189: // Call the specified method
190: Class paramTypes[] = new Class[1];
191: if (paramType != null) {
192: paramTypes[0] = digester.getClassLoader().loadClass(
193: paramType);
194: } else {
195: paramTypes[0] = child.getClass();
196: }
197:
198: if (useExactMatch) {
199:
200: MethodUtils.invokeExactMethod(parent, methodName,
201: new Object[] { child }, paramTypes);
202:
203: } else {
204:
205: MethodUtils.invokeMethod(parent, methodName,
206: new Object[] { child }, paramTypes);
207:
208: }
209: }
210:
211: /**
212: * Render a printable version of this Rule.
213: */
214: public String toString() {
215:
216: StringBuffer sb = new StringBuffer("SetNextRule[");
217: sb.append("methodName=");
218: sb.append(methodName);
219: sb.append(", paramType=");
220: sb.append(paramType);
221: sb.append("]");
222: return (sb.toString());
223:
224: }
225:
226: }
|