Coverage Report - org.apache.commons.betwixt.expression.MapEntryAdder

Classes in this File Line Coverage Branch Coverage Complexity
MapEntryAdder
75% 
100% 
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  
 }