Coverage Report - net.sf.sapjcosupport.ReflectionFieldIterator
 
Classes in this File Line Coverage Branch Coverage Complexity
ReflectionFieldIterator
53% 
80% 
3
 
 1  
 package net.sf.sapjcosupport;
 2  
 
 3  
 import org.apache.commons.lang.NotImplementedException;
 4  
 import org.apache.oro.text.regex.MalformedPatternException;
 5  
 import org.apache.oro.text.regex.Pattern;
 6  
 import org.apache.oro.text.regex.Perl5Compiler;
 7  
 import org.apache.oro.text.regex.Perl5Matcher;
 8  
 import org.apache.log4j.Logger;
 9  
 import org.springframework.core.NestedRuntimeException;
 10  
 
 11  
 import java.lang.reflect.Field;
 12  
 import java.util.ArrayList;
 13  
 import java.util.Iterator;
 14  
 import java.util.NoSuchElementException;
 15  
 
 16  
 /**
 17  
  * Iterator implementation that iterates through the fields declared in a class,
 18  
  * and retrieves the values for an instance.
 19  
  * <p/>
 20  
  * This implementation does not support item removal, and will throw a
 21  
  * <code>NotImplementedException</code> when the {@link #remove()} method is called.
 22  
  *
 23  
  * @author Jo Vandermeeren
 24  
  * @since 4 Jun, 2006 - 10:12:07 PM
 25  
  */
 26  
 public class ReflectionFieldIterator implements Iterator {
 27  
     private Object instance;
 28  
     private int counter;
 29  
     private Field[] fields;
 30  
     private Logger logger;
 31  
 
 32  
     /**
 33  
      * Simple constructor.
 34  
      *
 35  
      * @param instance instance to operate on
 36  
      * @see #ReflectionFieldIterator(Object, String)
 37  
      */
 38  
     public ReflectionFieldIterator(Object instance) {
 39  12
         this(instance, null);
 40  12
     }
 41  
 
 42  
     /**
 43  
      * Advanced constructor.
 44  
      * <p/>
 45  
      * This constructor accepts a regular expression by which the field names are evaluated.<br/>
 46  
      * Only fields with matching names will be considered; if the <code>regularExpression</code>
 47  
      * parameter is null or does not compile to a pattern, all fields will be considered.
 48  
      * <p/>
 49  
      * If the instance passed to this constructor is null, no exception will be thrown, but the
 50  
      * {@link #hasNext()} method will return <code>false</code>.
 51  
      *
 52  
      * @param instance          instance to operate on
 53  
      * @param regularExpression regular expression pattern to match field names
 54  
      */
 55  24
     public ReflectionFieldIterator(Object instance, String regularExpression) {
 56  24
         logger = Logger.getLogger(getClass());
 57  24
         this.instance = instance;
 58  24
         if (instance != null) {
 59  12
             fields = instance.getClass().getFields();
 60  12
             if (regularExpression != null) {
 61  6
                 filterFieldNames(regularExpression);
 62  
             }
 63  
         } else {
 64  12
             if (logger.isDebugEnabled()) {
 65  0
                 logger.debug("The instance that was passed to the iterator is null.");
 66  
             }
 67  12
             fields = null;
 68  
         }
 69  24
     }
 70  
 
 71  
     private void filterFieldNames(String regularExpression) {
 72  6
         if (fields == null) {
 73  0
             return;
 74  
         }
 75  
         Pattern pattern;
 76  
         try {
 77  6
             pattern = new Perl5Compiler().compile(regularExpression);
 78  0
         } catch (MalformedPatternException e) {
 79  0
             if (logger.isInfoEnabled()) {
 80  0
                 logger.info("Regular expression did not compile to a pattern; all fields will be considered.", e);
 81  
             }
 82  0
             return;
 83  6
         }
 84  6
         Perl5Matcher matcher = new Perl5Matcher();
 85  6
         ArrayList filteredFieldList = new ArrayList(fields.length);
 86  6
         for (int i = 0; i < fields.length; i++) {
 87  0
             if (matcher.matches(fields[i].getName(), pattern)) {
 88  0
                 filteredFieldList.add(fields[i]);
 89  
             }
 90  
         }
 91  6
         fields = (Field[]) filteredFieldList.toArray(new Field[filteredFieldList.size()]);
 92  6
     }
 93  
 
 94  
     /**
 95  
      * Checks if more fields are available to iterate through.
 96  
      *
 97  
      * @return true if more fields are available
 98  
      */
 99  
     public boolean hasNext() {
 100  8
         if (fields == null) {
 101  2
             return false;
 102  
         }
 103  6
         return counter < fields.length;
 104  
     }
 105  
 
 106  
     /**
 107  
      * Retrieves the next available field value.
 108  
      *
 109  
      * @return next object in sequence
 110  
      * @throws NoSuchElementException if no more objects are available
 111  
      */
 112  
     public Object next() {
 113  2
         if (!hasNext()) {
 114  2
             throw new NoSuchElementException("No more elements available in iterator");
 115  
         }
 116  
         try {
 117  0
             fields[++counter].setAccessible(true);
 118  0
             return fields[counter].get(instance);
 119  0
         } catch (IllegalAccessException e) {
 120  0
             throw new IllegalAccessRuntimeException(e);
 121  
         }
 122  
     }
 123  
 
 124  
     /**
 125  
      * @deprecated Do not use this method, it is not implemented and throws a runtime exception
 126  
      */
 127  
     public void remove() {
 128  0
         throw new NotImplementedException("This iterator implementation does not support item removal");
 129  
     }
 130  
 
 131  
     /**
 132  
      * RuntimeException wrapper for IllegalAccessException.
 133  
      */
 134  
     public class IllegalAccessRuntimeException extends RuntimeWrapperException {
 135  
         /**
 136  
          * Constructor.
 137  
          * <p/>
 138  
          * This method simply calls its parent with an <code>IllegalAccessException</code>
 139  
          * instance and an appropriate message.
 140  
          *
 141  
          * @param exception IllegalAccessException to wrap
 142  
          */
 143  0
         public IllegalAccessRuntimeException(final IllegalAccessException exception) {
 144  0
             super("Could not access field through reflection", exception);
 145  0
         }
 146  
     }
 147  
 
 148  
     /**
 149  
      * Wraps any Throwable instance in a RuntimeException.
 150  
      */
 151  
     public class RuntimeWrapperException extends NestedRuntimeException {
 152  
 
 153  
         /**
 154  
          * Uses the message provided by the original throwable.
 155  
          *
 156  
          * @param throwable instance to wrap
 157  
          */
 158  0
         public RuntimeWrapperException(Throwable throwable) {
 159  0
             super(throwable.getMessage(), throwable);
 160  0
         }
 161  
 
 162  
         /**
 163  
          * Uses a specified messaged to pass to the parent constructor.
 164  
          *
 165  
          * @param message   message to associate
 166  
          * @param throwable instance to wrap
 167  
          */
 168  0
         public RuntimeWrapperException(String message, Throwable throwable) {
 169  0
             super(message, throwable);
 170  0
         }
 171  
     }
 172  
 }