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 }