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