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 this(instance, null); 40 } 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 public ReflectionFieldIterator(Object instance, String regularExpression) { 56 logger = Logger.getLogger(getClass()); 57 this.instance = instance; 58 if (instance != null) { 59 fields = instance.getClass().getFields(); 60 if (regularExpression != null) { 61 filterFieldNames(regularExpression); 62 } 63 } else { 64 if (logger.isDebugEnabled()) { 65 logger.debug("The instance that was passed to the iterator is null."); 66 } 67 fields = null; 68 } 69 } 70 71 private void filterFieldNames(String regularExpression) { 72 if (fields == null) { 73 return; 74 } 75 Pattern pattern; 76 try { 77 pattern = new Perl5Compiler().compile(regularExpression); 78 } catch (MalformedPatternException e) { 79 if (logger.isInfoEnabled()) { 80 logger.info("Regular expression did not compile to a pattern; all fields will be considered.", e); 81 } 82 return; 83 } 84 Perl5Matcher matcher = new Perl5Matcher(); 85 ArrayList filteredFieldList = new ArrayList(fields.length); 86 for (int i = 0; i < fields.length; i++) { 87 if (matcher.matches(fields[i].getName(), pattern)) { 88 filteredFieldList.add(fields[i]); 89 } 90 } 91 fields = (Field[]) filteredFieldList.toArray(new Field[filteredFieldList.size()]); 92 } 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 if (fields == null) { 101 return false; 102 } 103 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 if (!hasNext()) { 114 throw new NoSuchElementException("No more elements available in iterator"); 115 } 116 try { 117 fields[++counter].setAccessible(true); 118 return fields[counter].get(instance); 119 } catch (IllegalAccessException e) { 120 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 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 public IllegalAccessRuntimeException(final IllegalAccessException exception) { 144 super("Could not access field through reflection", exception); 145 } 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 public RuntimeWrapperException(Throwable throwable) { 159 super(throwable.getMessage(), throwable); 160 } 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 public RuntimeWrapperException(String message, Throwable throwable) { 169 super(message, throwable); 170 } 171 } 172 }