1 package org.jmage.filterchain;
2
3 import org.apache.log4j.Logger;
4 import org.jmage.filter.ConfigurableImageFilter;
5 import org.jmage.filter.FilterException;
6 import org.jmage.filter.ImageFilter;
7 import org.jmage.filter.TwinResourceImageFilter;
8
9 import javax.media.jai.PlanarImage;
10 import java.lang.reflect.Field;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.UndeclaredThrowableException;
13 import java.util.*;
14
15 /***
16 * A FilterChain processes an image by passing it through a preconfigured, ordered set of filters.
17 */
18 public class FilterChain {
19 protected final String name;
20 protected List filters;
21
22 protected static Logger log = Logger.getLogger(FilterChain.class.getName());
23 private static final String FILTER_REMOVE_ERROR = "unable to remove filter from FilterChain, cannot find this filter.";
24 private static final String UNSUPPORTED_PROPERTY = "unable to configure filter with unsupported property: ";
25 private static final String UNSUPPORTED_VALUE = "unable to configure filter with unsupported value: ";
26 private static final String PROPERTY_CONFIG = "configured filter with property: ";
27
28 /***
29 * Use this to construct an empty FilterChain. FilterChains need a unique name.
30 */
31 public FilterChain(String name) {
32 filters = new ArrayList();
33 this.name = name;
34 }
35
36 /***
37 * Gets the FilterChain's unique name.
38 *
39 * @return the unique name
40 */
41 public String getName() {
42 return this.name;
43 }
44
45 /***
46 * Add a filter to the FilterChain's end.
47 *
48 * @param filter the filter
49 * @param filterProperties the filter's properties
50 * @throws org.jmage.filter.FilterException
51 * if the filter can't be properly initialized
52 */
53 public void addFilter(ConfigurableImageFilter filter, Properties filterProperties) throws FilterException {
54 filter.initialize(filterProperties);
55 this.filters.add(filter);
56 }
57
58 /***
59 * Add a filter to the FilterChain at the given position.
60 *
61 * @param filter the filter
62 * @param filterProperties the filter's properties
63 * @param position the position in the filter filterchain
64 * @throws FilterException if the filter can't be properly initialized
65 * @throws IndexOutOfBoundsException if the FilterChain can't add the filter at the position.
66 */
67 public void addFilter(ConfigurableImageFilter filter, Properties filterProperties, int position) throws FilterException, IndexOutOfBoundsException {
68 filter.initialize(filterProperties);
69 this.filters.add(position, filter);
70 }
71
72 /***
73 * Add a filter to the FilterChain at the given position.
74 *
75 * @param filter the filter
76 * @param position the position in the filter filterchain
77 * @throws IndexOutOfBoundsException if the FilterChain can't add the filter at the position.
78 */
79 public void addFilter(ImageFilter filter, int position) throws IndexOutOfBoundsException {
80 this.filters.add(position, filter);
81 }
82
83 /***
84 * Add a filter to the FilterChain at the end.
85 *
86 * @param filter the filter
87 */
88 public void addFilter(ImageFilter filter) {
89 this.filters.add(filter);
90 }
91
92 /***
93 * Removes a filter from a FilterChain.
94 *
95 * @param filter the filter
96 */
97 public void removeFilter(ImageFilter filter) throws FilterException {
98 if (this.filters.contains(filter)) {
99 this.filters.remove(filter);
100 } else {
101 throw new FilterException(FILTER_REMOVE_ERROR);
102 }
103 }
104
105 /***
106 * Iterates through all configurable image filters in the filter filterchain and applies
107 * additional filterProperties to them. Filters must declare public constants with the
108 * property name to be able to for this to work.
109 *
110 * @param filterProperties
111 * @throws FilterException
112 */
113 public void updateConfigurableFilters(Properties filterProperties) throws FilterException {
114
115 if (filterProperties == null) filterProperties = new Properties();
116
117 Iterator it = this.filters.iterator();
118
119
120 while (it.hasNext()) {
121 ImageFilter filter = (ImageFilter) it.next();
122 if (filter instanceof ConfigurableImageFilter) {
123
124 ConfigurableImageFilter configurableImageFilter = (ConfigurableImageFilter) filter;
125 Class filterClass = configurableImageFilter.getClass();
126
127 Properties filterLocal = configurableImageFilter.getFilterProperties();
128
129 if (filterLocal == null) filterLocal = new Properties();
130 boolean hasChanged = false;
131
132
133 Enumeration propertyNames = filterProperties.propertyNames();
134 while (propertyNames.hasMoreElements()) {
135 String propertyName = (String) propertyNames.nextElement();
136
137
138 try {
139 Field propertyConstant = filterClass.getField(propertyName);
140 filterLocal.setProperty(propertyName, filterProperties.getProperty(propertyName));
141 if (log.isDebugEnabled()) log.debug(PROPERTY_CONFIG + filter + " " + propertyName);
142 hasChanged = true;
143 } catch (NoSuchFieldException e) {
144 if (log.isDebugEnabled()) log.debug(UNSUPPORTED_PROPERTY + filter + " " + propertyName);
145 } catch (UndeclaredThrowableException e) {
146 if (log.isDebugEnabled()) log.debug(UNSUPPORTED_VALUE + filter + " " + propertyName);
147 } catch (Exception e) {
148 if (log.isDebugEnabled()) log.debug(UNSUPPORTED_PROPERTY + filter + " " + propertyName);
149 }
150 }
151
152
153 if (hasChanged || filter instanceof TwinResourceImageFilter) {
154 configurableImageFilter.initialize(filterLocal);
155 }
156 }
157 }
158 }
159
160 /***
161 * Filters the image by passing it through all filters in the filterchain.
162 *
163 * @param image the image
164 * @return the filtered image
165 * @throws FilterException
166 */
167 public PlanarImage filter(PlanarImage image) throws FilterException {
168 for (Iterator it = filters.iterator(); it.hasNext();) {
169 ImageFilter filter = (ImageFilter) it.next();
170 image = filter.filter(image);
171 }
172 if (log.isInfoEnabled()) log.info(" filtered image in filterchain: " + this.getName());
173 return image;
174 }
175
176 /***
177 * Override equals to check for unique name
178 *
179 * @param o the FilterChain
180 * @return true | false
181 */
182 public boolean equals(Object o) {
183 if (this == o) return true;
184 if (!(o instanceof FilterChain)) return false;
185
186 final FilterChain filterChain = (FilterChain) o;
187
188 if (!name.equals(filterChain.name)) return false;
189
190 return true;
191 }
192
193 /***
194 * Override hashCode to generate for "name" uniqueness
195 *
196 * @return the hashcode
197 */
198 public int hashCode() {
199 return name.hashCode();
200 }
201 }