public abstract class ArticleDetail extends ArticleList
implements Editable, Deletable {
/**
 * @subject A Useful Example in Java, Ruby, and Groovy
 * @topics Technology Java Computers Work
 * @permalink A_Useful_Example_in_Java_Ruby_and_Groovy
 * @trackback http://www.hjsoft.com/blog/trackback/A_Useful_Example_in_Java_Ruby_and_Groovy
 * @author john
 * @created 5/30/08 7:14:58 PM
 * @modified 6/4/08 9:56:06 AM
 */

We've been using Java and Ruby for work, and we've recently been asked to look more toward Groovy where we were using Groovy, so I rewrote a useful little iconv script which we use to re-encode large volumes of data (which the GNU iconv doesn't seem to want to handle).

For illustration, here are the implementations I wrote:

Java (40 lines):

import java.io.*;

public class Iconv {
    public static void main(final String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println(
                    "Usage: Iconv <in>:<encoding> <out>:<encoding>");
            return;
        }
        
        final String[] ins = args[0].split(":");
        final String[] outs = args[1].split(":");
        
        final String inFile = ins[0];
        final String inEnc = ins[1];
        final String outFile = outs[0];
        final String outEnc = outs[1];
        
        Writer writer = null;
        try {
            writer = new OutputStreamWriter(
                    new FileOutputStream(outFile), outEnc);
            Reader reader = null;
            try {
                reader = new InputStreamReader(
                        new FileInputStream(inFile), inEnc);
                Integer character = null;
                while ((character = reader.read()) >= 0) {
                    writer.write(character);
                }
            } finally {
                if (reader != null)
                    reader.close();
            }
        } finally {
            if (writer != null)
                writer.close();
        }
    }
}

Ruby (20 lines):

#!/usr/bin/env jruby

require 'iconv'
require 'fileutils'

if ARGV.size < 2 then
    puts "Usage: iconv.rb <from>:<enc> <to>:<enc>"
    exit
end

in_file, in_enc = ARGV[0].split(":")
out_file, out_enc = ARGV[1].split(":")

Iconv.open(out_enc, in_enc) do |conv|
    File.open(out_file, 'w') do |out|
        File.open(in_file, 'r').each_line do |line|
            out.write(conv.iconv(line))
        end
    end
end

Groovy (19 lines):

#!/usr/bin/env groovy

if (args.size() < 2) {
    println "Usage: iconv.groovy <from>:<enc> <to>:<enc>"
    return
}

final ins = args[0].split(":")
final outs = args[1].split(":")
final from = ins[0]
final fromEnc = ins[1]
final to = outs[0]
final toEnc = outs[1]

new FileOutputStream(to).withWriter(toEnc) { writer ->
    new FileInputStream(from).withReader(fromEnc) { reader ->
        writer << reader
    }
}

Using the Groovy 1.6 beta, I could have used the new left-side array assignment syntax to shorten the groovy version to 15 lines:

...
[from, fromEnc] = args[0].split(":")
...

I had initially set out to skip over Groovy and just learn Ruby, as I had viewed Groovy as a sort of compromise -- Ruby would be the real twist of the mind, but alas, my neural pathways seem a bit more rigid than I had hoped. I can think a little quicker in Groovy than Ruby and get a few things done. Groovy is also a bit more compatible with the work directives handed down from on high -- we want to be a Java shop.

Update (2008-06-04): I updated the code above to reflect some comments. To be complete, the Java code should null check the streams it's trying to close in the finally blocks.

public Comments displayComments() {
/*
 * Mats Henricson
 * http://www.henricson.se/mats
 * 6/1/08 3:59:44 PM
 * And what would it look like in Scala?
 */
Mats
/*
 * javaguy
 * 6/2/08 12:52:27 PM
 *
 */
If you use Commons io, you could potentially half that java code.

Whenever I start a project, I always add commons io
/*
 * Nick
 * 6/4/08 6:39:47 AM
 *
 */
You can trim java code by 10 lines if you cut off error handling in java as you did in ruby/groovy. It's broken anyway :))) (NullPointerException if writer or reader is not assigned.
/*
 * John
 * http://www.hjsoft.com/blog/
 * 6/4/08 6:52:45 AM
 * closures
 */
I specifically used the closure forms in Ruby and Groovy to provide that guaranteed close of the stream. Is my understanding correct? To get the same guarantee that it closed, I need to do the finally blocks in Java.

I tried to keep the 3 versions relatively comparable with no one version doing any extra checks over another. None of them actually catch and handle any exceptions -- they only intend to ensure closing the connection.
/*
 * Zeljko
 * 6/4/08 8:10:06 AM
 * Java bugs
 */
As Nick said, there is a problem with finally in Java version. If writer cannot be opened, finally will be called anyway on null reference. If you do it like this

Writer writer = new OutputStreamWriter(new FileOutputStream(outFile), outEnc);
try {
...
} finally {
writer.close();
}

it will work OK and also it will be two lines shorter.
/*
 * John Flinchbaugh
 * http://www.hjsoft.com/blog/
 * 6/4/08 9:11:35 AM
 * java version
 */
Ah, you're right. It's even longer to do it right! Maybe I'll edit to represent it better. I guess I have to dig in a moment and test, but I assume that using the closures will offer the right behavior without me having to actually write it.
/*
 * Zeljko
 * 6/5/08 4:40:40 AM
 * No null checking
 */
No, you don't have to do null checks. Just do it as I wrote before.

It is SHORTER!
/*
 * John Wilson
 * 6/5/08 11:41:22 AM
 * The Groovy version could be shorter
 */
A slightly shorter and less noisy version http://rifers.org/paste/show/7434


/*
 * John Wilson
 * 6/5/08 2:23:55 PM
 * The Groovy version could be even shorter
 */
Letting Groovy close the Writer on error

http://rifers.org/paste/show/7435

/*
 * John Flinchbaugh
 * http://www.hjsoft.com/blog/
 * 6/5/08 4:07:01 PM
 * script code becomes production
 */
I don't like to rely on my process being short-lived to get acceptable behavior out of the thing. I want to ensure I close both resources, because some day these bits of code could be used in longer-running processes in which the VM doesn't shutdown to close the resources.
A lot of these alternate code snippets are leaving the read file hanging open, I believe. I'll actually have to dig into the Groovy GDK code to see what it's doing behind the scenes.
/*
 * John Wilson
 * 6/5/08 4:41:35 PM
 * closing resources in Groovy
 */
Writable objects close themselves after writing themselves to the Writer (if they don't it's a bug).
withWriter is specifically designed to close the Writer when the closure exits.
/*
 * John Flinchbaugh
 * http://www.hjsoft.com/blog/
 * 6/5/08 9:29:11 PM
 * hard to screw up
 */
Neat! It looks like Groovy may be managing resources in a way that makes it hard to do it wrong!
/*
 * John Flinchbaugh
 * http://www.hjsoft.com/blog/
 * 10/9/08 9:33:42 PM
 * Groovy 1.5.7/1.6-beta-2
 */
It was a bit awkward in this example to have to specify FileInputSteam and FileOutputStream objects, but I did that for consistency, because Groovy only added an encoding-aware withWriter method to File and no encoding-aware withReader method. Bug GROOVY-2946 addresses this, and the latest Groovy releases apply a fix, so you can now do such things with just File:
new File("out.txt").withWriter("UTF-8") { w ->
  new File("in.txt").withReader("ISO-8859-1") { r ->
    w << r
  }
}
}
public Comment postNewComment() {
Name:
Email (hidden):
URL:
Subject:
Body:
 
 
}
}
 
Robots, look here.