Java Cookbook, Second Edition

This program implements a small subset of the Windows Find Files dialog or the Unix find command. However, it has much of the structure needed to build a more complete version of either of these. It uses a custom filename filter controlled by the -n command-line option, which is parsed using my GetOpt (see Recipe 2.6). It has a hook for filtering by file size, whose implementation is left as an exercise for the reader:

import com.darwinsys.util.*; import java.io.*; import java.io.*; /** * Find - find files by name, size, or other criteria. Non-GUI version. */ public class Find { /** Main program */ public static void main(String[] args) { Find finder = new Find( ); GetOpt argHandler = new GetOpt("n:s:"); int c; while ((c = argHandler.getopt(args)) != GetOpt.DONE) { switch(c) { case 'n': finder.filter.setNameFilter(argHandler.optarg( )); break; case 's': finder.filter.setSizeFilter(argHandler.optarg( )); break; default: System.out.println("Got: " + c); usage( ); } } if (args.length == 0 || argHandler.getOptInd( )-1 == args.length) { finder.doName("."); } else { for (int i = argHandler.getOptInd( )-1; i<args.length; i++) finder.doName(args[i]); } } protected FindFilter filter = new FindFilter( ); public static void usage( ) { System.err.println( "Usage: Find [-n namefilter][-s sizefilter][dir...]"); System.exit(1); } /** doName - handle one filesystem object by name */ private void doName(String s) { Debug.println("flow", "doName(" + s + ")"); File f = new File(s); if (!f.exists( )) { System.out.println(s + " does not exist"); return; } if (f.isFile( )) doFile(f); else if (f.isDirectory( )) { // System.out.println("d " + f.getPath( )); String objects[] = f.list(filter); for (int i=0; i<objects.length; i++) doName(s + f.separator + objects[i]); } else System.err.println("Unknown type: " + s); } /** doFile - process one regular file. */ private static void doFile(File f) { System.out.println("f " + f.getPath( )); } }

The program uses a class called FindFilter to implement matching:

import java.io.*; import java.util.regex.*; import com.darwinsys.util.Debug; /** Class to encapsulate the filtration for Find. * For now just setTTTFilter( ) methods. Really needs to be a real * data structure to allow complex things like * -n "*.html" -a \( -size < 0 -o mtime < 5 \). * @version $Id: ch11.xml,v 1.5 2004/05/04 20:12:20 ian Exp $ */ public class FindFilter implements FilenameFilter { boolean sizeSet; int size; String name; Pattern nameRE; public FindFilter( ) { } void setSizeFilter(String sizeFilter) { size = Integer.parseInt(sizeFilter); sizeSet = true; } /** Convert the given shell wildcard pattern into internal form (a regex) */ void setNameFilter(String nameFilter) { name = nameFilter; StringBuffer sb = new StringBuffer('^'); for (int i = 0; i < nameFilter.length( ); i++) { char c = nameFilter.charAt(i); switch(c) { case '.': sb.append("\\."); break; case '*': sb.append(".*"); break; case '?': sb.append('.'); break; default: sb.append(c); break; } } sb.append('$'); Debug.println("name", "RE=\"" + sb + "\"."); try { nameRE = Pattern.compile(sb.toString( )); } catch (PatternSyntaxException ex) { System.err.println("Error: RE " + sb.toString( ) + " didn't compile: " + ex); } } /** Do the filtering. For now, only filter on name */ public boolean accept(File dir, String fileName) { File f = new File(dir, fileName); if (f.isDirectory( )) { return true; // allow recursion } if (nameRE != null) { return nameRE.matcher(fileName).matches( ); } // TODO size handling. // Catchall return false; } }

Exercise for the reader: in the online source directory, you'll find a class called FindNumFilter, which is meant to (someday) allow relational comparison of sizes, modification times, and the like, as most find services already offer. Make this work from the command line, and write a GUI frontend to this program.

Категории