001/* 002 * Copyright 2010, 2011 Chris Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distringibuted under the License is distringibuted on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.bindings.core.general.unmarshaller; 017 018import java.lang.invoke.MethodHandle; 019import java.lang.invoke.MethodHandles; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022 023import org.jadira.bindings.core.api.BindingException; 024import org.jadira.bindings.core.api.FromUnmarshaller; 025 026/** 027 * Binding that supports an unmarshal method. The 028 * unmarshal method must be statically scoped. It must accept a single parameter 029 * of type S and return a type of T. For example: 030 * <p> 031 * {@code public static BoundType unmarshal(String string)} 032 * </p> 033 * @param <S> Source type for the conversion 034 * @param <T> Target type 035 */ 036public final class MethodFromUnmarshaller<S, T> implements FromUnmarshaller<S, T> { 037 038 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 039 040 private final MethodHandle unmarshalHandle; 041 042 private final Class<S> boundClass; 043 044 private final Class<T> targetClass; 045 046 /** 047 * Create a new instance 048 * @param boundClass Bound class 049 * @param unmarshal Unmarshal method on the target class 050 */ 051 public MethodFromUnmarshaller(Class<S> boundClass, Method unmarshal) { 052 053 this.boundClass = boundClass; 054 055 if (unmarshal.getParameterTypes().length != 1) { 056 throw new IllegalStateException("unmarshal method must define a single parameter"); 057 } 058 if (!Modifier.isStatic(unmarshal.getModifiers())) { 059 throw new IllegalStateException("unmarshal method must be defined as static"); 060 } 061 if (!boundClass.isAssignableFrom(unmarshal.getReturnType())) { 062 throw new IllegalStateException("unmarshal method must return " + boundClass.getSimpleName()); 063 } 064 065 try { 066 this.unmarshalHandle = LOOKUP.unreflect(unmarshal); 067 } catch (IllegalAccessException e) { 068 throw new IllegalStateException("Method is not accessible" + unmarshal); 069 } 070 071 @SuppressWarnings("unchecked") 072 Class<T> myTarget = (Class<T>)unmarshal.getParameterTypes()[0]; 073 this.targetClass = myTarget; 074 } 075 076 /** 077 * {@inheritDoc} 078 */ 079 /* @Override */ 080 public S unmarshal(T object) { 081 082 if (object != null && !targetClass.isAssignableFrom(object.getClass())) { 083 throw new IllegalArgumentException("Supplied object was not instance of target class"); 084 } 085 086 try { 087 return getBoundClass().cast(unmarshalHandle.invoke(object)); 088 } catch (Throwable ex) { 089 if (ex.getCause() instanceof RuntimeException) { 090 throw (RuntimeException) ex.getCause(); 091 } 092 throw new BindingException(ex.getMessage(), ex.getCause()); 093 } 094 } 095 096 /** 097 * {@inheritDoc} 098 */ 099 /* @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}