001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.betwixt.expression;
018
019 import java.lang.reflect.Array;
020 import java.util.Collection;
021
022 import org.apache.commons.logging.Log;
023 import org.apache.commons.logging.LogFactory;
024
025 /**
026 * Abstracts common features for strongly typed <code>Updater</code>'s.
027 * Strongly type <code>Updater</code>'s perform conversions based on this
028 * the expected type before the bean update is invoked.
029 * @since 0.7
030 * @author <a href='http://commons.apache.org'>Apache Commons Team</a>, <a href='http://www.apache.org'>Apache Software Foundation</a>
031 */
032 public abstract class TypedUpdater implements Updater {
033
034 /** Logger */
035 private static final Log log = LogFactory.getLog( TypedUpdater.class );
036
037
038 /** The type of the first parameter of the method */
039 private Class valueType;
040
041 /**
042 * Updates the current bean context with the given String value
043 * @param context the Context to be updated
044 * @param newValue the update to this new value
045 */
046 public void update(Context context, Object newValue) {
047 Object bean = context.getBean();
048 if ( bean != null ) {
049 if ( newValue instanceof String ) {
050 // try to convert into primitive types
051 if ( log.isTraceEnabled() ) {
052 log.trace("Converting primitive to " + valueType);
053 }
054 newValue = context.getObjectStringConverter()
055 .stringToObject( (String) newValue, valueType, context );
056 }
057 if ( newValue != null ) {
058 // check that it is of the correct type
059 /*
060 if ( ! valueType.isAssignableFrom( newValue.getClass() ) ) {
061 log.warn(
062 "Cannot call setter method: " + method.getName() + " on bean: " + bean
063 + " with type: " + bean.getClass().getName()
064 + " as parameter should be of type: " + valueType.getName()
065 + " but is: " + newValue.getClass().getName()
066 );
067 return;
068 }
069 */
070 }
071 // special case for collection objects into arrays
072 if (newValue instanceof Collection && valueType.isArray()) {
073 Collection valuesAsCollection = (Collection) newValue;
074 Class componentType = valueType.getComponentType();
075 if (componentType != null) {
076 Object[] valuesAsArray =
077 (Object[]) Array.newInstance(componentType, valuesAsCollection.size());
078 newValue = valuesAsCollection.toArray(valuesAsArray);
079 }
080 }
081
082 ;
083 try {
084 executeUpdate( context, bean, newValue );
085
086 } catch (Exception e) {
087 String valueTypeName = (newValue != null) ? newValue.getClass().getName() : "null";
088 log.warn(
089 "Cannot evaluate: " + this.toString() + " on bean: " + bean
090 + " of type: " + bean.getClass().getName() + " with value: " + newValue
091 + " of type: " + valueTypeName
092 );
093 handleException(context, e);
094 }
095 }
096 }
097
098
099
100 /**
101 * Gets the type expected.
102 * The value passed into {@link #update}
103 * will be converted on the basis of this type
104 * before being passed to {@link #executeUpdate}.
105 * @return <code>Class</code> giving expected type, not null
106 */
107 public Class getValueType() {
108 return valueType;
109 }
110
111 /**
112 * Sets the type expected.
113 * The value passed into {@link #update}
114 * will be converted on the basis of this type
115 * before being passed to {@link #executeUpdate}.
116 * @param valueType <code>Class</code> giving expected type, not null
117 */
118 public void setValueType(Class valueType) {
119 this.valueType = valueType;
120 }
121
122 /**
123 * Updates the bean with the given value.
124 * @param bean
125 * @param value value after type conversion
126 */
127 protected abstract void executeUpdate(Context context, Object bean, Object value) throws Exception;
128
129 /**
130 * Strategy method to allow derivations to handle exceptions differently.
131 * @param context the Context being updated when this exception occured
132 * @param e the Exception that occured during the update
133 */
134 protected void handleException(Context context, Exception e) {
135 log.info( "Caught exception: " + e, e );
136 }
137
138 }