package org.jruby.javasupport.ext;
import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.javasupport.JavaClass;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.JavaInternalBlockBody;
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.backtrace.TraceType;
import org.jruby.runtime.builtin.IRubyObject;
import java.lang.reflect.Modifier;
import static org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject;
import static org.jruby.javasupport.JavaUtil.isJavaObject;
import static org.jruby.javasupport.JavaUtil.unwrapIfJavaObject;
import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;
import static org.jruby.runtime.Visibility.PUBLIC;
public abstract class JavaLang {
public static void define(final Ruby runtime) {
JavaExtensions.put(runtime, java.lang.Iterable.class, (proxyClass) -> Iterable.define(runtime, proxyClass));
JavaExtensions.put(runtime, java.lang.Comparable.class, (proxyClass) -> Comparable.define(runtime, proxyClass));
JavaExtensions.put(runtime, java.lang.Throwable.class, (proxyClass) -> Throwable.define(runtime, (RubyClass) proxyClass));
JavaExtensions.put(runtime, java.lang.Runnable.class, (proxyClass) -> Runnable.define(runtime, proxyClass));
JavaExtensions.put(runtime, java.lang.Character.class, (proxyClass) -> Character.define(runtime, (RubyClass) proxyClass));
JavaExtensions.put(runtime, java.lang.Number.class, (proxyClass) -> Number.define(runtime, (RubyClass) proxyClass));
JavaExtensions.put(runtime, java.lang.Class.class, (proxyClass) -> Class.define(runtime, (RubyClass) proxyClass));
JavaExtensions.put(runtime, java.lang.ClassLoader.class, (proxyClass) -> ClassLoader.define(runtime, (RubyClass) proxyClass));
JavaExtensions.put(runtime, new byte[0].getClass(), (byteArray) -> {
byteArray.addMethod("ubyte_get", new UByteGet(byteArray));
byteArray.addMethod("ubyte_set", new UByteSet(byteArray));
});
JavaExtensions.put(runtime, java.lang.String.class, (proxyClass) -> {
proxyClass.defineAlias("to_str", "to_s");
});
}
@JRubyModule(name = "Java::JavaLang::Iterable", include = "Enumerable")
public static class Iterable {
static RubyModule define(final Ruby runtime, final RubyModule proxy) {
proxy.includeModule( runtime.getEnumerable() );
proxy.defineAnnotatedMethods(Iterable.class);
return proxy;
}
@JRubyMethod
public static IRubyObject each(final ThreadContext context, final IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;
if ( ! block.isGiven() ) {
return runtime.getEnumerator().callMethod("new", self, runtime.newSymbol("each"));
}
java.lang.Iterable iterable = unwrapIfJavaObject(self);
java.util.Iterator iterator = iterable.iterator();
while ( iterator.hasNext() ) {
final Object value = iterator.next();
block.yield(context, convertJavaToUsableRubyObject(runtime, value));
}
return self;
}
@JRubyMethod
public static IRubyObject each_with_index(final ThreadContext context, final IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;
if ( ! block.isGiven() ) {
return runtime.getEnumerator().callMethod("new", self, runtime.newSymbol("each_with_index"));
}
java.lang.Iterable iterable = unwrapIfJavaObject(self);
java.util.Iterator iterator = iterable.iterator();
final boolean arity2 = block.getSignature().arity() == Arity.TWO_ARGUMENTS;
int i = 0; while ( iterator.hasNext() ) {
final RubyInteger index = RubyFixnum.newFixnum(runtime, i++);
final Object value = iterator.next();
final IRubyObject rValue = convertJavaToUsableRubyObject(runtime, value);
if ( arity2 ) {
block.yieldSpecific(context, rValue, index);
} else {
block.yield(context, RubyArray.newArray(runtime, rValue, index));
}
}
return self;
}
@JRubyMethod(name = { "to_a", "entries" })
public static IRubyObject to_a(final ThreadContext context, final IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;
final RubyArray ary = runtime.newArray();
java.lang.Iterable iterable = unwrapIfJavaObject(self);
java.util.Iterator iterator = iterable.iterator();
while ( iterator.hasNext() ) {
final Object value = iterator.next();
ary.append( convertJavaToUsableRubyObject(runtime, value) );
}
return ary;
}
@JRubyMethod(name = "count")
public static IRubyObject count(final ThreadContext context, final IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;
java.lang.Iterable iterable = unwrapIfJavaObject(self);
if ( block.isGiven() ) {
return countBlock(context, iterable.iterator(), block);
}
if ( iterable instanceof java.util.Collection ) {
return RubyFixnum.newFixnum(runtime, ((java.util.Collection) iterable).size());
}
int count = 0;
for( java.util.Iterator it = iterable.iterator(); it.hasNext(); ) { it.next(); count++; }
return RubyFixnum.newFixnum(runtime, count);
}
static RubyFixnum countBlock(final ThreadContext context, final java.util.Iterator it, final Block block) {
final Ruby runtime = context.runtime;
int count = 0; while ( it.hasNext() ) {
IRubyObject next = convertJavaToUsableRubyObject( runtime, it.next() );
if ( block.yield( context, next ).isTrue() ) count++;
}
return RubyFixnum.newFixnum(runtime, count);
}
@JRubyMethod(name = "count")
public static IRubyObject count(final ThreadContext context, final IRubyObject self, final IRubyObject obj, final Block unused) {
final Ruby runtime = context.runtime;
java.lang.Iterable iterable = unwrapIfJavaObject(self);
int count = 0; for ( java.util.Iterator it = iterable.iterator(); it.hasNext(); ) {
IRubyObject next = convertJavaToUsableRubyObject( runtime, it.next() );
if ( RubyObject.equalInternal(context, next, obj) ) count++;
}
return RubyFixnum.newFixnum(runtime, count);
}
}
@JRubyClass(name = "Java::JavaLang::Comparable", include = "Comparable")
public static class Comparable {
static RubyModule define(final Ruby runtime, final RubyModule proxy) {
proxy.includeModule( runtime.getComparable() );
proxy.defineAnnotatedMethods(Comparable.class);
return proxy;
}
@JRubyMethod(name = "<=>")
public static IRubyObject cmp(final ThreadContext context, final IRubyObject self, final IRubyObject other) {
java.lang.Comparable comparable = unwrapIfJavaObject(self);
if ( other.isNil() ) return context.nil;
final java.lang.Object otherComp = unwrapIfJavaObject(other);
final int cmp;
try {
cmp = comparable.compareTo(otherComp);
}
catch (ClassCastException ex) {
throw context.runtime.newTypeError(ex.getMessage());
}
return RubyFixnum.newFixnum(context.runtime, cmp);
}
}
@JRubyClass(name = "Java::JavaLang::Throwable")
public static class Throwable {
static RubyModule define(final Ruby runtime, final RubyClass proxy) {
proxy.defineAnnotatedMethods(Throwable.class);
return proxy;
}
@JRubyMethod
public static IRubyObject backtrace(final ThreadContext context, final IRubyObject self) {
final Ruby runtime = context.runtime;
java.lang.Throwable throwable = unwrapIfJavaObject(self);
StackTraceElement[] stackTrace = throwable.getStackTrace();
if ( stackTrace == null ) return context.nil;
final int len = stackTrace.length;
if ( len == 0 ) return RubyArray.newEmptyArray(runtime);
IRubyObject[] backtrace = new IRubyObject[len];
for ( int i=0; i < len; i++ ) {
backtrace[i] = RubyString.newString(runtime, stackTrace[i].toString());
}
return RubyArray.newArrayMayCopy(runtime, backtrace);
}
@JRubyMethod
public static IRubyObject set_backtrace(final IRubyObject self, final IRubyObject backtrace) {
return self.getRuntime().getNil();
}
@JRubyMethod
public static IRubyObject message(final ThreadContext context, final IRubyObject self) {
java.lang.Throwable throwable = unwrapIfJavaObject(self);
final String msg = throwable.getLocalizedMessage();
return msg == null ? RubyString.newEmptyString(context.runtime) : RubyString.newString(context.runtime, msg);
}
@JRubyMethod
public static IRubyObject full_message(final ThreadContext context, final IRubyObject self) {
return full_message(context, self, null);
}
@JRubyMethod
public static IRubyObject full_message(final ThreadContext context, final IRubyObject self, final IRubyObject opts) {
return RubyString.newString(context.runtime, TraceType.printFullMessage(context, self, opts));
}
@JRubyMethod
public static IRubyObject to_s(final ThreadContext context, final IRubyObject self) {
return message(context, self);
}
@JRubyMethod
public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) {
java.lang.Throwable throwable = unwrapIfJavaObject(self);
return RubyString.newString(context.runtime, throwable.toString());
}
@JRubyMethod(name = "===", meta = true)
public static IRubyObject eqq(final ThreadContext context, final IRubyObject self, IRubyObject other) {
if (checkNativeException(self, other)) {
return context.tru;
}
return self.op_eqq(context, other);
}
@SuppressWarnings("deprecation")
private static boolean checkNativeException(IRubyObject self, IRubyObject other) {
if ( other instanceof NativeException ) {
final java.lang.Class java_class = (java.lang.Class) self.dataGetStruct();
if ( java_class.isAssignableFrom( ((NativeException) other).getCause().getClass() ) ) {
return true;
}
}
return false;
}
}
@JRubyModule(name = "Java::JavaLang::Runnable")
public static class Runnable {
static RubyModule define(final Ruby runtime, final RubyModule proxy) {
proxy.defineAnnotatedMethods(Runnable.class);
return proxy;
}
@JRubyMethod
public static IRubyObject to_proc(final ThreadContext context, final IRubyObject self) {
final Ruby runtime = context.runtime;
final java.lang.Runnable runnable = unwrapIfJavaObject(self);
final Block block = new Block(new RunBody(runtime, runnable));
return new RubyProc(runtime, runtime.getProc(), block, null, -1);
}
private static final class RunBody extends JavaInternalBlockBody {
private final java.lang.Runnable runnable;
RunBody(final Ruby runtime, final java.lang.Runnable runnable) {
super(runtime, Signature.NO_ARGUMENTS);
this.runnable = runnable;
}
@Override
public IRubyObject yield(ThreadContext context, IRubyObject[] args) {
return yieldImpl(context);
}
final IRubyObject yieldImpl(ThreadContext context) {
runnable.run(); return context.nil;
}
@Override
protected final IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
return yieldImpl(context);
}
@Override
protected final IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
return yieldImpl(context);
}
}
}
@JRubyClass(name = "Java::JavaLang::Number")
public static class Number {
static RubyClass define(final Ruby runtime, final RubyClass proxy) {
proxy.defineAnnotatedMethods(Number.class);
proxy.defineAlias("to_int", "longValue");
proxy.defineAlias("to_f", "doubleValue");
return proxy;
}
@JRubyMethod(name = "to_f")
public static IRubyObject to_f(final ThreadContext context, final IRubyObject self) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
return context.runtime.newFloat(val.doubleValue());
}
@JRubyMethod(name = "real?")
public static IRubyObject real_p(final ThreadContext context, final IRubyObject self) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
return context.runtime.newBoolean(val instanceof Integer || val instanceof Long ||
val instanceof Short || val instanceof Byte ||
val instanceof Float || val instanceof Double ||
val instanceof java.math.BigInteger || val instanceof java.math.BigDecimal);
}
@JRubyMethod(name = { "to_i", "to_int" })
public static IRubyObject to_i(final ThreadContext context, final IRubyObject self) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
if (val instanceof java.math.BigInteger) {
return RubyBignum.newBignum(context.runtime, (java.math.BigInteger) val);
}
return context.runtime.newFixnum(val.longValue());
}
@JRubyMethod(name = "integer?")
public static IRubyObject integer_p(final ThreadContext context, final IRubyObject self) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
return context.runtime.newBoolean(val instanceof Integer || val instanceof Long ||
val instanceof Short || val instanceof Byte ||
val instanceof java.math.BigInteger);
}
@JRubyMethod(name = "zero?")
public static IRubyObject zero_p(final ThreadContext context, final IRubyObject self) {
return context.runtime.newBoolean(isZero(self));
}
private static boolean isZero(final IRubyObject self) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
return Double.compare(val.doubleValue(), 0) == 0;
}
@JRubyMethod(name = "nonzero?")
public static IRubyObject nonzero_p(final ThreadContext context, final IRubyObject self) {
return isZero(self) ? context.nil : self;
}
@JRubyMethod(name = "coerce")
public static IRubyObject coerce(final ThreadContext context, final IRubyObject self, final IRubyObject type) {
java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class);
final IRubyObject value = convertJavaToUsableRubyObject(context.runtime, val);
return context.runtime.newArray(type, value);
}
}
@JRubyClass(name = "Java::JavaLang::Character")
public static class Character {
static RubyClass define(final Ruby runtime, final RubyClass proxy) {
proxy.defineAnnotatedMethods(Character.class);
return proxy;
}
@JRubyMethod(name = "java_identifier_start?", meta = true)
public static IRubyObject java_identifier_start_p(final ThreadContext context, final IRubyObject self,
final IRubyObject num) {
return context.runtime.newBoolean( java.lang.Character.isJavaIdentifierStart(int_char(num)) );
}
@JRubyMethod(name = "java_identifier_part?", meta = true)
public static IRubyObject java_identifier_part_p(final ThreadContext context, final IRubyObject self,
final IRubyObject num) {
return context.runtime.newBoolean( java.lang.Character.isJavaIdentifierPart(int_char(num)) );
}
private static int int_char(IRubyObject num) {
return num.toJava(java.lang.Character.TYPE);
}
@JRubyMethod(name = "to_i")
public static IRubyObject to_i(final ThreadContext context, final IRubyObject self) {
java.lang.Character c = (java.lang.Character) self.toJava(java.lang.Character.class);
return context.runtime.newFixnum(c);
}
}
@JRubyClass(name = "Java::JavaLang::Class")
public static class Class {
static RubyClass define(final Ruby runtime, final RubyClass proxy) {
proxy.includeModule( runtime.getComparable() );
proxy.defineAnnotatedMethods(Class.class);
return proxy;
}
@JRubyMethod(name = "ruby_class")
public static IRubyObject proxy_class(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return context.runtime.getJavaSupport().getProxyClassFromCache(klass);
}
@JRubyMethod
public static IRubyObject resource_as_stream(final ThreadContext context, final IRubyObject self, final IRubyObject name) {
final java.lang.Class klass = unwrapJavaObject(self);
final String resName = name.convertToString().toString();
return convertJavaToUsableRubyObject(context.runtime, klass.getResourceAsStream(resName));
}
@JRubyMethod
public static IRubyObject resource_as_string(final ThreadContext context, final IRubyObject self, final IRubyObject name) {
final java.lang.Class klass = unwrapJavaObject(self);
final String resName = name.convertToString().toString();
return new RubyIO(context.runtime, klass.getResourceAsStream(resName)).read(context);
}
@JRubyMethod
public static IRubyObject to_s(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return RubyString.newString(context.runtime, klass.getName());
}
@JRubyMethod
public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return RubyString.newString(context.runtime, klass.toString());
}
@JRubyMethod(name = "annotations?")
public static IRubyObject annotations_p(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return context.runtime.newBoolean(klass.getAnnotations().length > 0);
}
@JRubyMethod(name = "declared_annotations?")
public static IRubyObject declared_annotations_p(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return context.runtime.newBoolean(klass.getDeclaredAnnotations().length > 0);
}
@JRubyMethod
public static IRubyObject java_instance_methods(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
final RubyArray methods = RubyArray.newArray(context.runtime);
for ( java.lang.reflect.Method method : klass.getMethods() ) {
if ( ! Modifier.isStatic(method.getModifiers()) ) methods.add(method);
}
return methods;
}
@JRubyMethod
public static IRubyObject declared_instance_methods(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
final RubyArray methods = RubyArray.newArray(context.runtime);
for ( java.lang.reflect.Method method : klass.getDeclaredMethods() ) {
if ( ! Modifier.isStatic(method.getModifiers()) ) methods.add(method);
}
return methods;
}
@JRubyMethod
public static IRubyObject java_class_methods(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
final RubyArray methods = RubyArray.newArray(context.runtime);
for ( java.lang.reflect.Method method : klass.getMethods() ) {
if ( Modifier.isStatic(method.getModifiers()) ) methods.add(method);
}
return methods;
}
@JRubyMethod
public static IRubyObject declared_class_methods(final ThreadContext context, final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
final RubyArray methods = RubyArray.newArray(context.runtime);
for ( java.lang.reflect.Method method : klass.getDeclaredMethods() ) {
if ( Modifier.isStatic(method.getModifiers()) ) methods.add(method);
}
return methods;
}
@JRubyMethod(name = "<=>")
public static IRubyObject cmp(final ThreadContext context, final IRubyObject self, final IRubyObject other) {
final java.lang.Class that;
if ( other instanceof JavaClass ) {
that = ((JavaClass) other).getJavaClass();
}
else if ( isJavaObject(other) ) {
that = unwrapJavaObject(other);
}
else {
return context.nil;
}
final java.lang.Class thiz = unwrapJavaObject(self);
if ( thiz == that ) return context.runtime.newFixnum(0);
if ( thiz.isAssignableFrom(that) ) return context.runtime.newFixnum(+1);
if ( that.isAssignableFrom(thiz) ) return context.runtime.newFixnum(-1);
return context.nil;
}
@JRubyMethod(name = "anonymous?")
public static IRubyObject anonymous_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return self.getRuntime().newBoolean( klass.isAnonymousClass() );
}
@JRubyMethod(name = "abstract?")
public static IRubyObject abstract_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isAbstract( self, klass.getModifiers() );
}
@JRubyMethod(name = "public?")
public static IRubyObject public_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isPublic( self, klass.getModifiers() );
}
@JRubyMethod(name = "protected?")
public static IRubyObject protected_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isProtected(self, klass.getModifiers());
}
@JRubyMethod(name = "private?")
public static IRubyObject private_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isPrivate(self, klass.getModifiers());
}
@JRubyMethod(name = "final?")
public static IRubyObject final_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isFinal(self, klass.getModifiers());
}
@JRubyMethod(name = "static?")
public static IRubyObject static_p(final IRubyObject self) {
final java.lang.Class klass = unwrapJavaObject(self);
return JavaLangReflect.isStatic(self, klass.getModifiers());
}
}
@JRubyClass(name = "Java::JavaLang::ClassLoader")
public static class ClassLoader {
static RubyModule define(final Ruby runtime, final RubyClass proxy) {
proxy.defineAnnotatedMethods(ClassLoader.class);
return proxy;
}
@JRubyMethod
public static IRubyObject resource_as_url(final ThreadContext context, final IRubyObject self, final IRubyObject name) {
final java.lang.ClassLoader loader = unwrapIfJavaObject(self);
final String resName = name.convertToString().toString();
return convertJavaToUsableRubyObject(context.runtime, loader.getResource(resName));
}
@JRubyMethod
public static IRubyObject resource_as_stream(final ThreadContext context, final IRubyObject self, final IRubyObject name) {
final java.lang.ClassLoader loader = unwrapIfJavaObject(self);
final String resName = name.convertToString().toString();
return convertJavaToUsableRubyObject(context.runtime, loader.getResourceAsStream(resName));
}
@JRubyMethod
public static IRubyObject resource_as_string(final ThreadContext context, final IRubyObject self, final IRubyObject name) {
final java.lang.ClassLoader loader = unwrapIfJavaObject(self);
final String resName = name.convertToString().toString();
return new RubyIO(context.runtime, loader.getResourceAsStream(resName)).read(context);
}
}
private static final class UByteGet extends JavaMethod.JavaMethodOne {
UByteGet(RubyModule implClass) {
super(implClass, PUBLIC, "ubyte_get");
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject idx) {
final RubyInteger val = (RubyInteger) self.callMethod(context, "[]", idx);
int byte_val = val.getIntValue();
if ( byte_val >= 0 ) return val;
return RubyFixnum.newFixnum(context.runtime, byte_val + 256);
}
}
private static final class UByteSet extends JavaMethod.JavaMethodTwo {
UByteSet(RubyModule implClass) {
super(implClass, PUBLIC, "ubyte_set");
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject idx, IRubyObject val) {
int byte_val = ((RubyInteger) val).getIntValue();
if ( byte_val > 127 ) {
val = RubyFixnum.newFixnum(context.runtime, byte_val - 256);
}
return self.callMethod(context, "[]=", new IRubyObject[] { idx, val });
}
}
}