1. /* $Id: SetNextRule.java,v 1.22 2004/05/10 06:30:06 skitching Exp $
  2. *
  3. * Copyright 2001-2004 The Apache Software Foundation.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.commons.digester;
  18. import org.apache.commons.beanutils.MethodUtils;
  19. /**
  20. * <p>Rule implementation that calls a method on the (top-1) (parent)
  21. * object, passing the top object (child) as an argument. It is
  22. * commonly used to establish parent-child relationships.</p>
  23. *
  24. * <p>This rule now supports more flexible method matching by default.
  25. * It is possible that this may break (some) code
  26. * written against release 1.1.1 or earlier.
  27. * See {@link #isExactMatch()} for more details.</p>
  28. */
  29. public class SetNextRule extends Rule {
  30. // ----------------------------------------------------------- Constructors
  31. /**
  32. * Construct a "set next" rule with the specified method name. The
  33. * method's argument type is assumed to be the class of the
  34. * child object.
  35. *
  36. * @param digester The associated Digester
  37. * @param methodName Method name of the parent method to call
  38. *
  39. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  40. * Use {@link #SetNextRule(String methodName)} instead.
  41. */
  42. public SetNextRule(Digester digester, String methodName) {
  43. this(methodName);
  44. }
  45. /**
  46. * Construct a "set next" rule with the specified method name.
  47. *
  48. * @param digester The associated Digester
  49. * @param methodName Method name of the parent method to call
  50. * @param paramType Java class of the parent method's argument
  51. * (if you wish to use a primitive type, specify the corresonding
  52. * Java wrapper class instead, such as <code>java.lang.Boolean</code>
  53. * for a <code>boolean</code> parameter)
  54. *
  55. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  56. * Use {@link #SetNextRule(String methodName,String paramType)} instead.
  57. */
  58. public SetNextRule(Digester digester, String methodName,
  59. String paramType) {
  60. this(methodName, paramType);
  61. }
  62. /**
  63. * Construct a "set next" rule with the specified method name. The
  64. * method's argument type is assumed to be the class of the
  65. * child object.
  66. *
  67. * @param methodName Method name of the parent method to call
  68. */
  69. public SetNextRule(String methodName) {
  70. this(methodName, null);
  71. }
  72. /**
  73. * Construct a "set next" rule with the specified method name.
  74. *
  75. * @param methodName Method name of the parent method to call
  76. * @param paramType Java class of the parent method's argument
  77. * (if you wish to use a primitive type, specify the corresonding
  78. * Java wrapper class instead, such as <code>java.lang.Boolean</code>
  79. * for a <code>boolean</code> parameter)
  80. */
  81. public SetNextRule(String methodName,
  82. String paramType) {
  83. this.methodName = methodName;
  84. this.paramType = paramType;
  85. }
  86. // ----------------------------------------------------- Instance Variables
  87. /**
  88. * The method name to call on the parent object.
  89. */
  90. protected String methodName = null;
  91. /**
  92. * The Java class name of the parameter type expected by the method.
  93. */
  94. protected String paramType = null;
  95. /**
  96. * Should we use exact matching. Default is no.
  97. */
  98. protected boolean useExactMatch = false;
  99. // --------------------------------------------------------- Public Methods
  100. /**
  101. * <p>Is exact matching being used.</p>
  102. *
  103. * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code>
  104. * to introspect the relevent objects so that the right method can be called.
  105. * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
  106. * This matches methods very strictly
  107. * and so may not find a matching method when one exists.
  108. * This is still the behaviour when exact matching is enabled.</p>
  109. *
  110. * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
  111. * This method finds more methods but is less precise when there are several methods
  112. * with correct signatures.
  113. * So, if you want to choose an exact signature you might need to enable this property.</p>
  114. *
  115. * <p>The default setting is to disable exact matches.</p>
  116. *
  117. * @return true iff exact matching is enabled
  118. * @since Digester Release 1.1.1
  119. */
  120. public boolean isExactMatch() {
  121. return useExactMatch;
  122. }
  123. /**
  124. * <p>Set whether exact matching is enabled.</p>
  125. *
  126. * <p>See {@link #isExactMatch()}.</p>
  127. *
  128. * @param useExactMatch should this rule use exact method matching
  129. * @since Digester Release 1.1.1
  130. */
  131. public void setExactMatch(boolean useExactMatch) {
  132. this.useExactMatch = useExactMatch;
  133. }
  134. /**
  135. * Process the end of this element.
  136. */
  137. public void end() throws Exception {
  138. // Identify the objects to be used
  139. Object child = digester.peek(0);
  140. Object parent = digester.peek(1);
  141. if (digester.log.isDebugEnabled()) {
  142. if (parent == null) {
  143. digester.log.debug("[SetNextRule]{" + digester.match +
  144. "} Call [NULL PARENT]." +
  145. methodName + "(" + child + ")");
  146. } else {
  147. digester.log.debug("[SetNextRule]{" + digester.match +
  148. "} Call " + parent.getClass().getName() + "." +
  149. methodName + "(" + child + ")");
  150. }
  151. }
  152. // Call the specified method
  153. Class paramTypes[] = new Class[1];
  154. if (paramType != null) {
  155. paramTypes[0] =
  156. digester.getClassLoader().loadClass(paramType);
  157. } else {
  158. paramTypes[0] = child.getClass();
  159. }
  160. if (useExactMatch) {
  161. MethodUtils.invokeExactMethod(parent, methodName,
  162. new Object[]{ child }, paramTypes);
  163. } else {
  164. MethodUtils.invokeMethod(parent, methodName,
  165. new Object[]{ child }, paramTypes);
  166. }
  167. }
  168. /**
  169. * Render a printable version of this Rule.
  170. */
  171. public String toString() {
  172. StringBuffer sb = new StringBuffer("SetNextRule[");
  173. sb.append("methodName=");
  174. sb.append(methodName);
  175. sb.append(", paramType=");
  176. sb.append(paramType);
  177. sb.append("]");
  178. return (sb.toString());
  179. }
  180. }