View Javadoc

1   /*
2    * Fsgrep is a simple Java application which allows a user to
3    * search all files in a directory structure for lines matching
4    * a given pattern.  Its functionality is a combination of the
5    * Unix 'find' and 'grep' utilities.
6    * Visit [http://fsgrep.sourceforge.net/] for more information.
7    * 
8    * Copyright (C) 2003-2006 Murali Krishnan [murali_ca_us@users.sourceforge.net]
9    * 
10   * Fsgrep is free software; you can redistribute it and/or modify
11   * it under the terms of version 2 of the GNU General Public
12   * License as published by the Free Software Foundation.
13   * 
14   * Fsgrep is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public License
20   * along with Fsgrep (see the file named LICENSE.txt); if not, write
21   * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
22   * Boston, MA  02111-1307  USA
23   */
24  
25  package mk.fsgrep;
26  
27  
28  import java.io.*;
29  import java.util.*;
30  import java.util.regex.*;
31  
32  import mk.fsgrep.find.Finder;
33  import mk.fsgrep.find.ScanProfile;
34  import mk.fsgrep.gui.App;
35  import mk.fsgrep.gui.Preferences;
36  import mk.fsgrep.match.FileSearch;
37  import mk.fsgrep.match.MatchResult;
38  import mk.fsgrep.match.Replace;
39  import mk.fsgrep.match.ReplaceControl;
40  import mk.fsgrep.match.SearchFactory;
41  import mk.fsgrep.util.*;
42  import mk.fsgrep.util.Timer;
43  import mk.fsgrep.util.output.NullOutput;
44  import mk.fsgrep.util.output.OutputDestination;
45  
46  
47  /***
48   * The primary application class.
49   * 
50   * @author  Murali Krishnan
51   *
52   */
53  public class Fsgrep implements Runnable {
54  
55      //------------------------------------------------------------
56      //- Class Variables
57  
58      public static final ManifestProperties MANIFEST_PROPS = new ManifestProperties(Fsgrep.class);
59      public static final String USAGE = TextResource.get("USAGE.txt");
60      public static final String README = TextResource.get("README.txt");
61      public static final String LICENSE = MANIFEST_PROPS.getLicense();
62      public static final String VERSION = MANIFEST_PROPS.getProjectVersion();
63      public static final String VERSION_EXTRA = TextResource.get("VERSION_EXTRA.txt");
64  
65      public static final Action ACTION_UNDEFINED = new Action.Undefined();
66      public static final Action ACTION_SEARCH = new Action.Search();
67      public static final Action ACTION_REPLACE = new Action.Replace();
68      public static final Action ACTION_LISTFILES = new Action.ListFiles();
69      public static final Action ACTION_RESCAN = new Action.Rescan();
70  
71      protected static final SearchFactory factory = SearchFactory.getInstance();
72  
73      /***
74       * A reference to the persistent data object (singleton).
75       */
76      private static final PersistentData pdata = PersistentData.getInstance();
77  
78      public static final boolean DEBUG = Boolean.getBoolean("fsgrep.debug");
79  
80      protected static final Pattern RE_STRIP_EOL = Pattern.compile("(//n|//r//n|//r)//z");
81  
82  
83      //------------------------------------------------------------
84      //- Class Functions
85  
86  
87      static public void run(Args args) {
88          try {
89              Fsgrep fsgrep = new Fsgrep(args);
90              if (args.launchGui()) {
91                  if (!DEBUG) {
92                      // In debug mode, do not redirect stdout/stderr.
93                      OutputRedirector.getInstance().redirect();
94                  }
95                  App app = new App();
96                  app.launch(fsgrep);
97              } else {
98                  OutputDestination report = args.createReportOutput();
99                  OutputDestination result = args.createResultOutput();
100                 OutputDestination live = new NullOutput();
101                 OutputDestination status = new NullOutput();
102                 fsgrep.initialize(report,
103                                   result,
104                                   live,
105                                   status,
106                                   new TextProgressBar(report));
107                 fsgrep.setPattern(args.getPattern());
108                 fsgrep.setAction(ACTION_SEARCH);
109 
110                 fsgrep.run();
111             }
112         } catch (PatternSyntaxException rese) {
113             System.err.println("'" + args.getPattern() + "': " + rese.getMessage());
114         } catch (Throwable thr) {
115             System.err.println("Fatal error.");
116             thr.printStackTrace();
117         }
118     }
119 
120 
121     public static String stripEol(String string) {
122         String result = RE_STRIP_EOL.matcher(string).replaceAll("");
123         return result;
124     }
125 
126 
127 
128     //------------------------------------------------------------
129     //- Instance Variables
130 
131     private Args _args = null;
132     private Finder _finder = null;
133     private Finder vBackupFinder = null;
134     private List<MatchResult> fResultList = new ArrayList<MatchResult>();
135     private OutputDestination _reportOutput = null;
136     private OutputDestination _resultOutput = null;
137     private OutputDestination _liveOutput = null;
138     private OutputDestination _statusOutput = null;
139     private ProgressBar _progress = new NullProgressBar();
140     private ResultCounter _matchCounter = new ResultCounter();
141     private ResultCounter _finishedCounter = new ResultCounter();
142     private ResultCounter _remainingCounter = new ResultCounter();
143     private Pattern _re = null;
144     private String _pattern = "";
145     private String _replacement = "";
146     private boolean _aborted = false;
147     private Action _action = ACTION_UNDEFINED;
148     private int _totalLinesSearched = 0;
149 
150 
151 
152     //------------------------------------------------------------
153     //- Constructors
154 
155     public Fsgrep(Args pArgs) {
156         _args = pArgs;
157 
158         _finder = new Finder(this, new ScanProfile(pArgs.getRoot(), pArgs.getSuffix()));
159     }
160 
161     /***
162      * Only for testing.
163      * 
164      */
165     protected Fsgrep() {
166     }
167 
168 
169     public void initialize(OutputDestination pReport, OutputDestination pResult, OutputDestination pLive, OutputDestination pStatus, ProgressBar pBar) {
170         _reportOutput = pReport;
171         _resultOutput = pResult;
172         _liveOutput = pLive;
173         _statusOutput = pStatus;
174         _progress = pBar;
175     }
176 
177 
178     //------------------------------------------------------------
179     //- Accessors
180 
181     public Args getArgs() {return _args;}
182     public Finder getFinder() {return _finder;}
183     public List<MatchResult> getResultList() {return fResultList;}
184     public OutputDestination getReportOutput() {return _reportOutput;}
185     public OutputDestination getResultOutput() {return _resultOutput;}
186     public OutputDestination getLiveOutput() {return _liveOutput;}
187     public OutputDestination getStatusOutput() {return _statusOutput;}
188     protected ProgressBar getProgress() {return _progress;}
189     public ResultCounter getMatchCounter() {return _matchCounter;}
190     public ResultCounter getRemainingCounter() {return _remainingCounter;}
191     public ResultCounter getFinishedCounter() {return _finishedCounter;}
192     public Pattern getRE() {return _re;}
193     public String getPattern() {return _pattern;}
194     public String getReplacement() {return _replacement;}
195     public boolean isAborted() {return _aborted;}
196     public Action getAction() {return _action;}
197     protected int totalLinesSearched() {return _totalLinesSearched;}
198 
199     protected Finder getBackupFinder() {
200         return vBackupFinder;
201     }
202 
203 
204 
205     //------------------------------------------------------------
206     //- Settors
207 
208     public void setLiveOutput(OutputDestination val) {_liveOutput=val;}
209     public void setMatchCounter(ResultCounter val) {_matchCounter=val;}
210     public void setRemainingCounter(ResultCounter val) {_remainingCounter=val;}
211     public void setFinishedCounter(ResultCounter val) {_finishedCounter=val;}
212 
213     public void setFinder(Finder val) {
214         _finder=val;
215     }
216 
217     protected void setRE(Pattern val) {_re=val;}
218     public void setReplacement(String val) {_replacement=val;}
219     public void setAborted(boolean val) {_aborted=val;}
220     protected void totalLinesSearched(int val) {_totalLinesSearched=val;}
221 
222     protected void setBackupFinder(Finder val) {
223         vBackupFinder = val;
224     }
225 
226 
227     //------------------------------------------------------------
228     //- Private/Protected Utility Functions
229 
230     public void setAction(Action val) {
231         val.setModel(this);
232 
233         _action = val;
234     }
235 
236 
237     public boolean getAlwaysRescan() {
238         boolean result = pdata.getBooleanProperty(Preferences.KEY_RESCAN);
239 
240         return result;
241     }
242 
243 
244     protected void search() {
245         int lineCount = 0;
246         Iterator iterator = getFinder().getFileList().iterator();
247         while (!isAborted() && iterator.hasNext()) {
248             TargetFile file = (TargetFile) iterator.next();
249             FileSearch fileSearch = factory.create(file.getSuffix(), getArgs());
250             fileSearch.initialize(getRE(), file, this);
251 
252             // If we just want the list of files, then we do not care how
253             // many matches there are within a file.  Stop searching the
254             // file after the fist match to improve performance.
255             if (getArgs().isListFileMatches()) {
256                 fileSearch.stopAfterFirstMatch();
257             }
258 
259             fileSearch.run();
260             lineCount += fileSearch.getLinesSearched();
261         }
262 
263         getProgress().finish();
264         totalLinesSearched(lineCount);
265     }
266 
267 
268     protected void replace() {
269         int lineCount = 0;
270         ReplaceControl control = new ReplaceControl(getRE(), getReplacement());
271         control.setReplaceCount(getMatchCounter());
272 
273         Iterator iterator = getFinder().getFileList().iterator();
274         while (control.getDoContinue() && iterator.hasNext()) {
275             TargetFile file = (TargetFile) iterator.next();
276             Replace replace = new Replace(control, file);
277             replace.run();
278 
279             finishFile(null);
280         }
281         setAborted(iterator.hasNext());
282 
283         getProgress().finish();
284         totalLinesSearched(lineCount);
285     }
286 
287 
288     protected void searchForPattern() {
289             prepare();
290 
291             if (getArgs().isSearchFileNames()) {
292                 getStatusOutput().print("Applying the pattern to filenames ...");
293             } else {
294                 getStatusOutput().print("Searching for matches in files ...");
295             }
296 
297             getProgress().initialize(getFinder().getFileList().size());
298 
299             Timer timer = new Timer();
300             search();
301             timer.stop();
302 
303             if (getArgs().isListFileMatches()) {
304                 showFilelistResult();
305             } else {
306                 showMatchResult();
307             }
308 
309             showResult(timer);
310     }
311 
312 
313     protected void replacePattern() {
314         prepare();
315 
316         getStatusOutput().print("Searching for matches in files ...");
317 
318         getProgress().initialize(getFinder().getFileList().size());
319 
320         Timer timer = new Timer();
321         replace();
322         timer.stop();
323 
324         showResult(timer);
325     }
326 
327 
328     protected void showMatchResult()  {
329         if (!getResultList().isEmpty()) {
330             for (MatchResult result : getResultList()) {
331                 getResultOutput().print(result);
332             }
333 
334             getResultOutput().print(MatchResult.SEPARATOR);
335             getResultOutput().flush();
336         }
337 
338         // Report how many matches there were and the total number of lines
339         // searched.
340         {
341             StringBuffer buffer = new StringBuffer();
342             buffer.append(getMatchCounter().getValue()).append(" matches");
343             if (totalLinesSearched() > 0) {
344                 buffer.append("; ")
345                     .append(totalLinesSearched())
346                     .append(" total lines searched");
347             }
348             buffer.append(".");
349             getReportOutput().println(buffer);
350         }
351     }
352 
353 
354     protected List<String> getResultFilelist()  {
355         List<String> result = new ArrayList<String>();
356         for (MatchResult match : getResultList()) {
357             result.add(match.getFilename());
358         }
359 
360         if (getArgs().isListFilesWithoutMatch()) {
361             // If we want non-matches, then we do the difference calculation.
362             List<String> exclusions = result;
363             result = new ArrayList<String>();
364 
365             for (TargetFile file : getFinder().getFileList()) {
366                 String candidate = file.getName();
367 
368                 if (!exclusions.contains(candidate)) {
369                     result.add(candidate);
370                 }
371             }
372         }
373 
374         return result;
375     }
376 
377 
378     protected void showFilelistResult()  {
379         List<String> results = getResultFilelist();
380 
381         if (!results.isEmpty()) {
382             getReportOutput().print(MatchResult.SEPARATOR);
383 
384             for (String result : results) {
385                 getResultOutput().println(result);
386             }
387 
388             getReportOutput().print(MatchResult.SEPARATOR);
389             getResultOutput().flush();
390         }
391 
392         String resultsTag = getArgs().isListFilesWithoutMatch() ?
393             " files did not contain matches." :
394             " files contained matches.";
395         getReportOutput().println(results.size() + resultsTag);
396     }
397 
398 
399     protected void showResult(Timer timer)  {
400         long total = timer.getTotal();
401         int count = getFinder().getFileList().size();
402         double avg = timer.findAverage(count);
403 
404         getReportOutput().println("Search time: " + total + " ms, " +
405                                   count + " files, " + avg + " ms per file.");
406 
407         // printMemory();
408     }
409 
410 
411     protected void printMemory() {
412         Runtime runtime = Runtime.getRuntime();
413 
414         runtime.gc();
415 
416         long total = runtime.totalMemory() / 1024;
417         long free = runtime.freeMemory() / 1024;
418         long used = (total - free);
419 
420         System.out.println("Memory <used,free> = <" + used + "," + free + ">");
421     }
422 
423 
424     protected void prepare() {
425         fResultList = new ArrayList<MatchResult>();
426 
427         getMatchCounter().reset();
428         getFinishedCounter().reset();
429 
430         if (getFinder().getFileList().isEmpty() || getAlwaysRescan()) {
431             getRemainingCounter().reset();
432             getFinder().scan();
433         }
434         getRemainingCounter().setValue(getFinder().getFileCount().getValue());
435     }
436 
437 
438     protected void listFiles() {
439         prepare();
440 
441         getReportOutput().println("Found " + getFinder().getFileList().size() +
442                                   " files.");
443         getStatusOutput().print("Listing files ...");
444 
445         for (TargetFile targetFile : getFinder().getFileList()) {
446             String result = String.valueOf(targetFile);
447             getResultOutput().println(result);
448             getLiveOutput().println(result);
449         }
450 
451         getResultOutput().flush();
452     }
453 
454 
455     protected int getMatchFlag() {
456         int result = getArgs().isCaseSensitive() ?
457             0 : Pattern.CASE_INSENSITIVE;
458 
459         return result;
460     }
461 
462 
463 
464     //------------------------------------------------------------
465     //- Public Interface Functions
466 
467     public String toString() {
468         String result = "Fsgrep [" + getFinder().toProfileString() + ", \"" + getPattern() + "\"]";
469 
470         return result;
471     }
472 
473     public void run() {
474         setAborted(false);
475         getReportOutput().println(this);
476 
477         getAction().execute();
478         restoreFinder();
479     }
480 
481     public void setPattern(String pPattern) {
482         _pattern = pPattern;
483 
484         setRE(Pattern.compile(pPattern, getMatchFlag()));
485     }
486 
487 
488     public synchronized void finishFile(FileSearch fileSearch) {
489         if ((fileSearch != null) && (fileSearch.getResults() != null)) {
490             getResultList().add(fileSearch.getResults());
491             getMatchCounter().increment(fileSearch.getCount());
492             getLiveOutput().print(fileSearch.getResults());
493         }
494 
495         getFinishedCounter().increment();
496         getRemainingCounter().decrement();
497         getProgress().update();
498     }
499 
500 
501     public boolean isActionSearch() {
502         boolean result = getAction().isSearch();
503 
504         return result;
505     }
506 
507 
508     public void useTransientFinder(Finder pFinder) {
509         setBackupFinder(getFinder());
510         setFinder(pFinder);
511     }
512 
513 
514     public void restoreFinder() {
515         if (getBackupFinder() != null) {
516             setFinder(getBackupFinder());
517             setBackupFinder(null);
518         }
519     }
520 
521 
522     //------------------------------------------------------------
523     //- Class Interface Functions
524 
525 
526 
527     //------------------------------------------------------------
528     //- Inner Classes
529 
530 
531 
532     //------------------------------------------------------------
533     //- Main
534 
535     static public void main(String[] cmdLineArgs) {
536         Args args = new Args(cmdLineArgs);
537 
538         if (args.getShowUsage()) {
539             System.out.println(USAGE);
540         } else if (args.getShowVersion()) {
541             System.out.println("fsgrep (file system grep) " + VERSION + ", " + MANIFEST_PROPS.getBuildTime());
542             System.out.println();
543             System.out.println(VERSION_EXTRA);
544         } else {
545             File root = new File(args.getRoot());
546             if (root.isDirectory()) {
547                 run(args);
548             } else {
549                 System.err.println("No directory [" + args.getRoot() + "].");
550             }
551         }
552     }
553 
554 
555 }