View Javadoc
1   package eu.simuline.util;
2   
3   import java.io.File;
4   import java.io.PrintStream;
5   import java.io.IOException;
6   
7   import java.util.Stack;
8   import java.util.regex.Pattern;
9   
10  /**
11   * Emulates the unix shell command <code>find</code> which is invoked as 
12   * <code>find [-H] [-L] [-P] [-Olevel] 
13   * [-D help|tree|search|stat|rates|opt|exec] [path...] (expression)*</code>. 
14   * If <code>path</code> is left out (possibly more than one path?), 
15   * then the default is <code>.</code>, i.e. the current directory. 
16   * The <code>path</code> need not be a directory. 
17   * If it is a proper file then typically wildcards are used. 
18   * Then the name is matched. 
19   * If no expression is given, the default is <code>-print</code>. 
20   * So we may assume, 
21   * that both, a path and a non-empty sequence of expressions, are given. 
22   * <p>
23   * The idea behind the <code>find</code> command is, 
24   * that, starting with the files matching the <code>path</code>, 
25   * each <code>expression</code> serves as a filter, 
26   * feeding the filter corresponding to the following expression. 
27   * Each filter can have a side effect. 
28   * Expressions may be either tests or actions. 
29   * For tests, the focus is on the filter-functionality, 
30   * whereas actions are trivial filters just passing the files they receive. 
31   * Actions are applied because of their side effects like <code>print</code>. 
32   * <p>
33   * The most basic kind of finder corresponds with the command 
34   * <code>find path</code> iterating just over the files in the path. 
35   * This kind of finder is returned by the static method {@link #path(File)}. 
36   * Starting with this basic finder, further finders can be added to the pipe 
37   * using the member methods 
38   * {@link #name(String)} and {@link #print(PrintStream)}. 
39   *
40   * Created: Wed Nov 21 17:29:41 2012
41   *
42   * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
43   * @version 1.0
44   */
45  public abstract class Finder {
46  
47      /* -------------------------------------------------------------------- *
48       * inner classes.                                                       *
49       * -------------------------------------------------------------------- */
50  
51      /**
52       * The most basic kind of finder: 
53       * Method {@link #path(File)} returns an instance of this. 
54       * {@link #next()} returns the given file and, 
55       * if this is a folder all files therein. 
56       */
57      static class Primary extends Finder {
58  
59  	/* ---------------------------------------------------------------- *
60  	 * fields.                                                          *
61  	 * ---------------------------------------------------------------- */
62  
63  	/**
64  	 * The list of files to be returned by {@link #next()} 
65  	 * unwrapping folders recursively. 
66  	 */
67  	private final Stack<File> files;
68  
69  	/* ---------------------------------------------------------------- *
70  	 * constructors.                                                    *
71  	 * ---------------------------------------------------------------- */
72  
73  	/**
74  	 * Pushes <code>file</code> to {@link #files}. 
75  	 */
76  	Primary(File file) {
77  	    this.files = new Stack<File>();
78  	    this.files.push(file);
79  	}
80  
81  	Primary(File[] path) {
82  	    this.files = new Stack<File>();
83  	    for (File file : path) {
84  		this.files.push(file);
85  	    }
86  
87  	    // for (int i = 0; i < path.length; i++) {
88  	    // 	this.files.push(path[i]);
89  	    // }
90  	}
91  
92  	/* ---------------------------------------------------------------- *
93  	 * methods.                                                         *
94  	 * ---------------------------------------------------------------- */
95  
96  	/**
97  	 * Has a next file iff {@link #files} is not empty. 
98  	 */
99  	public boolean hasNext() {
100 	    return !this.files.isEmpty();
101 	}
102 
103 	/**
104 	 * If the topmost entry of {@link #files} is no folder, 
105 	 * this is returned as is. 
106 	 * otherwise, in addition, this folder is unwrapped 
107 	 * and the contents is pushed onto the stack {@link #files}. 
108 	 */
109 	public File next() {
110 	    assert hasNext();
111 	    File file = this.files.pop();
112 	    if (file.isDirectory()) {
113 		File[] list = file.listFiles();
114 		if (list == null) {
115 		    System.out.println("cannot read " + file);
116 		} else {
117 		    // push in inverse order 
118 		    for (int i = list.length - 1; i >= 0; i--) {
119 			this.files.push(list[i]);
120 		    }
121 		    //this.files.addAll(Arrays.asList(list));
122 		}
123 	    }
124 	    return file;
125 	}
126 
127     } // class Primary 
128 
129     /**
130      * A finder wrapping a {@link Finder.Filter}. 
131      * Files are read from {@link #encl} and are passed via {@link #next()} 
132      * iff they pass the filter {@link #filter}. 
133      */
134     static class Secondary extends Finder {
135 
136 	/* ---------------------------------------------------------------- *
137 	 * fields.                                                          *
138 	 * ---------------------------------------------------------------- */
139 
140 	/**
141 	 * The next file to be returned by {@link #next()} if any, 
142 	 * i.e. if  {@link #hasNext()} returns <code>true</code>; 
143 	 * otherwise this field is <code>null</code>. 
144 	 */
145 	private File next;
146 
147 	/**
148 	 * The source finder from which the stream of files is read. 
149 	 * **** this is superfluous if this is not static 
150 	 */
151 	private final Finder encl;
152 
153 	/**
154 	 * The filter to be passed 
155 	 * before a file is returned by {@link #next()}. 
156 	 */
157 	private final Filter filter;
158 
159 	/* ---------------------------------------------------------------- *
160 	 * constructors.                                                    *
161 	 * ---------------------------------------------------------------- */
162 
163 	/**
164 	 * Create a finder receiving a stream of files from <code>encl</code> 
165 	 * and passing them further via {@link #next()} 
166 	 * if they pass the filter <code>filter</code>. 
167 	 */
168 	Secondary(Finder encl, Filter filter) {
169 	    super();
170 	    this.encl = encl;
171 	    this.filter = filter;
172 	    updateNext();
173 	}
174 
175 	/* ---------------------------------------------------------------- *
176 	 * methods.                                                         *
177 	 * ---------------------------------------------------------------- */
178 
179 	// **** better implementation if this class were not static 
180 	protected void updateNext() {
181 //	    while (Finder.this.hasNext()) {
182 	    while (this.encl.hasNext()) {
183 //		this.next = Finder.this.next();
184 		this.next = this.encl.next();
185 		assert this.next != null;
186 		if (this.filter.pass(this.next)) {
187 		    return;
188 		}
189 	    }
190 	    this.next = null;
191 	}
192 
193 	public boolean hasNext() {
194 	    return this.next != null;
195 	}
196 
197 	public File next() {
198 	    assert hasNext();
199 	    File res = this.next;
200 	    assert res != null;
201 	    updateNext();
202 	    return res;
203 	}
204 
205 	protected File getNext() {
206 	    assert hasNext();
207 	    File res = this.next;
208 	    assert res != null;
209 	    return res;
210 	}
211     } // class Secondary 
212 
213     /**
214      * A filter passing all information and printing it on {@link #str}. 
215      */
216     class PrintFilter extends Secondary {
217 
218 	/* ---------------------------------------------------------------- *
219 	 * fields.                                                          *
220 	 * ---------------------------------------------------------------- */
221 
222 	private final PrintStream str;
223 	private int idx;
224 
225 
226 	/* ---------------------------------------------------------------- *
227 	 * constructors.                                                    *
228 	 * ---------------------------------------------------------------- */
229 
230 	PrintFilter(Finder encl, PrintStream str) {
231 	    super(encl, TRUE);
232 	    this.str = str;
233 	    this.idx = 0;
234 	}
235 
236 	/* ---------------------------------------------------------------- *
237 	 * methods.                                                         *
238 	 * ---------------------------------------------------------------- */
239 
240 	public File next() {
241 	    assert super.hasNext();
242 	    File res = getNext();
243 	    assert res != null;
244 	    this.idx++;
245 	    this.str.println(idx + " " + res.toString());
246 	    updateNext();
247 	    return res;
248 	}
249     } // class PrintFilter 
250 
251     /**
252      * Represents a file filter. 
253      */
254     abstract static class Filter {
255 
256 	/**
257 	 * Returns for the given file whether this file passes this filter. 
258 	 */
259 	abstract boolean pass(File file);
260 
261 	/**
262 	 * Returns a filter which passes a file 
263 	 * iff <code>filter</code> does not. 
264 	 * This corresponds the tests <code>\! expr1</code> 
265 	 * in the original find command. 
266 	 */
267 	Filter not() {
268 	    return new NegFilter(this);
269 	}
270 
271 	/**
272 	 * Returns a filter which passes a file 
273 	 * iff so do all filters in <code>filters</code>. 
274 	 * This corresponds the tests <code>expr1 -a .... exprn</code> 
275 	 * and <code>expr1 -and .... exprn</code> 
276 	 * in the original find command. 
277 	 *
278 	 * @param filters 
279 	 *    a sequence of filters which may be empty. 
280 	 *    If empty, this filter passes all files like {@link #TRUE}. 
281 	 * @return 
282 	 *    a lazy and-filter of type {@link Finder.AndFilter}. 
283 	 *    For more details see there. 
284 	 */
285 	static Filter and(Filter[] filters) {
286 	    return new AndFilter(filters);
287 	}
288 
289 	/**
290 	 * Returns a filter which passes a file 
291 	 * iff at least one filter in <code>filters</code> does so. 
292 	 * This corresponds the tests <code>expr1 -o .... exprn</code> 
293 	 * and <code>expr1 -or .... exprn</code> 
294 	 * in the original find command. 
295 	 *
296 	 * @param filters 
297 	 *    a sequence of filters which may be empty. 
298 	 *    If empty, this filter passes no file like {@link #FALSE}. 
299 	 * @return 
300 	 *    a lazy and-filter of type {@link Finder.AndFilter}. 
301 	 *    For more details see there. 
302 	 */
303 	static Filter or(Filter[] filters) {
304 	    return new  OrFilter(filters);
305 	}
306     }
307 
308     /**
309      * Filters files by name. See {@link Finder#name(String)}. 
310      */
311     static class NameFilter extends Filter {
312 
313 	/* ---------------------------------------------------------------- *
314 	 * fields.                                                          *
315 	 * ---------------------------------------------------------------- */
316 
317 	/**
318 	 * The pattern to filter the filenames with. 
319 	 */
320 	private final Pattern pattern;
321 
322 	/* ---------------------------------------------------------------- *
323 	 * constructors.                                                    *
324 	 * ---------------------------------------------------------------- */
325 
326 	NameFilter(String pattern) {
327 	    this.pattern = Pattern.compile(pattern);
328 	}
329 
330 	/* ---------------------------------------------------------------- *
331 	 * methods.                                                         *
332 	 * ---------------------------------------------------------------- */
333 
334 	public boolean pass(File file) {
335 	    return this.pattern.matcher(file.toString()).matches();
336 	}
337 
338     } // class NameFilter 
339 
340     /**
341      * Filter executing a shell command and passes the file received 
342      * if the shell command succeeds according to its return code. 
343      *
344      * @see Finder#exec(String[])
345      * @see Finder.ExecJavaFilter
346      */
347     static class ExecFilter extends Filter {
348 
349 	/* ---------------------------------------------------------------- *
350 	 * fields.                                                          *
351 	 * ---------------------------------------------------------------- */
352 
353 	/**
354 	 * The command to be executed including arguments 
355 	 * separated by space as to be passed to {@link Runtime#exec(String)} 
356 	 * in order to decide whether the given file passes the filter. 
357 	 * For more information on the entries 
358 	 * see {@link Finder#exec(String[])}. 
359 	 */
360 	private final String[] cmd;
361 
362 	/* ---------------------------------------------------------------- *
363 	 * constructors.                                                    *
364 	 * ---------------------------------------------------------------- */
365 
366 	/**
367 	 * Creates an execution filter from the given command and arguments. 
368 	 *
369 	 * @param cmd
370 	 *    The 0th entry is the command itself and the others are arguments. 
371 	 *    For details see {@link Finder#exec(String[])}. 
372 	 */
373 	ExecFilter(String[] cmd) {
374 	    this.cmd = cmd;
375 	}
376 
377 	/* ---------------------------------------------------------------- *
378 	 * methods.                                                         *
379 	 * ---------------------------------------------------------------- */
380 
381 	/**
382 	 * The given file passes this filter, 
383 	 * i.e. this method returns <code>true</code> 
384 	 * if the shell command given by {@link #cmd} 
385 	 * succeeds according to its return value (which is then zero). 
386 	 * <p>
387 	 * Execution proceeds in the following steps: 
388 	 * <ul>
389 	 * <li>
390 	 * Replace the arguments {@link Finder#EXEC_ARG} 
391 	 * by the long name of <code>file</code>. 
392 	 * <li>
393 	 * Create a separate process to execute the command. 
394 	 * <li>
395 	 * Wait for execution end and 
396 	 * <li>
397 	 * pass if the return value 0 indicates success. 
398 	 * </ul>
399 	 */
400 	public boolean pass(File file) {
401 	    Process proc;
402 	    int exitVal;
403 	    String nextStr = file.toString();
404 
405 	    // replace argument by filename 
406 	    String[] cmd = new String[this.cmd.length];
407 	    System.arraycopy(this.cmd, 0, cmd, 0, this.cmd.length);
408 	    // the 0th entry should not be EXEC_ARG 
409 	    for (int idx = 0; idx < this.cmd.length; idx++) {
410 		if (cmd[idx] == Finder.EXEC_ARG) {
411 		    cmd[idx] = nextStr;
412 		}
413 	    }
414 
415 	    try {
416 		//System.out.println(":"+(this.cmdName+" "+this.next));
417 		proc = Runtime.getRuntime().exec(cmd);
418 	    } catch (IOException e) {
419 		System.out.println("exec: " + e.getMessage());
420 		return false;
421 	    }
422 	    try {
423 		exitVal = proc.waitFor(); // exitValue
424 	    } catch (InterruptedException e) {
425 		System.out.println("exec:" + e.getMessage());
426 		return false;
427 	    }
428 	    return exitVal == 0;
429 	}
430 
431     } // class ExecFilter 
432 
433     /**
434      * To be implemented by java classes to apply a filter to a file 
435      * and give feedback whether this filter succeeded. 
436      * This is analogous to passing a file to as shell command 
437      * and reading back whether the command succeeded. 
438      * This interface should be used to define a {@link Finder.ExecJavaFilter}. 
439      */
440     public interface Callable {
441 	/**
442 	 * Applies some filter on the given file 
443 	 * and returns whether the called operation succeeded. 
444 	 */
445 	boolean call(File file);
446     } // interface Callable 
447 
448     /**
449      * Filter executing a java class implementing {@link Finder.Callable} 
450      * which passes the file received 
451      * if the  method {@link Finder.Callable#call(File)} succeeds 
452      * according to its return code. 
453      *
454      * @see Finder#exec(String[])
455      * @see Finder.ExecFilter
456      */
457     static class ExecJavaFilter extends Filter {
458 
459 	/* ---------------------------------------------------------------- *
460 	 * fields.                                                          *
461 	 * ---------------------------------------------------------------- */
462 
463 	/**
464 	 * The instance to be executed to decide 
465 	 * whether a given file passes the filter 
466 	 * invoking {@link Finder.Callable#call(File)}. 
467 	 * Besides filtering, other actions may be taken as side effect. 
468 	 * Parameters are passed to the callable when creating the instance 
469 	 * or later by a setter method, depending on the implementation. 
470 	 */
471 	private final Callable callable;
472 
473 	/* ---------------------------------------------------------------- *
474 	 * constructors.                                                    *
475 	 * ---------------------------------------------------------------- */
476 
477 	/**
478 	 * Creates a java execution filter 
479 	 * from the given {@link Finder.Callable}. 
480 	 *
481 	 * @param callable
482 	 *    The callable defining the filter. 
483 	 */
484 	ExecJavaFilter(Callable callable) {
485 	    this.callable = callable;
486 	}
487 
488 	/* ---------------------------------------------------------------- *
489 	 * methods.                                                         *
490 	 * ---------------------------------------------------------------- */
491 
492 	/**
493 	 * The given file passes this filter, 
494 	 * i.e. this method returns <code>true</code>, 
495 	 * if so does {@link #callable}. 
496 	 */
497 	public boolean pass(File file) {
498 	    return this.callable.call(file);
499 	}
500 
501     } // class ExecJavaFilter 
502 
503 
504     /**
505      * One of the logical operations of filters: 
506      * Returns a filter which passes a file 
507      * iff the original filter {@link #negFilter} does not. 
508      * <p>
509      * See {@link Finder.Filter#not()}. 
510      */
511     static class NegFilter extends Filter {
512 
513 	/* ---------------------------------------------------------------- *
514 	 * fields.                                                          *
515 	 * ---------------------------------------------------------------- */
516 
517 	/**
518 	 * The filter to be negated. 
519 	 */
520 	private final Filter negFilter;
521 
522 	/* ---------------------------------------------------------------- *
523 	 * constructors.                                                    *
524 	 * ---------------------------------------------------------------- */
525 
526 	NegFilter(Filter negFilter) {
527 	    this.negFilter = negFilter;
528 	}
529 
530 	/* ---------------------------------------------------------------- *
531 	 * methods.                                                         *
532 	 * ---------------------------------------------------------------- */
533 
534 	/**
535 	 * Passes the given file iff {@link #negFilter} does not. 
536 	 */
537 	public boolean pass(File file) {
538 	    return !this.negFilter.pass(file);
539 	}
540 
541     } // class NegFilter 
542 
543     /**
544      * One of the logical operations of filters: 
545      * Returns a filter which passes a file 
546      * iff all original filters in {@link #filters} do so. 
547      * <p>
548      * This is a lazy and-filter, i.e. if one of the filters rejects the file, 
549      * the filters later in the sequence are not executed any more. 
550      * So the ordering has an effect, if one of the filters has a side effect. 
551      * Ordering may also affect performance.  
552      * <p>
553      * Maybe lazy and-filters are not so useful, because, 
554      * unlike non-lazy and-filters and or-filters, 
555      * they could be realized as a sequence of filters. 
556      * <p>
557      * See {@link Finder#and(Filter[])}. 
558      */
559     static class AndFilter extends Filter {
560 
561 	/* ---------------------------------------------------------------- *
562 	 * fields.                                                          *
563 	 * ---------------------------------------------------------------- */
564 
565 	/**
566 	 * This filter passes a file iff all of these pass 
567 	 * if invoked in the natural ordering. 
568 	 */
569 	private final Filter[] filters;
570 
571 	/* ---------------------------------------------------------------- *
572 	 * constructors.                                                    *
573 	 * ---------------------------------------------------------------- */
574 
575 	AndFilter(Filter[] filters) {
576 	    this.filters = filters;
577 	}
578 
579 	/* ---------------------------------------------------------------- *
580 	 * methods.                                                         *
581 	 * ---------------------------------------------------------------- */
582 
583 
584 	public boolean pass(File file) {
585 	    for (int i = 0; i < filters.length; i++) {
586 		if (!this.filters[i].pass(file)) {
587 		    return false;
588 		}
589 	    } // for 
590 
591 	    return true;
592 	}
593 
594     } // class AndFilter 
595 
596     /**
597      * One of the logical operations of filters: 
598      * Returns a filter which passes a file 
599      * iff at least one of the original filters in {@link #filters} do so. 
600      * <p>
601      * This is a lazy or-filter, i.e. if one of the filters accepts the file, 
602      * the filters later in the sequence are not executed any more. 
603      * So the ordering has an effect, if one of the filters has a side effect.  
604      * Ordering may also affect performance. 
605      * <p>
606      * See {@link Finder#or(Filter[])}. 
607      */
608     static class OrFilter extends Filter {
609 
610 	/* ---------------------------------------------------------------- *
611 	 * fields.                                                          *
612 	 * ---------------------------------------------------------------- */
613 
614 	/**
615 	 * This filter passes a file iff at least one of of these passes 
616 	 * if invoked in the natural ordering. 
617 	 */
618 	private final Filter[] filters;
619 
620 	/* ---------------------------------------------------------------- *
621 	 * constructors.                                                    *
622 	 * ---------------------------------------------------------------- */
623 
624 	OrFilter(Filter[] filters) {
625 	    this.filters = filters;
626 	}
627 
628 	/* ---------------------------------------------------------------- *
629 	 * methods.                                                         *
630 	 * ---------------------------------------------------------------- */
631 
632 
633 	public boolean pass(File file) {
634 	    for (int i = 0; i < filters.length; i++) {
635 		if (this.filters[i].pass(file)) {
636 		    return true;
637 		}
638 	    } // for 
639 
640 	    return false;
641 	}
642 
643     } // class OrFilter 
644 
645 
646     /* -------------------------------------------------------------------- *
647      * Class constants (creator methods for Filters come later).            *
648      * -------------------------------------------------------------------- */
649 
650     /**
651      * For {@link Finder.ExecFilter}s 
652      * representing a filter invoking a shell command, 
653      * this string represents the argument of the command 
654      * which is in the original find a <code>{}</code> 
655      * and which is replaced by the file name 
656      * received by the preceding portion of the pipe. 
657      * CAUTION: It must be used this instance 
658      * and no other equivalent string <code>{}</code>. 
659      */
660     public static final String EXEC_ARG = "{}";
661 
662     /**
663      * A filter passing all files. 
664      * This corresponds the test <code>-true</code> 
665      * in the original find command. 
666      */
667     public static final Filter TRUE = new Filter() {
668 	    public boolean pass(File file) {
669 		return true;
670 	    }
671 	};
672 
673     /**
674      * A filter passing no file. 
675      * This corresponds the test <code>-false</code> 
676      * in the original find command. 
677      */
678     public static final Filter FALSE = new Filter() {
679 	    public boolean pass(File file) {
680 		return false;
681 	    }
682 	};
683 
684     /**
685      * Filter passing the file received iff it is executable. 
686      * This corresponds the test <code>-executable</code> 
687      * in the original find command. 
688      * Do not mix up with {@link Finder.ExecFilter}. 
689      */
690     public static final Filter CAN_EXEC  = new Filter() {
691 	    public boolean pass(File file) {
692 		return file.canExecute();
693 	    }
694 	};
695 
696     /**
697      * Filter passing the file received iff it is readable. 
698      * This corresponds the test <code>-readable</code> 
699      * in the original find command. 
700       */
701     public static final Filter CAN_READ  = new Filter() {
702 	    public boolean pass(File file) {
703 		return file.canRead();
704 	    }
705 	};
706 
707     /**
708      * Filter passing the file received iff it is writable. 
709      * This corresponds the test <code>-writable</code> 
710      * in the original find command. 
711      */
712     public static final Filter CAN_WRITE  = new Filter() {
713 	    public boolean pass(File file) {
714 		return file.canWrite();
715 	    }
716 	};
717 
718     /**
719      * Filter passing the file received iff it is a regular file. 
720      * This corresponds the test <code>-type f</code> 
721      * in the original find command. 
722      */
723     public static final Filter IS_FILE  = new Filter() {
724 	    public boolean pass(File file) {
725 		return file.isFile();
726 	    }
727 	};
728 
729     /**
730      * Filter passing the file received iff it is a regular file. 
731      * This corresponds the test <code>-type d</code> 
732      * in the original find command. 
733      */
734     public static final Filter IS_DIR  = new Filter() {
735 	    public boolean pass(File file) {
736 		return file.isDirectory();
737 	    }
738 	};
739 
740 
741 
742     /* -------------------------------------------------------------------- *
743      * constructors and creator methods for Finders.                        *
744      * -------------------------------------------------------------------- */
745 
746     /**
747      * This is declared <code>private</code> to prevent instantiation.
748      */
749     private Finder() {
750     }
751 
752     /**
753      * Returns a basic finder, emitting the given file 
754      * if it exists and is accessible and, recursively, 
755      * if it is a directory all files therein. 
756      * The ordering is the same as in original find-command. 
757      * The expression <code>Finder.path(file)</code> 
758      * corresponds with <code>find file</code>, 
759      * except that the find command implicitly adds a print filter 
760      * as defined in {@link #print(PrintStream)}. 
761      * Note also that, unlike th original find, 
762      * no wildcards are supported. 
763      *
764      * @return
765      *    an instance of {@link Finder.Primary}
766      */
767     public static Finder path(File file) {
768 	return new Finder.Primary(file);
769     }
770 
771     /**
772      * Adds a trivial filter passing all files received by this finder 
773      * printing their full names to <code>str</code>. 
774      */
775     public Finder print(PrintStream str) {
776 	return new PrintFilter(this, str);
777     }
778 
779     /**
780      * Returns a finder by attachig the given filter. 
781      * For adding the most common filters, there are convenience methods. 
782      */
783     public Finder add(Filter filter) {
784 	return new Secondary(this, filter);
785     }
786 
787     /**
788      * Convenience method: adds a name filter to this finder. 
789      *
790      * @see #nameFilter(String)
791      */
792     public Finder name(String pattern) {
793 	return add(nameFilter(pattern));
794     }
795 
796     /**
797      * Convenience method: adds an execution filter to this finder. 
798      * @return
799      *    an instance of {@link Finder.Secondary} 
800      *    instantiated with a filter of type {@link Finder.ExecFilter} 
801      *
802      * @see #execFilter(String[])
803      */
804     public Finder exec(String[] cmd) {
805 	return add(execFilter(cmd));
806     }
807 
808     /**
809      * Convenience method: adds a java execution filter to this finder. 
810      * @return
811      *    an instance of {@link Finder.Secondary} 
812      *    instantiated with a filter of type {@link Finder.ExecJavaFilter} 
813      *
814      * @see #execJavaFilter(Callable)
815      */
816     public Finder execJava(Callable callable) {
817 	return add(execJavaFilter(callable));
818     }
819 
820     /**
821      * Convenience method: 
822      * Returns a finder by attaching the inverse of the given filter. 
823      * This corresponds the tests <code>\! expr1</code> 
824      * in the original find command. 
825      *
826      * @see Finder.Filter#not()
827      */
828     public Finder not(Filter filter) {
829 	return add(filter.not());
830     }
831 
832     /**
833      * Convenience method: 
834      * Returns a finder by attaching an and-filter. 
835      * This corresponds the tests <code>\! expr1</code> 
836      * in the original find command. 
837      *
838      * @see Finder.Filter#and(Filter[])
839      */
840     public Finder and(Filter[] filters) {
841 	return add(Filter.and(filters));
842     }
843 
844     /**
845      * Convenience method: 
846      * Returns a finder by attaching an or-filter. 
847      * This corresponds the tests <code>\! expr1</code> 
848      * in the original find command. 
849      *
850      * @see Finder.Filter#or(Filter[])
851      */
852     public Finder or(Filter[] filters) {
853 	return add(Filter.or(filters));
854     }
855 
856     /* -------------------------------------------------------------------- *
857      * constructors and creator methods for Filters.                        *
858      * -------------------------------------------------------------------- */
859 
860     /**
861      * Returns a filter passing a file 
862      * iff its (short) names of which 
863      * match the regular expression <code>pattern</code>. 
864      *
865      * @return
866      *    an instance of {@link Finder.Secondary} 
867      *    instantiated with a filter of type {@link Finder.NameFilter} 
868      */
869     public static Filter nameFilter(String pattern) {
870 	return new NameFilter(pattern);
871     }
872 
873     /**
874      * Returns a filter invoking a shell command: 
875      * just passes the files received by this finder further 
876      * if the command succeeds according to its return value. 
877      * Example in original find-terminology: 
878      * <code>find . -exec grep "pattern without quotes" {} \; -print</code>. 
879      * Here, the portion specifying execution is 
880      * <code>-exec grep "pattern without quotes" {} \;</code>: 
881      * It starts with <code>-exec</code> and ends with <code>\;</code>. 
882      * Note that the command <code>grep</code> 
883      * has arguments <code>"pattern without quotes"</code> and <code>{}</code>. 
884      * The first argument must be quoted because it contains whitespace 
885      * and would otherwise be interpreted as three arguments. 
886      * The second argument <code>{}</code> 
887      * is silently replaced by the file name 
888      * received by the preceeding portion of the find-command. 
889      *
890      * @param cmd
891      *    a shell command with its arguments. 
892      *    The 0th entry is the command itself and the others are arguments. 
893      *    Be aware when escape sequences are needed. 
894      *    Quotes ensuring that an argument is interpreted as a unit 
895      *    <em>must</em> be omitted. 
896      *    <p>
897      *    In the original find command, 
898      *    <code>{}</code> represents arguments to be replaced by the file name 
899      *    received by the preceding portion of the pipe. 
900      *    These must be represented by the object instance {@link #EXEC_ARG} 
901      *    which is also a string <code>{}</code> 
902      *    but it is wrong to put an equivalent string as e.g. 
903      *    <code>new String("{}")</code>. 
904      *    <p>
905      *    For example to obtain a grep use 
906      *    <code>new String{} {"grep", "pattern without quotes", EXEC_ARG}</code>
907      * @return
908      *    a filter of type {@link Finder.ExecFilter} 
909      * @see #execJavaFilter(Callable)
910      */
911     public static Filter execFilter(String[] cmd) {
912 	return new ExecFilter(cmd);
913     }
914 
915     /**
916      * Returns a filter invoking method {@link Finder.Callable#call(File)} 
917      * of the given <code>callable</code>. 
918      *
919      * @param callable
920      *    A callable defining a filter. 
921      *    If parameters are required, 
922      *    these must be given when instantiating the callable 
923      *    or later using setter methods. 
924      * @return
925      *    a filter of type {@link Finder.ExecFilter} 
926      * @see #execFilter(String[])
927      */
928     public static Filter execJavaFilter(Callable callable) {
929 	return new ExecJavaFilter(callable);
930     }
931 
932 
933     /* -------------------------------------------------------------------- *
934      * abstract methods.                                                    *
935      * -------------------------------------------------------------------- */
936 
937     /**
938      * Returns whether this Finder can emit another file. 
939      *
940      * @see #next()
941      */
942     public abstract boolean hasNext();
943 
944     /**
945      * Returns the next file this finder can emit. 
946      * This does not throw an exception iff {@link #hasNext()} returns true. 
947      */
948     public abstract File next();
949 
950     public static void main(String[] args) {
951 
952 	File file = new File(args[0]);
953 
954 	Finder finder = Finder.path(file)
955 	    .name(".*\\.m")
956 //	    .exec(new String[] {"egrep", "matlab", EXEC_ARG})
957 	    .not(execFilter(new String[] {"grep", "'", EXEC_ARG}))
958 //	    .or(new Filter[] {
959 //		    execFilter(new String[] {"grep", "enum", EXEC_ARG}),
960 //		    execFilter(new String[] {"grep", "interface", EXEC_ARG})
961 //		}
962 //		    )
963 	    .print(System.out);
964 
965 	// Finder finder = Finder.path(file);
966 	// Multiplexer mult = finder.multiplexer();
967 	// Finder negFilter = mult.finder()
968 	//     .exec(new String[] {"grep", "'", EXEC_ARG});
969 	// finder = mult.finder().neg(negFilter);
970 
971 	while (finder.hasNext()) {
972 	    finder.next();
973 //	    System.out.println(""+finder.next());
974 	}
975     }
976 
977 }