View Javadoc

1   /*
2     Copyright (c) 2003, Vilmantas Baranauskas
3     All rights reserved.
4   
5     Redistribution and use in source and binary forms, with or without
6     modification, are permitted provided that the following conditions are met:
7       * Redistributions of source code must retain the above copyright notice,
8         this list of conditions and the following disclaimer.
9       * Redistributions in binary form must reproduce the above copyright notice,
10        this list of conditions and the following disclaimer in the documentation
11        and/or other materials provided with the distribution.
12      * The names of its contributors may not be used to endorse or promote
13        products derived from this software without specific prior written
14        permission.
15  
16    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26    POSSIBILITY OF SUCH DAMAGE.
27  */
28  package org.sovt.impl;
29  
30  import org.apache.commons.beanutils.MethodUtils;
31  import org.apache.commons.beanutils.PropertyUtils;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.sovt.*;
35  
36  import java.lang.reflect.InvocationTargetException;
37  import java.util.ArrayList;
38  import java.util.Iterator;
39  import java.util.List;
40  
41  /***
42   * This class is used to validate properties of JavaBean objects. It extracts
43   * property of JavaBean, forwards it to sub-validators/transformers/inspectors
44   * and then sets this property again if necessary (only for transform/inspect).
45   *
46   * @author Vilmantas Baranauskas (vilmantas_baranauskas@yahoo.com)
47   */
48  public class PropertySelector
49        implements Inspector, Transformer, Validator, Cloneable {
50     private static final Log log = LogFactory.getLog(PropertySelector.class);
51     //-- constants -------------------------------------------------------------
52     //-- variables -------------------------------------------------------------
53     /***
54      * List of validators/transformers/inspectors in this group.
55      */
56     private List elements = new ArrayList();
57  
58     /***
59      * Name of the property to work on.
60      */
61     private String propertyName;
62  
63     //-- constructors ----------------------------------------------------------
64     /***
65      * Creates instance of PropertySelector. The instance can be registered as
66      * any of: Inspector, Transformer, Validator.
67      */
68     public PropertySelector() {
69     }
70  
71     //-- methods ---------------------------------------------------------------
72     /***
73      * Adds inspector to the group.
74      *
75      * @param inspector Inspector to add.
76      */
77     public void addInspector(Inspector inspector) {
78        elements.add(inspector);
79     }
80  
81     /***
82      * Adds transformer to the group.
83      *
84      * @param transformer Transformer to add.
85      */
86     public void addTransformer(Transformer transformer) {
87        elements.add(transformer);
88     }
89  
90     /***
91      * Adds validator to the group.
92      *
93      * @param validator Validator to add.
94      */
95     public void addValidator(Validator validator) {
96        elements.add(validator);
97     }
98  
99     /***
100     * Performs a partly deep-cloning. New list of contained elements is created
101     * but elements themselfs are not cloned.
102     *
103     * @return clone of itself.
104     * @throws CloneNotSupportedException never.
105     */
106    public Object clone() throws CloneNotSupportedException {
107       PropertySelector inspector = (PropertySelector) super.clone();
108       inspector.elements = new ArrayList(elements);
109       return inspector;
110    }
111 
112    /***
113     * Extracts specified property from the given JavaBean.
114     *
115     * @param object Source object.
116     * @param propertyName Property to extract.
117     * @return extracted property or null if there is no such property.
118     */
119    private static Object extractProperty(Object object, String propertyName) {
120       try {
121          return PropertyUtils.getProperty(object, propertyName);
122       } catch (IllegalAccessException e) {
123          // ok, try next
124       } catch (InvocationTargetException e) {
125          // ok, try next
126       } catch (NoSuchMethodException e) {
127          // ok, try next
128       }
129 
130       // try get(propertyName) method
131       try {
132          return MethodUtils.invokeExactMethod(object, "get", propertyName);
133       } catch (NoSuchMethodException e) {
134          log.error("Cannot extract property [" + propertyName + "]", e);
135       } catch (IllegalAccessException e) {
136          log.error("Cannot extract property [" + propertyName + "]", e);
137       } catch (InvocationTargetException e) {
138          log.error("Cannot extract property [" + propertyName + "]", e);
139       }
140 
141       return null;
142    }
143 
144    /***
145     * Performs transformation and validation of given object. Transformers,
146     * validators and inspectors are invoked in the order they are registered.
147     *
148     * @param object Object to inspect.
149     * @param result Container for validation results.
150     * @return Transformed or completely new object which is also validated.
151     */
152    public Object inspect(Object object, ValidationResult result) {
153       result = new PrefixedValidationResult(result, propertyName);
154 
155       // work on the property instead of object
156       Object property = extractProperty(object, propertyName);
157       Object originalProperty = property;
158 
159       for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
160          Object element = iterator.next();
161          if (element instanceof Inspector) {
162             Inspector inspector = (Inspector) element;
163             property = inspector.inspect(property, result);
164          } else if (element instanceof Transformer) {
165             Transformer transformer = (Transformer) element;
166             property = transformer.transform(property);
167          } else if (element instanceof Validator) {
168             Validator validator = (Validator) element;
169             validator.validate(property, result);
170          }
171       }
172 
173       // Set the (possibly) modified property.
174       // Compare references because there is no need to set mutable objects.
175       if (property != originalProperty) {
176          setProperty(object, propertyName, property);
177       }
178       return object;
179    }
180 
181    /***
182     * Set the name of the property to work on. This can be specified as an
183     * attribute of a XML tag.
184     *
185     * @param property Name of the property to work on.
186     */
187    public void setName(String property) {
188       propertyName = property;
189    }
190 
191    /***
192     * Set specified property on the given object.
193     *
194     * @param object Destination object.
195     * @param propertyName Name of the property to set.
196     * @param property Property value.
197     */
198    private static void setProperty(
199          Object object,
200          String propertyName,
201          Object property
202          ) {
203       try {
204          PropertyUtils.setProperty(object, propertyName, property);
205          return;
206       } catch (IllegalAccessException e) {
207          // ok, try next
208       } catch (IllegalArgumentException e) {
209          // ok, try next
210       } catch (InvocationTargetException e) {
211          // ok, try next
212       } catch (NoSuchMethodException e) {
213          // ok, try next
214       }
215 
216       // try get(propertyName) method
217       try {
218          MethodUtils.invokeExactMethod(
219               object,
220               "set",
221                new Object[] {
222                   propertyName,
223                   property
224                }
225          );
226       } catch (NoSuchMethodException e) {
227          log.error("Cannot set property [" + propertyName + "]");
228       } catch (IllegalAccessException e) {
229          log.error("Cannot set property [" + propertyName + "]", e);
230       } catch (InvocationTargetException e) {
231          log.error("Cannot set property [" + propertyName + "]", e);
232       }
233    }
234 
235    /***
236     * Transforms given object. Given object can be modified or new object can
237     * be returned.
238     *
239     * @param object Object to transform.
240     * @return Transformed or completely new object.
241     */
242    public Object transform(Object object) {
243       // work on the property instead of object
244       Object property = extractProperty(object, propertyName);
245 
246       for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
247          Object element = iterator.next();
248          if (element instanceof Transformer) {
249             Transformer transformer = (Transformer) element;
250             property = transformer.transform(property);
251          }
252       }
253       // set the (possibly) modified property
254       setProperty(object, propertyName, property);
255       return object;
256    }
257 
258    /***
259     * Implementation should validate given object and use result parameter to
260     * store stores validation result.
261     *
262     * @param object Object to validate
263     * @param result Container for validation results.
264     */
265    public void validate(Object object, ValidationResult result) {
266       result = new PrefixedValidationResult(result, propertyName);
267 
268       // work on the property instead of object
269       Object property = extractProperty(object, propertyName);
270 
271       for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
272          Object element = iterator.next();
273          if (element instanceof Validator) {
274             Validator validator = (Validator) element;
275             validator.validate(property, result);
276          }
277       }
278    }
279 
280 }