package org.jruby.util;
import org.jruby.Ruby;
import org.jruby.RubyBasicObject;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyEncoding;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyHash;
import org.jruby.RubyIO;
import org.jruby.RubyInteger;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyString;
import org.jruby.RubySymbol;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.ClassIndex;
import org.jruby.RubyNil;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.JavaSites.TypeConverterSites;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.util.RubyStringBuilder.str;
import static org.jruby.util.RubyStringBuilder.types;
public class TypeConverter {
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, String convertMethod, boolean raise) {
return convertToType(target.getClassRuntime().getCurrentContext(), obj, target, convertMethod, raise);
}
public static IRubyObject convertToType(ThreadContext context, IRubyObject obj, RubyClass target, String convertMethod, boolean raise) {
IRubyObject r = obj.checkCallMethod(context, convertMethod);
return r == null ? handleUncoercibleObject(context.runtime, obj, target, raise) : r;
}
public static IRubyObject convertToType(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites, boolean raise) {
IRubyObject r = obj.checkCallMethod(context, sites);
return r == null ? handleUncoercibleObject(context.runtime, obj, target, raise) : r;
}
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(obj, target, convertMethod, true);
if (!target.isInstance(val)) throw newTypeError(obj.getRuntime(), obj, target, convertMethod, val);
return val;
}
public static IRubyObject convertToType(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(context, obj, target, sites, true);
if (!target.isInstance(val)) throw newTypeError(context.runtime, obj, target, sites.methodName, val);
return val;
}
@Deprecated
public static IRubyObject convertToType19(IRubyObject obj, RubyClass target, String convertMethod, boolean raise) {
return convertToType(obj, target, convertMethod, raise);
}
@Deprecated
public static IRubyObject convertToType19(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites, boolean raise) {
return convertToType(context, obj, target, sites, raise);
}
@Deprecated
public static IRubyObject convertToType19(IRubyObject obj, RubyClass target, String convertMethod) {
return convertToType(obj, target, convertMethod);
}
@Deprecated
public static IRubyObject convertToType19(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
return convertToType(context, obj, target, sites);
}
public static RubyFloat toFloat(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyNumeric) {
return ((RubyNumeric) obj).convertToFloat();
}
if (obj instanceof RubyString || obj.isNil()) {
throw runtime.newTypeError(obj, "Float");
}
return (RubyFloat) TypeConverter.convertToType(obj, runtime.getFloat(), "to_f", true);
}
public static IRubyObject checkData(IRubyObject obj) {
if (obj instanceof org.jruby.runtime.marshal.DataType) return obj;
Ruby runtime = obj.getRuntime();
throw runtime.newTypeError(str(runtime, "wrong argument type ", typeAsString(obj), " (expected Data)"));
}
public static RubyString typeAsString(IRubyObject obj) {
if (obj.isNil()) return obj.getRuntime().newString("nil");
if (obj instanceof RubyBoolean) return obj.getRuntime().newString(obj.isTrue() ? "true" : "false");
return obj.getMetaClass().getRealClass().rubyName();
}
public static RubySymbol checkID(IRubyObject obj) {
final Ruby runtime = obj.getRuntime();
if ( obj instanceof RubySymbol || obj instanceof RubyString ) {
return RubySymbol.newHardSymbol(runtime, obj);
}
final IRubyObject str = convertToTypeWithCheck(obj, runtime.getString(), "to_str");
if ( ! str.isNil() ) {
return RubySymbol.newHardSymbol(runtime, str);
}
final ThreadContext context = runtime.getCurrentContext();
throw runtime.newTypeError(obj.callMethod(context, "inspect") + " is not a symbol nor a string");
}
public static RubySymbol checkID(Ruby runtime, String name) {
return RubySymbol.newHardSymbol(runtime, name.intern());
}
public static IRubyObject convertToTypeWithCheck(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(obj, target, convertMethod, false);
if (val.isNil()) return val;
if (!target.isInstance(val)) {
throw newTypeError(obj, target, convertMethod, val);
}
return val;
}
public static IRubyObject convertToTypeWithCheck(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(context, obj, target, sites, false);
if (val == context.nil) return val;
if (!target.isInstance(val)) {
throw newTypeError(context.runtime, obj, target, sites.methodName, val);
}
return val;
}
@Deprecated
public static IRubyObject convertToTypeWithCheck19(IRubyObject obj, RubyClass target, String convertMethod) {
return convertToTypeWithCheck(obj, target, convertMethod);
}
@Deprecated
public static IRubyObject convertToTypeWithCheck19(ThreadContext context, IRubyObject obj, RubyClass target, JavaSites.CheckedSites sites) {
return convertToTypeWithCheck(context, obj, target, sites);
}
public static RaiseException newTypeError(IRubyObject obj, RubyClass target, String convertMethod, IRubyObject val) {
return newTypeError(obj.getRuntime(), obj, target, convertMethod, val);
}
public static RaiseException newTypeError(Ruby runtime, IRubyObject obj, RubyClass target, String methodName, IRubyObject val) {
IRubyObject className = types(runtime, obj.getMetaClass());
return runtime.newTypeError(str(runtime, "can't convert ", className, " to ", types(runtime, target), " (",
className, '#' + methodName + " gives ", types(runtime, val.getMetaClass()), ")"));
}
public static IRubyObject checkIntegerType(ThreadContext context, IRubyObject obj) {
if (obj instanceof RubyFixnum) return obj;
TypeConverterSites sites = sites(context);
IRubyObject conv = convertToTypeWithCheck(context, obj, context.runtime.getInteger(), sites.to_int_checked);
return conv instanceof RubyInteger ? conv : context.nil;
}
public static IRubyObject checkIntegerType(Ruby runtime, IRubyObject obj, String method) {
if (method.equals("to_int")) return checkIntegerType(runtime.getCurrentContext(), obj);
if (obj instanceof RubyFixnum) return obj;
if (method.equals("to_i")) {
ThreadContext context = runtime.getCurrentContext();
TypeConverterSites sites = sites(context);
IRubyObject conv = convertToTypeWithCheck(context, obj, runtime.getInteger(), sites.to_i_checked);
return conv instanceof RubyInteger ? conv : runtime.getNil();
}
IRubyObject conv = TypeConverter.convertToType(obj, runtime.getInteger(), method, false);
return conv instanceof RubyInteger ? conv : runtime.getNil();
}
public static IRubyObject checkFloatType(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyFloat) return obj;
if (!(obj instanceof RubyNumeric)) return runtime.getNil();
ThreadContext context = runtime.getCurrentContext();
TypeConverterSites sites = sites(context);
return TypeConverter.convertToTypeWithCheck(context, obj, runtime.getFloat(), sites.to_f_checked);
}
public static IRubyObject checkHashType(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyHash) return obj;
return TypeConverter.convertToTypeWithCheck(obj, runtime.getHash(), "to_hash");
}
public static IRubyObject checkHashType(ThreadContext context, JavaSites.CheckedSites sites, IRubyObject obj) {
if (obj instanceof RubyHash) return obj;
return TypeConverter.convertToTypeWithCheck(context, obj, context.runtime.getHash(), sites);
}
public static IRubyObject checkStringType(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyString) return obj;
return TypeConverter.convertToTypeWithCheck(obj, runtime.getString(), "to_str");
}
public static IRubyObject checkStringType(ThreadContext context, JavaSites.CheckedSites sites, IRubyObject obj) {
if (obj instanceof RubyString) return obj;
return TypeConverter.convertToTypeWithCheck(context, obj, context.runtime.getString(), sites);
}
public static IRubyObject checkStringType(ThreadContext context, JavaSites.CheckedSites sites, IRubyObject obj, RubyClass target) {
if (obj instanceof RubyString) return obj;
return TypeConverter.convertToTypeWithCheck(context, obj, target, sites);
}
public static IRubyObject checkArrayType(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyArray) return obj;
return TypeConverter.convertToTypeWithCheck(obj, runtime.getArray(), "to_ary");
}
public static IRubyObject checkArrayType(ThreadContext context, JavaSites.CheckedSites sites, IRubyObject obj) {
if (obj instanceof RubyArray) return obj;
return TypeConverter.convertToTypeWithCheck(context, obj, context.runtime.getArray(), sites);
}
public static IRubyObject ioCheckIO(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyIO) return obj;
return TypeConverter.convertToTypeWithCheck(obj, runtime.getIO(), "to_io");
}
public static RubyIO ioGetIO(Ruby runtime, IRubyObject obj) {
return (RubyIO)convertToType(obj, runtime.getIO(), "to_io");
}
public static IRubyObject checkArrayType(ThreadContext context, IRubyObject obj) {
return TypeConverter.convertToTypeWithCheck(context, obj, context.runtime.getArray(), sites(context).to_ary_checked);
}
@Deprecated
public static IRubyObject checkArrayType(IRubyObject obj) {
return checkArrayType(obj.getRuntime().getCurrentContext(), obj);
}
public static IRubyObject handleUncoercibleObject(boolean raise, IRubyObject obj, RubyClass target) {
return handleUncoercibleObject(obj.getRuntime(), obj, target, raise);
}
public static IRubyObject handleUncoercibleObject(Ruby runtime, IRubyObject obj, RubyClass target, boolean raise) {
if (raise) throw runtime.newTypeError(str(runtime, "no implicit conversion of ", typeAsString(obj), " into " , target));
return runtime.getNil();
}
@Deprecated
public static IRubyObject handleImplicitlyUncoercibleObject(boolean raise, IRubyObject obj, RubyClass target) {
return handleUncoercibleObject(obj.getRuntime(), obj, target, raise);
}
public static void checkType(ThreadContext context, IRubyObject x, final RubyModule type) {
assert x != RubyBasicObject.UNDEF;
ClassIndex xt = x.getMetaClass().getClassIndex();
if (xt != type.getClassIndex() && !type.isInstance(x)) {
Ruby runtime = context.runtime;
throw context.runtime.newTypeError(str(runtime, "wrong argument type ", types(runtime, x.getMetaClass()), " (expected ", types(runtime, type), ")"));
}
}
public static IRubyObject convertToInteger(ThreadContext context, IRubyObject val, int base) {
final Ruby runtime = context.runtime;
IRubyObject tmp;
for (;;) {
switch (val.getMetaClass().getClassIndex()) {
case FLOAT:
if (base != 0) raiseIntegerBaseError(context);
return RubyNumeric.dbl2ival(context.runtime, ((RubyFloat) val).getValue());
case INTEGER:
if (base != 0) raiseIntegerBaseError(context);
return val;
case STRING:
return RubyNumeric.str2inum(context.runtime, (RubyString) val, base, true);
case NIL:
if (base != 0) raiseIntegerBaseError(context);
throw context.runtime.newTypeError("can't convert nil into Integer");
default:
if (val instanceof RubyString) {
return RubyNumeric.str2inum(context.runtime, (RubyString) val, base, true);
}
}
if (base != 0) {
tmp = TypeConverter.checkStringType(context.runtime, val);
if (tmp != context.nil) continue;
raiseIntegerBaseError(context);
}
break;
}
tmp = TypeConverter.convertToType(context, val, runtime.getInteger(), sites(context).to_int_checked, false);
return (tmp != context.nil) ? tmp : TypeConverter.convertToType(context, val, runtime.getInteger(), sites(context).to_i_checked);
}
public static RubyArray rb_Array(ThreadContext context, IRubyObject val) {
IRubyObject tmp = checkArrayType(context, val);
if (tmp == context.nil) {
TypeConverterSites sites = sites(context);
tmp = convertToTypeWithCheck(context, val, context.runtime.getArray(), sites.to_a_checked);
if (tmp == context.nil) {
return context.runtime.newArray(val);
}
}
return (RubyArray) tmp;
}
public static RubyArray to_ary(ThreadContext context, IRubyObject ary) {
return (RubyArray) convertToType(context, ary, context.runtime.getArray(), sites(context).to_ary_checked);
}
private static void raiseIntegerBaseError(ThreadContext context) {
throw context.runtime.newArgumentError("base specified for non string value");
}
private static TypeConverterSites sites(ThreadContext context) {
return context.sites.TypeConverter;
}
@Deprecated
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, int convertMethodIndex, String convertMethod, boolean raise) {
if (!obj.respondsTo(convertMethod)) return handleUncoercibleObject(raise, obj, target);
return obj.callMethod(obj.getRuntime().getCurrentContext(), convertMethod);
}
@Deprecated
public static IRubyObject convertToType(IRubyObject obj, RubyClass target, int convertMethodIndex, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = convertToType(obj, target, convertMethod, true);
if (!target.isInstance(val)) {
Ruby runtime = obj.getRuntime();
throw runtime.newTypeError(str(runtime, types(runtime, obj.getMetaClass()), "#" + convertMethod + " should return ", types(runtime, target)));
}
return val;
}
@Deprecated
public static IRubyObject convertToTypeWithCheck(IRubyObject obj, RubyClass target, int convertMethodIndex, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = TypeConverter.convertToType(obj, target, convertMethod, false);
if (val.isNil()) return val;
if (!target.isInstance(val)) {
Ruby runtime = obj.getRuntime();
throw runtime.newTypeError(str(runtime, types(runtime, obj.getMetaClass()), "#" + convertMethod + " should return ", types(runtime, target)));
}
return val;
}
@Deprecated
public static String convertToIdentifier(IRubyObject obj) {
if (obj instanceof RubyString) {
return RubyEncoding.decodeISO(((RubyString) obj).getByteList()).intern();
}
return obj.asJavaString().intern();
}
@Deprecated
public static IRubyObject convertToTypeOrRaise(IRubyObject obj, RubyClass target, String convertMethod) {
if (target.isInstance(obj)) return obj;
IRubyObject val = TypeConverter.convertToType(obj, target, convertMethod, true);
if (val.isNil()) return val;
if (!target.isInstance(val)) {
Ruby runtime = obj.getRuntime();
throw runtime.newTypeError(str(runtime, types(runtime, obj.getMetaClass()), "#" + convertMethod + " should return ", types(runtime, target)));
}
return val;
}
}