1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.betwixt.expression;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.Method;
21 import java.util.Collection;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 /*** <p><code>MapEntryAdder</code> is used to add entries to a map.</p>
27 *
28 * <p>
29 * <code>MapEntryAdder</code> supplies two updaters:
30 * <ul>
31 * <li>{@link #getKeyUpdater()} which allows the entry key to be updated</li>
32 * <li>{@link #getValueUpdater()} which allows the entry value to be updated</li>
33 * </ul>
34 * When both of these updaters have been called, the entry adder method is called.
35 * Once this has happened then the values can be updated again.
36 * Note that only the <code>Context</code> passed by the last update will be used.
37 * </p>
38 *
39 * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
40 * @since 0.5
41 */
42 public class MapEntryAdder {
43
44
45
46
47
48 /*** Log used by this class */
49 private static Log log = LogFactory.getLog( MapEntryAdder.class );
50
51
52
53
54
55 /***
56 * Sets the logger used by this class.
57 *
58 * @param newLog log to this
59 */
60 public static void setLog(Log newLog) {
61 log = newLog;
62 }
63
64
65
66
67 /*** The method to be called to add a new map entry */
68 private Method adderMethod;
69
70 /*** Has the entry key been updated? */
71 private boolean keyUpdated = false;
72 /*** The entry key */
73 private Object key;
74
75 /*** Has the entry value been updated? */
76 private boolean valueUpdated = false;
77 /*** The entry value */
78 private Object value;
79
80
81
82
83
84 /***
85 * Construct a <code>MapEntryAdder</code> which adds entries to given method.
86 *
87 * @param method the <code>Method</code> called to add a key-value entry
88 * @throws IllegalArgumentException if the given method does not take two parameters
89 */
90 public MapEntryAdder(Method method) {
91
92 Class[] types = method.getParameterTypes();
93 if ( types == null || types.length != 2) {
94 throw new IllegalArgumentException(
95 "Method used to add entries to maps must have two parameter.");
96 }
97 this.adderMethod = method;
98 }
99
100
101
102
103 /***
104 * Gets the entry key <code>Updater</code>.
105 * This is used to update the entry key value to the read value.
106 * If {@link #getValueUpdater} has been called previously,
107 * then this trigger the updating of the adder method.
108 *
109 * @return the <code>Updater</code> which should be used to populate the entry key
110 */
111 public Updater getKeyUpdater() {
112
113 return new Updater() {
114 public void update( Context context, Object keyValue ) {
115
116 if ( !keyUpdated ) {
117 keyUpdated = true;
118 key = keyValue;
119 if ( log.isTraceEnabled() ) {
120 log.trace( "Setting entry key to " + key );
121 log.trace( "Current entry value is " + value );
122 }
123 if ( valueUpdated ) {
124 callAdderMethod( context );
125 }
126 }
127 }
128 };
129 }
130
131 /***
132 * Gets the entry value <code>Updater</code>.
133 * This is used to update the entry key value to the read value.
134 * If {@link #getKeyUpdater} has been called previously,
135 * then this trigger the updating of the adder method.
136 *
137 * @return the <code>Updater</code> which should be used to populate the entry value
138 */
139 public Updater getValueUpdater() {
140
141 return new Updater() {
142 public void update( Context context, Object valueValue ) {
143
144 if ( !valueUpdated ) {
145 valueUpdated = true;
146 value = valueValue;
147 if ( log.isTraceEnabled() ) {
148 log.trace( "Setting entry value to " + value);
149 log.trace( "Current entry key is " + key );
150 }
151 if ( keyUpdated ) {
152 callAdderMethod( context );
153 }
154 }
155 }
156 };
157 }
158
159
160
161
162
163
164 /***
165 * Call the adder method on the bean associated with the <code>Context</code>
166 * with the key, value entry values stored previously.
167 *
168 * @param context the Context against whose bean the adder method will be invoked
169 */
170 private void callAdderMethod(Context context) {
171 log.trace("Calling adder method");
172
173
174 keyUpdated = false;
175 valueUpdated = false;
176
177
178
179
180
181
182
183
184
185 Class[] types = adderMethod.getParameterTypes();
186
187 Class keyType = types[0];
188
189 Class valueType = types[1];
190
191 Object bean = context.getBean();
192 if ( bean != null ) {
193 if ( key instanceof String ) {
194
195 key = context.getObjectStringConverter()
196 .stringToObject( (String) key, keyType, context );
197 }
198
199 if ( value instanceof String ) {
200
201 value = context.getObjectStringConverter()
202 .stringToObject( (String) value, valueType, context );
203 }
204
205
206 if (value instanceof Collection && valueType.isArray()) {
207 Collection valuesAsCollection = (Collection) value;
208 Class componentType = valueType.getComponentType();
209 if (componentType != null) {
210 Object[] valuesAsArray =
211 (Object[]) Array.newInstance(componentType, valuesAsCollection.size());
212 value = valuesAsCollection.toArray(valuesAsArray);
213 }
214 }
215
216
217 Object[] arguments = { key, value };
218 try {
219 if ( log.isTraceEnabled() ) {
220 log.trace(
221 "Calling adder method: " + adderMethod.getName() + " on bean: " + bean
222 + " with key: " + key + " and value: " + value
223 );
224 }
225 adderMethod.invoke( bean, arguments );
226
227 } catch (Exception e) {
228 log.warn(
229 "Cannot evaluate adder method: " + adderMethod.getName() + " on bean: " + bean
230 + " of type: " + bean.getClass().getName() + " with value: " + value
231 + " of type: " + valueType + " and key: " + key
232 + " of type: " + keyType
233 );
234 log.debug(e);
235 }
236 }
237 }
238 }