package org.jruby.javasupport;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name="Java::JavaConstructor")
public class JavaConstructor extends JavaCallable {
private final Constructor<?> constructor;
public final Constructor getValue() { return constructor; }
public static RubyClass createJavaConstructorClass(Ruby runtime, RubyModule javaModule) {
RubyClass result =
javaModule.defineClassUnder("JavaConstructor", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
JavaAccessibleObject.registerRubyMethods(runtime, result);
JavaCallable.registerRubyMethods(runtime, result);
result.defineAnnotatedMethods(JavaConstructor.class);
return result;
}
public JavaConstructor(Ruby runtime, Constructor<?> constructor) {
super(runtime, runtime.getJavaSupport().getJavaConstructorClass(), constructor.getParameterTypes());
this.constructor = constructor;
}
public static JavaConstructor create(Ruby runtime, Constructor<?> constructor) {
return new JavaConstructor(runtime, constructor);
}
public static JavaConstructor getMatchingConstructor(final Ruby runtime,
final Class<?> javaClass, final Class<?>[] argumentTypes) {
try {
return create(runtime, javaClass.getConstructor(argumentTypes));
}
catch (NoSuchMethodException e) {
final int argLength = argumentTypes.length;
Search: for (Constructor<?> ctor : javaClass.getConstructors()) {
final Class<?>[] ctorTypes = ctor.getParameterTypes();
final int ctorLength = ctorTypes.length;
if ( ctorLength != argLength ) continue Search;
if ( ctorLength == 0 && argLength == 0 ) {
return create(runtime, ctor);
}
boolean found = true;
TypeScan: for ( int i = 0; i < argLength; i++ ) {
if ( ctorTypes[i].isAssignableFrom(argumentTypes[i]) ) {
found = true;
} else {
continue Search;
}
}
if ( found ) return create(runtime, ctor);
}
}
return null;
}
@Override
public final boolean equals(Object other) {
return other instanceof JavaConstructor &&
this.constructor.equals( ((JavaConstructor) other).constructor );
}
@Override
public final int hashCode() {
return constructor.hashCode();
}
@Override
protected String nameOnInspection() {
return getType().toString();
}
@JRubyMethod
public RubyString inspect() {
StringBuilder str = new StringBuilder();
str.append("#<");
str.append( getType().toString() );
inspectParameterTypes(str, this);
str.append('>');
return RubyString.newString(getRuntime(), str);
}
@Override
public final Class<?>[] getExceptionTypes() {
return constructor.getExceptionTypes();
}
@Override
public Type[] getGenericParameterTypes() {
return constructor.getGenericParameterTypes();
}
@Override
public Type[] getGenericExceptionTypes() {
return constructor.getGenericExceptionTypes();
}
public Annotation[][] getParameterAnnotations() {
return constructor.getParameterAnnotations();
}
@Override
public final boolean isVarArgs() {
return constructor.isVarArgs();
}
@Override
public final int getModifiers() {
return constructor.getModifiers();
}
public String toGenericString() {
return constructor.toGenericString();
}
public Class<?> getDeclaringClass() {
return constructor.getDeclaringClass();
}
public AccessibleObject accessibleObject() {
return constructor;
}
@JRubyMethod
public IRubyObject type_parameters() {
return Java.getInstance(getRuntime(), constructor.getTypeParameters());
}
@JRubyMethod
public IRubyObject return_type() {
return getRuntime().getNil();
}
@JRubyMethod
public IRubyObject declaring_class() {
return JavaClass.get(getRuntime(), getDeclaringClass());
}
@JRubyMethod(rest = true)
public final IRubyObject new_instance(final IRubyObject[] args) {
checkArity(args.length);
return newInstanceExactArity( convertArguments(args) );
}
public final IRubyObject new_instance(final Object[] arguments) {
checkArity(arguments.length);
return newInstanceExactArity(arguments);
}
private IRubyObject newInstanceExactArity(Object[] arguments) {
try {
Object result = constructor.newInstance(arguments);
return JavaObject.wrap(getRuntime(), result);
}
catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, false, arguments);
}
catch (IllegalAccessException iae) {
throw getRuntime().newTypeError("illegal access");
}
catch (InvocationTargetException ite) {
getRuntime().getJavaSupport().handleNativeException(ite.getTargetException(), constructor);
assert false;
return null;
}
catch (InstantiationException ie) {
throw getRuntime().newTypeError("can't make instance of " + constructor.getDeclaringClass().getName());
}
}
public Object newInstanceDirect(ThreadContext context, Object... arguments) {
checkArity(arguments.length);
try {
return constructor.newInstance(arguments);
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, arguments);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
public Object newInstanceDirect(ThreadContext context) {
checkArity(0);
try {
return constructor.newInstance();
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
public Object newInstanceDirect(ThreadContext context, Object arg0) {
checkArity(1);
try {
return constructor.newInstance(arg0);
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, arg0);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
public Object newInstanceDirect(ThreadContext context, Object arg0, Object arg1) {
checkArity(2);
try {
return constructor.newInstance(arg0, arg1);
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, arg0, arg1);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
public Object newInstanceDirect(ThreadContext context, Object arg0, Object arg1, Object arg2) {
checkArity(3);
try {
return constructor.newInstance(arg0, arg1, arg2);
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, arg0, arg1, arg2);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
public Object newInstanceDirect(ThreadContext context, Object arg0, Object arg1, Object arg2, Object arg3) {
checkArity(4);
try {
return constructor.newInstance(arg0, arg1, arg2, arg3);
} catch (IllegalArgumentException iae) {
return handlelIllegalArgumentEx(iae, constructor, arg0, arg1, arg2, arg3);
} catch (IllegalAccessException iae) {
return handleIllegalAccessEx(iae, constructor);
} catch (InvocationTargetException ite) {
return handleInvocationTargetEx(context, ite);
} catch (Throwable t) {
return handleThrowable(context, t);
}
}
boolean isConstructor() { return true; }
}