Friday, November 2, 2007

Searching Jars Redux

A previous post translated a Groovy file into Java using the closure proposal. The main goal was to see how the new Function Type fit into the Java syntax.

Unfortunately, as Neal pointed out, the resulting Java file wasn't very closure-esque. Here is the same idea, cleaned up.

The new file does this:

  • Accepts a search directory and a target string
  • Recursively traverses the search directory and, for each file found, applies a concept
  • The concept is, in essence, a closure. In this case, the concept is to (a) check to see if the file is a jar and, if so, (b) check the entry list of the jar for a target string
  • The key is that we now have an algorithm that calls a block of code. The block of code is bundled with any local state that it needs to process the input parameters.
Here is the code listing. It is very instructive to contrast (1) the original Groovy file (2) the first attempt at Java and (3) this program. Note that #2 in this list does something slightly different (i.e. it counts occurences, and does not traverse recursively).


import java.io.*;
import java.util.jar.*;
import java.util.Enumeration;
import java.util.*;

class FileExtension {
private String dirStr = null;

public FileExtension(String dirStr) {
this.dirStr = dirStr;
}

// @param block a closure
// @throws IOException (because the closure declares it)
// This method will recursively traverse a directory
// structure and call block for every file found.

public void eachFileRecurse(
{ File => void throws IOException } block
) throws IOException {
File file = new File(dirStr);

for( File thisFile : file.listFiles() ) {
if( thisFile.isFile() ) {
// file
block.invoke( thisFile );
} else {
// dir
FileExtension subFileExtension =
new FileExtension( thisFile.toString() );
subFileExtension.eachFileRecurse( block );
}
}
}
}

public class JarSearcher2 {
static final private String JAR = ".jar";
static private String target;

// closure block
// Iterates through a directory tree and applies the
// closure to each file
// @param file an input file
// @free JAR used as an extension
// @free a target string in a jar's list of entries
// @throws IOException

static private { File => void throws IOException } myClosure =
{ File file =>
String fileName = file.getName();

if( fileName.contains(JAR) ) {

JarFile jarFile = new JarFile(file);

// old-style Enumeration
// don't blame me or closures!
for( Enumeration e = jarFile.entries() ;
e.hasMoreElements() ; ) {
JarEntry entry = (JarEntry) e.nextElement();

if( entry.getName().contains(target) ) {
System.out.println("found " + target
+ " in: " + file.toString() );
}
}
}
};

// iterates through a directory tree and applies the
// closure to each file
// @param args[]
// @throws IOException
public static void main(String[] args) throws IOException {
String searchDirStr = args[0];
target = args[1];

(new FileExtension(searchDirStr)).eachFileRecurse( myClosure );
}
}

1 comment:

Stephan.Schmidt said...

When dealing with Enumerations a static utility method is quite useful.

public static Iterable iterate(Enumeration enum) { ... }

Then you can write:

for (File file: iterate(entries)) {
...
}

Peace
-stephan

--
Stephan Schmidt :: stephan@reposita.org
Reposita Open Source - Monitor your software development
http://www.reposita.org
Blog at http://stephan.reposita.org - No signal. No noise.