View Javadoc
1   /*
2    *  Copyright 2010, 2011 Chris Pheby
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   *  distringibuted under the License is distringibuted 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  package org.jadira.bindings.core.general.unmarshaller;
17  
18  import java.lang.invoke.MethodHandle;
19  import java.lang.invoke.MethodHandles;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  
23  import org.jadira.bindings.core.api.BindingException;
24  import org.jadira.bindings.core.api.FromUnmarshaller;
25  
26  /**
27   * Binding that supports an unmarshal method. The
28   * unmarshal method must be statically scoped. It must accept a single parameter
29   * of type S and return a type of T. For example:
30   * <p>
31   * {@code public static BoundType unmarshal(String string)}
32   * </p>
33   * @param <S> Source type for the conversion
34   * @param <T> Target type
35   */
36  public final class MethodFromUnmarshaller<S, T> implements FromUnmarshaller<S, T> {
37  
38  	private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
39  	
40      private final MethodHandle unmarshalHandle;
41  
42      private final Class<S> boundClass;
43  
44      private final Class<T> targetClass;
45      
46      /**
47       * Create a new instance
48       * @param boundClass Bound class
49       * @param unmarshal Unmarshal method on the target class
50       */
51      public MethodFromUnmarshaller(Class<S> boundClass, Method unmarshal) {
52  
53          this.boundClass = boundClass;
54          
55          if (unmarshal.getParameterTypes().length != 1) {
56              throw new IllegalStateException("unmarshal method must define a single parameter");
57          }
58          if (!Modifier.isStatic(unmarshal.getModifiers())) {
59              throw new IllegalStateException("unmarshal method must be defined as static");
60          }
61          if (!boundClass.isAssignableFrom(unmarshal.getReturnType())) {
62              throw new IllegalStateException("unmarshal method must return " + boundClass.getSimpleName());
63          }
64  
65          try {
66  			this.unmarshalHandle = LOOKUP.unreflect(unmarshal);
67  		} catch (IllegalAccessException e) {
68  			throw new IllegalStateException("Method is not accessible" + unmarshal);
69  		}
70          
71          @SuppressWarnings("unchecked")
72          Class<T> myTarget = (Class<T>)unmarshal.getParameterTypes()[0];
73          this.targetClass = myTarget;
74      }
75  
76  	/**
77  	 * {@inheritDoc}
78  	 */
79  	/* @Override */
80      public S unmarshal(T object) {
81  
82      	if (object != null && !targetClass.isAssignableFrom(object.getClass())) {
83      		throw new IllegalArgumentException("Supplied object was not instance of target class");
84      	}
85      	
86          try {
87              return getBoundClass().cast(unmarshalHandle.invoke(object));
88          } catch (Throwable ex) {
89          	if (ex.getCause() instanceof RuntimeException) {
90                  throw (RuntimeException) ex.getCause();
91              }
92              throw new BindingException(ex.getMessage(), ex.getCause());
93          }
94      }
95      
96  	/**
97  	 * {@inheritDoc}
98  	 */
99  	/* @Override */
100     public Class<S> getBoundClass() {
101         return boundClass;
102     }
103     
104 	/**
105 	 * {@inheritDoc}
106 	 */
107 	/* @Override */
108     public Class<T> getTargetClass() {
109         return targetClass;
110     }
111 }