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