001/* 002 * Copyright 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 * distributed under the License is distributed 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.marshaller; 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.ToMarshaller; 025 026/** 027 * Base class providing capability to perform marshalling of source object type 028 * to target. This class uses reflection. 029 * <p> 030 * The marshal method must either 031 * </p> 032 * <p> 033 * a) be instance scoped and defined as part of class S. It must accept no 034 * parameters and return a type of T. For example: 035 * </p> 036 * <p> 037 * {@code public String marshal()} 038 * </p> 039 * <p> 040 * b) be statically scoped. It must accept a single parameter of type S and 041 * return a type of T. For example: 042 * </p> 043 * <p> 044 * {@code public static String marshal(BoundType param)} 045 * </p> 046 * @param <S> Source type for the conversion 047 * @param <T> Source type 048 */ 049public class MethodToMarshaller<S, T> implements ToMarshaller<S, T> { 050 051 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 052 053 private final Class<S> boundClass; 054 055 private final Class<T> targetClass; 056 057 private final MethodHandle marshalHandle; 058 059 /** 060 * Create a new instance 061 * @param boundClass Bound class 062 * @param targetClass Destination class 063 * @param marshal Marshal instance method on the target class 064 */ 065 public MethodToMarshaller(Class<S> boundClass, Class<T> targetClass, Method marshal) { 066 067 if (marshal.getParameterTypes().length == 0 && Modifier.isStatic(marshal.getModifiers())) { 068 throw new IllegalStateException("marshal method must either be instance scope or define a single parameter"); 069 } else if (marshal.getParameterTypes().length == 1 && (!Modifier.isStatic(marshal.getModifiers()))) { 070 throw new IllegalStateException("marshal method must either be instance scope or define a single parameter"); 071 } else if (marshal.getParameterTypes().length >= 2) { 072 throw new IllegalStateException("marshal method must either be instance scope or define a single parameter"); 073 } 074 075 if (!targetClass.isAssignableFrom(marshal.getReturnType())) { 076 throw new IllegalStateException("marshal method must return an instance of target class"); 077 } 078 if (!marshal.getDeclaringClass().isAssignableFrom(boundClass) && !Modifier.isStatic(marshal.getModifiers())) { 079 throw new IllegalStateException("marshal method must be defined as part of " + boundClass.getSimpleName()); 080 } 081 082 this.boundClass = boundClass; 083 this.targetClass = targetClass; 084 085 try { 086 this.marshalHandle = LOOKUP.unreflect(marshal); 087 } catch (IllegalAccessException e) { 088 throw new IllegalStateException("Method is not accessible" + marshal); 089 } 090 091 } 092 093 /** 094 * {@inheritDoc} 095 */ 096 /* @Override */ 097 public T marshal(S object) { 098 099 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}