View Javadoc

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