Searching Jars with Java Closures (original) (raw)
(Ed's note: see this post for a better example of closures. This one is intended for direct contrast with a Groovy script: the resulting Java is awkward.)
The last post has a "Hello World", of sorts, for Neal Gafter's closure proposal.
In it, I mentioned that I have converted a Groovy jar searcher into pure Java, using Neal Gafter's prototype.
Here is the code. It is probably best to understand the previous post before looking into this. I may also be instructive to understand the Groovy code first.
The code is documented but here is a view from space:
- The program takes a directory and a target string as arguments. It searches all jars in the directory for the target string, and reports the number of matches found.
- A use-case is that you are looking for
MyMissingClass
in a bunch of jar files. - The program uses 2 closures as private members to the class.
- One closure is
myEntryChecker
, which accepts aJarEntry
and uses the target variable as a 'free lexical binding'. It returns aboolean
if the name of theJarEntry
contains the target string. - The other closure is
myFileChecker
, which accepts a File and uses the above closure as a 'free lexical binding'. Note that this one declares anIOException
. - Note that there is a cool proposal for loop abstractions that is not yet implemented. This will make
run()
much more elegant. In fact, the invocation of closures seems to be a major motivation for the proposal.
(Btw, if you find the example less-than-elegant, blame me, not closures. The intent is not to introduce closures, per se, but instead to illustrate the new Java syntax by leveraging a simple-yet-useful program implemented in Groovy.)
import java.io.;
import java.util.jar.;
import java.util.Enumeration;
public class JarSearcher {
private String target;
private String fileName;
// This closure checks a JarEntry's name for a target string
// @param jarEntry
// @return boolean true if found
// @free target the string we're looking for
// ('free' is my way of denoting a free variable)
private { JarEntry => boolean } myEntryChecker = {
JarEntry jarEntry =>
// expression! no return statement or semi-colon
// Thanks to Christian Ullenboom for the simpler version
jarEntry.getName().contains(target)
};
// This closure iterates over the entries in a jar file and
// checks each one for a target string (via another closure)
// @param file
// @return int # of occurences
// @throws IOException
// @free myEntryChecker
// ('free' is my way of denoting a free variable)
private { File => int throws IOException } myFileChecker = {
File file =>
int count = 0;
fileName = file.getName();
if( fileName.indexOf(".jar") != -1 ) {
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( myEntryChecker.invoke(entry) ) {
count++;
}
}
}
// expression! no return statement
count
};
// This method lists the files in a directory and
// uses a closure to check each file for a target
// string (if it is a jar file)
// @param args[]
// @throws IOException (because the closure declares it)
public void run(String[] args) throws IOException {
String searchDirStr = args[0];
target = args[1];
File searchDir = new File(searchDirStr);
// NOTE: Neal has a proposal that would make this
// iteration much easier
for(File file : searchDir.listFiles() ) {
int count = myFileChecker.invoke(file);
if( count != 0 ) {
System.out.println(
"found " + count + " match(es) in " + fileName );
}
}
}
public static void main(String[] args) {
try {
JarSearcher jarSearcher = new JarSearcher();
jarSearcher.run(args);
} catch(Exception ex) {
// TODO
}
}
}