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