View Javadoc

1   /*
2    * Common Library for TBAS Softwares
3    * Language: Java
4    *
5    * All Rights Reserved.
6    * (c) Copyright 2002 by Tsukuba Bunko.
7    *
8    * $Id: GenericListener.java,v 1.1 2005/07/11 12:49:19 ppoi Exp $
9    */
10  package tsukuba_bunko.util;
11  
12  import	java.lang.reflect.Method;
13  import	java.lang.reflect.InvocationHandler;
14  import	java.lang.reflect.InvocationTargetException;
15  import	java.lang.reflect.Proxy;
16  
17  
18  /***
19   * イベントソースとイベントハンドラを接続するための枠組みを提供します. この枠組みを利用するためには,
20   * Dynamic Proxy Class API を VM がサポートしている必要があります.
21   * @author	$Author: ppoi $
22   */
23  public class GenericListener	{
24  
25  	/***
26  	 * <code>GenericListener</code> はインスタンスを生成できません。
27  	 */
28  	private GenericListener(){;}
29  
30  
31  	/***
32  	 * イベントソースとイベントハンドラを接続します。このメソッドで登録した場合、イベントハンドラはイベントソースと同じスレッドで実行されます。
33  	 * @param	eventSource		イベントソース
34  	 * @param	eventTarget		イベントハンドラ
35  	 * @param	listenerClass	イベントソースに本来接続されるリスナクラス
36  	 * @param	sourceMethod	イベントソースで呼び出されるメソッド名
37  	 * @param	targetMethod	イベントハンドラで実際にイベントを処理するメソッド名
38  	 * @return	イベントソースとイベントハンドラが接続された場合 <code>true</code>
39  	 */
40  	public static boolean connect( Object eventSource, Object eventTarget, Class listenerClass, String sourceMethod, String targetMethod )
41  	{
42  		return connect( eventSource, eventTarget, listenerClass, sourceMethod, targetMethod, false );
43  	}
44  
45  	/***
46  	 * イベントソースとイベントハンドラを接続します。
47  	 * 新しいスレッドを起動してイベントハンドラを実行する場合、戻り値を返すことは出来ません。また、例外の発生は通知されません。
48  	 * @param	eventSource		イベントソース
49  	 * @param	eventTarget		イベントハンドラ
50  	 * @param	listenerClass	イベントソースに本来接続されるリスナクラス
51  	 * @param	sourceMethod	イベントソースで呼び出されるメソッド名
52  	 * @param	targetMethod	イベントハンドラで実際にイベントを処理するメソッド名
53  	 * @param	newThread	新しいスレッドを起動してイベントハンドラを実行する場合 <code>true</code>、それ以外の場合 <code>false</code>
54  	 * @return	イベントソースとイベントハンドラが接続された場合 <code>true</code>
55  	 */
56  	public static boolean connect( Object eventSource, Object eventTarget, Class listenerClass, String sourceMethod, String targetMethod, boolean newThread )
57  	{
58  		try	{
59  		//	"add" メソッドの取得.
60  		Method	addMethod = eventSource.getClass().getMethod( ("add" + identifierOf(listenerClass)), new Class[]{listenerClass} );
61  		if( addMethod == null )	{
62  			//	エラー
63  			return false;
64  		}
65  
66  		//	リスナのインスタンスの取得.
67  		Method	sourceMethodObject = getListenerMethod( listenerClass, sourceMethod );
68  		Method	targetMethodObject = getTargetMethod( eventTarget.getClass(), targetMethod, sourceMethodObject.getParameterTypes() );
69  		Object	listenerImple = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{listenerClass}, new InvocHandler(eventTarget, targetMethodObject, sourceMethodObject, newThread) );
70  
71  		addMethod.invoke( eventSource, new Object[]{listenerImple} );
72  		}
73  		catch( Exception e )
74  		{
75  			e.printStackTrace();
76  			return false;
77  		}
78  
79  		return true;
80  	}
81  
82  	/***
83  	 * 指定されたクラスの Identifier を取得します. ここでの Identidier はクラス宣言の際に
84  	 *用いられるクラス名のことを指します.
85  	 * @param	c	Identifier を取得するクラス
86  	 * @return	Identifier
87  	 */
88  	private static String identifierOf( Class c )
89  	{
90  		String	className = c.getName();
91  		return className.substring( className.lastIndexOf('.') + 1, className.length() );
92  	}
93  
94  	/***
95  	 * イベントソースで呼び出されるメソッドを取得します.
96  	 * @param	listenerClass	リスナクラス
97  	 * @param	methodName		メソッド名
98  	 */
99  	private static Method getListenerMethod( Class listenerClass, String methodName )
100 		throws NoSuchMethodException
101 	{
102 		Method	methods[] = listenerClass.getMethods();
103 		for( int i = methods.length - 1; i >= 0; --i )	{
104 			if( methodName.equals(methods[i].getName()) )	{
105 				return methods[i];
106 			}
107 		}
108 		throw new NoSuchMethodException( "No such method \"" + methodName + "\" in \"" + listenerClass.getName() + "\"." );
109 	}
110 
111 	/***
112 	 * 指定されたクラスにおいて指定された名前で定義されたメソッドを取得します.
113 	 * @param	c	メソッドが定義されているクラス
114 	 * @param	methodName	メソッド名
115 	 * @param	parameterTypes	仮引数の型リスト
116 	 */
117 	private static Method getTargetMethod( Class c, String methodName, Class[] parameterTypes )
118 		throws NoSuchMethodException
119 	{
120 		Method	method = null;
121 		try	{
122 			method = c.getDeclaredMethod( methodName, parameterTypes );
123 		}
124 		catch( NoSuchMethodException nsme )	{
125 			method = c.getDeclaredMethod( methodName, null );
126 		}
127 
128 		try	{
129 			method.setAccessible( true );
130 		}
131 		catch( SecurityException se )	{
132 			//	ignore.
133 		}
134 		return method;
135 	}
136 }
137 
138 
139 /***
140  * <code>InvocationHandler</code> の実装です.
141  */
142 class InvocHandler	implements InvocationHandler	{
143 
144 	/***
145 	 * 処理を実際に行うクラス
146 	 */
147 	private Object	_target =null;
148 
149 	/***
150 	 * 処理を実際に行うメソッド
151 	 */
152 	private Method	_targetMethod = null;
153 
154 	/***
155 	 * イベントソースによって呼び出されるメソッド
156 	 */
157 	private Method	_sourceMethod = null;
158 
159 	/***
160 	 * 別スレッドを起動するかどうか
161 	 */
162 	private boolean	_newThread = false;
163 
164 
165 	/***
166 	 * <code>InvocHandler</code> のインスタンスを生成します.
167 	 * @param	target	実際のイベントハンドラ
168 	 * @param	targetMethod	実際に処理を行うメソッド
169 	 * @param	sourceMethod	イベントソースによって呼び出されるメソッド
170 	 */
171 	InvocHandler( Object target, Method targetMethod, Method sourceMethod, boolean newThread )
172 	{
173 		_target = target;
174 		_targetMethod = targetMethod;
175 		_sourceMethod = sourceMethod;
176 		_newThread = newThread;
177 	}
178 
179 
180 //
181 //	InvocationHandler の実装
182 //
183 	/***
184 	 * Processes a method invocation on a proxy instance and returns the result. This method
185 	 * will be invoked on an invocation handler when a method is invoked on a proxy instance
186 	 * that it is associated with.
187 	 * @param	proxy	the proxy instance that the method was invoked on
188 	 * @param	meth	the Method instance corresponding to the interface method invoked on
189 	 * the proxy instance. The declaring class of the Method object will be the interface that
190 	 * the method was declared in, which may be a superinterface of the proxy interface that
191 	 * the proxy class inherits the method through.
192 	 * @param	args	an array of objects containing the values of the arguments passed in
193 	 * the method invocation on the proxy instance, or null if interface method takes no arguments.
194 	 * Arguments of primitive types are wrapped in instances of the appropriate primitive wrapper
195 	 * class, such as <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
196 	 * @return	the value to return from the method invocation on the proxy instance. If the
197 	 * declared return type of the interface method is a primitive type, then the value returned
198 	 * by this method must be an instance of the corresponding primitive wrapper class; otherwise,
199 	 * it must be a type assignable to the declared return type. If the value returned by this
200 	 * method is <code>null</code> and the interface method's return type is primitive, then a
201 	 * <code>NullPointerException<code> will be thrown by the method invocation on the proxy
202 	 * instance. If the value returned by this method is otherwise not compatible with the
203 	 * interface method's declared return type as described above, a <code>ClassCastException</code>
204 	 * will be thrown by the method invocation on the proxy instance
205 	 */
206 	public Object invoke( Object proxy, Method meth, Object[] args )
207 		throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
208 	{
209 		if( !_sourceMethod.equals(meth) )	{
210 			return null;
211 		}
212 
213 		if( _targetMethod.getParameterTypes().length == 0 )	{
214 			args = null;
215 		}
216 
217 		if( _newThread )	{
218 			new Thread( new InvokerRunner(_target, _targetMethod, args) ).start();
219 			return null;
220 		}
221 		else	{
222 			return _targetMethod.invoke( _target, args );
223 		}
224 	}
225 }
226 
227 class InvokerRunner implements Runnable	{
228 
229 	protected Object	_target = null;
230 
231 	protected Method	_method = null;
232 
233 	protected Object[]	_args = null;
234 
235 
236 	InvokerRunner( Object target, Method method, Object[] args )
237 	{
238 		super();
239 		_target = target;
240 		_method = method;
241 		_args = args;
242 	}
243 
244 
245 	public void run()
246 	{
247 		try	{
248 			_method.invoke( _target, _args );
249 		}
250 		catch( Exception e )	{
251 			//ignore.
252 		}
253 	}
254 }