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