View Javadoc
1   /*
2    *  Copyright 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   *  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  package org.jadira.bindings.core.general.marshaller;
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.ToMarshaller;
25  
26  /**
27   * Base class providing capability to perform marshalling of source object type
28   * to target. This class uses reflection.
29   * <p>
30   * The marshal method must either
31   * </p>
32   * <p>
33   * a) be instance scoped and defined as part of class S. It must accept no
34   * parameters and return a type of T. For example:
35   * </p>
36   * <p>
37   * {@code public String marshal()}
38   * </p>
39   * <p>
40   * b) be statically scoped. It must accept a single parameter of type S and
41   * return a type of T. For example:
42   * </p>
43   * <p>
44   * {@code public static String marshal(BoundType param)}
45   * </p>
46   * @param <S> Source type for the conversion
47   * @param <T> Source type
48   */
49  public class MethodToMarshaller<S, T> implements ToMarshaller<S, T> {
50  
51  	private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
52  	
53      private final Class<S> boundClass;
54  
55      private final Class<T> targetClass;
56      
57      private final MethodHandle marshalHandle;
58  
59      /**
60       * Create a new instance
61       * @param boundClass Bound class
62       * @param targetClass Destination class
63       * @param marshal Marshal instance method on the target class
64       */
65      public MethodToMarshaller(Class<S> boundClass, Class<T> targetClass, Method marshal) {
66          
67          if (marshal.getParameterTypes().length == 0 && Modifier.isStatic(marshal.getModifiers())) {
68              throw new IllegalStateException("marshal method must either be instance scope or define a single parameter");
69          } else if (marshal.getParameterTypes().length == 1 && (!Modifier.isStatic(marshal.getModifiers()))) {
70              throw new IllegalStateException("marshal method must either be instance scope or define a single parameter");
71          } else if (marshal.getParameterTypes().length >= 2) {
72              throw new IllegalStateException("marshal method must either be instance scope or define a single parameter");
73          }
74          
75          if (!targetClass.isAssignableFrom(marshal.getReturnType())) {
76              throw new IllegalStateException("marshal method must return an instance of target class");
77          }
78          if (!marshal.getDeclaringClass().isAssignableFrom(boundClass) && !Modifier.isStatic(marshal.getModifiers())) {
79              throw new IllegalStateException("marshal method must be defined as part of " + boundClass.getSimpleName());
80          }
81  
82          this.boundClass = boundClass;
83          this.targetClass = targetClass;
84  
85          try {
86  			this.marshalHandle = LOOKUP.unreflect(marshal);
87  		} catch (IllegalAccessException e) {
88  			throw new IllegalStateException("Method is not accessible" + marshal);
89  		}
90  
91      }
92  
93  	/**
94  	 * {@inheritDoc}
95  	 */
96  	/* @Override */
97      public T marshal(S object) {
98  
99          try {
100         	final T result = (T) marshalHandle.invoke(object);
101             return result;
102         } catch (Throwable ex) {
103         	if (ex.getCause() instanceof RuntimeException) {
104                 throw (RuntimeException) ex.getCause();
105             }
106             throw new BindingException(ex.getMessage(), ex.getCause());
107         }
108     }
109    
110 	/**
111 	 * {@inheritDoc}
112 	 */
113 	/* @Override */
114     public Class<S> getBoundClass() {
115         return boundClass;
116     }
117     
118 	/**
119 	 * {@inheritDoc}
120 	 */
121 	/* @Override */
122 	public Class<T> getTargetClass() {
123 		return targetClass;
124 	}
125 }