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.binding;
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.Binding;
24  import org.jadira.bindings.core.api.BindingException;
25  import org.jadira.bindings.core.general.marshaller.MethodToMarshaller;
26  
27  /**
28   * Binding that supports a marshal contract, and a unmarshal method. The
29   * unmarshal method must be statically scoped. It must accept a single parameter
30   * of type S and return a type of T. For example:
31   * <p>
32   * {@code public static BoundType unmarshal(String string)}
33   * </p>
34   * @param <S> Source type for the conversion
35   * @param <T> Target type
36   */
37  public final class MethodsBinding<S, T> extends MethodToMarshaller<S, T> implements Binding<S, T> {
38  
39  	private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
40  	
41      private final MethodHandle unmarshalHandle;
42  
43      /**
44       * Constructs a binding that supports a marshal method and an unmarshal method
45       * @param marshal The marshalling method
46       * @param unmarshal The unmarshalling method
47       * @param boundClass The source class for unmarshalling from
48       * @param targetClass The target class for marshalling to
49       */
50      public MethodsBinding(Method marshal, Method unmarshal, Class<S> boundClass, Class<T> targetClass) {
51          
52      	super(boundClass, targetClass, marshal);
53  
54          if (unmarshal.getParameterTypes().length != 1) {
55              throw new IllegalStateException("unmarshal method must define a single parameter");
56          }
57          if (!Modifier.isStatic(unmarshal.getModifiers())) {
58              throw new IllegalStateException("unmarshal method must be defined as static");
59          }
60  
61          if (unmarshal.getParameterTypes()[0] != targetClass) {
62              throw new IllegalStateException("unmarshal method must be parameterized by " + targetClass.getSimpleName());
63          }
64          if (!boundClass.isAssignableFrom(unmarshal.getReturnType())) {
65              throw new IllegalStateException("unmarshal method must return " + boundClass.getSimpleName());
66          }
67          
68          try {
69  			this.unmarshalHandle = LOOKUP.unreflect(unmarshal);
70  		} catch (IllegalAccessException e) {
71  			throw new IllegalStateException("Method is not accessible" + unmarshal);
72  		}
73      }
74  
75  	/**
76  	 * {@inheritDoc}
77  	 */
78  	/* @Override */
79      public S unmarshal(T string) {
80  
81          try {
82              return getBoundClass().cast(unmarshalHandle.invoke(string));
83          } catch (Throwable ex) {
84          	if (ex.getCause() instanceof RuntimeException) {
85                  throw (RuntimeException) ex.getCause();
86              }
87              throw new BindingException(ex.getMessage(), ex.getCause());
88          }
89      }
90  }