org.jruby.javasupport.JavaUtil Maven / Gradle / Ivy
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.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-v20.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) 2001 Alan Moore
* Copyright (C) 2001-2004 Jan Arne Petersen
* Copyright (C) 2002 Anders Bengtsson
* Copyright (C) 2002 Benoit Cerrina
* Copyright (C) 2002 Don Schwartz
* Copyright (C) 2004 Stefan Matthias Aust
* Copyright (C) 2006 Kresten Krab Thorup
*
* 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;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static java.lang.Character.isLetter;
import static java.lang.Character.isLowerCase;
import static java.lang.Character.isUpperCase;
import static java.lang.Character.isDigit;
import static java.lang.Character.toLowerCase;
import static java.lang.Character.toUpperCase;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBasicObject;
import org.jruby.RubyInstanceConfig;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.RubyBignum;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyEncoding;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyModule;
import org.jruby.RubyNil;
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyProc;
import org.jruby.RubyString;
import org.jruby.RubyTime;
import org.jruby.java.proxies.ArrayJavaProxy;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.java.proxies.RubyObjectHolderProxy;
import org.jruby.javasupport.proxy.InternalJavaProxy;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.CodegenUtils;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;
public class JavaUtil {
public static final boolean CAN_SET_ACCESSIBLE;
static {
boolean canSetAccessible = false;
if (RubyInstanceConfig.CAN_SET_ACCESSIBLE) {
try {
// We want to check if we can access a commonly-existing private field through reflection.
// If so, we're probably able to access some other fields too later on.
Field f = Java.class.getDeclaredField(Java.HIDDEN_STATIC_FIELD_NAME);
f.setAccessible(true);
canSetAccessible = f.getByte(null) == 72;
}
catch (Exception t) {
// added this so if things are weird in the future we can debug without
// spinning a new binary
if (Options.JI_LOGCANSETACCESSIBLE.load()) {
t.printStackTrace();
}
// assume any exception means we can't suppress access checks
canSetAccessible = false;
}
}
CAN_SET_ACCESSIBLE = canSetAccessible;
}
public static IRubyObject[] convertJavaArrayToRuby(final Ruby runtime, final Object[] objects) {
if ( objects == null || objects.length == 0 ) return IRubyObject.NULL_ARRAY;
if (objects instanceof String[]) return convertStringArrayToRuby(runtime, (String[]) objects, JAVA_STRING_CONVERTER);
IRubyObject[] rubyObjects = new IRubyObject[objects.length];
for (int i = 0; i < objects.length; i++) {
rubyObjects[i] = convertJavaToUsableRubyObject(runtime, objects[i]);
}
return rubyObjects;
}
public static IRubyObject[] convertStringArrayToRuby(final Ruby runtime, final String[] strings, StringConverter converter) {
if ( strings == null || strings.length == 0 ) return IRubyObject.NULL_ARRAY;
IRubyObject[] rubyObjects = new IRubyObject[strings.length];
for (int i = 0; i < strings.length; i++) {
rubyObjects[i] = convertJavaToUsableRubyObjectWithConverter(runtime, strings[i], converter);
}
return rubyObjects;
}
public static RubyArray convertJavaArrayToRubyWithNesting(final ThreadContext context, final Object array) {
final int length = Array.getLength(array);
final IRubyObject[] rubyElements = new IRubyObject[length];
for ( int i = 0; i < length; i++ ) {
final Object element = Array.get(array, i);
if ( element instanceof ArrayJavaProxy ) {
rubyElements[i] = convertJavaArrayToRubyWithNesting(context, ((ArrayJavaProxy) element).getObject());
}
else if ( element != null && element.getClass().isArray() ) {
rubyElements[i] = convertJavaArrayToRubyWithNesting(context, element);
}
else {
rubyElements[i] = convertJavaToUsableRubyObject(context.runtime, element);
}
}
return context.runtime.newArrayNoCopy(rubyElements);
}
public static JavaConverter getJavaConverter(Class clazz) {
final JavaConverter converter = JAVA_CONVERTERS.get(clazz);
return converter == null ? JAVA_DEFAULT_CONVERTER : converter;
}
public static IRubyObject convertJavaToRuby(Ruby runtime, Object object) {
return convertJavaToUsableRubyObject(runtime, object);
}
public static IRubyObject convertJavaToRuby(Ruby runtime, Object object, Class javaClass) {
return convertJavaToUsableRubyObjectWithConverter(runtime, object, getJavaConverter(javaClass));
}
public static IRubyObject convertJavaToRuby(Ruby runtime, int i) {
return runtime.newFixnum(i);
}
public static IRubyObject convertJavaToRuby(Ruby runtime, long l) {
return runtime.newFixnum(l);
}
public static IRubyObject convertJavaToRuby(Ruby runtime, float f) {
return runtime.newFloat(f);
}
public static IRubyObject convertJavaToRuby(Ruby runtime, double d) {
return runtime.newFloat(d);
}
public static IRubyObject convertJavaToRuby(Ruby runtime, boolean b) {
return runtime.newBoolean(b);
}
/**
* Returns a usable RubyObject; for types that are not converted to Ruby native
* types, a Java proxy will be returned.
*
* @param runtime
* @param object
* @return corresponding Ruby type, or a functional Java proxy
*/
public static IRubyObject convertJavaToUsableRubyObject(Ruby runtime, Object object) {
IRubyObject result = trySimpleConversions(runtime, object);
if (result != null) return result;
JavaConverter converter = getJavaConverter(object.getClass());
if (converter == null || converter == JAVA_DEFAULT_CONVERTER) {
return Java.getInstance(runtime, object);
}
return converter.convert(runtime, object);
}
public static IRubyObject convertJavaToUsableRubyObjectWithConverter(Ruby runtime, Object object, JavaConverter converter) {
IRubyObject result = trySimpleConversions(runtime, object);
if (result != null) return result;
if (converter == null || converter == JAVA_DEFAULT_CONVERTER) {
return Java.getInstance(runtime, object);
}
return converter.convert(runtime, object);
}
public static IRubyObject convertJavaArrayElementToRuby(Ruby runtime, JavaConverter converter, Object array, int i) {
if (converter == null || converter == JAVA_DEFAULT_CONVERTER) {
return convertJavaToUsableRubyObject(runtime, ((Object[])array)[i]);
}
return converter.get(runtime, array, i);
}
public static Class> primitiveToWrapper(final Class> type) {
return type.isPrimitive() ? CodegenUtils.getBoxType(type) : type;
}
public static boolean isDuckTypeConvertable(final Class> argumentType, final Class> targetType) {
return targetType.isInterface() &&
! targetType.isAssignableFrom(argumentType) &&
RubyObject.class.isAssignableFrom(argumentType);
}
public static T convertProcToInterface(ThreadContext context, RubyObject rubyObject, Class targetType) {
return convertProcToInterface(context, (RubyBasicObject) rubyObject, targetType);
}
@SuppressWarnings("unchecked")
public static T convertProcToInterface(ThreadContext context, RubyBasicObject rubyObject, Class targetType) {
final Ruby runtime = context.runtime;
final RubyModule ifaceModule = Java.getInterfaceModule(runtime, JavaClass.get(runtime, targetType));
if ( ! ifaceModule.isInstance(rubyObject) ) {
ifaceModule.callMethod(context, "extend_object", rubyObject);
ifaceModule.callMethod(context, "extended", rubyObject);
}
if ( rubyObject instanceof RubyProc ) {
// Proc implementing an interface, pull in the catch-all code that lets the proc get invoked
// no matter what method is called on the interface
final RubyClass singletonClass = rubyObject.getSingletonClass();
final Java.ProcToInterface procToIface = new Java.ProcToInterface(singletonClass);
singletonClass.addMethod("method_missing", procToIface);
// similar to Iface.impl { ... } - bind interface method(s) to avoid Java-Ruby conflicts
// ... e.g. calling a Ruby implemented Predicate#test should not dispatch to Kernel#test
// getMethods for interface returns all methods (including ones from super-interfaces)
for ( Method method : targetType.getMethods() ) {
Java.ProcToInterface.ConcreteMethod implMethod = procToIface.getConcreteMethod(method.getName());
if ( Modifier.isAbstract(method.getModifiers()) ) {
singletonClass.addMethodInternal(method.getName(), implMethod);
}
}
}
JavaObject javaObject = (JavaObject) Helpers.invoke(context, rubyObject, "__jcreate_meta!");
return (T) javaObject.getValue();
}
public static NumericConverter getNumericConverter(Class target) {
final NumericConverter converter = NUMERIC_CONVERTERS.get(target);
return converter == null ? NUMERIC_TO_OTHER : converter;
}
/**
* Test if a passed instance is a wrapper Java object.
* @param object
* @return true if the object is wrapping a Java object
*/
public static boolean isJavaObject(final IRubyObject object) {
return object instanceof JavaProxy || object.dataGetStruct() instanceof JavaObject;
}
/**
* Unwrap a wrapped Java object.
* @param object
* @return Java object
* @see JavaUtil#isJavaObject(IRubyObject)
*/
public static T unwrapJavaObject(final IRubyObject object) {
if ( object instanceof JavaProxy ) {
return (T) ((JavaProxy) object).getObject();
}
return (T) ((JavaObject) object.dataGetStruct()).getValue();
}
/**
* Unwrap if the passed object is a Java object, otherwise return object.
* @param object
* @return java object or passed object
* @see JavaUtil#isJavaObject(IRubyObject)
*/
public static T unwrapIfJavaObject(final IRubyObject object) {
if ( object instanceof JavaProxy ) {
return (T) ((JavaProxy) object).getObject();
}
final Object unwrap = object.dataGetStruct();
if ( unwrap instanceof JavaObject ) {
return (T) ((JavaObject) unwrap).getValue();
}
return (T) object; // assume correct instance
}
@Deprecated // no longer used
public static Object unwrapJavaValue(final Ruby runtime, final IRubyObject object, final String errorMessage) {
if ( object instanceof JavaProxy ) {
return ((JavaProxy) object).getObject();
}
if ( object instanceof JavaObject ) {
return ((JavaObject) object).getValue();
}
final Object unwrap = object.dataGetStruct();
if ( unwrap instanceof IRubyObject ) {
return unwrapJavaValue(runtime, (IRubyObject) unwrap, errorMessage);
}
throw runtime.newTypeError(errorMessage);
}
/**
* @param object
* @note Returns null if not a wrapped Java value.
* @return unwrapped Java (object's) value
*/
public static Object unwrapJavaValue(final IRubyObject object) {
if ( object instanceof JavaProxy ) {
return ((JavaProxy) object).getObject();
}
if ( object instanceof JavaObject ) {
return ((JavaObject) object).getValue();
}
final Object unwrap = object.dataGetStruct();
if ( unwrap instanceof IRubyObject ) {
return unwrapJavaValue((IRubyObject) unwrap);
}
return null;
}
/**
* For methods that match /(get|set|is)([A-Z0-9])(.*)/, return the "name"
* part of the property with leading lower-case.
*
* @note Does not use regular expression for performance reasons.
*
* @param beanMethodName the bean method from which to extract a name
* @return the bean property name (or null)
*/
public static String getJavaPropertyName(final String beanMethodName) {
final int length = beanMethodName.length(); char ch;
final boolean maybeGetOrSet = length > 3 && beanMethodName.charAt(2) == 't';
if ( maybeGetOrSet && ( beanMethodName.startsWith("get") || beanMethodName.startsWith("set") ) ) {
if (isUpperDigit(ch = beanMethodName.charAt(3))) {
if ( length == 4 ) return Character.toString(toLowerCase(ch));
return toLowerCase(ch) + beanMethodName.substring(4);
}
}
else if ( beanMethodName.startsWith("is") && length > 2 ) {
if (isUpperDigit(ch = beanMethodName.charAt(2))) {
if ( length == 3 ) return Character.toString( toLowerCase(ch) );
return toLowerCase(ch) + beanMethodName.substring(3);
}
}
return null;
}
// property -> getProperty
public static String toJavaGetName(final String propertyName) {
if ( propertyName == null ) return null;
final int len = propertyName.length();
if ( len == 0 ) return null;
final char first = toUpperCase(propertyName.charAt(0));
if ( len == 1 ) return "get" + first;
return "get" + first + propertyName.substring(1);
}
// property -> isProperty
public static String toJavaIsName(final String propertyName) {
if ( propertyName == null ) return null;
final int len = propertyName.length();
if ( len == 0 ) return null;
final char first = toUpperCase(propertyName.charAt(0));
if ( len == 1 ) return "is" + first;
return "is" + first + propertyName.substring(1);
}
/**
* Build a Ruby name from a Java name by treating '_' as divider and successive
* caps as all the same word.
* @param javaCasedName
* @return Ruby (under-score) cased named e.g. "get_foo_bar"
*/
public static String getRubyCasedName(final String javaCasedName) {
final char[] javaName = javaCasedName.toCharArray();
final int len = javaName.length;
final StringBuilder rubyName = new StringBuilder(len + 8);
int behind = 0;
for (int i = 0; i < len; i++) {
if ( behind < 2 ) behind++;
else behind = consume(rubyName, javaName, i);
}
if (behind == 2) {
final char c1 = javaName[len - 1], c2 = javaName[len - 2];
rubyName.append( toLowerCase( c2 ) );
if ( isUpperCase( c1 ) && ! isUpperCase( c2 ) ) rubyName.append('_');
rubyName.append( toLowerCase( c1 ) );
}
else if (behind > 0) {
if ( behind > 1 ) {
rubyName.append( toLowerCase( javaName[len - 2] ) );
}
rubyName.append( toLowerCase( javaName[len - 1] ) );
}
return rubyName.toString();
}
private static int consume(final StringBuilder rubyName, final char[] javaName, int i) {
final char prev1 = javaName[i - 1], prev2 = javaName[i - 2];
if ( isLowerDigit( prev2 ) && isUpperCase( prev1 ) ) {
rubyName.append( prev2 ).append('_').append( toLowerCase(prev1) );
return 1;
}
char cur;
if ( isLetterDigit( prev2 ) && isUpperCase( prev1 ) && isLowerCase( cur = javaName[i] )) {
rubyName.append( toLowerCase(prev2) ).append('_').append( toLowerCase(prev1) ).append(cur);
return 0;
}
rubyName.append( toLowerCase(prev2) );
return 2;
}
private static boolean isUpperDigit(char c) {
return isUpperCase(c) || isDigit(c);
}
private static boolean isLowerDigit(char c) {
return isLowerCase(c) || isDigit(c);
}
private static boolean isLetterDigit(char c) {
return isLetter(c) || isDigit(c);
}
private static final Pattern RUBY_CASE_SPLITTER = Pattern.compile("([a-z][0-9]*)_([a-z])");
public static String getJavaCasedName(String javaCasedName) {
Matcher m = RUBY_CASE_SPLITTER.matcher(javaCasedName);
StringBuffer newName = new StringBuffer();
if (!m.find()) {
return null;
}
m.reset();
while (m.find()) {
m.appendReplacement(newName, m.group(1) + Character.toUpperCase(m.group(2).charAt(0)));
}
m.appendTail(newName);
return newName.toString();
}
/**
* Given a simple Java method name and the Java Method objects that represent
* all its overloads, add to the given nameSet all possible Ruby names that would
* be valid.
*
* @param javaName
* @param methods
* @return method names
*/
public static Set getRubyNamesForJavaName(final String javaName, final List methods) {
final String javaPropertyName = getJavaPropertyName(javaName);
final String rubyName = getRubyCasedName(javaName);
final int len = methods.size();
final LinkedHashSet nameSet = new LinkedHashSet(6 * len + 2, 1f); // worse-case 6
nameSet.add(javaName);
nameSet.add(rubyName);
if ( len == 1 ) { // hot-path - most of the time no-overloads for a given method name
addRubyNamesForJavaName(javaName, methods.get(0), javaPropertyName, rubyName, nameSet);
}
else {
for ( int i = 0; i < len; i++ ) { // passed list is ArrayList
addRubyNamesForJavaName(javaName, methods.get(i), javaPropertyName, rubyName, nameSet);
}
}
return nameSet;
}
private static void addRubyNamesForJavaName(final String javaName, final Method method,
final String javaPropertyName, final String rubyName, final LinkedHashSet nameSet) {
final Class> resultType = method.getReturnType();
// Add property name aliases
if (javaPropertyName != null) {
final Class>[] argTypes = method.getParameterTypes();
final int argCount = argTypes.length;
// string starts-with "get_" or "set_" micro-optimization :
final boolean maybeGetOrSet_ = rubyName.length() > 3 && rubyName.charAt(3) == '_';
if (maybeGetOrSet_ && rubyName.startsWith("get")) { // rubyName.startsWith("get_")
if (argCount == 0 || // getFoo => foo
argCount == 1 && argTypes[0] == int.class) { // getFoo(int) => foo(int)
final String rubyPropertyName = rubyName.substring(4);
nameSet.add(javaPropertyName);
nameSet.add(rubyPropertyName);
if (resultType == boolean.class) { // getFooBar() => fooBar?, foo_bar?(*)
nameSet.add(javaPropertyName + '?');
nameSet.add(rubyPropertyName + '?');
}
}
}
else if (maybeGetOrSet_ && rubyName.startsWith("set")) { // rubyName.startsWith("set_")
if (argCount == 1 && resultType == void.class) { // setFoo(Foo) => foo=(Foo)
final String rubyPropertyName = rubyName.substring(4);
nameSet.add(javaPropertyName + '=');
nameSet.add(rubyPropertyName + '=');
}
}
else if (rubyName.startsWith("is_")) {
if (resultType == boolean.class) { // isFoo() => foo?, isFoo(*) => foo(*)
final String rubyPropertyName = rubyName.substring(3);
nameSet.add(javaPropertyName); // NOTE: these are really a bad idea - and can cause issues
nameSet.add(rubyPropertyName); // GH-3470 unfortunately due backwards-compat they stay ;(
nameSet.add(javaPropertyName + '?');
nameSet.add(rubyPropertyName + '?');
}
}
} else {
// If not a property, but is boolean add ?-postfixed aliases.
if (resultType == boolean.class) {
// is_something?, contains_thing?
nameSet.add(javaName + '?');
nameSet.add(rubyName + '?');
}
}
}
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 clone
method.
* @param object
* @return cloned object or null (if method is not found or inaccessible)
*/
public static 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;}
public abstract IRubyObject convert(Ruby runtime, Object object);
public abstract IRubyObject get(Ruby runtime, Object array, int i);
public abstract void set(Ruby runtime, Object array, int i, IRubyObject value);
public String toString() {return type.getName() + " converter";}
}
public interface NumericConverter {
T coerce(RubyNumeric numeric, Class target);
}
public static IRubyObject trySimpleConversions(Ruby runtime, Object object) {
if ( object == null ) return runtime.getNil();
if ( object instanceof IRubyObject ) return (IRubyObject) object;
if ( object instanceof RubyObjectHolderProxy ) {
return ((RubyObjectHolderProxy) object).__ruby_object();
}
if ( object instanceof InternalJavaProxy ) {
final InternalJavaProxy internalJavaProxy = (InternalJavaProxy) object;
IRubyObject orig = internalJavaProxy.___getInvocationHandler().getOrig();
if (orig != null) return orig;
}
return null;
}
public static final JavaConverter JAVA_DEFAULT_CONVERTER = new JavaConverter(Object.class) {
public IRubyObject convert(Ruby runtime, Object object) {
IRubyObject result = trySimpleConversions(runtime, object);
return result == null ? JavaObject.wrap(runtime, object) : result;
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Object[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Object[])array)[i] = value.toJava(Object.class);
}
};
public static final JavaConverter JAVA_BOOLEAN_CONVERTER = new JavaConverter(Boolean.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyBoolean.newBoolean(runtime, ((Boolean)object).booleanValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Boolean[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Boolean[])array)[i] = (Boolean)value.toJava(Boolean.class);
}
};
public static final JavaConverter JAVA_FLOAT_CONVERTER = new JavaConverter(Float.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFloat.newFloat(runtime, ((Float)object).doubleValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Float[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Float[])array)[i] = (Float)value.toJava(Float.class);
}
};
public static final JavaConverter JAVA_DOUBLE_CONVERTER = new JavaConverter(Double.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFloat.newFloat(runtime, ((Double)object).doubleValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Double[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Double[])array)[i] = (Double)value.toJava(Double.class);
}
};
public static final JavaConverter JAVA_CHAR_CONVERTER = new JavaConverter(Character.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Character)object).charValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Character[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Character[])array)[i] = (Character)value.toJava(Character.class);
}
};
public static final JavaConverter JAVA_BYTE_CONVERTER = new JavaConverter(Byte.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Byte)object).byteValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Byte[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Byte[])array)[i] = (Byte)value.toJava(Byte.class);
}
};
public static final JavaConverter JAVA_SHORT_CONVERTER = new JavaConverter(Short.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Short)object).shortValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Short[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Short[])array)[i] = (Short)value.toJava(Short.class);
}
};
public static final JavaConverter JAVA_INT_CONVERTER = new JavaConverter(Integer.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Integer)object).intValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Integer[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Integer[])array)[i] = (Integer)value.toJava(Integer.class);
}
};
public static final JavaConverter JAVA_LONG_CONVERTER = new JavaConverter(Long.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Long)object).longValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((Long[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((Long[])array)[i] = (Long)value.toJava(Long.class);
}
};
public static final JavaConverter JAVA_BOOLEANPRIM_CONVERTER = new JavaConverter(boolean.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyBoolean.newBoolean(runtime, ((Boolean)object).booleanValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyBoolean.newBoolean(runtime, ((boolean[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((boolean[])array)[i] = (Boolean)value.toJava(boolean.class);
}
};
public static final JavaConverter JAVA_FLOATPRIM_CONVERTER = new JavaConverter(float.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFloat.newFloat(runtime, ((Float)object).doubleValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFloat.newFloat(runtime, ((float[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((float[])array)[i] = (Float)value.toJava(float.class);
}
};
public static final JavaConverter JAVA_DOUBLEPRIM_CONVERTER = new JavaConverter(double.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFloat.newFloat(runtime, ((Double)object).doubleValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFloat.newFloat(runtime, ((double[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((double[])array)[i] = (Double)value.toJava(double.class);
}
};
public static final JavaConverter JAVA_CHARPRIM_CONVERTER = new JavaConverter(char.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Character)object).charValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFixnum.newFixnum(runtime, ((char[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((char[])array)[i] = (Character)value.toJava(char.class);
}
};
public static final JavaConverter JAVA_BYTEPRIM_CONVERTER = new JavaConverter(byte.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Byte)object).byteValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFixnum.newFixnum(runtime, ((byte[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((byte[])array)[i] = (Byte)value.toJava(byte.class);
}
};
public static final JavaConverter JAVA_SHORTPRIM_CONVERTER = new JavaConverter(short.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Short)object).shortValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFixnum.newFixnum(runtime, ((short[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((short[])array)[i] = (Short)value.toJava(short.class);
}
};
public static final JavaConverter JAVA_INTPRIM_CONVERTER = new JavaConverter(int.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Integer)object).intValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFixnum.newFixnum(runtime, ((int[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((int[])array)[i] = (Integer)value.toJava(int.class);
}
};
public static final JavaConverter JAVA_LONGPRIM_CONVERTER = new JavaConverter(long.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyFixnum.newFixnum(runtime, ((Long)object).longValue());
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return RubyFixnum.newFixnum(runtime, ((long[])array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((long[])array)[i] = (Long)value.toJava(long.class);
}
};
public static class StringConverter extends JavaConverter {
public StringConverter() {
super(String.class);
}
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyString.newUnicodeString(runtime, (String)object);
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((String[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((String[])array)[i] = value.toJava(String.class);
}
}
public static final StringConverter JAVA_STRING_CONVERTER = new StringConverter();
public static final JavaConverter JAVA_CHARSEQUENCE_CONVERTER = new JavaConverter(CharSequence.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyString.newUnicodeString(runtime, (CharSequence)object);
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((CharSequence[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((CharSequence[])array)[i] = (CharSequence)value.toJava(CharSequence.class);
}
};
public static final JavaConverter BYTELIST_CONVERTER = new JavaConverter(ByteList.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyString.newString(runtime, (ByteList)object);
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((ByteList[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((ByteList[])array)[i] = (ByteList)value.toJava(ByteList.class);
}
};
public static final JavaConverter JAVA_BIGINTEGER_CONVERTER = new JavaConverter(BigInteger.class) {
public IRubyObject convert(Ruby runtime, Object object) {
if (object == null) return runtime.getNil();
return RubyBignum.newBignum(runtime, (BigInteger)object);
}
public IRubyObject get(Ruby runtime, Object array, int i) {
return convert(runtime, ((BigInteger[]) array)[i]);
}
public void set(Ruby runtime, Object array, int i, IRubyObject value) {
((BigInteger[])array)[i] = (BigInteger)value.toJava(BigInteger.class);
}
};
private static final Map JAVA_CONVERTERS = new IdentityHashMap<>(24);
static {
JAVA_CONVERTERS.put(Byte.class, JAVA_BYTE_CONVERTER);
JAVA_CONVERTERS.put(Byte.TYPE, JAVA_BYTEPRIM_CONVERTER);
JAVA_CONVERTERS.put(Short.class, JAVA_SHORT_CONVERTER);
JAVA_CONVERTERS.put(Short.TYPE, JAVA_SHORTPRIM_CONVERTER);
JAVA_CONVERTERS.put(Character.class, JAVA_CHAR_CONVERTER);
JAVA_CONVERTERS.put(Character.TYPE, JAVA_CHARPRIM_CONVERTER);
JAVA_CONVERTERS.put(Integer.class, JAVA_INT_CONVERTER);
JAVA_CONVERTERS.put(Integer.TYPE, JAVA_INTPRIM_CONVERTER);
JAVA_CONVERTERS.put(Long.class, JAVA_LONG_CONVERTER);
JAVA_CONVERTERS.put(Long.TYPE, JAVA_LONGPRIM_CONVERTER);
JAVA_CONVERTERS.put(Float.class, JAVA_FLOAT_CONVERTER);
JAVA_CONVERTERS.put(Float.TYPE, JAVA_FLOATPRIM_CONVERTER);
JAVA_CONVERTERS.put(Double.class, JAVA_DOUBLE_CONVERTER);
JAVA_CONVERTERS.put(Double.TYPE, JAVA_DOUBLEPRIM_CONVERTER);
JAVA_CONVERTERS.put(Boolean.class, JAVA_BOOLEAN_CONVERTER);
JAVA_CONVERTERS.put(Boolean.TYPE, JAVA_BOOLEANPRIM_CONVERTER);
JAVA_CONVERTERS.put(String.class, JAVA_STRING_CONVERTER);
JAVA_CONVERTERS.put(CharSequence.class, JAVA_CHARSEQUENCE_CONVERTER);
JAVA_CONVERTERS.put(ByteList.class, BYTELIST_CONVERTER);
JAVA_CONVERTERS.put(BigInteger.class, JAVA_BIGINTEGER_CONVERTER);
}
private static final NumericConverter NUMERIC_TO_BYTE = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongByteable(value) ) return (byte) value;
throw numeric.getRuntime().newRangeError("too big for byte: " + numeric);
};
private static final NumericConverter NUMERIC_TO_SHORT = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongShortable(value) ) return (short) value;
throw numeric.getRuntime().newRangeError("too big for short: " + numeric);
};
private static final NumericConverter NUMERIC_TO_CHARACTER = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongCharable(value) ) return (char) value;
throw numeric.getRuntime().newRangeError("too big for char: " + numeric);
};
private static final NumericConverter NUMERIC_TO_INTEGER = (numeric, target) -> {
final long value = numeric.getLongValue();
if ( isLongIntable(value) ) return (int) value;
throw numeric.getRuntime().newRangeError("too big for int: " + numeric);
};
private static final NumericConverter NUMERIC_TO_LONG = (numeric, target) -> numeric.getLongValue();
private static final NumericConverter NUMERIC_TO_FLOAT = (numeric, target) -> {
final double value = numeric.getDoubleValue();
// many cases are ok to convert to float; if not one of these, error
if ( isDoubleFloatable(value) ) return (float) value;
throw numeric.getRuntime().newTypeError("too big for float: " + numeric);
};
private static final NumericConverter NUMERIC_TO_DOUBLE = (numeric, target) -> numeric.getDoubleValue();
private static final NumericConverter NUMERIC_TO_BIGINTEGER = (numeric, target) -> numeric.getBigIntegerValue();
private static final NumericConverter NUMERIC_TO_OTHER = (numeric, target) -> {
if (target.isAssignableFrom(numeric.getClass())) {
// just return as-is, since we can't do any coercion
return numeric;
}
// otherwise, error; no conversion available
throw numeric.getRuntime().newTypeError("could not coerce " + numeric.getMetaClass() + " to " + target);
};
private static final NumericConverter
© 2015 - 2025 Weber Informatics LLC | Privacy Policy