View Javadoc

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         //Filters should decide whether they need params
115         if (filterProperties == null) filterProperties = new Properties();
116 
117         Iterator it = this.filters.iterator();
118 
119         //iterate through filters
120         while (it.hasNext()) {
121             ImageFilter filter = (ImageFilter) it.next();
122             if (filter instanceof ConfigurableImageFilter) {
123                 //cast to configurable
124                 ConfigurableImageFilter configurableImageFilter = (ConfigurableImageFilter) filter;
125                 Class filterClass = configurableImageFilter.getClass();
126 
127                 Properties filterLocal = configurableImageFilter.getFilterProperties();
128                 //for previously uninitialized filters
129                 if (filterLocal == null) filterLocal = new Properties();
130                 boolean hasChanged = false;
131 
132                 //iterate through properties
133                 Enumeration propertyNames = filterProperties.propertyNames();
134                 while (propertyNames.hasMoreElements()) {
135                     String propertyName = (String) propertyNames.nextElement();
136 
137                     //inspect filter to verify if property is supported.
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                 //update if property changes occured for this filter
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 }