Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
MapEntryAdder |
|
| 3.142857142857143;3.143 |
1 | 299 | /* |
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 | ||
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 | 91 | public class MapEntryAdder { |
42 | ||
43 | ||
44 | // Class Attributes |
|
45 | //------------------------------------------------------------------------- |
|
46 | ||
47 | /** Log used by this class */ |
|
48 | 403 | private static Log log = LogFactory.getLog( MapEntryAdder.class ); |
49 | ||
50 | ||
51 | // Class Methods |
|
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 | 0 | log = newLog; |
61 | 0 | } |
62 | ||
63 | // Attributes |
|
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 | 767 | private boolean keyUpdated = false; |
71 | /** The entry key */ |
|
72 | 156 | private Object key; |
73 | ||
74 | /** Has the entry value been updated? */ |
|
75 | 767 | private boolean valueUpdated = false; |
76 | /** The entry value */ |
|
77 | 156 | private Object value; |
78 | ||
79 | ||
80 | // Constructors |
|
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 | 299 | public MapEntryAdder(Method method) { |
90 | ||
91 | 299 | Class[] types = method.getParameterTypes(); |
92 | 299 | if ( types == null || types.length != 2) { |
93 | 0 | throw new IllegalArgumentException( |
94 | 0 | "Method used to add entries to maps must have two parameter."); |
95 | } |
|
96 | 299 | this.adderMethod = method; |
97 | 299 | } |
98 | ||
99 | // Properties |
|
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 | 299 | return new Updater() { |
113 | public void update( Context context, Object keyValue ) { |
|
114 | // might as well make sure that his can only be set once |
|
115 | 156 | if ( !keyUpdated ) { |
116 | 156 | keyUpdated = true; |
117 | 156 | key = keyValue; |
118 | 156 | if ( log.isTraceEnabled() ) { |
119 | 0 | log.trace( "Setting entry key to " + key ); |
120 | 0 | log.trace( "Current entry value is " + value ); |
121 | } |
|
122 | 156 | if ( valueUpdated ) { |
123 | 0 | callAdderMethod( context ); |
124 | } |
|
125 | } |
|
126 | 156 | } |
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 | 299 | return new Updater() { |
141 | public void update( Context context, Object valueValue ) { |
|
142 | // might as well make sure that his can only be set once |
|
143 | 156 | if ( !valueUpdated ) { |
144 | 156 | valueUpdated = true; |
145 | 156 | value = valueValue; |
146 | 156 | if ( log.isTraceEnabled() ) { |
147 | 0 | log.trace( "Setting entry value to " + value); |
148 | 0 | log.trace( "Current entry key is " + key ); |
149 | } |
|
150 | 156 | if ( keyUpdated ) { |
151 | 156 | callAdderMethod( context ); |
152 | } |
|
153 | } |
|
154 | 156 | } |
155 | }; |
|
156 | } |
|
157 | ||
158 | ||
159 | ||
160 | // Implementation methods |
|
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 | 156 | private void callAdderMethod(Context context) { |
170 | 156 | log.trace("Calling adder method"); |
171 | ||
172 | // this allows the same instance to be used multiple times. |
|
173 | 156 | keyUpdated = false; |
174 | 156 | valueUpdated = false; |
175 | ||
176 | // |
|
177 | // XXX This is (basically) cut and pasted from the MethodUpdater code |
|
178 | // I haven't abstracted this code just yet since I think that adding |
|
179 | // handling for non-beans will mean adding quite a lot more structure |
|
180 | // and only once this is added will the proper position for this method |
|
181 | // become clear. |
|
182 | // |
|
183 | ||
184 | 156 | Class[] types = adderMethod.getParameterTypes(); |
185 | // key is first parameter |
|
186 | 156 | Class keyType = types[0]; |
187 | // value is the second |
|
188 | 156 | Class valueType = types[1]; |
189 | ||
190 | 156 | Object bean = context.getBean(); |
191 | 156 | if ( bean != null ) { |
192 | 156 | if ( key instanceof String ) { |
193 | // try to convert into primitive types |
|
194 | 312 | key = context.getObjectStringConverter() |
195 | 156 | .stringToObject( (String) key, keyType, context ); |
196 | } |
|
197 | ||
198 | 156 | if ( value instanceof String ) { |
199 | // try to convert into primitive types |
|
200 | 208 | value = context.getObjectStringConverter() |
201 | 104 | .stringToObject( (String) value, valueType, context ); |
202 | } |
|
203 | ||
204 | // special case for collection objects into arrays |
|
205 | 156 | if (value instanceof Collection && valueType.isArray()) { |
206 | 13 | Collection valuesAsCollection = (Collection) value; |
207 | 13 | Class componentType = valueType.getComponentType(); |
208 | 13 | if (componentType != null) { |
209 | 13 | Object[] valuesAsArray = |
210 | 13 | (Object[]) Array.newInstance(componentType, valuesAsCollection.size()); |
211 | 13 | value = valuesAsCollection.toArray(valuesAsArray); |
212 | } |
|
213 | } |
|
214 | ||
215 | ||
216 | 156 | Object[] arguments = { key, value }; |
217 | try { |
|
218 | 156 | if ( log.isTraceEnabled() ) { |
219 | 0 | log.trace( |
220 | 0 | "Calling adder method: " + adderMethod.getName() + " on bean: " + bean |
221 | 0 | + " with key: " + key + " and value: " + value |
222 | ); |
|
223 | } |
|
224 | 156 | adderMethod.invoke( bean, arguments ); |
225 | ||
226 | 0 | } catch (Exception e) { |
227 | 0 | log.warn( |
228 | 0 | "Cannot evaluate adder method: " + adderMethod.getName() + " on bean: " + bean |
229 | 0 | + " of type: " + bean.getClass().getName() + " with value: " + value |
230 | 0 | + " of type: " + valueType + " and key: " + key |
231 | 0 | + " of type: " + keyType |
232 | ); |
|
233 | 0 | log.debug(e); |
234 | } |
|
235 | } |
|
236 | 156 | } |
237 | } |