forked from exercism/java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReflectionProxy.java
More file actions
488 lines (452 loc) · 16.5 KB
/
Copy pathReflectionProxy.java
File metadata and controls
488 lines (452 loc) · 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import static java.lang.Class.forName;
public abstract class ReflectionProxy {
/**
* An instance of the target class (if found)
*/
private final Object target;
/**
* A constructor to instantiate the target class with parameters
* @param args An array of parameters matching the constructor from the target class
*/
protected ReflectionProxy(Object... args) {
this.target = instantiateTarget(args);
}
/**
* Abstract method that defines the fully qualified name of the target class
* @return The fully qualified name of the target class
*/
public abstract String getTargetClassName();
/**
* Getter for the target instance
* @return The target instance
*/
public Object getTarget() {
return target;
}
/**
* Gets the target class
* @return The target class if it exists, null otherwise
*/
public Class<?> getTargetClass() {
try {
return forName(this.getTargetClassName());
} catch (ClassNotFoundException e) {
return null;
}
}
/**
* Checks if the target class has a specific method
* @param name The name of the method to find
* @param parameterTypes The list of parameter types
* @return True if the method is found, false otherwise
*/
public boolean hasMethod(String name, Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Method m = targetClass.getDeclaredMethod(name, parameterTypes);
return m != null;
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Checks if a method from the target class is public
* @param name The name of the method
* @param parameterTypes A list of method parameters
* @return True if the method exists and is public, false otherwise
*/
public boolean isMethodPublic(String name, Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Method m = targetClass.getDeclaredMethod(name, parameterTypes);
return Modifier.isPublic(m.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Checks if a method from the target class returns the correct type
* @param returnType The type of return value
* @param name The name of the method
* @param parameterTypes The list of method parameters
* @return
*/
public boolean isMethodReturnType(Class<?> returnType, String name, Class<?>... parameterTypes) {
return isMethodReturnType(returnType, null, name, parameterTypes);
}
/**
* Invokes a method from the target instance
* @param methodName The name of the method
* @param returnType The class representing the expected return type
* @param parameterTypes The list of parameter types
* @param parameterValues The list with values for the method parameters
* @param <T> The result type we expect the method to be
* @return The value returned by the method
*/
protected <T> T invokeMethod(String methodName, Class<T> returnType, Class<?>[] parameterTypes,
Object... parameterValues) {
if (target == null) {
return null;
}
try {
// getDeclaredMethod is used to get protected/private methods
Method method = target.getClass().getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return returnType.cast(method.invoke(target, parameterValues));
} catch (NoSuchMethodException e) {
try {
// try getting it from parent class, but only public methods will work
Method method = target.getClass().getMethod(methodName, parameterTypes);
method.setAccessible(true);
return returnType.cast(method.invoke(target, parameterValues));
} catch (Exception ex) {
return null;
}
} catch (Exception e) {
return null;
}
}
/**
* Creates an instance of the target class
* @param args The list of constructor parameters
* @return An instance of the target class, if found, or null otherwise
*/
private Object instantiateTarget(Object... args) {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return null;
}
Constructor<?>[] constructors = getAllConstructors();
for (Constructor<?> c : constructors) {
if (c.getParameterCount() == args.length) {
try {
return c.newInstance(args);
} catch (Exception e) {
// do nothing;
}
}
}
return null;
}
/**
* Gets a list with all the constructors defined by the target class
* @return A list with all constructor definitions
*/
private Constructor<?>[] getAllConstructors() {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return new Constructor<?>[]{};
}
return targetClass.getDeclaredConstructors();
}
//region Unused
/**
* The default constructor, for when you have already an instance of the target class
* @param target An instance of the target class
*/
protected ReflectionProxy(Object target) {
this.target = target;
}
/**
* Checks if the target class exists
* @return True if the class exists, false otherwise
*/
public boolean existsClass() {
return getTargetClass() != null;
}
/**
* Checks if the class implements a specific interface
* @param anInterface The interface to check
* @return True if the class implements the referred interface, false otherwise
*/
public boolean implementsInterface(Class<?> anInterface) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || anInterface == null) {
return false;
}
return anInterface.isAssignableFrom(targetClass);
}
/**
* Checks if the target class has a specific property
* @param name The name of the property to find
* @return True if the property is found, false otherwise
*/
public boolean hasProperty(String name) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Field f = targetClass.getDeclaredField(name);
return f != null;
} catch (NoSuchFieldException e) {
return false;
}
}
/**
* Checks if an existing property has the type we expect
* @param name The name of the property to check
* @param type The type you are expecting the property to be
* @return True if the property is found and has the specified type, false otherwise
*/
public boolean isPropertyOfType(String name, Class<?> type) {
return isPropertyOfType(name, type, null);
}
/**
* Checks if an existing Collection type has the parameterized type (Generics) as expected (eg. List<String>)
* @param name The name of the property
* @param type The type of the property (eg. List)
* @param parameterizedType The parameterized property (eg. String)
* @return True if the parameterized type matches the desired type, false otherwise
*/
public boolean isPropertyOfType(String name, Class<?> type, Class<?> parameterizedType) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null || type == null) {
return false;
}
try {
Field f = targetClass.getDeclaredField(name);
if (!f.getType().equals(type)) {
return false;
}
if (parameterizedType == null) {
return true;
}
if (!(f.getGenericType() instanceof ParameterizedType)) {
return false;
}
ParameterizedType pType = (ParameterizedType) f.getGenericType();
return pType.getActualTypeArguments()[0].equals(parameterizedType);
} catch (NoSuchFieldException e) {
return false;
}
}
/**
* Checks if a property is private
* @param name The name of the property
* @return True if the property exists and is private, false otherwise
*/
public boolean isPropertyPrivate(String name) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Field f = targetClass.getDeclaredField(name);
return Modifier.isPrivate(f.getModifiers());
} catch (NoSuchFieldException e) {
return false;
}
}
/**
* Checks if a method from the target class returns a correct parameterized collection (Generics)
* @param returnType The return type we expect (eg. List)
* @param parameterizedType The parameterized type we expect (eg. String)
* @param name The name of the method
* @param parameterTypes A list of method parameter types
* @return True if the method returns the correct parameterized collection, false otherwise
*/
public boolean isMethodReturnType(Class<?> returnType, Class<?> parameterizedType,
String name, Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Method m = targetClass.getDeclaredMethod(name, parameterTypes);
if (!m.getReturnType().equals(returnType)) {
return false;
}
if (parameterizedType == null) {
return true;
}
if (!(m.getGenericReturnType() instanceof ParameterizedType)) {
return false;
}
ParameterizedType pType = (ParameterizedType) m.getGenericReturnType();
return pType.getActualTypeArguments()[0].equals(parameterizedType);
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Checks if a target class has a specific constructor
* @param parameterTypes The list of desired parameter types
* @return True if the constructor exists, false otherwise
*/
public boolean hasConstructor(Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return false;
}
try {
Constructor<?> c = targetClass.getDeclaredConstructor(parameterTypes);
return c != null;
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Checks if a specific constructor from the target class is public
* @param parameterTypes A list of parameter types
* @return True if the constructor is found and is public, false otherwise
*/
public boolean isConstructorPublic(Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return false;
}
try {
Constructor<?> c = targetClass.getDeclaredConstructor(parameterTypes);
return Modifier.isPublic(c.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Proxy for the 'equals' method
* @param obj The ReflexionProxy object you want to compare against
* @return True if both targets are equal, false otherwise
*/
public boolean equals(Object obj) {
if (target == null || !(obj instanceof ReflectionProxy)) {
return false;
}
try {
Method method = target.getClass().getMethod("equals", Object.class);
method.setAccessible(true);
return (boolean) method.invoke(target, ((ReflectionProxy) obj).getTarget());
} catch (Exception e) {
return false;
}
}
/**
* Proxy for the 'hashCode' method
* @return The hashCode from the target class
*/
public int hashCode() {
if (target == null) {
return 0;
}
try {
Method method = target.getClass().getMethod("hashCode");
method.setAccessible(true);
return (int) method.invoke(target);
} catch (Exception e) {
return 0;
}
}
/**
* Proxy for the 'toString' method from the target class
* @return The result of 'toString' from the target instance
*/
public String toString() {
return invokeMethod("toString", String.class, new Class[]{ });
}
/**
* Gets a property value from the target instance (if it exists)
* @param propertyName The name of the property
* @param propertyType The class representing the property's type
* @param <T> The type we are expecting it to be
* @return The value of the property (if it exists)
*/
protected <T> T getPropertyValue(String propertyName, Class<T> propertyType) {
if (target == null || !hasProperty(propertyName)) {
return null;
}
try {
Field field = target.getClass().getDeclaredField(propertyName);
field.setAccessible(true);
return propertyType.cast(field.get(target));
} catch (Exception e) {
return null;
}
}
/**
* Checks if the target class is abstract
* @return True if the target class exists and is abstract, false otherwise
*/
public boolean isAbstract() {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return false;
}
return Modifier.isAbstract(targetClass.getModifiers());
}
/**
* Checks if the target class extends another
* @param className The fully qualified name of the class it should extend
* @return True if the target class extends the specified one, false otherwise
*/
public boolean extendsClass(String className) {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return false;
}
try {
Class<?> parentClass = Class.forName(className);
return parentClass.isAssignableFrom(targetClass);
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Checks if the target class is an interface
* @return True if the target class exists and is an interface, false otherwise
*/
public boolean isInterface() {
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
return false;
}
return targetClass.isInterface();
}
/**
* Checks if a method is abstract
* @param name The name of the method
* @param parameterTypes The list of method parameter types
* @return True if the method exists and is abstract, false otherwise
*/
public boolean isMethodAbstract(String name, Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Method m = targetClass.getDeclaredMethod(name, parameterTypes);
return Modifier.isAbstract(m.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Checks if a method is protected
* @param name The name of the method
* @param parameterTypes The list of method parameter types
* @return True if the method exists and is protected, false otherwise
*/
public boolean isMethodProtected(String name, Class<?>... parameterTypes) {
Class<?> targetClass = getTargetClass();
if (targetClass == null || name == null) {
return false;
}
try {
Method m = targetClass.getDeclaredMethod(name, parameterTypes);
return Modifier.isProtected(m.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}
//endregion
}