View Javadoc
1   /*
2    * Copyright 2007 Kasper B. Graversen
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.supercsv.util;
17  
18  import static org.supercsv.util.ReflectionUtils.GET_PREFIX;
19  import static org.supercsv.util.ReflectionUtils.SET_PREFIX;
20  
21  import java.lang.reflect.InvocationHandler;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Proxy;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  /**
28   * This is part of the internal implementation of Super CSV.
29   * <p>
30   * This class creates bean instances based on an interface. This allows you, given an interface for a bean (but no
31   * implementation), to generate a bean implementation on-the-fly. This instance can then be used for fetching and
32   * storing state. It assumes all get methods starts with "get" and all set methods start with "set" and takes only 1
33   * argument.
34   * 
35   * @author Kasper B. Graversen
36   * @author James Bassett
37   */
38  public final class BeanInterfaceProxy implements InvocationHandler {
39  	
40  	private final Map<String, Object> beanState = new HashMap<String, Object>();
41  	
42  	// no instantiation
43  	private BeanInterfaceProxy() {
44  	}
45  	
46  	/**
47  	 * Creates a proxy object which implements a given bean interface.
48  	 * 
49  	 * @param proxyInterface
50  	 *            the interface the the proxy will implement
51  	 * @param <T>
52  	 *            the proxy implementation type
53  	 * @return the proxy implementation
54  	 * @throws NullPointerException
55  	 *             if proxyInterface is null
56  	 */
57  	public static <T> T createProxy(final Class<T> proxyInterface) {
58  		if( proxyInterface == null ) {
59  			throw new NullPointerException("proxyInterface should not be null");
60  		}
61  		return proxyInterface.cast(Proxy.newProxyInstance(proxyInterface.getClassLoader(),
62  			new Class[] { proxyInterface }, new BeanInterfaceProxy()));
63  	}
64  	
65  	/**
66  	 * {@inheritDoc}
67  	 * <p>
68  	 * If a getter method is encountered then this method returns the stored value from the bean state (or null if the
69  	 * field has not been set).
70  	 * <p>
71  	 * If a setter method is encountered then the bean state is updated with the value of the first argument and the
72  	 * value is returned (to allow for method chaining)
73  	 * 
74  	 * @throws IllegalArgumentException
75  	 *             if the method is not a valid getter/setter
76  	 */
77  	public Object invoke(final Object proxy, final Method method, final Object[] args) {
78  		
79  		final String methodName = method.getName();
80  		
81  		if( methodName.startsWith(GET_PREFIX) ) {
82  			
83  			if( method.getParameterTypes().length > 0 ) {
84  				throw new IllegalArgumentException(String.format(
85  					"method %s.%s() should have no parameters to be a valid getter", method.getDeclaringClass()
86  						.getName(), methodName));
87  			}
88  			
89  			// simulate getter by retrieving value from bean state
90  			return beanState.get(methodName.substring(GET_PREFIX.length()));
91  			
92  		} else if( methodName.startsWith(SET_PREFIX) ) {
93  			
94  			if( args == null || args.length != 1 ) {
95  				throw new IllegalArgumentException(String.format(
96  					"method  %s.%s() should have exactly one parameter to be a valid setter", method
97  						.getDeclaringClass().getName(), methodName));
98  			}
99  			
100 			// simulate setter by storing value in bean state
101 			beanState.put(methodName.substring(SET_PREFIX.length()), args[0]);
102 			return proxy;
103 			
104 		} else {
105 			throw new IllegalArgumentException(String.format("method %s.%s() is not a valid getter/setter", method
106 				.getDeclaringClass().getName(), methodName));
107 		}
108 		
109 	}
110 }