View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * Portions of this file developed by Keith Visco after Jan 19 2005 are
44   * Copyright (C) 2005 Keith Visco. All Rights Reserverd.
45   *
46   * $Id$
47   */
48  package org.exolab.castor.xml.handlers;
49  
50  import java.lang.reflect.Array;
51  import java.lang.reflect.InvocationTargetException;
52  import java.lang.reflect.Method;
53  import java.lang.reflect.Modifier;
54  
55  import org.exolab.castor.mapping.FieldHandler;
56  import org.exolab.castor.mapping.ValidityException;
57  
58  /**
59   * A specialized FieldHandler for the XML Schema enumeration types.
60   *
61   * @author <a href="keith AT kvisco  DOT com">Keith Visco</a>
62   * @version $Revision$ $Date: 2006-04-13 06:47:36 -0600 (Thu, 13 Apr 2006) $
63   */
64  public class EnumFieldHandler implements FieldHandler {
65  
66      /** Used to find the method "valueOf" taking an argument of type String. */
67      private static final Class[] STRING_ARGS = new Class[] {String.class};
68      /** The Factory Method name. */
69      private static final String  METHOD_VALUEOF = "valueOf";
70      private static final String  METHOD_FROMVALUE = "fromValue";
71      private static final String  METHOD_VALUE = "value";
72      private static final String  METHOD_TOSTRING = "toString";
73  
74      /** The <code>valueOf(String)</code> method for the provided enumtype. */
75      private final Method _valueOf;
76      /** The field handler to which we delegate. */
77      private final FieldHandler _handler;
78      
79      //----------------/
80      //- Constructors -/
81      //----------------/
82  
83      /**
84       * Creates a new EnumFieldHandler with the given type and FieldHandler.
85       *
86       * @param enumType the Class type of the described field
87       * @param handler the FieldHandler to delegate to
88       */
89      public EnumFieldHandler(final Class enumType, final FieldHandler handler) {
90          this._handler = handler;
91          this._valueOf = getUnmarshallMethod(enumType);
92      } //-- EnumFieldHandler
93  
94      /**
95       * Reflectively finds the <code>valueOf(String)</code> method for the
96       * provided class type.
97       *
98       * @param type the Class for which to locate the valueOf method.
99       * @return the Method <code>valueOf(String)</code>
100      */
101     private Method getUnmarshallMethod(final Class type) {
102         if (type == null) {
103             String err = "The Class argument passed to the "
104                     + "constructor of EnumMarshalDescriptor cannot be null.";
105             throw new IllegalArgumentException(err);
106         }
107 
108         Method method = null;
109         
110         // try the fromValue method to support enums with value object
111         try {
112             method = type.getMethod(METHOD_FROMVALUE, STRING_ARGS);
113             return method;
114         } catch(NoSuchMethodException exception) {
115         	// do nothing, check valueOf
116         }
117         
118         // 
119         try {
120             method = type.getMethod(METHOD_VALUEOF, STRING_ARGS);
121         } catch (NoSuchMethodException nsme) {
122             String err = type.getName()
123                     + " does not contain one of the required methods public static "
124                     + type.getName() + " valueOf(String); " +
125                     		"or public static " + type.getName() +  ".fromvalue(String value)";
126             throw new IllegalArgumentException(err);
127         }
128 
129         if (!Modifier.isStatic(method.getModifiers())) {
130             String err = type.getName() + " public " + type.getName()
131                     + " valueOf(String); exists but is not static";
132             throw new IllegalArgumentException(err);
133         }
134         return method;
135     } 
136     
137     private Method getMarshallMethod(final Class type) {
138         if (type == null) {
139             String err = "The Class argument passed to the "
140                     + "constructor of EnumMarshalDescriptor cannot be null.";
141             throw new IllegalArgumentException(err);
142         }
143 
144         Method method = null;
145         
146         // try the value() method to support enums with value object
147         try {
148             method = type.getMethod(METHOD_VALUE, null);
149             return method;
150         } catch(NoSuchMethodException exception) {
151             // do nothing, use toString
152         }
153         
154         // 
155         try {
156             method = type.getMethod(METHOD_TOSTRING, null);
157         } catch (NoSuchMethodException nsme) {
158             String err = type.getName()
159                     + " does not contain one of the required methods value() or toString() ";
160             throw new IllegalArgumentException(err);
161         }
162 
163         return method;
164     } 
165 
166     //------------------/
167     //- Public Methods -/
168     //------------------/
169 
170     /**
171      * Returns the value of the field associated with this descriptor from the
172      * given target object.
173      *
174      * @param target the object to get the value from
175      * @return the value of the field associated with this descriptor from the
176      *         given target object.
177      * @throws IllegalStateException The Java object has changed and is no
178      *         longer supported by this handler, or the handler is not
179      *         compatible with the Java object
180      */
181     public Object getValue(final Object target) throws java.lang.IllegalStateException {
182         Object val = _handler.getValue(target);
183         if (val == null) {
184             return val;
185         }
186 
187         Object result = null;
188         if (val.getClass().isArray()) {
189            int size = Array.getLength(val);
190             String[] values = new String[size];
191 
192             for (int i = 0; i < size; i++) {
193                 Object obj = Array.get(val, i);
194                 try {
195                     values[i] = (String) getMarshallMethod(obj.getClass()).invoke(obj, null);
196                 } catch (Exception e) {
197                     throw new IllegalStateException(e.toString());
198                 }
199             }
200             result = values;
201         } else {
202             try {
203                 result = getMarshallMethod(val.getClass()).invoke(val, null);
204             } catch (Exception e) {
205                 throw new IllegalStateException(e.toString());
206             }
207         }
208         return result;
209     } //-- getValue
210 
211     /**
212      * Sets the value of the field associated with this descriptor.
213      *
214      * @param target the object in which to set the value
215      * @param value the value of the field
216      * @throws IllegalStateException The Java object has changed and is no
217      *         longer supported by this handler, or the handler is not
218      *         compatible with the Java object.
219      */
220     public void setValue(final Object target, final Object value)
221                                         throws java.lang.IllegalStateException {
222         Object obj = null;
223         if (value != null) {
224             Object[] args = new String[1];
225             args[0] = value.toString();
226             try {
227                 obj = _valueOf.invoke(null, args);
228             } catch (java.lang.reflect.InvocationTargetException ite) {
229                 Throwable toss = ite.getTargetException();
230                 throw new IllegalStateException(toss.toString());
231             } catch (java.lang.IllegalAccessException iae) {
232                 throw new IllegalStateException(iae.toString());
233             }
234         }
235         _handler.setValue(target, obj);
236     } //-- setValue
237 
238     /**
239      * Sets the value of the field to a default value -- for enum, no action
240      * needed.
241      *
242      * @param target The object.
243      */
244     public void resetValue(final Object target) {
245         // No action needed
246     }
247 
248     /**
249      * Checks the field validity. Returns successfully if the field can be
250      * stored, is valid, etc, throws an exception otherwise.
251      *
252      * @param object The object
253      * @throws ValidityException The field is invalid, is required and null, or
254      *         any other validity violation
255      * @throws IllegalStateException The Java object has changed and is no
256      *         longer supported by this handler, or the handler is not
257      *         compatiable with the Java object
258      */
259     public void checkValidity(final Object object) throws ValidityException, IllegalStateException {
260         //-- do nothing for now
261     } //-- checkValidity
262 
263     /**
264      * Creates a new instance of the object described by this field.
265      *
266      * @param parent The object for which the field is created
267      * @return A new instance of the field's value
268      * @throws IllegalStateException This field is a simple type and cannot be
269      *         instantiated
270      */
271     public Object newInstance(final Object parent) throws IllegalStateException {
272         return "";
273     } //-- newInstance
274 
275     /**
276      * Returns true if the given object is an XMLFieldHandler that is equivalent
277      * to the delegated handler. An equivalent XMLFieldHandler is an
278      * XMLFieldHandler that is an instances of the same class.
279      *
280      * @return true if the given object is an XMLFieldHandler that is equivalent
281      *         to this one.
282      */
283     public boolean equals(final Object obj) {
284         if (obj == null) {
285             return false;
286         }
287         if (obj == this) {
288             return true;
289         }
290         if (!(obj instanceof FieldHandler)) {
291             return false;
292         }
293         return (_handler.getClass().isInstance(obj) || getClass().isInstance(obj));
294     } //-- equals
295 
296 } //-- EnumFieldHandler