Skip to content

Commit

Permalink
Showing 34 changed files with 3,673 additions and 356 deletions.
6 changes: 1 addition & 5 deletions core/src/main/java/org/jruby/RubyComparable.java
Original file line number Diff line number Diff line change
@@ -35,13 +35,9 @@

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.common.RubyWarnings;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.concurrent.Callable;

import static org.jruby.runtime.Helpers.invokedynamic;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_CMP;

@@ -68,7 +64,7 @@ public static RubyModule createComparable(Ruby runtime) {
*
*/
public static int cmpint(ThreadContext context, IRubyObject val, IRubyObject a, IRubyObject b) {
if (val.isNil()) cmperr(a, b);
if (val == context.nil) cmperr(a, b);
if (val instanceof RubyFixnum) {
final int asInt = RubyNumeric.fix2int((RubyFixnum) val);

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyProc.java
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, String fi
}


RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
public RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
this(runtime, rubyClass, block.type);
this.block = block;
this.file = file;
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/RubyRange.java
Original file line number Diff line number Diff line change
@@ -760,6 +760,8 @@ public IRubyObject size(ThreadContext context) {
return context.nil;
}

public final boolean isExcludeEnd() { return isExclusive; }

private static final ObjectMarshal RANGE_MARSHAL = new ObjectMarshal() {
@Override
public void marshalTo(Ruby runtime, Object obj, RubyClass type,
60 changes: 59 additions & 1 deletion core/src/main/java/org/jruby/java/proxies/ArrayJavaProxy.java
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

public class ArrayJavaProxy extends JavaProxy {
public final class ArrayJavaProxy extends JavaProxy {

private final JavaUtil.JavaConverter converter;

@@ -343,6 +343,64 @@ public int hashCode() {
return 11 * Arrays.hashCode((Object[]) array);
}

@Override
public IRubyObject dup() {
final Ruby runtime = getRuntime();

RubyObject dup = new ArrayJavaProxy(runtime, getMetaClass(), cloneObject(), converter);

if (isTaint()) dup.setTaint(true);
initCopy(dup, this, "initialize_dup");

return dup;
}

private static void initCopy(IRubyObject clone, IRubyObject original, String method) {
original.copySpecialInstanceVariables(clone);
if (original.hasVariables()) clone.syncVariables(original);
}

@Override
@JRubyMethod(name = "clone")
public IRubyObject rbClone() {
final Ruby runtime = getRuntime();

RubyObject clone = new ArrayJavaProxy(runtime, getMetaClass(), cloneObject(), converter);
clone.setMetaClass(getSingletonClassClone());

if (isTaint()) clone.setTaint(true);
initCopy(clone, this, "initialize_clone");
if (isFrozen()) clone.setFrozen(true);

return clone;
}

@Override
protected Object cloneObject() {
final Object array = getObject();
final Class<?> componentType = array.getClass().getComponentType();
if ( componentType.isPrimitive() ) {
switch ( componentType.getName().charAt(0) ) {
case 'b':
if (componentType == byte.class) return ((byte[]) array).clone();
else /* if (componentType == boolean.class) */ return ((boolean[]) array).clone();
case 's':
/* if (componentType == short.class) */ return ((short[]) array).clone();
case 'c':
/* if (componentType == char.class) */ return ((char[]) array).clone();
case 'i':
/* if (componentType == int.class) */ return ((int[]) array).clone();
case 'l':
/* if (componentType == long.class) */ return ((long[]) array).clone();
case 'f':
/* if (componentType == float.class) */ return ((float[]) array).clone();
case 'd':
/* if (componentType == double.class) */ return ((double[]) array).clone();
}
}
return ((Object[]) array).clone();
}

public IRubyObject getRange(ThreadContext context, IRubyObject[] args) {
if (args.length == 1) {
return getRange(context, args[0]);
13 changes: 12 additions & 1 deletion core/src/main/java/org/jruby/java/proxies/JavaProxy.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.JavaMethod;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
@@ -171,10 +172,20 @@ public static IRubyObject persistent(final ThreadContext context, final IRubyObj
public IRubyObject initialize_copy(IRubyObject original) {
super.initialize_copy(original);
// because we lazily init JavaObject in the data-wrapped slot, explicitly copy over the object
setObject( ((JavaProxy) original).getObject() );
setObject( ((JavaProxy) original).cloneObject() );
return this;
}

protected Object cloneObject() {
final Object object = getObject();
if (object instanceof Cloneable) {
// sufficient for java.util collection classes e.g. HashSet, ArrayList
Object clone = JavaUtil.clone(object);
return clone == null ? object : clone;
}
return object; // this is what JRuby did prior to <= 9.0.5
}

/**
* Create a name/newname map of fields to be exposed as methods.
*/
12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/java/proxies/MapJavaProxy.java
Original file line number Diff line number Diff line change
@@ -638,7 +638,7 @@ public RubyArray keys() {
/** rb_hash_values
*
*/
@JRubyMethod(name = "values")
@JRubyMethod(name = { "values", "ruby_values" }) // collision with java.util.Map#values
public RubyArray rb_values() {
return getOrCreateRubyHashMap().rb_values();
}
@@ -694,8 +694,8 @@ public IRubyObject reject_bang(final ThreadContext context, final Block block) {
/** rb_hash_clear
*
*/
@JRubyMethod(name = "clear")
public RubyHash rb_clear() {
@JRubyMethod(name = { "clear", "ruby_clear" }) // collision with java.util.Map#clear (return type)
public IRubyObject rb_clear() {
return getOrCreateRubyHashMap().rb_clear();
}

@@ -710,15 +710,15 @@ public RubyHash invert(final ThreadContext context) {
/** rb_hash_merge_bang
*
*/
@JRubyMethod(name = {"merge!", "update"}, required = 1)
@JRubyMethod(name = { "merge!", "update" }, required = 1)
public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
return getOrCreateRubyHashMap().merge_bang(context, other, block);
}

/** rb_hash_merge
*
*/
@JRubyMethod(name = "merge")
@JRubyMethod(name = { "merge", "ruby_merge" }) // collision with java.util.Map#merge on Java 8+
public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
return getOrCreateRubyHashMap().merge(context, other, block);
}
@@ -734,7 +734,7 @@ public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
/** rb_hash_replace
*
*/
@JRubyMethod(name = "replace", required = 1)
@JRubyMethod(name = { "replace", "ruby_replace" }, required = 1) // collision with java.util.Map#replace on Java 8+
public RubyHash replace(final ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().replace(context, other);
}
15 changes: 11 additions & 4 deletions core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -91,13 +91,11 @@
import org.jruby.java.proxies.JavaInterfaceTemplate;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.java.proxies.RubyObjectHolderProxy;
import org.jruby.java.util.BlankSlateWrapper;
import org.jruby.java.util.SystemPropertiesMap;
import org.jruby.javasupport.proxy.JavaProxyClassFactory;
import org.jruby.util.OneShotClassLoader;
import org.jruby.util.ByteList;
import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.ClassProvider;
import org.jruby.util.IdUtil;
import org.jruby.util.JRubyClassLoader;
import org.jruby.util.SafePropertyAccessor;
@@ -118,10 +116,19 @@ public void load(Ruby runtime, boolean wrap) {

JavaPackage.createJavaPackageClass(runtime, Java);

org.jruby.javasupport.ext.Kernel.define(runtime);

org.jruby.javasupport.ext.JavaLang.define(runtime);
org.jruby.javasupport.ext.JavaLangReflect.define(runtime);
org.jruby.javasupport.ext.JavaUtil.define(runtime);
org.jruby.javasupport.ext.JavaUtilRegex.define(runtime);
org.jruby.javasupport.ext.JavaIo.define(runtime);
org.jruby.javasupport.ext.JavaNet.define(runtime);

// load Ruby parts of the 'java' library
runtime.getLoadService().load("jruby/java.rb", false);

// rewite ArrayJavaProxy superclass to point at Object, so it inherits Object behaviors
// rewire ArrayJavaProxy superclass to point at Object, so it inherits Object behaviors
final RubyClass ArrayJavaProxy = runtime.getClass("ArrayJavaProxy");
ArrayJavaProxy.setSuperClass(runtime.getJavaSupport().getObjectJavaClass().getProxyClass());
ArrayJavaProxy.includeModule(runtime.getEnumerable());
@@ -689,7 +696,7 @@ public static RubyModule getJavaPackageModule(final Ruby runtime, final Package
return getJavaPackageModule(runtime, pkg == null ? "" : pkg.getName());
}

private static RubyModule getJavaPackageModule(final Ruby runtime, final String packageString) {
public static RubyModule getJavaPackageModule(final Ruby runtime, final String packageString) {
final String packageName; final int length;
if ( ( length = packageString.length() ) == 0 ) {
packageName = "Default";
9 changes: 2 additions & 7 deletions core/src/main/java/org/jruby/javasupport/JavaCallable.java
Original file line number Diff line number Diff line change
@@ -139,16 +139,11 @@ protected final void checkArity(final int length) {
}

final Object[] convertArguments(final IRubyObject[] args) {
return convertArguments(args, 0);
return JavaUtil.convertArguments(args, parameterTypes, 0);
}

final Object[] convertArguments(final IRubyObject[] args, int offset) {
final Object[] arguments = new Object[ args.length - offset ];
final Class<?>[] types = parameterTypes;
for ( int i = arguments.length; --i >= 0; ) {
arguments[i] = args[ i + offset ].toJava( types[i] );
}
return arguments;
return JavaUtil.convertArguments(args, parameterTypes, offset);
}

protected final IRubyObject handleThrowable(ThreadContext context, final Throwable ex) {
36 changes: 36 additions & 0 deletions core/src/main/java/org/jruby/javasupport/JavaUtil.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.Character.isLetter;
import static java.lang.Character.isLowerCase;
@@ -507,6 +508,41 @@ else if (rubyName.startsWith("is_")) {
}
}

public static Object[] convertArguments(final IRubyObject[] args, final Class<?>[] types) {
return convertArguments(args, types, 0);
}

public static Object[] convertArguments(final IRubyObject[] args, final Class<?>[] types, int offset) {
final Object[] arguments = new Object[ args.length - offset ];
for ( int i = arguments.length; --i >= 0; ) {
arguments[i] = args[ i + offset ].toJava( types[i] );
}
return arguments;
}

/**
* Clone a Java object, assuming its class has an accessible <code>clone</code> method.
* @param object
* @return cloned object or null (if method is not found or inaccessible)
*/
public static <T> T clone(final Object object) {
return (T) clone(object, false);
}

static Object clone(final Object object, final boolean silent) {
try {
final Method clone = object.getClass().getMethod("clone");
return clone.invoke(object);
}
catch (NoSuchMethodException|IllegalAccessException e) {
return null;
}
catch (InvocationTargetException e) {
if ( ! silent ) Helpers.throwException(e.getTargetException());
return null;
}
}

public static abstract class JavaConverter {
private final Class type;
public JavaConverter(Class type) {this.type = type;}
140 changes: 140 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaIo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.Ruby;
import org.jruby.RubyIO;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;
import static org.jruby.runtime.Visibility.PUBLIC;

/**
* <code>java.io</code> package Ruby additions.
*
* @author kares
*/
public abstract class JavaIo {

public static void define(final Ruby runtime) {
RubyModule proxyClass;

proxyClass = Java.getProxyClass(runtime, java.io.InputStream.class);
proxyClass.addMethodInternal("to_io", new InputStreamToIO(proxyClass));

proxyClass = Java.getProxyClass(runtime, java.io.OutputStream.class);
proxyClass.addMethodInternal("to_io", new OutputStreamToIO(proxyClass));

proxyClass = Java.getProxyClass(runtime, java.nio.channels.Channel.class);
proxyClass.addMethodInternal("to_io", new ChannelToIO(proxyClass));
}

private static final class InputStreamToIO extends JavaMethod.JavaMethodZeroOrOne {

InputStreamToIO(RubyModule implClass) {
super(implClass, PUBLIC);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return to_io(context, (java.io.InputStream) unwrapJavaObject(self), null);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject opts) {
return to_io(context, (java.io.InputStream) unwrapJavaObject(self), opts);
}

}

static RubyIO to_io(final ThreadContext context, final java.io.InputStream stream, final IRubyObject opts) {
final RubyIO io = new RubyIO(context.runtime, stream);
setAutoclose(context, io, opts);
return io;
}

private static final class OutputStreamToIO extends JavaMethod.JavaMethodZeroOrOne {

OutputStreamToIO(RubyModule implClass) {
super(implClass, PUBLIC);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return to_io(context, (java.io.OutputStream) unwrapJavaObject(self), null);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject opts) {
return to_io(context, (java.io.OutputStream) unwrapJavaObject(self), opts);
}

}

static RubyIO to_io(final ThreadContext context, final java.io.OutputStream stream, final IRubyObject opts) {
final RubyIO io = new RubyIO(context.runtime, stream);
setAutoclose(context, io, opts);
return io;
}

private static final class ChannelToIO extends JavaMethod.JavaMethodZeroOrOne {

ChannelToIO(RubyModule implClass) {
super(implClass, PUBLIC);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return to_io(context, self, null);
}

@Override
public RubyIO call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject opts) {
return to_io(context, self, opts);
}

private static RubyIO to_io(final ThreadContext context, final IRubyObject self, final IRubyObject opts) {
final RubyIO io = new RubyIO(context.runtime, (java.nio.channels.Channel) unwrapJavaObject(self));
setAutoclose(context, io, opts);
return io;
}

}

private static void setAutoclose(final ThreadContext context, final RubyIO io, final IRubyObject opts) {
if ( opts != null && opts != context.nil ) {
IRubyObject autoclose = opts.callMethod(context, "[]", context.runtime.newSymbol("autoclose"));
if ( autoclose != context.nil ) io.setAutoclose( autoclose.isTrue() );
}
}

}
511 changes: 511 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaLang.java

Large diffs are not rendered by default.

330 changes: 330 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaLangReflect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.reflect.InvocationTargetException;

import static org.jruby.javasupport.JavaUtil.convertArguments;
import static org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject;
import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;

/**
* Java::JavaLangReflect package extensions.
*
* @author kares
*/
public abstract class JavaLangReflect {

public static void define(final Ruby runtime) {
Constructor.define(runtime);
Field.define(runtime);
Method.define(runtime);
}

@JRubyClass(name = "Java::JavaLangReflect::Constructor")
public static class Constructor {

static RubyClass define(final Ruby runtime) {
final RubyModule Constructor = Java.getProxyClass(runtime, java.lang.reflect.Constructor.class);
Constructor.defineAnnotatedMethods(Constructor.class);
return (RubyClass) Constructor;
}

@JRubyMethod
public static IRubyObject return_type(final ThreadContext context, final IRubyObject self) {
return context.nil;
}

@JRubyMethod // alias argument_types parameter_types
public static IRubyObject argument_types(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return convertJavaToUsableRubyObject(context.runtime, thiz.getParameterTypes());
}

//

@JRubyMethod
public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.AccessibleObject thiz = unwrapJavaObject(self);
return RubyString.newString(context.runtime, thiz.toString());
}

// JavaUtilities::ModifiedShortcuts :

@JRubyMethod(name = "public?")
public static IRubyObject public_p(final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return isPublic(self, thiz.getModifiers());
}

@JRubyMethod(name = "protected?")
public static IRubyObject protected_p(final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return isProtected(self, thiz.getModifiers());
}

@JRubyMethod(name = "private?")
public static IRubyObject private_p(final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return isPrivate(self, thiz.getModifiers());
}

@JRubyMethod(name = "final?")
public static IRubyObject final_p(final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return isFinal(self, thiz.getModifiers());
}

@JRubyMethod(name = "static?")
public static IRubyObject static_p(final IRubyObject self) {
final java.lang.reflect.Constructor thiz = unwrapJavaObject(self);
return isStatic(self, thiz.getModifiers());
}

}

@JRubyClass(name = "Java::JavaLangReflect::Method")
public static class Method {

static RubyClass define(final Ruby runtime) {
final RubyModule Method = Java.getProxyClass(runtime, java.lang.reflect.Method.class);
Method.defineAnnotatedMethods(Method.class);
return (RubyClass) Method;
}

@JRubyMethod
public static IRubyObject return_type(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return convertJavaToUsableRubyObject(context.runtime, thiz.getReturnType());
}

@JRubyMethod // alias argument_types parameter_types
public static IRubyObject argument_types(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return convertJavaToUsableRubyObject(context.runtime, thiz.getParameterTypes());
}

@JRubyMethod(rest = true)
public static IRubyObject invoke_static(final ThreadContext context, final IRubyObject self, final IRubyObject[] args) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
final Object[] javaArgs = convertArguments(args, thiz.getParameterTypes());
try {
return convertJavaToUsableRubyObject(context.runtime, thiz.invoke(null, javaArgs));
}
catch (IllegalAccessException|InvocationTargetException e) {
Helpers.throwException(e); return null;
}
}

//

@JRubyMethod
public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.AccessibleObject thiz = unwrapJavaObject(self);
return RubyString.newString(context.runtime, thiz.toString());
}

@JRubyMethod(name = "abstract?")
public static IRubyObject abstract_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isAbstract(self, thiz.getModifiers());
}

// JavaUtilities::ModifiedShortcuts :

@JRubyMethod(name = "public?")
public static IRubyObject public_p(final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return isPublic(self, thiz.getModifiers());
}

@JRubyMethod(name = "protected?")
public static IRubyObject protected_p(final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return isProtected(self, thiz.getModifiers());
}

@JRubyMethod(name = "private?")
public static IRubyObject private_p(final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return isPrivate(self, thiz.getModifiers());
}

@JRubyMethod(name = "final?")
public static IRubyObject final_p(final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return isFinal(self, thiz.getModifiers());
}

@JRubyMethod(name = "static?")
public static IRubyObject static_p(final IRubyObject self) {
final java.lang.reflect.Method thiz = unwrapJavaObject(self);
return isStatic(self, thiz.getModifiers());
}

}

@JRubyClass(name = "Java::JavaLangReflect::Field")
public static class Field {

static RubyClass define(final Ruby runtime) {
final RubyModule Field = Java.getProxyClass(runtime, java.lang.reflect.Field.class);
Field.defineAnnotatedMethods(Field.class);
return (RubyClass) Field;
}

@JRubyMethod // alias value_type name
public static IRubyObject value_type(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.Field field = unwrapJavaObject(self);
return convertJavaToUsableRubyObject(context.runtime, field.getName());
}

@JRubyMethod // alias value get
public static IRubyObject value(final ThreadContext context, final IRubyObject self, final IRubyObject obj) {
final java.lang.reflect.Field field = unwrapJavaObject(self);
try {
return convertJavaToUsableRubyObject(context.runtime, field.get(unwrapJavaObject(obj)));
}
catch (IllegalAccessException e) {
Helpers.throwException(e); return null;
}
}

@JRubyMethod // alias set_value set
public static IRubyObject set_value(final ThreadContext context, final IRubyObject self, final IRubyObject obj,
final IRubyObject value) {
final java.lang.reflect.Field field = unwrapJavaObject(self);
try {
final Object val = value.toJava(field.getType());
field.set(unwrapJavaObject(obj), val);
}
catch (IllegalAccessException e) {
Helpers.throwException(e); return null;
}
return context.nil;
}

@JRubyMethod
public static IRubyObject static_value(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.Field field = unwrapJavaObject(self);
try {
return convertJavaToUsableRubyObject(context.runtime, field.get(null));
}
catch (IllegalAccessException e) {
Helpers.throwException(e); return null;
}
}

@JRubyMethod
public static IRubyObject set_static_value(final ThreadContext context, final IRubyObject self, final IRubyObject value) {
final java.lang.reflect.Field field = unwrapJavaObject(self);
try {
final Object val = value.toJava(field.getType());
field.set(null, val);
}
catch (IllegalAccessException e) {
Helpers.throwException(e); return null;
}
return context.nil;
}

//

@JRubyMethod
public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) {
final java.lang.reflect.AccessibleObject thiz = unwrapJavaObject(self);
return RubyString.newString(context.runtime, thiz.toString());
}

// JavaUtilities::ModifiedShortcuts :

@JRubyMethod(name = "public?")
public static IRubyObject public_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isPublic(self, thiz.getModifiers());
}

@JRubyMethod(name = "protected?")
public static IRubyObject protected_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isProtected(self, thiz.getModifiers());
}

@JRubyMethod(name = "private?")
public static IRubyObject private_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isPrivate(self, thiz.getModifiers());
}

@JRubyMethod(name = "final?")
public static IRubyObject final_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isFinal(self, thiz.getModifiers());
}

@JRubyMethod(name = "static?")
public static IRubyObject static_p(final IRubyObject self) {
final java.lang.reflect.Field thiz = unwrapJavaObject(self);
return isStatic(self, thiz.getModifiers());
}

}

static RubyBoolean isAbstract(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isAbstract(mod));
}

static RubyBoolean isPublic(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isPublic(mod));
}

static RubyBoolean isProtected(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isProtected(mod));
}

static RubyBoolean isPrivate(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isPrivate(mod));
}

static RubyBoolean isFinal(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isFinal(mod));
}

static RubyBoolean isStatic(final IRubyObject self, final int mod) {
return self.getRuntime().newBoolean(java.lang.reflect.Modifier.isStatic(mod));
}

}
101 changes: 101 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaNet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.Ruby;
import org.jruby.RubyIO;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.io.IOException;
import java.io.InputStream;

import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;
import static org.jruby.runtime.Visibility.PUBLIC;

/**
* <code>java.net</code> package Ruby additions.
*
* @author kares
*/
public abstract class JavaNet {

public static void define(final Ruby runtime) {
RubyModule proxyClass;

proxyClass = Java.getProxyClass(runtime, java.net.URL.class);
proxyClass.addMethodInternal("open", new URLOpenMethod(proxyClass));
}

private static final class URLOpenMethod extends JavaMethod.JavaMethodZeroOrNBlock {

URLOpenMethod(RubyModule implClass) {
super(implClass, PUBLIC);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
java.net.URL url = unwrapJavaObject(self);
final InputStream stream; final RubyIO io;
try {
stream = url.openStream();
io = JavaIo.to_io(context, stream, null);

if ( block.isGiven() ) {
try {
return block.yield(context, io);
}
finally {
stream.close();
}
}
}
catch (IOException e) {
Helpers.throwException(e); return null; // throw context.runtime.newIOErrorFromException(e);
}
return io; // unless block_given?
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
return call(context, self, clazz, name, block);
}

}

}
614 changes: 614 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java

Large diffs are not rendered by default.

272 changes: 272 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/JavaUtilRegex.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject;
import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;

/**
* Java::JavaLangReflect package extensions.
*
* @author kares
*/
public abstract class JavaUtilRegex {

public static void define(final Ruby runtime) {
Pattern.define(runtime);
Matcher.define(runtime);
}

@JRubyClass(name = "Java::JavaUtilRegex::Pattern")
public static class Pattern {

static RubyClass define(final Ruby runtime) {
final RubyModule Pattern = Java.getProxyClass(runtime, java.util.regex.Pattern.class);
Pattern.defineAnnotatedMethods(Pattern.class);
return (RubyClass) Pattern;
}

@JRubyMethod(name = "=~", required = 1)
public static IRubyObject op_match(final ThreadContext context, final IRubyObject self, IRubyObject str) {
final java.util.regex.Matcher matcher = matcher(self, str);
return matcher.find() ? context.runtime.newFixnum(matcher.start()) : context.nil;
}

@JRubyMethod(name = "match", required = 1)
public static IRubyObject match(final ThreadContext context, final IRubyObject self, IRubyObject str) {
final java.util.regex.Matcher matcher = matcher(self, str);
if ( ! matcher.find() ) return context.nil;
final RubyObject matcherProxy = (RubyObject) convertJavaToUsableRubyObject(context.runtime, matcher);
matcherProxy.setInternalVariable("str", str); // matcher.str = str
return matcherProxy;
}

@JRubyMethod(name = "===", required = 1)
public static IRubyObject eqq(final ThreadContext context, final IRubyObject self, IRubyObject str) {
return context.runtime.newBoolean( matcher(self, str).find() );
}

@JRubyMethod(name = "casefold?")
public static IRubyObject casefold_p(final ThreadContext context, final IRubyObject self) {
final java.util.regex.Pattern regex = unwrapJavaObject(self);
boolean i = ( regex.flags() & java.util.regex.Pattern.CASE_INSENSITIVE ) != 0;
return context.runtime.newBoolean(i);
}

private static java.util.regex.Matcher matcher(final IRubyObject self, final IRubyObject str) {
final java.util.regex.Pattern regex = unwrapJavaObject(self);
return regex.matcher((CharSequence) str.toJava(CharSequence.class));
}

}

@JRubyClass(name = "Java::JavaUtilRegex::Matcher")
public static class Matcher {

static RubyClass define(final Ruby runtime) {
final RubyModule Matcher = Java.getProxyClass(runtime, java.util.regex.Matcher.class);
Matcher.defineAnnotatedMethods(Matcher.class);
return (RubyClass) Matcher;
}

@JRubyMethod
public static IRubyObject regexp(final ThreadContext context, final IRubyObject self) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
return convertJavaToUsableRubyObject(context.runtime, matcher.pattern());
}

@JRubyMethod
public static IRubyObject begin(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
if ( idx instanceof RubySymbol ) {
return context.runtime.newFixnum( matcherStart(matcher, idx.toString()) );
}
final int group = idx.convertToInteger().getIntValue();
return context.runtime.newFixnum(matcher.start(group));
}

@JRubyMethod
public static IRubyObject end(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
if ( idx instanceof RubySymbol ) {
return context.runtime.newFixnum( matcherEnd(matcher, idx.toString()) );
}
final int group = idx.convertToInteger().getIntValue();
return context.runtime.newFixnum(matcher.end(group));
}

@JRubyMethod
public static IRubyObject offset(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
final IRubyObject beg; final IRubyObject end;
if ( idx instanceof RubySymbol ) {
beg = context.runtime.newFixnum( matcherStart(matcher, idx.toString()) );
end = context.runtime.newFixnum( matcherEnd(matcher, idx.toString()) );
}
else {
final int group = idx.convertToInteger().getIntValue();
beg = context.runtime.newFixnum( matcher.start(group) );
end = context.runtime.newFixnum( matcher.end(group) );
}
return RubyArray.newArrayNoCopy(context.runtime, new IRubyObject[]{beg, end});
}

@JRubyMethod(name = { "length", "size" })
public static RubyFixnum size(final ThreadContext context, final IRubyObject self) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
return context.runtime.newFixnum(matcher.groupCount() + 1); // the Ruby way!
}

@JRubyMethod
public static RubyString string(final ThreadContext context, final IRubyObject self) {
return str(context, self);
}

private static RubyString str(final ThreadContext context, final IRubyObject self) {
final IRubyObject str = (IRubyObject) self.getInternalVariables().getInternalVariable("str");
return /* str == null ? string(context, self) : */ str.convertToString();
}

@JRubyMethod // str[ 0..start(0) ]
public static IRubyObject pre_match(final ThreadContext context, final IRubyObject self) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
return str(context, self).substr(context.runtime, 0, matcher.start(0));
}

@JRubyMethod // str[ end(0)..-1 ]
public static IRubyObject post_match(final ThreadContext context, final IRubyObject self) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
final RubyString str = str(context, self);
final int offset = matcher.end(0);
return str.substr(context.runtime, offset, str.size() - offset);
}

@JRubyMethod
public static RubyArray to_a(final ThreadContext context, final IRubyObject self) {
return RubyArray.newArrayNoCopy(context.runtime, groups(context, self, 0));
}

@JRubyMethod
public static RubyArray captures(final ThreadContext context, final IRubyObject self) {
return RubyArray.newArrayNoCopy(context.runtime, groups(context, self, 1));
}

private static IRubyObject[] groups(final ThreadContext context, final IRubyObject self, final int off) {
final Ruby runtime = context.runtime;
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
final IRubyObject[] arr = new IRubyObject[ matcher.groupCount() - off + 1 ];
for ( int i = 0; i < arr.length; i++ ) {
if ( matcher.start(i + off) == -1 ) {
arr[i] = context.nil;
}
else {
arr[i] = runtime.newString(matcher.group(i + off));
}
}
return arr;
}

@JRubyMethod(name = "[]", required = 1)
public static IRubyObject aref(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
final java.util.regex.Matcher matcher = unwrapJavaObject(self);
if ( idx instanceof RubySymbol || idx instanceof RubyString ) {
return context.runtime.newString( matcher.group(idx.toString()) );
}
if ( idx instanceof RubyInteger ) {
final int group = ((RubyInteger) idx).getIntValue();
return context.runtime.newString( matcher.group(group) );
}
return to_a(context, self).aref(idx); // Range
}

@JRubyMethod(name = "[]", required = 2)
public static IRubyObject aref(final ThreadContext context, final IRubyObject self,
final IRubyObject arg0, final IRubyObject arg1) {
return to_a(context, self).aref(arg0, arg1);
}

@JRubyMethod(rest = true)
public static IRubyObject values_at(final ThreadContext context, final IRubyObject self, final IRubyObject[] args) {
return to_a(context, self).values_at(args);
}

private static Integer matcherStart(final java.util.regex.Matcher matcher, final String group) {
if ( startMethod == null ) { // only available since Java 8
throw new UnsupportedOperationException("start(String) only works on Java 8+");
}
return invoke(startMethod, matcher, group);
}

private static Integer matcherEnd(final java.util.regex.Matcher matcher, final String group) {
if ( endMethod == null ) { // only available since Java 8
throw new UnsupportedOperationException("end(String) only works on Java 8+");
}
return invoke(endMethod, matcher, group);
}

private static Integer invoke(final Method method, final java.util.regex.Matcher matcher, final String group) {
try {
return (Integer) method.invoke(matcher, group);
}
catch (IllegalAccessException e) {
Helpers.throwException(e); return null;
}
catch (InvocationTargetException e) {
Helpers.throwException(e.getTargetException()); return null;
}
}

private static final Method startMethod;
private static final Method endMethod;

static {
Method start, end;
try {
start = java.util.regex.Matcher.class.getMethod("start", String.class);
end = java.util.regex.Matcher.class.getMethod("end", String.class);
}
catch (NoSuchMethodException e) {
start = end = null; // Java 7
}
startMethod = start; endMethod = end;
}

}

}
75 changes: 75 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ext/Kernel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import static org.jruby.runtime.Visibility.PUBLIC;

/**
* Kernel added Java short-cut methods.
*
* @author kares
*/
public final class Kernel {

public static void define(final Ruby runtime) {
runtime.getKernel().defineAnnotatedMethods(Kernel.class);
final RubyModule Kernel = runtime.getKernel();
// can share the method since it receives its interned name
JavaPackageMethod get_pkg = new JavaPackageMethod(Kernel);
Kernel.addMethodInternal("java", get_pkg);
Kernel.addMethodInternal("javax", get_pkg);
Kernel.addMethodInternal("javafx", get_pkg);
Kernel.addMethodInternal("com", get_pkg);
Kernel.addMethodInternal("org", get_pkg);
}

private static final class JavaPackageMethod extends JavaMethod.JavaMethodZero {

JavaPackageMethod(RubyModule implClass) {
super(implClass, PUBLIC);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return get_pkg(context, name);
}
}

private static IRubyObject get_pkg(final ThreadContext context, final String name) {
RubyModule module = Java.getJavaPackageModule(context.runtime, name);
return module == null ? context.nil : module;
}

}
2 changes: 1 addition & 1 deletion core/src/main/ruby/jruby/java/core_ext.rb
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@
# These are loads so they don't pollute LOADED_FEATURES
load 'jruby/java/core_ext/module.rb'
load 'jruby/java/core_ext/object.rb'
load 'jruby/java/core_ext/kernel.rb'
#load 'jruby/java/core_ext/kernel.rb' - moved to org.jruby.javasupport.ext.Kernel
10 changes: 5 additions & 5 deletions core/src/main/ruby/jruby/java/java_ext.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Extensions to Java classes

# These are loads so they don't pollute LOADED_FEATURES
load 'jruby/java/java_ext/java.lang.rb'
load 'jruby/java/java_ext/java.util.rb'
load 'jruby/java/java_ext/java.util.regex.rb'
load 'jruby/java/java_ext/java.io.rb'
load 'jruby/java/java_ext/java.net.rb'
# load 'jruby/java/java_ext/java.lang.rb' - moved to native
# load 'jruby/java/java_ext/java.util.rb' - moved to native
# load 'jruby/java/java_ext/java.util.regex.rb' - moved to native
# load 'jruby/java/java_ext/java.io.rb' - moved to native
# load 'jruby/java/java_ext/java.net.rb' - moved to native
49 changes: 31 additions & 18 deletions core/src/main/ruby/jruby/java/java_ext/java.io.rb
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
# NOTE: these Ruby extensions were moved to native code!
# @see org.jruby.javasupport.ext.JavaIo.java
# this file is no longer loaded but is kept to provide doc stubs

# Java *java.io.InputStream* objects are convertible to Ruby `IO`.
# @note Only explicit (or customized) Ruby methods are listed here,
# instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html
class Java::java::io::InputStream
# Convert a Java input stream to a Ruby `IO`.
# @option opts [Types] autoclose changes `IO#autoclose=` if set
# @return [IO]
def to_io(opts = nil)
ruby_io = org.jruby.RubyIO.new(JRuby.runtime, self)
if opts && !opts[:autoclose]
ruby_io.setAutoclose(false)
end
JRuby.dereference(ruby_io)
# stub implemented in org.jruby.javasupport.ext.JavaIo.java
end
end
end if false

# Java *java.io.OutputStream* objects are convertible to Ruby `IO`.
# @note Only explicit (or customized) Ruby methods are listed here,
# instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html
class Java::java::io::OutputStream
# Convert a Java output stream to a Ruby `IO`.
# @option opts [Types] autoclose changes `IO#autoclose=` if set
# @return [IO]
def to_io(opts = nil)
ruby_io = org.jruby.RubyIO.new(JRuby.runtime, self)
if opts && !opts[:autoclose]
ruby_io.setAutoclose(false)
end
JRuby.dereference(ruby_io)
# stub implemented in org.jruby.javasupport.ext.JavaIo.java
end
end
end if false

# Java channels (*java.nio.channels.Channel*) are convertible to Ruby `IO`.
# @note Only explicit (or customized) Ruby methods are listed here,
# instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/nio/channels/Channel.html
module Java::java::nio::channels::Channel
# Convert a Java channel to a Ruby `IO`.
# @option opts [Types] autoclose changes `IO#autoclose=` if set
# @return [IO]
def to_io(opts = nil)
ruby_io = org.jruby.RubyIO.new(JRuby.runtime, self)
if opts && !opts[:autoclose]
ruby_io.setAutoclose(false)
end
JRuby.dereference(ruby_io)
# stub implemented in org.jruby.javasupport.ext.JavaIo.java
end
end
end if false
358 changes: 276 additions & 82 deletions core/src/main/ruby/jruby/java/java_ext/java.lang.rb

Large diffs are not rendered by default.

37 changes: 24 additions & 13 deletions core/src/main/ruby/jruby/java/java_ext/java.net.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
# NOTE: these Ruby extensions were moved to native code!
# - **org.jruby.javasupport.ext.JavaNet.java**
# this file is no longer loaded but is kept to provide doc stubs

# *java.net.URL* extensions.
# @note Only explicit (or customized) Ruby methods are listed here,
# instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/net/URL.html
class Java::java::net::URL
def open(*rest, &block)
stream = openStream
io = stream.to_io
if block
begin
block.call(io)
ensure
stream.close
end
else
io
end
# Open the URL stream and yield it as a Ruby `IO`.
# @return [IO] if no block given, otherwise yielded result
def open(&block)
# stub implemented in org.jruby.javasupport.ext.JavaNet.java
# stream = openStream
# io = stream.to_io
# if block
# begin
# block.call(io)
# ensure
# stream.close
# end
# else
# io
# end
end
end
end if false
496 changes: 398 additions & 98 deletions core/src/main/ruby/jruby/java/java_ext/java.util.rb

Large diffs are not rendered by default.

192 changes: 137 additions & 55 deletions core/src/main/ruby/jruby/java/java_ext/java.util.regex.rb
Original file line number Diff line number Diff line change
@@ -1,83 +1,165 @@
# NOTE: these Ruby extensions were moved to native code!
# @see org.jruby.javasupport.ext.JavaUtilRegex.java
# this file is no longer loaded but is kept to provide doc stubs

# *java.util.regex.Pattern* enhanced to act similar to Ruby's `Regexp`.
# @note Only explicit (or customized) Ruby methods are listed, instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html
class Java::java::util::regex::Pattern
# Matches this pattern against provided string.
# @return [Integer, nil] start (index) of the match if any
def =~(str)
m = self.matcher(str)
m.find ? m.start : nil
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
# m = matcher(str)
# m.find ? m.start : nil
end


# Case equality for Java patterns.
# @return [true, false]
def ===(str)
self.matcher(str).find
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
# matcher(str).find
end

# Returns a `Matcher` object describing the match against the string (or nil if there was no match).
# @example
# pattern = java.util.regex.Pattern.compile('[a-f]')
# matcher = pattern.match('abcdef') # java.util.regex.Matcher[pattern=[a-f] region=0,6 lastmatch=a]
# @return [Java::java::util::regex::Matcher, nil]
def match(str)
m = self.matcher(str)
m.str = str
m.find ? m : nil
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
# m = matcher(str)
# m.find ? m : nil
end

# Returns the value of the case-insensitive flag.
# @return [true, false]
# @since 9.1
def casefold?
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end
end
end if false

# *java.util.regex.Matcher* represents a Java regex `Pattern` match, customized to quack like Ruby's `MatchData`.
# @note Only explicit (or customized) Ruby methods are listed, instances will have all of their Java methods available.
# @see http://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html
class Java::java::util::regex::Matcher
attr_accessor :str

# @private
#attr_accessor :str

# @return [Java::java::util::regex::Pattern]
# @since 9.1
def regexp
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def string
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

# Returns an array of captures.
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# pattern.match('THX1138.').captures # ['H', 'X', '113', '8']
# @return [Array]
# @see #to_a
def captures
g = self.group_count
capt = []
count.times do |i|
capt << self.group(i+1)
end
capt
end

# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

# Matcher acts like an array and its capture elements might be accessed.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# matcher = pattern.match('THX1138.')
# expect( m[0] ).to eq 'HX1138'
# expect( m[1, 2] ).to eq ['H', 'X']
# expect( m[1..3] ).to eq ['H', 'X', '113']
# @return [Array]
# @see #to_a
def [](*args)
self.to_a[*args]
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def begin(ix)
self.start(ix)
# Returns the array of matches.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# pattern.match('THX1138.').captures # ['HX1138', 'H', 'X', '113', '8']
# @return [Array]
def to_a
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end
def end(ix)
self.java_send(:end, [Java::int], ix)

def values_at(*args)
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def to_a
arr = []
self.group_count.times do |gg|
if self.start(gg) == -1
arr << nil
else
arr << self.group(gg)
end
end
arr
end


# Returns the number of elements in the match array.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# pattern.match('THX1138.').size # 5
# @return [Integer]
def size
self.group_count
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

alias length size

def values_at(*args)
self.to_a.values_at(*args)

# Returns the offset of the start of the n-th element of the match array in the string.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# matcher = pattern.match('THX1138.')
# expect( matcher.begin(0) ).to eq 1
# expect( matcher.begin(2) ).to eq 2
# @param n can be a string or symbol to reference a named capture
# @return [Integer]
# @see #offset
# @see #end
# @note Named captures referencing is not available on Java 7.
def begin(n)
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def select
yield self.to_a
# Returns the offset of the character immediately following the end of the n-th element of the match array in the string.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# matcher = pattern.match('THX1138.')
# expect( matcher.begin(0) ).to eq 7
# expect( matcher.begin(2) ).to eq 3
# @param n can be a string or symbol to reference a named capture
# @return [Integer]
# @see #offset
# @see #begin
# @note Named captures referencing is not available on Java 7.
def end(n)
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def offset(ix)
[self.start(ix), self.end(ix)]

# Returns the offset of the character immediately following the end of the n-th element of the match array in the string.
# @example
# pattern = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
# matcher = pattern.match('THX1138.')
# expect( m.offset(0) ).to eq [1, 7]
# expect( m.offset(4) ).to eq [6, 7]
#
# pattern = java.util.regex.Pattern.compile("(?<foo>.)(.)(?<bar>.)")
# matcher = pattern.match('hoge')
# expect( m.offset(:bar) ).to eq [2, 3]
# @param n can be a string or symbol to reference a named capture
# @return [Array]
# @see #begin
# @see #end
def offset(n)
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

# Returns the portion of the original string before the current match.
# @return [String]
def pre_match
self.str[0..(self.start(0))]
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end


# Returns the portion of the original string after the current match.
# @return [String]
def post_match
self.str[(self.end(0))..-1]
# stub implemented in org.jruby.javasupport.ext.JavaUtilRegex.java
end

def string
self.group(0)
end
end

end if false
41 changes: 41 additions & 0 deletions spec/java_integration/classes/eqq_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe "java.lang.Throwable" do

it 'class is not === to itself (like in Ruby)' do
# NOTE: Exception === Exception does not hold!
expect( java.lang.Throwable === Java::JavaLang::Throwable ).to be false
expect( java.lang.Throwable === java.lang.Throwable.java_class ).to be false

expect( java.lang.RuntimeException === java.lang.RuntimeException ).to be false
end

it '=== matches instances' do
expect( java.lang.Throwable === java.lang.Throwable.new('msg') ).to be true
expect( java.lang.Exception === java.lang.RuntimeException.new(nil, nil) ).to be true
begin
java.lang.Integer.parseInt('aa', 10)
rescue java.lang.Exception => ex
expect( java.lang.Throwable === ex ).to be true
expect( java.lang.Exception === ex ).to be true
expect( java.lang.NumberFormatException === ex ).to be true
expect( java.lang.IllegalStateException === ex ).to be false
else
fail 'excepted to rescue!'
end
end

it '=== matches NativeException instances' do
begin
java.lang.Integer.parseInt('gg', 16)
rescue NativeException => ex
expect( java.lang.Throwable === ex ).to be true
expect( java.lang.Exception === ex ).to be true
expect( java.lang.NumberFormatException === ex ).to be true
expect( java.lang.IllegalStateException === ex ).to be false
else
fail 'excepted to rescue!'
end
end

end
173 changes: 173 additions & 0 deletions spec/java_integration/extensions/collection_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe "Collection Ruby extensions" do
before(:each) do
@data = ['foo', 'baz', 'bar', '']
end

it 'iterates using each' do
set = java.util.LinkedHashSet.new
@data.each { |elem| set.add elem }
data = []
set.each { |elem| data << elem }
expect(data).to eq @data

data = []
java.util.concurrent.LinkedBlockingQueue.new(@data).each { |elem| data << elem }
expect(data).to eq @data
end

it 'iterates with an Enumerator on #each' do
enum = java.util.concurrent.LinkedBlockingQueue.new(@data).each
expect( enum.next ).to eq 'foo'
expect( enum.next ).to eq 'baz'
expect( enum.next ).to eq 'bar'
enum.next
expect { enum.next }.to raise_error(StopIteration)
end

it 'iterates with index' do
set = java.util.LinkedHashSet.new
@data.each { |elem| set.add elem }
data = []; idx = []
set.each_with_index { |elem, i| data << elem; idx << i }
expect(data).to eq @data
expect(idx).to eq [0, 1, 2, 3]

data = []
java.util.concurrent.LinkedBlockingQueue.new.each_with_index { |elem| data << elem }
expect(data).to eq []
end

it 'iterates with an Enumerator on #each_with_index' do
enum = java.util.LinkedHashSet.new(@data).each_with_index
expect( enum.next[0] ).to eq 'foo'
expect( enum.next[1] ).to eq 1
expect( enum.next[0] ).to eq 'bar'
expect( enum.next[1] ).to eq 3
expect { enum.next }.to raise_error(StopIteration)
end

it 'supports (Enumerable\'s) first' do
set = java.util.LinkedHashSet.new [ 'foo', 'bar', 'baz' ]
expect( set.first ).to eq 'foo'
expect( set.first(2) ).to eq ['foo', 'bar']
expect( set.first(1) ).to eq ['foo']
expect( set.first(8) ).to eq ['foo', 'bar', 'baz']
expect( set.first(0) ).to eq []
set.clear
expect( set.first ).to be nil

# java.util.Queue conflicts since it has getFirst :
que = java.util.ArrayDeque.new [1, 2, 3]
expect( que.first ).to eq 1
expect( que.ruby_first(2).to_a ).to eq [1, 2]
expect( que.ruby_first(0).to_a ).to eq []
que.clear
expect( que.ruby_first ).to be nil
end

it 'handles << as add' do
set = java.util.HashSet.new
set << 'ZZZ'
expect( set.iterator.next ).to eq 'ZZZ'
set << 'ZZZ'; set << 'AAA'; set << 'ZZZ'
expect( set.size ).to eq 2
end

it 'converts to_a' do
coll = java.util.ArrayDeque.new(@data)
expect(coll.to_a).to eq(@data.to_a)

coll = java.util.HashSet.new
expect(coll.to_a).to eq([])
end

it 'reports list size/length' do
list = java.util.TreeSet.new(@data)
expect(list.length).to eq(@data.size)
expect(list.size).to eq(list.length)
expect( java.util.Collections.emptySet.length ).to eq 0
end

it 'adds collections' do
set = java.util.LinkedHashSet.new ['foo', 'baz', 'bar', '']
vec = java.util.Vector.new ['', 'baz', 'zzz', 1]
expect( coll = set + vec ).to be_a java.util.LinkedHashSet
expect( coll.to_a ).to eql ['foo', 'baz', 'bar', '', 'zzz', 1]

vec = java.util.ArrayList.new ['', 'baz', 'zzz', 1]
expect( coll = vec + set ).to be_a java.util.ArrayList
expect( coll.to_a ).to eql ['', 'baz', 'zzz', 1, 'foo', 'baz', 'bar', '']

expect( vec.to_a ).to eql ['', 'baz', 'zzz', 1] # not affected!
expect( set.to_a ).to eql ['foo', 'baz', 'bar', ''] # not affected!
end

it 'distracts collections' do
set = java.util.LinkedHashSet.new ['foo', 'baz', 'bar', '']
vec = java.util.ArrayList.new ['', 'baz', 'zzz', 1]
expect( coll = set - vec ).to be_a java.util.LinkedHashSet
expect( coll.to_a ).to eql ['foo', 'bar']

vec = java.util.Vector.new ['', 'baz', 'zzz', 1]
expect( coll = vec - set ).to be_a java.util.Vector
expect( coll.to_a ).to eql ['zzz', 1]

expect( vec.to_a ).to eql ['', 'baz', 'zzz', 1] # not affected!
expect( set.to_a ).to eql ['foo', 'baz', 'bar', ''] # not affected!
end

it 'dups' do
set = java.util.HashSet.new ['0']
expect( set.dup ).to be_a java.util.HashSet
set.dup.add '1'
expect( set.to_a ).to eql ['0']

arr = java.util.ArrayList.new [0]
expect( arr.dup ).to be_a java.util.ArrayList
arr.dup.set(0, 1)
expect( arr.to_a ).to eql [0]

# a non java.lang.Cloneable collection :
arr = java.util.concurrent.LinkedBlockingQueue.new; arr.add 42
expect( arr.dup ).to be_a java.util.concurrent.LinkedBlockingQueue
expect( arr.dup.poll ).to eq 42
expect( arr.to_a ).to eql [42]
end

it 'clones' do
arr = java.util.concurrent.CopyOnWriteArrayList.new ['0']
expect( arr.clone ).to be_a java.util.concurrent.CopyOnWriteArrayList
arr.clone.add '1'
expect( arr.to_a ).to eql ['0']

arr = java.util.LinkedList.new [0]
expect( arr.clone ).to be_a java.util.LinkedList
arr.clone.set(0, 1)
expect( arr.to_a ).to eql [0]

# a non java.lang.Cloneable collection :
set = java.util.concurrent.CopyOnWriteArraySet.new ['0']
expect( set.clone ).to be_a java.util.concurrent.CopyOnWriteArraySet
set.clone.add '1'
expect( set.to_a ).to eql ['0']
end

it "should respect to_ary objects defined on iteration" do
class Pair
def initialize(a, b)
@a = a ; @b = b
end

def to_ary
[@a, @b]
end
end

java.util.HashSet.new([Pair.new(:x, :y)]).each do |car, cdr|
expect(car).to eq(:x)
expect(cdr).to eq(:y)
end
end
end
50 changes: 50 additions & 0 deletions spec/java_integration/extensions/iterable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe 'java.lang.Iterable' do

# java.nio.file.Path extends Iterable<Path>

it 'iterates using #each' do
file_system = java.nio.file.FileSystems.getDefault
path = file_system.getPath(__FILE__)
paths = []
path.each { |p| paths << p.to_s }
expect( paths ).to_not be_empty
expect( paths.last ).to eq 'iterable_spec.rb'
end

it 'iterates with an Enumerator on #each' do
file_system = java.nio.file.FileSystems.getDefault
path = file_system.getPath(__FILE__)
enum = path.each
expect( enum.next ).to_not be nil
end

it 'iterates using #each_with_index' do
file_system = java.nio.file.FileSystems.getDefault
path = file_system.getPath(__FILE__)
paths = []; idxs = []
path.each_with_index { |p| paths << p }
expect( paths ).to_not be_empty
expect( paths[-1][0].to_s ).to eq 'iterable_spec.rb'
expect( paths[-1][1] ).to eq paths.size - 1
end

it 'iterates with an Enumerator on #each_with_index' do
file_system = java.nio.file.FileSystems.getDefault
path = file_system.getPath(__FILE__)
enum = path.each_with_index
n = enum.next
expect( n[0] ).to_not be nil
expect( n[1] ).to eql 0
end

it 'does #map' do
file_system = java.nio.file.FileSystems.getDefault
path = file_system.getPath(__FILE__)
paths = path.map { |p| p.to_s.upcase }
expect( paths ).to_not be_empty
expect( paths.last ).to eq 'ITERABLE_SPEC.RB'
end

end
180 changes: 162 additions & 18 deletions spec/java_integration/extensions/list_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
require File.dirname(__FILE__) + "/../spec_helper"

java_import "java.util.ArrayList"

describe "List Ruby extensions" do
before(:each) do
@data = ["foo", "quux", "bar", "aa"]
@list = ArrayList.new(@data)
@list = java.util.ArrayList.new(@data)
end

it "should support index() with one argument" do
@@ -16,28 +14,54 @@
expect(@list.index(451)).to eq(nil)
end

it "should return nil on rindex() with one argument that does not exist" do
expect(@list.rindex(-24)).to eq(nil)
list = java.util.Stack.new
expect(list.rindex('foo')).to eq(nil)
expect(list.rindex(nil)).to eq(nil)
end

it "should support index() with a block" do
expect(@list.index{|x| x == "bar" }).to eq(2)
expect(@list.index{ |x| x == 'bar' }).to eq(2)
end

it "should support rindex() with a block" do
list = java.util.Vector.new @data; list << 'bar'; list << 'aa'
expect(list.rindex{ |x| x == 'bar' }).to eq(4)
end

it "should support index() with a block that does not exist" do
expect(@list.index{|x| x == :nazgul }).to eq(nil)
expect(@list.index{ |x| x == :nazgul }).to eq(nil)
end

it "should support index() with nil (not found)" do
expect(@list.index(nil)).to eq(nil)
end

it "should support rindex() with nil (not found)" do
expect(@list.rindex(nil)).to eq(nil)
end

it "should support index() with nil (found)" do
expect(ArrayList.new(["foo", "quux", nil, "bar", "aa"]).index(nil)).to eq(2)
expect(java.util.LinkedList.new(["foo", "quux", nil, "bar", "aa"]).index(nil)).to eq(2)
end

it "should support rindex() with nil (found)" do
expect(java.util.ArrayList.new(["foo", "quux", nil, "bar", nil, "aa"]).rindex(nil)).to eq(4)
end

it "should support index() with no arguments" do
expect(@list.index.each {|x| x == "foo" }).to eq(0)
expect( @list.index.each { |x| x == "foo" } ).to eq(0)
end

it "should support index() with no arguments (not existing)" do
expect(@list.index.each {|x| x == ":-(" }).to eq(nil)
expect( @list.index.each { |x| x == ":-(" } ).to eq(nil)
end

it "should support rindex() with no arguments" do
list = java.util.Stack.new
list << 1; list << 2; list << 3; list << 1; list << 2; list << 3
expect( list.rindex.each {|x| x < 2 } ).to eq(3)
end

# Java 8 adds a single-parameter sort method to List that sorts in-place
@@ -60,13 +84,13 @@
end

it "should be sortable with sort!() without block" do
list = ArrayList.new(@data)
list = java.util.LinkedList.new(@data)
list.sort!
expect(list.to_a).to eq(@data.sort)
end

it "should be sortable with sort!() with block" do
list = ArrayList.new(@data)
list = java.util.ArrayList.new(@data)
list.sort! do |a, b|
a.length <=> b.length
end
@@ -78,31 +102,151 @@
expect(list.to_a).to eq(expected)
end

it "should support slicing with 2 arguments" do
expect(@list[0,3].to_a).to eq(@data[0,3])
it 'returns same collection type as target on sort' do
list = java.util.Vector.new ['b','a']

# NOTE: collides with Java 8's sort :
java.util.Vector.class_eval { alias sort ruby_sort }

list = list.sort { |a, b| a.length <=> b.length }
expect( list ).to be_a java.util.Vector
expect( list.to_a ).to eq ['b','a']

list = java.util.LinkedList.new ['b','a','c']
# list = list.sort { |a, b| a <=> b }
if TestHelper::JAVA_8
sorted = list.ruby_sort { |a, b| a <=> b }
else
sorted = list.sort { |a, b| a <=> b }
end
expect( sorted ).to be_a java.util.LinkedList
expect( sorted.to_a ).to eq ['a','b','c']
expect( sorted ).to_not be list

expect( java.util.ArrayList.new.ruby_sort ).to be_a java.util.ArrayList
end

it "should support slicing with inclusive ranges" do
expect(@list[0..3].to_a).to eq(@data[0..3])
it 'converts to_ary' do
list = java.util.LinkedList.new(@data)
expect(list.to_ary).to eq(@data)

list = java.util.ArrayList.new(@data)
list.sort!
expect(list.to_ary).to eq(@data.sort)
end

it 'reports list size/length' do
list = java.util.Vector.new(@data)
expect(list.length).to eq(@data.size)
expect(list.size).to eq(list.length)
expect( java.util.Collections.emptyList.length ).to eq 0
end

it '[] with 1 arg returns element like an Array' do
expect(@list[0]).to eq(@data[0])
expect(@list[3]).to eq(@data[3])
expect(@list[-1]).to eq(@data[-1])
expect(@list[11]).to eq(@data[11]) # nil
expect(@list[-9]).to eq(@data[-9]) # nil
end

it "should support slicing with exclusive ranges" do
it 'slices with 2 arguments like an Array' do
expect(@list[0,3].to_a).to eq(@data[0,3])
expect(@list[2,3].to_a).to eq(@data[2,3])
expect(@list[3,8].to_a).to eq(@data[3,8])
expect(@list[4,8].to_a).to eq(@data[4,8])
expect(@list[5,8]).to be nil
expect(@list[-1,1].to_a).to eq(@data[-1,1])
expect(@list[-2,3].to_a).to eq(@data[-2,3])
expect(@list[-5,3]).to be nil
expect(@list[0,0].empty?).to be true
end

it 'slices with a range argument like an Array' do
expect(@list[0..3].to_a).to eq(@data[0..3])
expect(@list[0...2].to_a).to eq(@data[0...2])
expect(@list[0..0].to_a).to eq(@data[0..0])
expect(@list[0...0].to_a).to eq(@data[0...0])
expect(@list[1...-1].to_a).to eq(@data[1...-1])
expect(@list[-2..-1].to_a).to eq(@data[-2..-1])
expect(@list[-2...-1].to_a).to eq(@data[-2...-1])
expect(@list[-4..-2].to_a).to eq(@data[-4..-2])
expect(@list[-4...8].to_a).to eq(@data[-4...8])
expect(@list[-5..-1]).to be nil
expect(@list[-5...-1]).to be nil
expect(@list[7..8]).to be nil
expect(@list[-2...-2].empty?).to be true
expect(@list[1...1].empty?).to be true
end

it 'slice returns a subList' do
expect(@list[0,3]).to be_a java.util.List
expect(@list[0,0]).to be_a java.util.List # empty

expect(@list[0..3]).to be_a java.util.List
expect(@list[-2...-2]).to be_a java.util.List # empty
end

it '[]= with 1 arg sets an element' do
list = java.util.Vector.new(@data)
list[0] = nil
expect( list.get(0) ).to be nil
list[-1] = -1
expect( list.get(list.size() - 1) ).to eq -1

list = java.util.LinkedList.new(@data)
list[0] = nil
expect( list.get(0) ).to be nil
list[-1] = -1
expect( list.get(list.size() - 1) ).to eq -1
end

it '[]= with arg gt than size adds and sets like an Array' do
list = java.util.LinkedList.new @data
list[5] = 5 # list.size == 4
expect( list.get(4) ).to be nil
expect( list.get(5) ).to eq 5
expect( list.size ).to eq 6

list = java.util.Stack.new
list[3] = 3; list[0] = 0
expect( list.to_a ).to eq [0, nil, nil, 3]
end

it 'supports (Array-like) first/last' do
expect( @list.first ).to eq 'foo'
list = java.util.ArrayList.new [1, 2, 3]
expect( list.first(2).to_a ).to eq [1, 2]
expect( list.first(1).to_a ).to eq [1]
expect( list.first(0).to_a ).to eq []
expect( list.first(5).to_a ).to eq [1, 2, 3]

# LinkedList does getList, unless we alias first ruby_first
expect( java.util.LinkedList.new.ruby_first ).to be nil
expect( java.util.LinkedList.new.ruby_last ).to be nil

list = java.util.LinkedList.new [1, 2, 3]
expect( list.ruby_last ).to eq 3
expect( java.util.Vector.new.last ).to be nil
expect( list.ruby_last(1).to_a ).to eq [3]
expect( list.ruby_last(2) ).to be_a java.util.List
expect( list.ruby_last(2).to_a ).to eq [2, 3]
expect( list.ruby_last(5).to_a ).to eq [1, 2, 3]
expect( list.ruby_last(0) ).to be_a java.util.List
end

it "should respect to_ary objects defined on iteration" do
class Pair
def initialize(a, b)
@a = a
@b = b
@a = a ; @b = b
end

def to_ary
[@a, @b]
end
end

ArrayList.new([Pair.new(:x, :y)]).each do |car, cdr|
java.util.ArrayList.new([Pair.new(:x, :y)]).each do |car, cdr|
expect(car).to eq(:x)
expect(cdr).to eq(:y)
end
125 changes: 125 additions & 0 deletions spec/java_integration/extensions/regex_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe 'java.util.regex.Pattern' do

it 'returns match start on match using =~' do
i = java.util.regex.Pattern::CASE_INSENSITIVE
p = java.util.regex.Pattern.compile("ab", i)
expect( p =~ '0123a56b89abbab0ab' ).to eq 10
expect( p =~ '0123a56b89' ).to be nil
end

it 'reports match with === as boolean' do
p = java.util.regex.Pattern.compile("abb")
expect( p === '0123a56b89abbab0ab' ).to be true
expect( p === '0123a56b89' ).to be false
end

it 'returns matcher on match' do
i = java.util.regex.Pattern::CASE_INSENSITIVE
p = java.util.regex.Pattern.compile("ab", i)
expect( p.match '0123a56b89abbab0AB' ).to be_a java.util.regex.Matcher
expect( p.match '0123a56b89' ).to be nil
end

it 'reports casefold?' do
i = java.util.regex.Pattern::CASE_INSENSITIVE
p = java.util.regex.Pattern.compile("bar", i)
expect( p.casefold? ).to be true
p = java.util.regex.Pattern.compile("bar")
expect( p.casefold? ).to be false
end

end

describe 'java.util.regex.Matcher' do

it 'returns regexp pattern' do
p = java.util.regex.Pattern.compile("[a-f]")
m = p.match('abcdef')
expect( m.regexp ).to be p
end

it 'reports group begin/end and offset' do
p = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
m = p.match('THX1138.')
expect( m.begin(0) ).to eq 1
expect( m.begin(2) ).to eq 2
expect( m.end(0) ).to eq 7
expect( m.end(2) ).to eq 3
expect( m.offset(0) ).to eq [1, 7]
expect( m.offset(4) ).to eq [6, 7]

p = java.util.regex.Pattern.compile("(?<foo>.)(.)(?<bar>.)")
m = p.match('hoge')

if TestHelper::JAVA_8
expect( m.begin(:foo) ).to eq 0
expect( m.begin(:bar) ).to eq 2
expect( m.end(:foo) ).to eq 1
expect( m.end(:bar) ).to eq 3
expect( m.offset(:bar) ).to eq [2, 3]
end
end

it 'reports size and string' do
p = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
m = p.match('THX1138.')
expect( m.length ).to eq 5
expect( m.size ).to eq 5
end

it 'returns matched string' do
p = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
m = p.match('THX1138.')

# expect( m.string ).to eq 'HX1138' # different from Ruby
expect( m.string ).to eq 'THX1138.'
end

it 'return captures as an array' do
p = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
m = p.match('THX1138.')

expect( m.to_a ).to eq ['HX1138', 'H', 'X', '113', '8']
expect( m.captures ).to eq ['H', 'X', '113', '8']

# /([a-c]+)(b|d)?.*(\d+)/.match 'Xaaaeb111Z'
# #<MatchData "aaaeb111" 1:"aaa" 2:nil 3:"1">
p = java.util.regex.Pattern.compile("([a-c]+)(b|d)?.*?(\\d+)")
m = p.match('Xaaaeb111Z')

expect( m.to_a ).to eq ['aaaeb111', 'aaa', nil, '111']
expect( m.captures ).to eq ['aaa', nil, '111']
end

it 'returns groups using []' do
p = java.util.regex.Pattern.compile("(.)(.)(\\d+)(\\d)")
m = p.match('THX1138.')

expect( m[0] ).to eq 'HX1138'
expect( m[1, 2] ).to eq ['H', 'X']
expect( m[1..3] ).to eq ['H', 'X', '113']
end

it 'returns named groups using []' do
p = java.util.regex.Pattern.compile("(?<foo>a+)b")
m = p.match('ccaaab')

expect( m[:foo] ).to eq 'aaa'
expect( m['foo'] ).to eq 'aaa'
end

it "end does not blow stack" do
# See https://jira.codehaus.org/browse/JRUBY-6571

s = 'hello world'
r = java.util.regex.Pattern.compile("[eo]")
m = r.matcher(s)

while m.find()
expect{ m.end(0) }.not_to raise_error
end
end

end
17 changes: 0 additions & 17 deletions spec/java_integration/regex/matcher_end_spec.rb

This file was deleted.

1 change: 1 addition & 0 deletions spec/java_integration/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
RSpec.configure do |config|
require File.expand_path('../../../test/jruby/test_helper', __FILE__)
config.include TestHelper
# config.extend TestHelper # so that TestHelper constants work
end

# Works like 'should include('str1', 'str2') but for arrays of
40 changes: 40 additions & 0 deletions spec/java_integration/types/array_spec.rb
Original file line number Diff line number Diff line change
@@ -179,6 +179,16 @@
it "makes an ascii 8 bit string on to_s" do
expect([86, 87].to_java(:byte).to_s).to eq("VW")
end

it "clones" do
arr = Java::byte[10].new
arr[1] = 1

dup = arr.clone
expect( dup.object_id ).to_not eql arr.object_id
dup[1] = 11
expect(arr[1]).to eq(1)
end
end

describe "char" do
@@ -636,6 +646,16 @@
arr = [13, 42, 120].to_java :long
expect(arr.inspect).to match(/^long\[13, 42, 120\]@[0-9a-f]+$/)
end

it "clones" do
arr = Java::long[5].new
arr[1] = 1

dup = arr.clone
expect( dup.object_id ).to_not eql arr.object_id
dup[1] = 11
expect(arr[1]).to eq(1)
end
end

describe "short" do
@@ -708,6 +728,16 @@
arr = [13, 42, 120].to_java :short
expect(arr.inspect).to match(/^short\[13, 42, 120\]@[0-9a-f]+$/)
end

it "dups" do
arr = Java::short[5].new
arr[1] = 1

dup = arr.dup
expect( dup.object_id ).to_not eql arr.object_id
dup[1] = 11
expect(arr[1]).to eq(1)
end
end

describe "string" do
@@ -780,6 +810,16 @@
arr = ['foo', 'bar', 'baz'].to_java :string
expect(arr.inspect).to match(/^java.lang.String\[foo, bar, baz\]@[0-9a-f]+$/)
end

it "dups" do
arr = java.lang.String[3].new
arr[1] = '000'

dup = arr.dup
expect( dup.object_id ).to_not eql arr.object_id
dup[1] = 'DUP'
expect(arr[1]).to eq('000')
end
end

describe "Object" do
40 changes: 18 additions & 22 deletions spec/java_integration/types/map_spec.rb
Original file line number Diff line number Diff line change
@@ -70,6 +70,13 @@
expect( h.any? { |e| e[1] > 10 } ).to be true
end

it 'returns self on clear like a Hash (if aliased)' do
java.util.concurrent.ConcurrentSkipListMap.class_eval { alias clear ruby_clear }
m = java.util.concurrent.ConcurrentSkipListMap.new({ '1' => 1, '2' => 2 })
expect( m.clear ).to_not be nil # Java's clear returns void
expect( m.empty? ).to be true
end

it "supports Hash-like operations" do
h = java.util.HashMap.new
test_ok(h.kind_of? java.util.Map)
@@ -124,12 +131,8 @@
test_ok(h.clear.empty?)

# Java 8 adds a replace method to Map that takes a key and value
if ENV_JAVA['java.specification.version'] < '1.8'
h.replace({1=>100})
else
h.clear
h[1]=100
end
h.ruby_replace({1=>100})

test_equal({1=>100}, h)
h[2]=200; h[3]=300
test_equal(300, h.fetch(3))
@@ -160,9 +163,12 @@
# Java 8 adds a merge method to Map used for merging multiple values for a given key in-place
if ENV_JAVA['java.specification.version'] < '1.8'
test_equal({"a"=>100, "b"=>254, "c"=>300}, h1.merge(h2))
test_equal({"a"=>100, "b"=>454, "c"=>300}, h1.merge(h2) { |k, o, n| o+n })
test_equal("{\"a\"=>100, \"b\"=>200}", h1.inspect)
else
test_equal({"a"=>100, "b"=>254, "c"=>300}, h1.ruby_merge(h2))
end
test_equal({"a"=>100, "b"=>454, "c"=>300}, h1.ruby_merge(h2) { |k, o, n| o+n })
test_equal("{\"a\"=>100, \"b\"=>200}", h1.inspect)

h1.merge!(h2) { |k, o, n| o }
test_equal("{\"a\"=>100, \"b\"=>200, \"c\"=>300}", h1.inspect)
test_equal(Java::JavaUtil::LinkedHashMap, h1.class)
@@ -175,12 +181,7 @@
test_equal("{1=>100, 2=>200}", h.inspect)

# Java 8 adds a replace method to Map that takes a key and value
if ENV_JAVA['java.specification.version'] < '1.8'
test_equal({"c"=>300, "d"=>400, "e"=>500}, h.replace({"c"=>300, "d"=>400, "e"=>500}))
else
h.clear
h.put_all({"c"=>300, "d"=>400, "e"=>500})
end
test_equal({"c"=>300, "d"=>400, "e"=>500}, h.ruby_replace({"c"=>300, "d"=>400, "e"=>500}))
test_equal(Java::JavaUtil::LinkedHashMap, h.class)

test_equal({"d"=>400, "e"=>500}, h.select {|k,v| k > "c"})
@@ -190,8 +191,7 @@
if ENV_JAVA['java.specification.version'] < '1.8'
h.replace({"a"=>20, "d"=>10, "c"=>30, "b"=>0})
else
h.clear
h.put_all({"a"=>20, "d"=>10, "c"=>30, "b"=>0})
h.ruby_replace({"a"=>20, "d"=>10, "c"=>30, "b"=>0})
end
test_equal([["a", 20], ["b", 0], ["c", 30], ["d", 10]], h.sort)
test_equal([["b", 0], ["d", 10], ["a", 20], ["c", 30]], h.sort { |a, b| a[1]<=>b[1] })
@@ -211,12 +211,8 @@
test_equal([["a", 20], ["d", 10]], h.take(2))

# Java 8 adds a replace method to Map that takes a key and value
if ENV_JAVA['java.specification.version'] < '1.8'
h.replace({"a"=>100, "b"=>200})
else
h.clear
h.put_all({"a"=>100, "b"=>200})
end
h.ruby_replace({"a"=>100, "b"=>200})

h2 = {"b"=>254, "c"=>300}
test_equal({"a"=>100, "b"=>200, "c"=>300}, h.update(h2) { |k, o, n| o })
test_equal("{\"a\"=>100, \"b\"=>200, \"c\"=>300}", h.inspect)
2 changes: 2 additions & 0 deletions test/jruby/test_helper.rb
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@ module TestHelper

IBM_JVM = RbConfig::CONFIG['host_vendor'] =~ /IBM Corporation/

JAVA_8 = ENV_JAVA['java.specification.version'] >= '1.8'

def q
WINDOWS ? '"' : '\''
end
18 changes: 16 additions & 2 deletions test/jruby/test_java_extension.rb
Original file line number Diff line number Diff line change
@@ -43,8 +43,22 @@ def test_comparable
two = short.new(2)
three = short.new(3)
list = [three, two, one]
list = list.sort
assert_equal([one, two, three], list)
list.sort!
assert_equal [one, two, three], list

assert_equal ['a'.to_java, 'b'.to_java], [java.lang.String.new('b'), java.lang.String.new('a')].sort
end

def test_comparable_error
list = [3.to_java(:int), java.lang.Byte.new(2), 1, 0]
assert_raise(TypeError) { list.sort }

begin
[ java.lang.Short.new(1), java.lang.Integer.new(0) ].sort!
rescue => e
assert_instance_of TypeError, e
assert_equal 'java.lang.Short cannot be cast to java.lang.Integer', e.message
end
end

def test_map

0 comments on commit 08e4b1b

Please sign in to comment.