001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    
021    package net.sourceforge.pmd.util.filter;
022    
023    import java.io.File;
024    import java.io.FilenameFilter;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.List;
028    
029    /**
030     * Utility class for working with Filters.  Contains builder style methods,
031     * apply methods, as well as mechanisms for adapting Filters and FilenameFilters.
032     */
033    public class Filters {
034    
035            /**
036             * Filter a given Collection.
037             * @param <T> Type of the Collection.
038             * @param filter A Filter upon the Type of objects in the Collection.
039             * @param collection The Collection to filter.
040             * @return A List containing only those objects for which the Filter returned <code>true</code>.
041             */
042            public static <T> List<T> filter(Filter<T> filter, Collection<T> collection) {
043                    List<T> list = new ArrayList<T>();
044                    for (T obj : collection) {
045                            if (filter.filter(obj)) {
046                                    list.add(obj);
047                            }
048                    }
049                    return list;
050            }
051    
052            /**
053             * Get a File Filter for files with the given extensions, ignoring case.
054             * @param extensions The extensions to filter.
055             * @return A File Filter.
056             */
057            public static Filter<File> getFileExtensionFilter(String... extensions) {
058                    return new FileExtensionFilter(extensions);
059            }
060    
061            /**
062             * Get a File Filter for directories.
063             * @return A File Filter.
064             */
065            public static Filter<File> getDirectoryFilter() {
066                    return DirectoryFilter.INSTANCE;
067            }
068    
069            /**
070             * Get a File Filter for directories or for files with the given extensions, ignoring case.
071             * @param extensions The extensions to filter.
072             * @return A File Filter.
073             */
074            public static Filter<File> getFileExtensionOrDirectoryFilter(String... extensions) {
075                    return new OrFilter<File>(getFileExtensionFilter(extensions), getDirectoryFilter());
076            }
077    
078            /**
079             * Given a String Filter, expose as a File Filter.  The File paths are
080             * normalized to a standard pattern using <code>/</code> as a path separator
081             * which can be used cross platform easily in a regular expression based
082             * String Filter.
083             * 
084             * @param filter A String Filter.
085             * @return A File Filter.
086             */
087            public static Filter<File> toNormalizedFileFilter(final Filter<String> filter) {
088                    return new Filter<File>() {
089                            public boolean filter(File file) {
090                                    String path = file.getPath();
091                                    path = path.replace('\\', '/');
092                                    return filter.filter(path);
093                            }
094    
095                            public String toString() {
096                                    return filter.toString();
097                            }
098                    };
099            }
100    
101            /**
102             * Given a String Filter, expose as a Filter on another type.  The
103             * <code>toString()</code> method is called on the objects of the other
104             * type and delegated to the String Filter.
105             * @param <T> The desired type.
106             * @param filter The existing String Filter.
107             * @return A Filter on the desired type.
108             */
109            public static <T> Filter<T> fromStringFilter(final Filter<String> filter) {
110                    return new Filter<T>() {
111                            public boolean filter(T obj) {
112                                    return filter.filter(obj.toString());
113                            }
114    
115                            public String toString() {
116                                    return filter.toString();
117                            }
118                    };
119            }
120    
121            /**
122             * Given a File Filter, expose as a FilenameFilter.
123             * @param filter The File Filter.
124             * @return A FilenameFilter.
125             */
126            public static FilenameFilter toFilenameFilter(final Filter<File> filter) {
127                    return new FilenameFilter() {
128                            public boolean accept(File dir, String name) {
129                                    return filter.filter(new File(dir, name));
130                            }
131    
132                            public String toString() {
133                                    return filter.toString();
134                            }
135                    };
136            }
137    
138            /**
139             * Given a FilenameFilter, expose as a File Filter.
140             * @param filter The FilenameFilter.
141             * @return A File Filter.
142             */
143            public static Filter<File> toFileFilter(final FilenameFilter filter) {
144                    return new Filter<File>() {
145                            public boolean filter(File file) {
146                                    return filter.accept(file.getParentFile(), file.getName());
147                            }
148    
149                            public String toString() {
150                                    return filter.toString();
151                            }
152                    };
153            }
154    
155            /**
156             * Construct a String Filter using set of include and exclude regular
157             * expressions.  If there are no include regular expressions provide, then
158             * a regular expression is added which matches every String by default.
159             * A String is included as long as it matches an include regular expression
160             * and does not match an exclude regular expression.
161             * <p>
162             * In other words, exclude patterns override include patterns.
163             * 
164             * @param includeRegexes The include regular expressions.  May be <code>null</code>.
165             * @param excludeRegexes The exclude regular expressions.  May be <code>null</code>.
166             * @return A String Filter.
167             */
168            public static Filter<String> buildRegexFilterExcludeOverInclude(List<String> includeRegexes,
169                            List<String> excludeRegexes) {
170                    OrFilter<String> includeFilter = new OrFilter<String>();
171                    if (includeRegexes == null || includeRegexes.size() == 0) {
172                            includeFilter.addFilter(new RegexStringFilter(".*"));
173                    } else {
174                            for (String includeRegex : includeRegexes) {
175                                    includeFilter.addFilter(new RegexStringFilter(includeRegex));
176                            }
177                    }
178    
179                    OrFilter<String> excludeFilter = new OrFilter<String>();
180                    if (excludeRegexes != null) {
181                            for (String excludeRegex : excludeRegexes) {
182                                    excludeFilter.addFilter(new RegexStringFilter(excludeRegex));
183                            }
184                    }
185    
186                    return new AndFilter<String>(includeFilter, new NotFilter<String>(excludeFilter));
187            }
188    
189            /**
190             * Construct a String Filter using set of include and exclude regular
191             * expressions.  If there are no include regular expressions provide, then
192             * a regular expression is added which matches every String by default.
193             * A String is included as long as the case that there is an include which
194             * matches or there is not an exclude which matches.
195             * <p>
196             * In other words, include patterns override exclude patterns.
197             * 
198             * @param includeRegexes The include regular expressions.  May be <code>null</code>.
199             * @param excludeRegexes The exclude regular expressions.  May be <code>null</code>.
200             * @return A String Filter.
201             */
202            public static Filter<String> buildRegexFilterIncludeOverExclude(List<String> includeRegexes,
203                            List<String> excludeRegexes) {
204                    OrFilter<String> includeFilter = new OrFilter<String>();
205                    if (includeRegexes != null) {
206                            for (String includeRegex : includeRegexes) {
207                                    includeFilter.addFilter(new RegexStringFilter(includeRegex));
208                            }
209                    }
210    
211                    OrFilter<String> excludeFilter = new OrFilter<String>();
212                    if (excludeRegexes != null) {
213                            for (String excludeRegex : excludeRegexes) {
214                                    excludeFilter.addFilter(new RegexStringFilter(excludeRegex));
215                            }
216                    }
217    
218                    return new OrFilter<String>(includeFilter, new NotFilter<String>(excludeFilter));
219            }
220    }