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