All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 NUMERIC_TO_OBJECT = (numeric, target) -> {
        // for Object, default to natural wrapper type
        if (numeric instanceof RubyFixnum) {
            long value = numeric.getLongValue();
            return Long.valueOf(value);
        } else if (numeric instanceof RubyFloat) {
            double value = numeric.getDoubleValue();
            return Double.valueOf(value);
        } else if (numeric instanceof RubyBignum) {
            return ((RubyBignum)numeric).getValue();
        } else if (numeric instanceof RubyBigDecimal) {
            return ((RubyBigDecimal)numeric).getValue();
        } else {
            return NUMERIC_TO_OTHER.coerce(numeric, target);
        }
    };
    private static final NumericConverter NUMERIC_TO_VOID = (numeric, target) -> null;
    private static boolean isDoubleFloatable(double value) {
        return true;
    }
    private static boolean isLongByteable(long value) {
        return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE;
    }
    private static boolean isLongShortable(long value) {
        return value >= Short.MIN_VALUE && value <= Short.MAX_VALUE;
    }
    private static boolean isLongCharable(long value) {
        return value >= Character.MIN_VALUE && value <= Character.MAX_VALUE;
    }
    private static boolean isLongIntable(long value) {
        return value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE;
    }

    private static final Map NUMERIC_CONVERTERS = new IdentityHashMap<>(24);

    static {
        NUMERIC_CONVERTERS.put(Byte.TYPE, NUMERIC_TO_BYTE);
        NUMERIC_CONVERTERS.put(Byte.class, NUMERIC_TO_BYTE);
        NUMERIC_CONVERTERS.put(Short.TYPE, NUMERIC_TO_SHORT);
        NUMERIC_CONVERTERS.put(Short.class, NUMERIC_TO_SHORT);
        NUMERIC_CONVERTERS.put(Character.TYPE, NUMERIC_TO_CHARACTER);
        NUMERIC_CONVERTERS.put(Character.class, NUMERIC_TO_CHARACTER);
        NUMERIC_CONVERTERS.put(Integer.TYPE, NUMERIC_TO_INTEGER);
        NUMERIC_CONVERTERS.put(Integer.class, NUMERIC_TO_INTEGER);
        NUMERIC_CONVERTERS.put(Long.TYPE, NUMERIC_TO_LONG);
        NUMERIC_CONVERTERS.put(Long.class, NUMERIC_TO_LONG);
        NUMERIC_CONVERTERS.put(Float.TYPE, NUMERIC_TO_FLOAT);
        NUMERIC_CONVERTERS.put(Float.class, NUMERIC_TO_FLOAT);
        NUMERIC_CONVERTERS.put(Double.TYPE, NUMERIC_TO_DOUBLE);
        NUMERIC_CONVERTERS.put(Double.class, NUMERIC_TO_DOUBLE);
        NUMERIC_CONVERTERS.put(BigInteger.class, NUMERIC_TO_BIGINTEGER);
        NUMERIC_CONVERTERS.put(Object.class, NUMERIC_TO_OBJECT);
        NUMERIC_CONVERTERS.put(Number.class, NUMERIC_TO_OBJECT);
        NUMERIC_CONVERTERS.put(Serializable.class, NUMERIC_TO_OBJECT);
        NUMERIC_CONVERTERS.put(void.class, NUMERIC_TO_VOID);
    }

    public static Object objectFromJavaProxy(IRubyObject self) {
        return ((JavaProxy)self).getObject();
    }

    public static final Map PRIMITIVE_CLASSES;
    static {
        Map primitiveClasses = new HashMap<>(10, 1);
        primitiveClasses.put("boolean", Boolean.TYPE);
        primitiveClasses.put("byte", Byte.TYPE);
        primitiveClasses.put("char", Character.TYPE);
        primitiveClasses.put("short", Short.TYPE);
        primitiveClasses.put("int", Integer.TYPE);
        primitiveClasses.put("long", Long.TYPE);
        primitiveClasses.put("float", Float.TYPE);
        primitiveClasses.put("double", Double.TYPE);
        PRIMITIVE_CLASSES = Collections.unmodifiableMap(primitiveClasses);
    }

    public static Class getPrimitiveClass(final String name) {
        switch (name) {
            case "boolean": return Boolean.TYPE;
            case "byte": return Byte.TYPE;
            case "char": return Character.TYPE;
            case "short": return Short.TYPE;
            case "int": return Integer.TYPE;
            case "long": return Long.TYPE;
            case "float": return Float.TYPE;
            case "double": return Double.TYPE;

            case "void": return Void.TYPE;
        }
        return null;
    }

    @Deprecated
    public static Object convertRubyToJava(IRubyObject rubyObject) {
        return convertRubyToJava(rubyObject, Object.class);
    }

    @Deprecated
    public static Object convertRubyToJava(IRubyObject rubyObject, Class javaClass) {
        if ( javaClass == void.class || rubyObject == null || rubyObject.isNil() ) {
            return null;
        }

        final Ruby runtime = rubyObject.getRuntime();
        final ThreadContext context = runtime.getCurrentContext();

        IRubyObject origObject = rubyObject;
        if (rubyObject.dataGetStruct() instanceof JavaObject) {
            rubyObject = (IRubyObject) rubyObject.dataGetStruct();
            if ( rubyObject == null ) {
                throw new RuntimeException("dataGetStruct returned null for " + origObject.getType().getName());
            }
        } else if (rubyObject.respondsTo("java_object")) {
            rubyObject = rubyObject.callMethod(context, "java_object");
            if( rubyObject == null ) {
                throw new RuntimeException("java_object returned null for " + origObject.getType().getName());
            }
        }

        if (rubyObject instanceof JavaObject) {
            Object value =  ((JavaObject) rubyObject).getValue();
            return convertArgument(runtime, value, value.getClass());
        }

        if (javaClass == Object.class || javaClass == null) {
            /* The Java method doesn't care what class it is, but we need to
               know what to convert it to, so we use the object's own class.
               If that doesn't help, we use String to force a call to the
               object's "to_s" method. */
            javaClass = rubyObject.getJavaClass();
        }

        if (javaClass.isInstance(rubyObject)) {
            // rubyObject is already of the required jruby class (or subclass)
            return rubyObject;
        }

        // the converters handle not only primitive types but also their boxed versions, so we should check
        // if we have a converter before checking for isPrimitive()
        RubyConverter converter = RUBY_CONVERTERS.get(javaClass);
        if (converter != null) {
            return converter.convert(context, rubyObject);
        }

        if (javaClass.isPrimitive()) {
            String s = ((RubyString) TypeConverter.convertToType(rubyObject, runtime.getString(), "to_s", true)).getUnicodeValue();
            if ( s.length() > 0 ) return s.charAt(0);
            return '\0';
        }
        if (javaClass == String.class) {
            RubyString rubyString = (RubyString) rubyObject.callMethod(context, "to_s");
            ByteList bytes = rubyString.getByteList();
            return RubyEncoding.decodeUTF8(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
        }
        if (javaClass == ByteList.class) {
            return rubyObject.convertToString().getByteList();
        }
        if (javaClass == BigInteger.class) {
         	if ( rubyObject instanceof RubyBignum ) {
         		return ((RubyBignum) rubyObject).getValue();
         	}
            if ( rubyObject instanceof RubyNumeric ) {
 				return BigInteger.valueOf( ((RubyNumeric) rubyObject).getLongValue() );
         	}
            if ( rubyObject.respondsTo("to_i") ) {
         		RubyNumeric rubyNumeric = ((RubyNumeric) rubyObject.callMethod(context, "to_f"));
 				return  BigInteger.valueOf( rubyNumeric.getLongValue() );
         	}
        }
        if (javaClass == BigDecimal.class && !(rubyObject instanceof JavaObject)) {
         	if (rubyObject.respondsTo("to_f")) {
             	double double_value = ((RubyNumeric)rubyObject.callMethod(context, "to_f")).getDoubleValue();
             	return new BigDecimal(double_value);
         	}
        }

        try {
            if ( isDuckTypeConvertable(rubyObject.getClass(), javaClass) ) {
                return convertProcToInterface(context, (RubyObject) rubyObject, javaClass);
            }
            return ((JavaObject) rubyObject).getValue();
        }
        catch (ClassCastException ex) {
            if (runtime.getDebug().isTrue()) ex.printStackTrace();
            return null;
        }
    }

    @Deprecated
    public static byte convertRubyToJavaByte(IRubyObject rubyObject) {
        return (Byte) convertRubyToJava(rubyObject, byte.class);
    }

    @Deprecated
    public static short convertRubyToJavaShort(IRubyObject rubyObject) {
        return (Short) convertRubyToJava(rubyObject, short.class);
    }

    @Deprecated
    public static char convertRubyToJavaChar(IRubyObject rubyObject) {
        return (Character) convertRubyToJava(rubyObject, char.class);
    }

    @Deprecated
    public static int convertRubyToJavaInt(IRubyObject rubyObject) {
        return (Integer) convertRubyToJava(rubyObject, int.class);
    }

    @Deprecated
    public static long convertRubyToJavaLong(IRubyObject rubyObject) {
        return (Long) convertRubyToJava(rubyObject, long.class);
    }

    @Deprecated
    public static float convertRubyToJavaFloat(IRubyObject rubyObject) {
        return (Float) convertRubyToJava(rubyObject, float.class);
    }

    @Deprecated
    public static double convertRubyToJavaDouble(IRubyObject rubyObject) {
        return (Double) convertRubyToJava(rubyObject, double.class);
    }

    @Deprecated
    public static boolean convertRubyToJavaBoolean(IRubyObject rubyObject) {
        return (Boolean) convertRubyToJava(rubyObject, boolean.class);
    }

    @Deprecated
    public static Object convertArgumentToType(ThreadContext context, IRubyObject arg, Class target) {
        return arg.toJava(target);
    }

    @Deprecated
    public static Object coerceNilToType(RubyNil nil, Class target) {
        return nil.toJava(target);
    }

    @Deprecated
    public static final RubyConverter RUBY_BOOLEAN_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.isTrue();
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_BYTE_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_i")) {
                return (byte) ((RubyNumeric) rubyObject.callMethod(context, "to_i")).getLongValue();
            }
            return (byte) 0;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_SHORT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_i")) {
                return (short) ((RubyNumeric) rubyObject.callMethod(context, "to_i")).getLongValue();
            }
            return (short) 0;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_CHAR_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_i")) {
                return (char) ((RubyNumeric) rubyObject.callMethod(context, "to_i")).getLongValue();
            }
            return (char) 0;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_INTEGER_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_i")) {
                return (int) ((RubyNumeric) rubyObject.callMethod(context, "to_i")).getLongValue();
            }
            return (int) 0;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_LONG_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_i")) {
                return ((RubyNumeric) rubyObject.callMethod(context, "to_i")).getLongValue();
            }
            return 0L;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_FLOAT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_f")) {
                return (float) ((RubyNumeric) rubyObject.callMethod(context, "to_f")).getDoubleValue();
            }
            return 0.0f;
        }
    };

    @Deprecated
    public static final RubyConverter RUBY_DOUBLE_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            if (rubyObject.respondsTo("to_f")) {
                return ((RubyNumeric) rubyObject.callMethod(context, "to_f")).getDoubleValue();
            }
            return 0.0d;
        }
    };

    @Deprecated
    public static final Map RUBY_CONVERTERS = new HashMap<>(16, 1);
    static {
        RUBY_CONVERTERS.put(Boolean.class, RUBY_BOOLEAN_CONVERTER);
        RUBY_CONVERTERS.put(Boolean.TYPE, RUBY_BOOLEAN_CONVERTER);
        RUBY_CONVERTERS.put(Byte.class, RUBY_BYTE_CONVERTER);
        RUBY_CONVERTERS.put(Byte.TYPE, RUBY_BYTE_CONVERTER);
        RUBY_CONVERTERS.put(Short.class, RUBY_SHORT_CONVERTER);
        RUBY_CONVERTERS.put(Short.TYPE, RUBY_SHORT_CONVERTER);
        RUBY_CONVERTERS.put(Integer.class, RUBY_INTEGER_CONVERTER);
        RUBY_CONVERTERS.put(Integer.TYPE, RUBY_INTEGER_CONVERTER);
        RUBY_CONVERTERS.put(Long.class, RUBY_LONG_CONVERTER);
        RUBY_CONVERTERS.put(Long.TYPE, RUBY_LONG_CONVERTER);
        RUBY_CONVERTERS.put(Float.class, RUBY_FLOAT_CONVERTER);
        RUBY_CONVERTERS.put(Float.TYPE, RUBY_FLOAT_CONVERTER);
        RUBY_CONVERTERS.put(Double.class, RUBY_DOUBLE_CONVERTER);
        RUBY_CONVERTERS.put(Double.TYPE, RUBY_DOUBLE_CONVERTER);
    }

    @Deprecated
    public static IRubyObject convertJavaToRuby(Ruby runtime, JavaConverter converter, Object object) {
        if (converter == null || converter == JAVA_DEFAULT_CONVERTER) {
            return Java.getInstance(runtime, object);
        }
        return converter.convert(runtime, object);
    }

    @Deprecated
    public interface RubyConverter {
        public Object convert(ThreadContext context, IRubyObject rubyObject);
    }

    @Deprecated
    public static final RubyConverter ARRAY_BOOLEAN_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Boolean.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_BYTE_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Byte.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_SHORT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Short.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_CHAR_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Character.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_INT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Integer.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_LONG_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Long.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_FLOAT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Float.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_DOUBLE_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Double.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_OBJECT_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Object.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_CLASS_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(Class.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_STRING_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(String.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_BIGINTEGER_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(BigInteger.class);
        }
    };

    @Deprecated
    public static final RubyConverter ARRAY_BIGDECIMAL_CONVERTER = new RubyConverter() {
        public Object convert(ThreadContext context, IRubyObject rubyObject) {
            return rubyObject.toJava(BigDecimal.class);
        }
    };

    @Deprecated
    public static final Map ARRAY_CONVERTERS = new HashMap<>(24, 1);
    static {
        ARRAY_CONVERTERS.put(Boolean.class, ARRAY_BOOLEAN_CONVERTER);
        ARRAY_CONVERTERS.put(Boolean.TYPE, ARRAY_BOOLEAN_CONVERTER);
        ARRAY_CONVERTERS.put(Byte.class, ARRAY_BYTE_CONVERTER);
        ARRAY_CONVERTERS.put(Byte.TYPE, ARRAY_BYTE_CONVERTER);
        ARRAY_CONVERTERS.put(Short.class, ARRAY_SHORT_CONVERTER);
        ARRAY_CONVERTERS.put(Short.TYPE, ARRAY_SHORT_CONVERTER);
        ARRAY_CONVERTERS.put(Character.class, ARRAY_CHAR_CONVERTER);
        ARRAY_CONVERTERS.put(Character.TYPE, ARRAY_CHAR_CONVERTER);
        ARRAY_CONVERTERS.put(Integer.class, ARRAY_INT_CONVERTER);
        ARRAY_CONVERTERS.put(Integer.TYPE, ARRAY_INT_CONVERTER);
        ARRAY_CONVERTERS.put(Long.class, ARRAY_LONG_CONVERTER);
        ARRAY_CONVERTERS.put(Long.TYPE, ARRAY_LONG_CONVERTER);
        ARRAY_CONVERTERS.put(Float.class, ARRAY_FLOAT_CONVERTER);
        ARRAY_CONVERTERS.put(Float.TYPE, ARRAY_FLOAT_CONVERTER);
        ARRAY_CONVERTERS.put(Double.class, ARRAY_DOUBLE_CONVERTER);
        ARRAY_CONVERTERS.put(Double.TYPE, ARRAY_DOUBLE_CONVERTER);
        ARRAY_CONVERTERS.put(String.class, ARRAY_STRING_CONVERTER);
        ARRAY_CONVERTERS.put(Class.class, ARRAY_CLASS_CONVERTER);
        ARRAY_CONVERTERS.put(BigInteger.class, ARRAY_BIGINTEGER_CONVERTER);
        ARRAY_CONVERTERS.put(BigDecimal.class, ARRAY_BIGDECIMAL_CONVERTER);
    }

    @Deprecated
    public static RubyConverter getArrayConverter(Class type) {
        RubyConverter converter = ARRAY_CONVERTERS.get(type);
        if (converter == null) {
            return ARRAY_OBJECT_CONVERTER;
        }
        return converter;
    }

    /**
     * High-level object conversion utility.
     */
    @Deprecated
    public static IRubyObject ruby_to_java(final IRubyObject recv, IRubyObject object, Block unusedBlock) {
        if (object.respondsTo("to_java_object")) {
            IRubyObject result = (IRubyObject)object.dataGetStruct();
            if (result == null) {
                result = object.callMethod(recv.getRuntime().getCurrentContext(), "to_java_object");
            }
            if (result instanceof JavaObject) {
                recv.getRuntime().getJavaSupport().getObjectProxyCache().put(((JavaObject) result).getValue(), object);
            }
            return result;
        }

        return primitive_to_java(recv, object, unusedBlock);
    }

    @Deprecated
    public static IRubyObject java_to_primitive(IRubyObject recv, IRubyObject object, Block unusedBlock) {
        if (object instanceof JavaObject) {
            return JavaUtil.convertJavaToRuby(recv.getRuntime(), ((JavaObject) object).getValue());
        }

        return object;
    }

    @Deprecated
    public static IRubyObject primitive_to_java(IRubyObject recv, IRubyObject object, Block unusedBlock) {
        if (object instanceof JavaObject) {
            return object;
        }
        Ruby runtime = recv.getRuntime();
        Object javaObject;
        switch (object.getMetaClass().getClassIndex()) {
        case NIL:
            javaObject = null;
            break;
        case INTEGER:
            if (object instanceof RubyFixnum) {
                javaObject = Long.valueOf(((RubyFixnum) object).getLongValue());
            } else {
                javaObject = ((RubyBignum) object).getValue();
            }
            break;
        case FLOAT:
            javaObject = new Double(((RubyFloat) object).getValue());
            break;
        case STRING:
            ByteList bytes = ((RubyString) object).getByteList();
            javaObject = RubyEncoding.decodeUTF8(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
            break;
        case TRUE:
            javaObject = Boolean.TRUE;
            break;
        case FALSE:
            javaObject = Boolean.FALSE;
            break;
        case TIME:
            javaObject = ((RubyTime) object).getJavaDate();
            break;
        default:
            // it's not one of the types we convert, so just pass it out as-is without wrapping
            return object;
        }

        // we've found a Java type to which we've coerced the Ruby value, wrap it
        return JavaObject.wrap(runtime, javaObject);
    }

    @Deprecated
    public static Object convertArgument(Ruby runtime, Object argument, Class parameterType) {
        if (argument == null) {
          if(parameterType.isPrimitive()) {
            throw runtime.newTypeError("primitives do not accept null");
          } else {
            return null;
          }
        }

        if (argument instanceof JavaObject) {
            argument = ((JavaObject) argument).getValue();
            if (argument == null) {
                return null;
            }
        }
        Class type = primitiveToWrapper(parameterType);

        if (argument.getClass() == type) return argument;

        if (type == Void.class) {
            return null;
        }

        if (argument instanceof Number) {
            final Number number = (Number) argument;
            if (type == Long.class) {
                return number.longValue();
            } else if (type == Integer.class) {
                return number.intValue();
            } else if (type == Byte.class) {
                return number.byteValue();
            } else if (type == Character.class) {
                return (char)number.intValue();
            } else if (type == Double.class) {
                return number.doubleValue();
            } else if (type == Float.class) {
                return number.floatValue();
            } else if (type == Short.class) {
                return number.shortValue();
            }
        }
        if (isDuckTypeConvertable(argument.getClass(), parameterType)) {
            RubyObject rubyObject = (RubyObject) argument;
            if (!rubyObject.respondsTo("java_object")) {
                return convertProcToInterface(runtime.getCurrentContext(), rubyObject, parameterType);
            }
        }
        return argument;
    }

    /**
     * High-level object conversion utility function 'java_to_primitive' is the low-level version
     */
    @Deprecated
    public static IRubyObject java_to_ruby(Ruby runtime, IRubyObject object) {
        if (object instanceof JavaObject) {
            return JavaUtil.convertJavaToUsableRubyObject(runtime, ((JavaObject) object).getValue());
        }
        return object;
    }

    // FIXME: This doesn't actually support anything but String
    @Deprecated
    public static Object coerceStringToType(RubyString string, Class target) {
        try {
            ByteList bytes = string.getByteList();

            // 1.9 support for encodings
            // TODO: Fix charset use for JRUBY-4553
            return new String(bytes.getUnsafeBytes(), bytes.begin(), bytes.length(), string.getEncoding().toString());
        } catch (UnsupportedEncodingException uee) {
            return string.toString();
        }
    }

    @Deprecated
    public static Object coerceOtherToType(ThreadContext context, IRubyObject arg, Class target) {
        if (isDuckTypeConvertable(arg.getClass(), target)) {
            RubyObject rubyObject = (RubyObject) arg;
            if (!rubyObject.respondsTo("java_object")) {
                return convertProcToInterface(context, rubyObject, target);
            }
        }

        // it's either as converted as we can make it via above logic or it's
        // not one of the types we convert, so just pass it out as-is without wrapping
        return arg;
    }

    @Deprecated
    public static Object coerceJavaObjectToType(ThreadContext context, Object javaObject, Class target) {
        if (javaObject != null && isDuckTypeConvertable(javaObject.getClass(), target)) {
            RubyObject rubyObject = (RubyObject) javaObject;
            if (!rubyObject.respondsTo("java_object")) {
                return convertProcToInterface(context, rubyObject, target);
            }

            // can't be converted any more, return it
            return javaObject;
        } else {
            return javaObject;
        }
    }

    @Deprecated
    public static JavaObject unwrapJavaObject(Ruby runtime, IRubyObject convertee, String errorMessage) {
        IRubyObject obj = convertee;
        if(!(obj instanceof JavaObject)) {
            if (obj.dataGetStruct() != null && (obj.dataGetStruct() instanceof JavaObject)) {
                obj = (JavaObject)obj.dataGetStruct();
            } else {
                throw runtime.newTypeError(errorMessage);
            }
        }
        return (JavaObject)obj;
    }
}