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 }