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 }