Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ws/jaxme/xs/parser/impl/AttributeSetterImpl.java


1   /*
2    * Copyright 2003, 2004  The Apache Software Foundation
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15  
16   */
17  package org.apache.ws.jaxme.xs.parser.impl;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.lang.reflect.UndeclaredThrowableException;
24  
25  import org.apache.ws.jaxme.xs.XSParser;
26  import org.apache.ws.jaxme.xs.parser.*;
27  import org.xml.sax.SAXException;
28  
29  
30  /** <p>Default implementation of the {@link org.apache.ws.jaxme.xs.parser.AttributeSetter}
31   * interface.</p>
32   *
33   * @author <a href="mailto:joe@ispsoft.de">Jochen Wiedmann</a>
34   */
35  public class AttributeSetterImpl implements AttributeSetter {
36    static final Class[] ONE_STRING_CLASS = new Class[]{String.class};
37    private static final Class[] FOUR_STRING_CLASSES =
38      new Class[]{String.class, String.class, String.class, String.class};
39  
40    protected XSContext getData() {
41      XSContext result = XSParser.getRunningInstance().getContext();
42      if (result == null) {
43        throw new IllegalStateException("Parser data is not set.");
44      }
45      return result;
46    }
47  
48    /** <p>This method configures the bean <code>pBean</code> as follows:
49     * <ol>
50     *    <li>If the bean has a method
51     *      <code>setAttribute(String, String, String)</code>, it is invoked
52     *      with the following arguments:
53     *      <ul>
54     *        <li>The attributes namespace URI (empty string for the default
55     *          namespace),</li>
56     *        <li>the attributes local name,</li>
57     *        <li>and the property value</li>
58     *      </ul>
59     *    </li>
60     *    <li>Otherwise invokes its own method {@link #setProperty(Object, String, String, String)}.</li>
61     * </ol>
62     */
63    public void setAttribute(String pQName, String pNamespaceURI, String pLocalName, String pValue)
64        throws SAXException {
65      XsSAXParser handler = ((XsSAXParser) getData().getCurrentContentHandler());
66      if (handler == null) {
67        throw new IllegalStateException("Current XsSAXParser is null.");
68      }
69      Object bean = ((XsSAXParser) getData().getCurrentContentHandler()).getBean();
70      try {
71        Method m = bean.getClass().getMethod("setAttribute", FOUR_STRING_CLASSES);
72        if (Modifier.isPublic(m.getModifiers())) {
73          Object[] o = new Object[]{pQName, pNamespaceURI, pLocalName, pValue};
74          Object result = invokeMethod(bean, m, pQName, o);
75          if (!boolean.class.equals(m.getReturnType())  ||  ((Boolean) result).booleanValue()) {
76            return;
77          }
78        }
79      } catch (NoSuchMethodException e) {
80      }
81  
82      if (!setProperty(bean, pQName, pLocalName, pValue)) {
83        throw new IllegalStateException("Unknown attribute of " + bean.getClass().getName() + ": " + pQName);
84      }
85    }
86  
87  
88    /** <p>This method invokes the beans <code>pBean</code> method <code>pMethod</code>,
89     * setting the attribute <code>pName</code> to the value <code>pArgs</code>.</p>
90     */
91    protected Object invokeMethod(Object pBean, Method pMethod, String pName, Object[] pArgs) throws SAXException {
92      try {
93        return pMethod.invoke(pBean, pArgs);
94      } catch (InvocationTargetException e) {
95        Throwable t = e.getTargetException();
96        if (t instanceof SAXException) {
97          throw (SAXException) t;
98        } else if (t instanceof RuntimeException) {
99          throw (RuntimeException) t;
100       } else {
101         throw new UndeclaredThrowableException(t);
102       }
103     } catch (IllegalAccessException e) {
104       StringBuffer sb = new StringBuffer("Failed to invoke method ");
105       sb.append(pMethod.getName()).append(" of class ").append(pBean.getClass().getName());
106       sb.append(" with argument ");
107       for (int i = 0;  i < pArgs.length;  i++) {
108         if (i > 0) {
109           sb.append(", ");
110         }
111         sb.append(pArgs[i]);
112       }
113       sb.append(": ").append(e.getClass().getName()).append(", ").append(e.getMessage());
114       throw new IllegalStateException(sb.toString());
115     }
116   }
117 
118   private interface ParameterClass {
119     public Object matches(Class pClass);
120     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue,
121                         Method pMethod, Object pMethodObject, String pQName)
122       throws SAXException;
123   }
124 
125   private static class StringClass implements ParameterClass {
126     public Object matches(Class pClass) {
127       return String.class.equals(pClass) ? Boolean.TRUE : null;
128     }
129     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue,
130                         Method pMethod, Object pMethodObject, String pQName) throws SAXException {
131       pAttributeSetter.invokeMethod(pBean, pMethod, pQName, new Object[]{pValue});
132     }
133   }
134 
135   private static class ValueOfParameterClass implements ParameterClass {
136     public Object matches(Class pClass) {
137       try {
138         Method valueOfMethod = pClass.getMethod("valueOf", ONE_STRING_CLASS);
139         if (Modifier.isPublic(valueOfMethod.getModifiers())  &&  !void.class.equals(valueOfMethod.getReturnType())) {
140           return valueOfMethod;
141         }
142       } catch (NoSuchMethodException e) {
143       }
144       return null;
145     }
146     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue,
147                         Method pMethod, Object pMethodObject, String pQName) throws SAXException {
148       Method m = (Method) pMethodObject; 
149       Object o;
150       try {
151         o = m.invoke(null, new Object[]{pValue});
152       } catch (InvocationTargetException e) {
153         throw new IllegalArgumentException("Illegal argument for attribute '" + pQName + "': " + pValue +
154                                             "; " + e.getTargetException().getClass().getName() +
155                                             ", " + e.getTargetException().getMessage());
156       } catch (IllegalAccessException e) {
157         throw new IllegalStateException("Invalid access to method " + m.getName() + " of class " +
158                                          pBean.getClass() + ": IllegalAccessException, " + e.getMessage());
159       }
160       pAttributeSetter.invokeMethod(pBean, pMethod, pQName, new Object[]{o});
161     }
162   }
163 
164   private static class StringConstructorClass implements ParameterClass {
165     public Object matches(Class pClass) {
166       try {
167         Constructor con = pClass.getConstructor(ONE_STRING_CLASS);
168         if (Modifier.isPublic(con.getModifiers())) {
169           return con;
170         }
171       } catch (NoSuchMethodException e) {
172       }
173       return null;
174     }
175     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue,
176                         Method pMethod, Object pMethodObject, String pQName) throws SAXException {
177       Constructor con = (Constructor) pMethodObject; 
178       Object o;
179       try {
180         o = con.newInstance(new Object[]{pValue});
181       } catch (InvocationTargetException e) {
182         throw new IllegalArgumentException("Illegal argument for attribute '" + pQName + "': " + pValue +
183                                             "; " + e.getTargetException().getClass().getName() +
184                                             ", " + e.getTargetException().getMessage());
185       } catch (InstantiationException e) {
186         throw new IllegalStateException("Invalid access to constructor " + pBean.getClass().getName() +
187                                          "(): " + e.getClass().getName() + ", " + e.getMessage());
188       } catch (IllegalAccessException e) {
189         throw new IllegalStateException("Invalid access to constructor " + pBean.getClass().getName() +
190                                          "(): " + e.getClass().getName() + ", " + e.getMessage());
191       }
192       pAttributeSetter.invokeMethod(pBean, pMethod, pQName, new Object[]{o});
193     }
194   }
195 
196   private static class PrimitiveParameterClass extends StringConstructorClass {
197     private final Class primitiveClass;
198     private final Class nonPrimitiveClass;
199     private final Constructor stringConstructor;
200     private PrimitiveParameterClass(Class pPrimitiveClass, Class pNonPrimitiveClass) {
201       primitiveClass = pPrimitiveClass;
202       nonPrimitiveClass = pNonPrimitiveClass;
203       try {
204         stringConstructor = pNonPrimitiveClass.getConstructor(ONE_STRING_CLASS);
205       } catch (NoSuchMethodException e) {
206         throw new IllegalStateException("The primitive class " + pNonPrimitiveClass.getName() +
207                                          " doesn't have a string valued constructor!");
208       }
209     }
210     public Object matches(Class pClass) {
211       return (primitiveClass.equals(pClass) || nonPrimitiveClass.equals(pClass)) ? stringConstructor : null;
212     }
213   }
214 
215   private static class CharacterClass implements ParameterClass {
216     public Object matches(Class pClass) {
217       return (Character.TYPE.equals(pClass)  ||  Character.class.equals(pClass)) ? Boolean.TRUE : null;
218     }
219 
220     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue, Method pMethod, Object pMethodObject, String pQName) throws SAXException {
221       if (pValue.length() != 1) {
222         throw new IllegalArgumentException("Invalid value for '" + pQName +"': " + pValue +
223                                             "; must have exactly one character.");
224       }
225       pAttributeSetter.invokeMethod(pBean, pMethod, pQName, new Object[]{new Character(pValue.charAt(0))});
226     }
227   }
228 
229   private static class BooleanClass implements ParameterClass {
230     public Object matches(Class pClass) {
231       return (Boolean.TYPE.equals(pClass)  ||  Boolean.class.equals(pClass)) ? Boolean.TRUE : null;
232     }
233 
234     public void invoke(AttributeSetterImpl pAttributeSetter, Object pBean, String pValue, Method pMethod, Object pMethodObject, String pQName) throws SAXException {
235       Boolean b = ("true".equals(pValue) || "1".equals(pValue)) ? Boolean.TRUE : Boolean.FALSE;
236       pAttributeSetter.invokeMethod(pBean, pMethod, pQName, new Object[]{b});
237     }
238   }
239 
240   private static final ParameterClass[] knownClasses = new ParameterClass[]{
241     new BooleanClass(),
242     new StringClass(),
243     new ValueOfParameterClass(),
244     new StringConstructorClass(),
245     new CharacterClass(),
246     new PrimitiveParameterClass(long.class, Long.class),
247     new PrimitiveParameterClass(int.class, Integer.class),
248     new PrimitiveParameterClass(short.class, Short.class),
249     new PrimitiveParameterClass(byte.class, Byte.class),
250     new PrimitiveParameterClass(double.class, Double.class),
251     new PrimitiveParameterClass(float.class, Float.class),
252     new CharacterClass(),
253   };
254 
255   /** <p>This method is invoked from within {@link #setAttribute(String, String, String, String)}.
256    * It configures the bean <code>pBean</code> as follows;
257    * <ol>
258    *    <li>If the bean has a method <code>setProperty(String)</code>
259    *      this method is invoked with the attribute value.</li>
260    *   <li>If the bean has a method <code>setProperty(T)</code>, and
261    *     the class <code>T</code> has either of a method
262    *     <code>public static T valueOf(String)</code> or a constructor
263    *     <code>public T(String)</code> (in that order), then the method
264    *     <code>setProperty(T)</code> is invoked with the value obtained
265    *     by an invocation of the method <code>valueOf()</code>, or
266    *     the constructor, respectively. Note, that this applies in
267    *     particular to the classes {@link Long}, {@link Integer},
268    *     {@link Short}, {@link Byte}, {@link Double}, {@link Float},
269    *     <code>java.math.BigInteger</code>, <code>java.math.BigDecimal</code>,
270    *     {@link java.io.File}, and {@link java.lang.StringBuffer}.</li>
271    *    <li>If the bean has a method <code>setProperty(boolean)</code>,
272    *      the method will be invoked with the value <i>true</i>
273    *      (the value specified in the XML file is either of
274    *      <code>true</code>, or <code>1</code>, otherwise with the
275    *      value <code>false</code>.</li>
276    *   <li>If the bean has a method <code>setProperty(char)</code>,
277    *     or <code>setProperty(Character)</code>, the method will be
278    *     invoked with the first character of the value specified in
279    *     the XML file. If the value contains zero or multiple characters,
280    *     an {@link IllegalArgumentException} is thrown.</li>
281    *   <li>If the bean has either of the following methods, in that order:
282    *     <ul>
283    *       <li><code>setProperty(long)</code></li>
284    *       <li><code>setProperty(int)</code></li>
285    *       <li><code>setProperty(short)</code></li>
286    *       <li><code>setProperty(byte)</code></li>
287    *       <li><code>setProperty(double)</code></li>
288    *       <li><code>setProperty(float)</code></li>
289    *     </ul>
290    *     then the property value is converted into the respective type
291    *     and the method is invoked. An {@link IllegalArgumentException}
292    *     is thrown, if the conversion fails.</li>
293    *   <li>If the bean has a method <code>java.lang.Class</code>, the
294    *     <code>XsSAXParser</code> will interpret the value given in the
295    *     XML file as a Java class name and load the named class from its
296    *     class loader. If the class cannot be loaded, it will also try
297    *     to use the current threads context class loader. An
298    *     exception is thrown, if neither of the class loaders can
299    *     load the class.</li>
300    * </ol>
301    * </p>
302    *
303    * @return True, if a method for setting the property was found. Otherwise
304    *   false.
305    */
306   protected boolean setProperty(Object pBean, String pQName, String pName, String pValue)
307       throws SAXException {
308     Class c = pBean.getClass();
309     String s = "set" + Character.toUpperCase(pName.charAt(0)) + pName.substring(1);
310     int parameterClassNum = knownClasses.length;
311     Method[] methods = c.getMethods();
312     Method method = null;
313     Object methodObject = null;
314     for (int i = 0;  i < methods.length;  i++) {
315       Method m = methods[i];
316       if (!s.equals(m.getName())  ||  !Modifier.isPublic(m.getModifiers())) {
317         continue;
318       }
319 
320       Class[] params = m.getParameterTypes();
321       if (params.length != 1) {
322         continue;
323       }
324 
325       Class paramsClass = params[0];
326       for (int j = 0;  j < parameterClassNum;  j++) {
327         ParameterClass parameterClass = knownClasses[j];
328         Object o = parameterClass.matches(paramsClass);
329         if (o != null) {
330           parameterClassNum = j;
331           method = m;
332           methodObject = o;
333           break;
334         }
335       }
336     }
337 
338     if (method == null) {
339       return false;
340     } else {
341       knownClasses[parameterClassNum].invoke(this, pBean, pValue, method, methodObject, pQName);
342       return true;
343     }
344   }
345 }