View Javadoc

1   /*
2    * Copyright (c) 2004, Henri Yandell
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or 
6    * without modification, are permitted provided that the 
7    * following conditions are met:
8    * 
9    * + Redistributions of source code must retain the above copyright notice, 
10   *   this list of conditions and the following disclaimer.
11   * 
12   * + Redistributions in binary form must reproduce the above copyright notice, 
13   *   this list of conditions and the following disclaimer in the documentation 
14   *   and/or other materials provided with the distribution.
15   * 
16   * + Neither the name of Genjava-Core nor the names of its contributors 
17   *   may be used to endorse or promote products derived from this software 
18   *   without specific prior written permission.
19   * 
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  
33  package com.generationjava.io.find;
34  
35  import java.util.Collection;
36  import java.io.File;
37  import java.io.FileFilter;
38  import java.util.Map;
39  import java.util.List;
40  import java.util.LinkedList;
41  import java.util.Iterator;
42  import java.util.regex.Pattern;
43  import java.util.regex.Matcher;
44  
45  import com.generationjava.util.WildcardUtils;
46  
47  /***
48   * This is where most of the find functionality occurs. Nearly every option 
49   * to find is mapped to a FileFilter, which are then chained together inside 
50   * this class. 
51   */
52  public class FindingFilter implements FileFilter {
53  
54      private Map options;
55      private List filters = new LinkedList();
56      private boolean daystart;
57  
58      public FindingFilter(Map options) {
59          this.options = options;
60          Collection entries = options.entrySet();
61          Iterator itr = entries.iterator();
62          while(itr.hasNext()) {
63              Map.Entry entry = (Map.Entry)itr.next();
64              if( entry.getKey().equals(Finder.DAYSTART) ) {
65                  this.daystart = true; 
66                  continue;
67              }
68              // knows that the key is a String
69              filters.add( createFilter(entry.getKey().toString(), entry.getValue()) );
70          }
71      }
72  
73      private FileFilter createFilter(String option, Object argument) {
74  
75          boolean invert = false;
76          if( option.startsWith(Finder.NOT) ) {
77              invert = true;
78              // knows that option is a String. Bad. Needs an Enum?
79              option = option.substring(Finder.NOT.length());
80          }
81          if( option.equals(Finder.MIN) ) {
82              return new MinFilter(option, argument, invert, this);
83          }
84          if( option.equals(Finder.NEWER) ) {
85              return new NewerFilter(option, argument, invert);
86          }
87          if( option.equals(Finder.TIME) ) {
88              return new TimeFilter(option, argument, invert, this);
89          }
90          if( option.equals(Finder.EMPTY) ) {
91              return new EmptyFilter(option, argument, invert);
92          }
93          if( option.equals(Finder.SIZE) ) {
94              return new SizeFilter(option, argument, invert);
95          }
96          if( option.equals(Finder.NAME) ) {
97              return new NameFilter(option, argument, invert, false);
98          }
99          if( option.equals(Finder.INAME) ) {
100             return new NameFilter(option, argument, invert, true);
101         }
102         if( option.equals(Finder.PATH) ) {
103             return new PathFilter(option, argument, invert, false);
104         }
105         if( option.equals(Finder.IPATH) ) {
106             return new PathFilter(option, argument, invert, true);
107         }
108         if( option.equals(Finder.REGEX) ) {
109             return new RegexFilter(option, argument, invert, false);
110         }
111         if( option.equals(Finder.IREGEX) ) {
112             return new RegexFilter(option, argument, invert, true);
113         }
114         if( option.equals(Finder.TYPE) ) {
115             return new TypeFilter(option, argument, invert);
116         }
117         if( option.equals(Finder.HIDDEN) ) {
118             return new HiddenFilter(option, argument, invert);
119         }
120         if( option.equals(Finder.CAN_READ) ) {
121             return new CanReadFilter(option, argument, invert);
122         }
123         if( option.equals(Finder.CAN_WRITE) ) {
124             return new CanWriteFilter(option, argument, invert);
125         }
126         return null;
127     }
128 
129     public boolean accept(File file) {
130         Iterator itr = filters.iterator();
131         while(itr.hasNext()) {
132             FileFilter filter = (FileFilter) itr.next();
133             if(filter == null) {
134                 continue;
135             }
136             boolean result = filter.accept(file);
137             if(result == false) {
138                 return false;
139             }
140         }
141         return true;
142     }
143 
144     public boolean isDaystartConfigured() {
145         return this.daystart;
146     }
147 
148     // helper method to make the inverting easier.
149     // possibly the Filters should be inner classes.
150     // possibly there should be an abstract FindFilter class.
151     static boolean invert(boolean invert, boolean answer) {
152         if(invert) {
153             answer = !answer;
154         }
155         return answer;
156     }
157 
158 }
159 
160     // need to implement the daystart bits
161     class MinFilter implements FileFilter {
162         private Object option;
163         private boolean invert;
164         private int argument;
165         private FindingFilter parent;
166         public MinFilter(Object option, Object argument, boolean invert, FindingFilter parent) {
167             this.option = option;
168             this.invert = invert;
169             try {
170                 this.argument = Integer.parseInt(argument.toString());
171             } catch(NumberFormatException nfe) {
172                 throw new IllegalArgumentException("Argument "+argument+" must be an integer.  ");
173             }
174             this.parent = parent;
175         }
176         public boolean accept(File file) {
177             boolean daystart = this.parent.isDaystartConfigured();
178             return FindingFilter.invert( this.invert,  file.lastModified() > System.currentTimeMillis() - this.argument * 60000 );
179         }
180     }
181 
182     class NewerFilter implements FileFilter {
183         private Object option;
184         private boolean invert;
185         private File argument;
186         public NewerFilter(Object option, Object argument, boolean invert) {
187             this.option = option;
188             this.invert = invert;
189             this.argument = new File(argument.toString());
190         }
191         public boolean accept(File file) {
192             return FindingFilter.invert( this.invert,  file.lastModified() > this.argument.lastModified() );
193         }
194     }
195 
196     // implement daystart
197     class TimeFilter implements FileFilter {
198         private Object option;
199         private boolean invert;
200         private int argument;
201         private FindingFilter parent;
202         public TimeFilter(Object option, Object argument, boolean invert, FindingFilter parent) {
203             this.option = option;
204             this.invert = invert;
205             try {
206                 this.argument = Integer.parseInt(argument.toString());
207             } catch(NumberFormatException nfe) {
208                 throw new IllegalArgumentException("Argument "+argument+" must be an integer.  ");
209             }
210             this.parent = parent;
211         }
212         public boolean accept(File file) {
213             boolean daystart = this.parent.isDaystartConfigured();
214             return FindingFilter.invert( this.invert,  file.lastModified() > System.currentTimeMillis() - this.argument * 60000*60*24 );
215         }
216     }
217 
218     class EmptyFilter implements FileFilter {
219         private Object option;
220         private boolean invert;
221         private boolean argument;
222         public EmptyFilter(Object option, Object argument, boolean invert) {
223             this.option = option;
224             this.invert = invert;
225             this.argument = new Boolean(argument.toString()).booleanValue();
226         }
227         public boolean accept(File file) {
228             return FindingFilter.invert( this.invert,  (file.length() == 0) == this.argument );
229         }
230     }
231 
232     // needs to handle +5 for > 5 and -5 for < 5. Also needs 
233     // to handle k, m, g, as suffixes.
234     class SizeFilter implements FileFilter {
235         private Object option;
236         private boolean invert;
237         private int argument;
238         public SizeFilter(Object option, Object argument, boolean invert) {
239             this.option = option;
240             this.invert = invert;
241             try {
242                 this.argument = Integer.parseInt(argument.toString());
243             } catch(NumberFormatException nfe) {
244                 throw new IllegalArgumentException("Argument "+argument+" must be an integer.  ");
245             }
246         }
247         public boolean accept(File file) {
248             return FindingFilter.invert( this.invert,  (int)(file.length()/512) == this.argument );
249         }
250     }
251 
252     class NameFilter implements FileFilter {
253         private Object option;
254         private boolean invert;
255         private Object argument;
256         private boolean ignoreCase;
257         public NameFilter(Object option, Object argument, boolean invert, boolean ignoreCase) {
258             this.option = option;
259             this.invert = invert;
260             this.argument = argument;
261             this.ignoreCase = ignoreCase;
262         }
263         public boolean accept(File file) {
264             if(this.ignoreCase) {
265                 return FindingFilter.invert( this.invert,  WildcardUtils.match(file.getName().toLowerCase(), this.argument.toString().toLowerCase()) );
266             } else {
267                 return FindingFilter.invert( this.invert,  WildcardUtils.match(file.getName(), this.argument.toString()) );
268             }
269         }
270     }
271 
272     class PathFilter implements FileFilter {
273         private Object option;
274         private boolean invert;
275         private Object argument;
276         private boolean ignoreCase;
277         public PathFilter(Object option, Object argument, boolean invert, boolean ignoreCase) {
278             this.option = option;
279             this.invert = invert;
280             this.argument = argument;
281             this.ignoreCase = ignoreCase;
282         }
283         public boolean accept(File file) {
284             if(this.ignoreCase) {
285                 return FindingFilter.invert( this.invert,  WildcardUtils.match(file.getPath().toLowerCase(), this.argument.toString().toLowerCase()) );
286             } else {
287                 return FindingFilter.invert( this.invert,  WildcardUtils.match(file.getPath(), this.argument.toString()) );
288             }
289         }
290     }
291 
292     class RegexFilter implements FileFilter {
293         private Object option;
294         private boolean invert;
295         private Object argument;
296         private boolean ignoreCase;
297         public RegexFilter(Object option, Object argument, boolean invert, boolean ignoreCase) {
298             this.option = option;
299             this.invert = invert;
300             this.argument = argument;
301             this.ignoreCase = ignoreCase;
302         }
303         public boolean accept(File file) {
304             if(this.ignoreCase) {
305                 Pattern pattern = Pattern.compile(this.argument.toString(), Pattern.CASE_INSENSITIVE);
306                 Matcher matcher = pattern.matcher(file.getPath());
307                 return FindingFilter.invert( this.invert,  matcher.matches() );
308             } else {
309                 return FindingFilter.invert( this.invert,  file.getPath().matches(this.argument.toString()) );
310             }
311         }
312     }
313 
314     class TypeFilter implements FileFilter {
315         private Object option;
316         private boolean invert;
317         private Object argument;
318         public TypeFilter(Object option, Object argument, boolean invert) {
319             this.option = option;
320             this.invert = invert;
321             if(!"d".equals(argument) && !"f".equals(argument)) {
322                 throw new IllegalArgumentException("Type option must be 'f' or 'd'. ");
323             }
324             this.argument = argument;
325         }
326         public boolean accept(File file) {
327             if("d".equals(argument)) {
328                 return FindingFilter.invert( this.invert,  file.isDirectory() );
329             } else
330             if("f".equals(argument)) {
331                 return FindingFilter.invert( this.invert,  !file.isDirectory() );
332             } else {
333                 throw new IllegalArgumentException("Type option must be 'f' or 'd'. ");
334             }
335         }
336     }
337 
338     class HiddenFilter implements FileFilter {
339         private Object option;
340         private boolean invert;
341         private boolean argument;
342         public HiddenFilter(Object option, Object argument, boolean invert) {
343             this.option = option;
344             this.invert = invert;
345             this.argument = new Boolean(argument.toString()).booleanValue();
346         }
347         public boolean accept(File file) {
348             return FindingFilter.invert( this.invert,  file.isHidden() == this.argument );
349         }
350     }
351 
352     class CanReadFilter implements FileFilter {
353         private Object option;
354         private boolean invert;
355         private boolean argument;
356         public CanReadFilter(Object option, Object argument, boolean invert) {
357             this.option = option;
358             this.invert = invert;
359             this.argument = new Boolean(argument.toString()).booleanValue();
360         }
361         public boolean accept(File file) {
362             return FindingFilter.invert( this.invert,  file.canRead() == this.argument );
363         }
364     }
365 
366     class CanWriteFilter implements FileFilter {
367         private Object option;
368         private boolean invert;
369         private boolean argument;
370         public CanWriteFilter(Object option, Object argument, boolean invert) {
371             this.option = option;
372             this.invert = invert;
373             this.argument = new Boolean(argument.toString()).booleanValue();
374         }
375         public boolean accept(File file) {
376             return FindingFilter.invert( this.invert,  file.canWrite() == this.argument );
377     }
378 }
379