Dear Thomas
At Fri, 27 Apr 2007 18:59:50 -0500,
Thomas E Enebo wrote:
>
> SOS...SOS
...snip..
> JRUBY-309: Implement IConv Exception classes (e.g.
> Iconv::IllegalSequence and friends)
> JRUBY-310: Implement Iconv instance methods iconv, close
> JRUBY-788: Implement IConv#open
>
> These last three are probably meant for one person (and 309 may
> actually be done...we just need someone to verify that).
I reimplemented Iconv to be more compatible with CRuby 1.8.5.
Please check the attached patch and test cases.
The following problems are still not fixed.
--- RubyIconv.java ---
>> // FIXME: We are assuming that original string will be raw bytes. If -Ku is
>> provided
>> // this will not be true, but that is ok for now. Deal with that when
>> someone needs it.
----------------------
I think this was the last task in your SOS message.
Is there any other task to try? :)
Thanks,
--
___/_ __/ ___/ ITOCHU TECHNO-SOLUTIONS Corp.
/ / / Advanced Technology Team - Koichiro Ohba
____/ __/ ____/ mailto:[EMAIL PROTECTED]
Index: test/test_iconv.rb
===================================================================
--- test/test_iconv.rb (revision 0)
+++ test/test_iconv.rb (revision 0)
@@ -0,0 +1,61 @@
+require 'test/unit'
+require 'stringio'
+require 'iconv'
+
+class TestIconv < Test::Unit::TestCase
+ def test_euc2sjis
+ euc = ["a4a2a4a4a4a6a4a8a4aa"].pack("H*")
+ sjis = ["82a082a282a482a682a8"].pack("H*")
+ iconv = Iconv.new('SHIFT_JIS', 'EUC-JP')
+ str = iconv.iconv(euc)
+ str << iconv.iconv(nil)
+ assert_equal( sjis, str )
+ end
+
+ def test_close
+ euc = ["a4a2a4a4a4a6a4a8a4aa"].pack("H*")
+ sjis = ["82a082a282a482a682a8"].pack("H*")
+ iconv = Iconv.new('Shift_JIS', 'EUC-JP')
+ output = ""
+ begin
+ output += iconv.iconv(euc)
+ output += iconv.iconv(nil)
+ ensure
+ assert_respond_to(iconv, :close)
+ assert_equal("", iconv.close)
+ assert_equal(sjis, output)
+ end
+ end
+
+ def test_open_with_block
+ euc = ["a4a2a4a4a4a6a4a8a4aa"].pack("H*")
+ sjis = ["82a082a282a482a682a8"].pack("H*")
+ assert_respond_to(Iconv, :open)
+ iconv = Iconv.open('SHIFT_JIS', 'EUC-JP')
+ str = iconv.iconv(euc)
+ str << iconv.iconv(nil)
+ assert_equal( sjis, str )
+ end
+
+ def test_open_without_block
+ input = StringIO.new
+ input << ["a4a2a4a4a4a6a4a8a4aa"].pack("H*")
+ input << "\n"
+ input << ["a4a2a4a4a4a6a4a8a4aa"].pack("H*")
+ input << "\n"
+ output = ""
+ Iconv.open("Shift_JIS", "EUC-JP") do |cd|
+ input.rewind
+ input.each do |s|
+ output << cd.iconv(s)
+ end
+ output << cd.iconv(nil)
+ end
+ sjis = ""
+ sjis << ["82a082a282a482a682a8"].pack("H*")
+ sjis << "\n"
+ sjis << ["82a082a282a482a682a8"].pack("H*")
+ sjis << "\n"
+ assert_equal(sjis, output)
+ end
+end
Index: src/org/jruby/RubyIconv.java
===================================================================
--- src/org/jruby/RubyIconv.java (revision 3639)
+++ src/org/jruby/RubyIconv.java (working copy)
@@ -12,6 +12,7 @@
* rights and limitations under the License.
*
* Copyright (C) 2006 Thomas E Enebo <[EMAIL PROTECTED]>
+ * Copyright (C) 2007 Koichiro Ohba <[EMAIL PROTECTED]>
*
* 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"),
@@ -34,8 +35,11 @@
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.MalformedInputException;
import java.nio.charset.UnmappableCharacterException;
import java.nio.charset.UnsupportedCharsetException;
+
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
@@ -46,10 +50,13 @@
import org.jruby.util.ByteList;
public class RubyIconv extends RubyObject {
+ private CharsetDecoder fromEncoding;
+ private CharsetEncoder toEncoding;
+
public RubyIconv(Ruby runtime, RubyClass type) {
super(runtime, type);
}
-
+
private static ObjectAllocator ICONV_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyIconv(runtime, klass);
@@ -58,24 +65,82 @@
public static void createIconv(Ruby runtime) {
RubyClass iconvClass = runtime.defineClass("Iconv",
runtime.getObject(), ICONV_ALLOCATOR);
-
- RubyClass argumentError = runtime.getClass("ArgumentError");
- iconvClass.defineClassUnder("IllegalSequence", argumentError,
argumentError.getAllocator());
- iconvClass.defineClassUnder("InvalidCharacter", argumentError,
argumentError.getAllocator());
- iconvClass.defineClassUnder("InvalidEncoding", argumentError,
argumentError.getAllocator());
-
CallbackFactory callbackFactory =
runtime.callbackFactory(RubyIconv.class);
iconvClass.getMetaClass().defineFastMethod("iconv",
callbackFactory.getOptSingletonMethod("iconv"));
iconvClass.getMetaClass().defineFastMethod("conv",
callbackFactory.getOptSingletonMethod("conv"));
-
+ iconvClass.getMetaClass().defineMethod("open",
callbackFactory.getSingletonMethod("open", RubyKernel.IRUBY_OBJECT,
RubyKernel.IRUBY_OBJECT));
+
iconvClass.defineMethod("initialize",
callbackFactory.getOptMethod("initialize"));
- //iconvClass.defineMethod("iconv",
callbackFactory.getOptMethod("iconv"));
+ iconvClass.defineFastMethod("iconv",
callbackFactory.getFastOptMethod("iconv"));
+ iconvClass.defineFastMethod("close",
callbackFactory.getFastMethod("close"));
- // FIXME: JRUBY-310: Add all other iconv methods...Sopen, Iclose,
Iiconv
- // FIXME: JRUBY-309: Implement IConv Exception classes (e.g.
Iconv::IllegalSequence and friends)
+ RubyModule failure = iconvClass.defineModuleUnder("Failure");
+ CallbackFactory failureCallbackFactory =
runtime.callbackFactory(RubyFailure.class);
+ RubyClass argumentError = runtime.getClass("ArgumentError");
+
+ String[] iconvErrors = {"IllegalSequence", "InvalidCharacter",
+ "InvalidEncoding", "OutOfRange", "BrokenLibrary"};
+ for (int i = 0; i < iconvErrors.length; i++) {
+ RubyClass subClass = iconvClass.defineClassUnder(iconvErrors[i],
argumentError, RubyFailure.ICONV_FAILURE_ALLOCATOR);
+ subClass.defineMethod("initialize",
failureCallbackFactory.getOptMethod("initialize"));
+ subClass.defineFastMethod("success",
failureCallbackFactory.getFastMethod("success"));
+ subClass.defineFastMethod("failed",
failureCallbackFactory.getFastMethod("failed"));
+ subClass.defineFastMethod("inspect",
failureCallbackFactory.getFastMethod("inspect"));
+ subClass.includeModule(failure);
+ }
}
+ public static class RubyFailure extends RubyException {
+ private RubyString success;
+ private RubyString failed;
+
+ public static RubyFailure newInstance(Ruby runtime, RubyClass
excptnClass, String msg) {
+ return new RubyFailure(runtime, excptnClass, msg);
+ }
+
+ protected static ObjectAllocator ICONV_FAILURE_ALLOCATOR = new
ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new RubyFailure(runtime, klass);
+ }
+ };
+
+ protected RubyFailure(Ruby runtime, RubyClass rubyClass) {
+ this(runtime, rubyClass, null);
+ }
+
+ public RubyFailure(Ruby runtime, RubyClass rubyClass, String message) {
+ super(runtime, rubyClass, message);
+ }
+
+ public IRubyObject initialize(IRubyObject[] args, Block block) {
+ Arity.checkArgumentCount(getRuntime(), args, 3, 3);
+ super.initialize(args, block);
+ success = (RubyString) args[1];
+ failed = (RubyString) args[2];
+
+ return this;
+ }
+
+ public IRubyObject success() {
+ return success;
+ }
+
+ public IRubyObject failed() {
+ return failed;
+ }
+
+ public IRubyObject inspect() {
+ RubyModule rubyClass = getMetaClass();
+ StringBuffer sb = new StringBuffer("#<");
+ sb.append(rubyClass.getName()).append(": ")
+ .append(success.inspect().toString()).append(", ")
+ .append(failed.inspect().toString()).append(">");
+
+ return getRuntime().newString(sb.toString());
+ }
+ }
+
// FIXME: I believe that we are suppose to keep partial character contents
between calls
// so that we can pass in arbitrary chunks of bytes. Charset Encoder
needs to be able to
// handle this or we need to be able detect it somehow.
@@ -95,14 +160,24 @@
return array;
}
*/
-
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- Arity.checkArgumentCount(getRuntime(), args, 2, 2);
+
+// ----- Ruby Class Methods
----------------------------------------------------
+
+ public static IRubyObject open(IRubyObject recv, IRubyObject to,
IRubyObject from, Block block) {
+ Ruby runtime = recv.getRuntime();
+ RubyIconv iconv =
+ (RubyIconv) runtime.getClass("Iconv").newInstance(
+ new IRubyObject[] { to, from }, Block.NULL_BLOCK);
+ if (!block.isGiven()) return iconv;
- //toEncoding = args[0].convertToString().toString();
- //fromEncoding = args[1].convertToString().toString();
-
- return this;
+ IRubyObject result = runtime.getNil();
+ try {
+ result = block.yield(recv.getRuntime().getCurrentContext(), iconv);
+ } finally {
+ iconv.close();
+ }
+
+ return result;
}
public static IRubyObject iconv(IRubyObject recv, IRubyObject[] args,
Block unusedBlock) {
@@ -164,4 +239,88 @@
}
return original.getRuntime().getNil();
}
+
+// ----- Ruby Instance Methods
-------------------------------------------------
+
+ public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
+ Arity.checkArgumentCount(getRuntime(), args, 2, 2);
+ Ruby runtime = getRuntime();
+ if (!args[0].respondsTo("to_str")) {
+ throw runtime.newTypeError("can't convert " +
args[0].getMetaClass() + " into String");
+ }
+ if (!args[1].respondsTo("to_str")) {
+ throw runtime.newTypeError("can't convert " +
args[1].getMetaClass() + " into String");
+ }
+
+ String to = args[0].convertToString().toString();
+ String from = args[1].convertToString().toString();
+
+ try {
+
+ fromEncoding = Charset.forName(from).newDecoder();
+ toEncoding = Charset.forName(to).newEncoder();
+
+ fromEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
+ toEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
+
+ } catch (IllegalCharsetNameException e) {
+ throw runtime.newArgumentError("invalid encoding");
+ } catch (UnsupportedCharsetException e) {
+ throw runtime.newArgumentError("invalid encoding");
+ } catch (Exception e) {
+ throw runtime.newSystemCallError(e.toString());
+ }
+
+ return this;
+ }
+
+ public IRubyObject close() {
+ toEncoding = null;
+ fromEncoding = null;
+ return getRuntime().newString("");
+ }
+
+ public IRubyObject iconv(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ args = Arity.scanArgs(runtime, args, 1, 2);
+ int start = 0;
+ int length = -1;
+
+ if (args[0].isNil()) {
+ fromEncoding.reset();
+ toEncoding.reset();
+ return runtime.newString("");
+ }
+ if (!args[0].respondsTo("to_str")) {
+ throw runtime.newTypeError("can't convert " +
args[0].getMetaClass() + " into String");
+ }
+ if (!args[1].isNil()) start = RubyNumeric.fix2int(args[1]);
+ if (!args[2].isNil()) {
+ length = RubyNumeric.fix2int(args[2]);
+ } else {
+ length = -1;
+ }
+
+ IRubyObject result = _iconv(args[0].convertToString(), start, length);
+ return result;
+ }
+
+ // FIXME: We are assuming that original string will be raw bytes. If -Ku
is provided
+ // this will not be true, but that is ok for now. Deal with that when
someone needs it.
+ private IRubyObject _iconv(RubyString str, int start, int length) {
+ ByteList bytes = str.getByteList();
+ if (length < 0) length = bytes.length() - start;
+ ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), start, length);
+ try {
+ CharBuffer cbuf = fromEncoding.decode(buf);
+ buf = toEncoding.encode(cbuf);
+ } catch (MalformedInputException e) {
+ } catch (UnmappableCharacterException e) {
+ } catch (CharacterCodingException e) {
+ throw getRuntime().newInvalidEncoding("invalid sequence");
+ } catch (IllegalStateException e) {
+ }
+ byte[] arr = buf.array();
+ return getRuntime().newString(new ByteList(arr, 0, buf.limit()));
+ }
}
---------------------------------------------------------------------
To unsubscribe from this list please visit:
http://xircles.codehaus.org/manage_email