1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.betwixt.expression;
  17. import java.lang.reflect.Array;
  18. import java.lang.reflect.Method;
  19. import java.util.Collection;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. /** <p><code>MethodUpdater</code> updates the current bean context
  23. * by calling a WriteMethod with the String value from the XML attribute
  24. * or element.</p>
  25. *
  26. * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  27. * @version $Revision: 1.13 $
  28. */
  29. public class MethodUpdater implements Updater {
  30. /** Logger */
  31. private static Log log = LogFactory.getLog( MethodUpdater.class );
  32. /**
  33. * Programmatically set log
  34. * @param aLog the implementation to which this class should log
  35. */
  36. public static void setLog( Log aLog ) {
  37. log = aLog;
  38. }
  39. /** The method to call on the bean */
  40. private Method method;
  41. /** The type of the first parameter of the method */
  42. private Class valueType;
  43. /** Base constructor */
  44. public MethodUpdater() {
  45. }
  46. /**
  47. * Convenience constructor sets method property
  48. * @param method the Method to be invoked on the context's bean in the update
  49. */
  50. public MethodUpdater(Method method) {
  51. setMethod( method );
  52. }
  53. /**
  54. * Updates the current bean context with the given String value
  55. * @param context the Context to be updated
  56. * @param newValue the update to this new value
  57. */
  58. public void update(Context context, Object newValue) {
  59. Object bean = context.getBean();
  60. if ( bean != null ) {
  61. if ( newValue instanceof String ) {
  62. // try to convert into primitive types
  63. if ( log.isTraceEnabled() ) {
  64. log.trace("Converting primitive to " + valueType);
  65. }
  66. newValue = context.getObjectStringConverter()
  67. .stringToObject( (String) newValue, valueType, null, context );
  68. }
  69. if ( newValue != null ) {
  70. // check that it is of the correct type
  71. /*
  72. if ( ! valueType.isAssignableFrom( newValue.getClass() ) ) {
  73. log.warn(
  74. "Cannot call setter method: " + method.getName() + " on bean: " + bean
  75. + " with type: " + bean.getClass().getName()
  76. + " as parameter should be of type: " + valueType.getName()
  77. + " but is: " + newValue.getClass().getName()
  78. );
  79. return;
  80. }
  81. */
  82. }
  83. // special case for collection objects into arrays
  84. if (newValue instanceof Collection && valueType.isArray()) {
  85. Collection valuesAsCollection = (Collection) newValue;
  86. Class componentType = valueType.getComponentType();
  87. if (componentType != null) {
  88. Object[] valuesAsArray =
  89. (Object[]) Array.newInstance(componentType, valuesAsCollection.size());
  90. newValue = valuesAsCollection.toArray(valuesAsArray);
  91. }
  92. }
  93. Object[] arguments = { newValue };
  94. try {
  95. if ( log.isDebugEnabled() ) {
  96. log.debug(
  97. "Calling setter method: " + method.getName() + " on bean: " + bean
  98. + " with new value: " + newValue
  99. );
  100. }
  101. method.invoke( bean, arguments );
  102. } catch (Exception e) {
  103. String valueTypeName = (newValue != null) ? newValue.getClass().getName() : "null";
  104. log.warn(
  105. "Cannot evaluate method: " + method.getName() + " on bean: " + bean
  106. + " of type: " + bean.getClass().getName() + " with value: " + newValue
  107. + " of type: " + valueTypeName
  108. );
  109. handleException(context, e);
  110. }
  111. }
  112. }
  113. /**
  114. * Gets the method which will be invoked by the update
  115. *
  116. * @return the Method to be invoked by the update
  117. */
  118. public Method getMethod() {
  119. return method;
  120. }
  121. /**
  122. * Sets the constant value of this expression
  123. * @param method the Method to be invoked by the update
  124. */
  125. public void setMethod(Method method) {
  126. this.method = method;
  127. Class[] types = method.getParameterTypes();
  128. if ( types == null || types.length <= 0 ) {
  129. throw new IllegalArgumentException( "The Method must have at least one parameter" );
  130. }
  131. this.valueType = types[0];
  132. }
  133. // Implementation methods
  134. //-------------------------------------------------------------------------
  135. /**
  136. * Strategy method to allow derivations to handle exceptions differently.
  137. * @param context the Context being updated when this exception occured
  138. * @param e the Exception that occured during the update
  139. */
  140. protected void handleException(Context context, Exception e) {
  141. log.info( "Caught exception: " + e, e );
  142. }
  143. /**
  144. * Returns something useful for logging.
  145. * @return something useful for logging
  146. */
  147. public String toString() {
  148. return "MethodUpdater [method=" + method + "]";
  149. }
  150. }