%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
tsukuba_bunko.util.GenericListener |
|
|
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 | 0 | 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 | 0 | 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, class="keyword">boolean newThread ) |
|
57 | { |
|
58 | try { |
|
59 | // "add" メソッドの取得. |
|
60 | 0 | Method addMethod = eventSource.getClass().getMethod( ("add" + identifierOf(listenerClass)), new Class[]{listenerClass} ); |
61 | 0 | if( addMethod == null ) { |
62 | // エラー |
|
63 | 0 | return false; |
64 | } |
|
65 | ||
66 | // リスナのインスタンスの取得. |
|
67 | 0 | Method sourceMethodObject = getListenerMethod( listenerClass, sourceMethod ); |
68 | 0 | Method targetMethodObject = getTargetMethod( eventTarget.getClass(), targetMethod, sourceMethodObject.getParameterTypes() ); |
69 | 0 | Object listenerImple = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{listenerClass}, class="keyword">new InvocHandler(eventTarget, targetMethodObject, sourceMethodObject, class="keyword">newThread) ); |
70 | ||
71 | 0 | addMethod.invoke( eventSource, new Object[]{listenerImple} ); |
72 | } |
|
73 | 0 | catch( Exception e ) |
74 | { |
|
75 | 0 | e.printStackTrace(); |
76 | 0 | return false; |
77 | 0 | } |
78 | ||
79 | 0 | 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 | 0 | String className = c.getName(); |
91 | 0 | 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 | 0 | Method methods[] = listenerClass.getMethods(); |
103 | 0 | for( int i = methods.length - 1; i >= 0; --i ) { |
104 | 0 | if( methodName.equals(methods[i].getName()) ) { |
105 | 0 | return methods[i]; |
106 | } |
|
107 | } |
|
108 | 0 | 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 | 0 | Method method = null; |
121 | try { |
|
122 | 0 | method = c.getDeclaredMethod( methodName, parameterTypes ); |
123 | } |
|
124 | 0 | catch( NoSuchMethodException nsme ) { |
125 | 0 | method = c.getDeclaredMethod( methodName, null ); |
126 | 0 | } |
127 | ||
128 | try { |
|
129 | 0 | method.setAccessible( true ); |
130 | } |
|
131 | 0 | catch( SecurityException se ) { |
132 | // ignore. |
|
133 | 0 | } |
134 | 0 | 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( class="keyword">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 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |