-
-
Notifications
You must be signed in to change notification settings - Fork 938
Expand file tree
/
Copy pathClassUtils.java
More file actions
198 lines (173 loc) · 7.32 KB
/
ClassUtils.java
File metadata and controls
198 lines (173 loc) · 7.32 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
package org.jruby.java.util;
import org.jruby.RubyModule;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.CodegenUtils;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Utility functions for working with Java classes and their Ruby proxies.
*/
public class ClassUtils {
public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
public static boolean assignable(Class<?> target, Class<?> from) {
if ( target.isPrimitive() ) target = CodegenUtils.getBoxType(target);
else if ( from == Void.TYPE || target.isAssignableFrom(from) ) {
return true;
}
if ( from.isPrimitive() ) from = CodegenUtils.getBoxType(from);
if ( target.isAssignableFrom(from) ) return true;
if ( Number.class.isAssignableFrom(target) ) {
if ( Number.class.isAssignableFrom(from) ) {
return true;
}
if ( from == Character.class ) {
return true;
}
}
else if ( target == Character.class ) {
if ( Number.class.isAssignableFrom(from) ) {
return true;
}
}
return false;
}
public static Class<?>[] getArgumentTypes(final ThreadContext context, final IRubyObject[] args, final int offset) {
final int length = args.length; // offset == 0 || 1
if ( length == offset ) return EMPTY_CLASS_ARRAY;
final Class<?>[] argumentTypes = new Class[length - offset];
for ( int i = offset; i < length; i++ ) {
argumentTypes[ i - offset ] = Java.resolveClassType(context, args[i]);
}
return argumentTypes;
}
public static AccessibleObject getMatchingCallable(Class<?> javaClass, String methodName, Class<?>[] argumentTypes) {
if ( methodName.length() == 6 && "<init>".equals(methodName) ) {
return getMatchingConstructor(javaClass, argumentTypes);
}
// FIXME: do we really want 'declared' methods? includes private/protected, and does _not_
// include superclass methods
return getMatchingDeclaredMethod(javaClass, methodName, argumentTypes);
}
public static Constructor getMatchingConstructor(final Class<?> javaClass, final Class<?>[] argumentTypes) {
try {
return javaClass.getConstructor(argumentTypes);
}
catch (NoSuchMethodException e) {
final int argLength = argumentTypes.length;
// Java reflection does not allow retrieving constructors like methods
Search: for (Constructor<?> ctor : javaClass.getConstructors()) {
final Class<?>[] ctorTypes = ctor.getParameterTypes();
final int ctorLength = ctorTypes.length;
if ( ctorLength != argLength ) continue Search;
// for zero args case we can stop searching
if ( ctorLength == 0 && argLength == 0 ) {
return ctor;
}
boolean found = true;
TypeScan: for ( int i = 0; i < argLength; i++ ) {
//if ( i >= ctorLength ) found = false;
if ( ctorTypes[i].isAssignableFrom(argumentTypes[i]) ) {
found = true; // continue TypeScan;
} else {
continue Search; // not-found
}
}
// if we get here, we found a matching method, use it
// TODO: choose narrowest method by continuing to search
if ( found ) return ctor;
}
}
return null; // no matching ctor found
}
public static Method getMatchingDeclaredMethod(Class<?> javaClass, String methodName, Class<?>[] argumentTypes) {
// FIXME: do we really want 'declared' methods? includes private/protected, and does _not_
// include superclass methods. also, the getDeclared calls may throw SecurityException if
// we're running under a restrictive security policy.
try {
return javaClass.getDeclaredMethod(methodName, argumentTypes);
}
catch (NoSuchMethodException e) {
// search through all declared methods to find a closest match
MethodSearch: for ( Method method : javaClass.getDeclaredMethods() ) {
if ( method.getName().equals(methodName) ) {
Class<?>[] targetTypes = method.getParameterTypes();
// for zero args case we can stop searching
if (targetTypes.length == 0 && argumentTypes.length == 0) {
return method;
}
TypeScan: for (int i = 0; i < argumentTypes.length; i++) {
if (i >= targetTypes.length) continue MethodSearch;
if (targetTypes[i].isAssignableFrom(argumentTypes[i])) {
continue TypeScan;
} else {
continue MethodSearch;
}
}
// if we get here, we found a matching method, use it
// TODO: choose narrowest method by continuing to search
return method;
}
}
}
// no matching method found
return null;
}
public static Field[] getDeclaredFields(final Class<?> clazz) {
try {
return clazz.getDeclaredFields();
}
catch (SecurityException e) {
return getFields(clazz);
}
}
public static Field[] getFields(final Class<?> clazz) {
try {
return clazz.getFields();
}
catch (SecurityException e) { return new Field[0]; }
}
public static Class<?>[] getDeclaredClasses(Class<?> clazz) {
try {
return clazz.getDeclaredClasses();
}
catch (SecurityException e) {
return new Class<?>[0];
}
catch (NoClassDefFoundError cnfe) {
// This is a Scala-specific hack, since Scala uses peculiar
// naming conventions and class attributes that confuse Java's
// reflection logic and cause a blow up in getDeclaredClasses.
// See http://lampsvn.epfl.ch/trac/scala/ticket/2749
return new Class<?>[0];
}
}
public static String getSimpleName(Class<?> clazz) {
if (clazz.isArray()) {
return getSimpleName(clazz.getComponentType()) + "[]";
}
String className = clazz.getName();
int len = className.length();
int i = className.lastIndexOf('$');
if (i != -1) {
do {
i++;
} while (i < len && Character.isDigit(className.charAt(i)));
return className.substring(i);
}
return className.substring(className.lastIndexOf('.') + 1);
}
public static Constructor[] getConstructors(final Class<?> clazz) {
try {
return clazz.getConstructors();
}
catch (SecurityException e) { return new Constructor[0]; }
}
public static boolean isJavaClassProxyType(final RubyModule clazz) {
return JavaUtil.getJavaClass(clazz, null) != null;
}
}