View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.spi;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.nio.charset.Charset;
36  import java.nio.charset.StandardCharsets;
37  import java.util.Arrays;
38  import java.util.Collection;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.LinkedList;
43  import java.util.Map;
44  import java.util.Set;
45  import org.apache.commons.io.FileUtils;
46  import org.apache.commons.io.filefilter.DirectoryFileFilter;
47  import org.apache.commons.io.filefilter.IOFileFilter;
48  import org.apache.commons.io.filefilter.WildcardFileFilter;
49  
50  /**
51   * Environment.
52   *
53   * @since 0.3
54   */
55  @SuppressWarnings("PMD.TooManyMethods")
56  public interface Environment {
57  
58      /**
59       * Get project's basedir.
60       * @return The directory
61       */
62      File basedir();
63  
64      /**
65       * Get directory to keep temporary files in.
66       * @return The directory
67       */
68      File tempdir();
69  
70      /**
71       * Get directory where <tt>.class</tt> files are stored.
72       * @return The directory
73       */
74      File outdir();
75  
76      /**
77       * Get parameter by name, and return default if it's not set.
78       * @param name The name of parameter
79       * @param value Default value to return as default
80       * @return The value
81       */
82      String param(String name, String value);
83  
84      /**
85       * Get classloader for this project.
86       * @return The classloader
87       */
88      ClassLoader classloader();
89  
90      /**
91       * Get list of paths in classpath.
92       * @return The collection of paths
93       */
94      Collection<String> classpath();
95  
96      /**
97       * Returns the files matching the specified pattern.
98       *
99       * <p>The pattern matching scheme used is wildcard matching. The characters
100      * '?' and '*' represents single or multiple wildcard characters,
101      * respectively. Pattern matching is case sensitive.
102      *
103      * @param pattern File name pattern.
104      * @return Collection of files, matching the specified pattern.
105      */
106     Collection<File> files(String pattern);
107 
108     /**
109      * Shall this item be excluded from report?
110      * @param check Name of the check that is asking
111      * @param name File or any other item, which is subject of validation
112      * @return TRUE if it should be ignored
113      */
114     boolean exclude(String check, String name);
115 
116     /**
117      * List of exclude patterns for given checker.
118      * Each list element will contain exactly one exclude pattern which,
119      * depending on the plugin that uses the excludes might be either wildcard
120      * (CodeNarc) pattern or regex pattern (FindBugs).
121      * @param checker Name of the checker that is asking (pmd, codenarc ...)
122      * @return Exclude patterns
123      */
124     Collection<String> excludes(String checker);
125 
126     /**
127      * Encoding for the files.
128      * @return Source files charset
129      */
130     Charset encoding();
131 
132     /**
133      * Mock of {@link Environment}.
134      *
135      * @since 0.1
136      */
137     final class Mock implements Environment {
138         /**
139          * The basedir.
140          */
141         private final File basedir;
142 
143         /**
144          * Files for classpath.
145          */
146         private final Set<String> classpath;
147 
148         /**
149          * Map of params.
150          */
151         private final Map<String, String> params;
152 
153         /**
154          * Exclude patterns.
155          */
156         private String excl;
157 
158         /**
159          * Public ctor.
160          * @throws IOException If some IO problem
161          */
162         @SuppressWarnings(
163             "PMD.ConstructorOnlyInitializesOrCallOtherConstructors"
164             )
165         public Mock() throws IOException {
166             this.params = new HashMap<>();
167             this.classpath = new HashSet<>(1);
168             final File temp = File.createTempFile(
169                 "mock", ".qulice",
170                 new File(System.getProperty("java.io.tmpdir"))
171             );
172             if (!temp.delete()) {
173                 throw new IllegalStateException("files collision");
174             }
175             if (!temp.mkdirs()) {
176                 throw new IllegalStateException("mkdir failed");
177             }
178             FileUtils.forceDeleteOnExit(temp);
179             this.basedir = new File(temp, "basedir");
180             if (this.basedir.mkdirs()) {
181                 assert this.basedir != null;
182             }
183             this.classpath.add(
184                 this.outdir().getAbsolutePath().replace(File.separatorChar, '/')
185             );
186         }
187 
188         /**
189          * With this param and its value.
190          * @param name Param name
191          * @param value Param value
192          * @return This object
193          */
194         public Environment.Mock withParam(final String name,
195             final String value) {
196             this.params.put(name, value);
197             return this;
198         }
199 
200         /**
201          * With this file on board.
202          * @param name File name related to basedir
203          * @param content File content to write
204          * @return This object
205          * @throws IOException If some IO problem
206          */
207         public Environment.Mock withFile(final String name,
208             final String content) throws IOException {
209             final File file = new File(this.basedir, name);
210             FileUtils.writeStringToFile(
211                 file,
212                 content,
213                 StandardCharsets.UTF_8
214             );
215             return this;
216         }
217 
218         /**
219          * With this file on board.
220          * @param name File name related to basedir
221          * @param bytes File content to write
222          * @return This object
223          * @throws IOException If some IO problem
224          */
225         public Environment.Mock withFile(final String name,
226             final byte[] bytes) throws IOException {
227             final File file = new File(this.basedir, name);
228             FileUtils.writeByteArrayToFile(file, bytes);
229             return this;
230         }
231 
232         /**
233          * With exclude patterns.
234          * @param excludes Exclude patterns
235          * @return This object
236          */
237         public Environment.Mock withExcludes(final String excludes) {
238             this.excl = excludes;
239             return this;
240         }
241 
242         /**
243          * With default classpath.
244          * @return This object
245          */
246         @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
247         public Environment.Mock withDefaultClasspath() {
248             Collections.addAll(
249                 this.classpath,
250                 System.getProperty("java.class.path")
251                     .split(System.getProperty("path.separator"))
252             );
253             return this;
254         }
255 
256         @Override
257         public File basedir() {
258             return this.basedir;
259         }
260 
261         @Override
262         public File tempdir() {
263             final File file = new File(this.basedir, "target/tempdir");
264             if (file.mkdirs()) {
265                 assert file != null;
266             }
267             return file;
268         }
269 
270         @Override
271         public File outdir() {
272             final File file = new File(this.basedir, "target/classes");
273             if (file.mkdirs()) {
274                 assert file != null;
275             }
276             return file;
277         }
278 
279         @Override
280         public String param(final String name, final String value) {
281             String val = this.params.get(name);
282             if (val == null) {
283                 val = value;
284             }
285             return val;
286         }
287 
288         @Override
289         public ClassLoader classloader() {
290             return Thread.currentThread().getContextClassLoader();
291         }
292 
293         @Override
294         public Collection<String> classpath() {
295             return Collections.unmodifiableCollection(this.classpath);
296         }
297 
298         @Override
299         @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
300         public Collection<File> files(final String pattern) {
301             final Collection<File> files = new LinkedList<>();
302             final IOFileFilter filter = WildcardFileFilter.builder().setWildcards(pattern).get();
303             for (final String dir : new String[]{"src"}) {
304                 final File sources = new File(this.basedir(), dir);
305                 if (sources.exists()) {
306                     files.addAll(
307                         FileUtils.listFiles(
308                             sources,
309                             filter,
310                             DirectoryFileFilter.INSTANCE
311                         )
312                     );
313                 }
314             }
315             return files;
316         }
317 
318         @Override
319         public boolean exclude(final String check, final String name) {
320             return false;
321         }
322 
323         @Override
324         public Collection<String> excludes(final String checker) {
325             final Collection<String> exc;
326             if (this.excl == null) {
327                 exc = Collections.emptyList();
328             } else {
329                 exc = Arrays.asList(this.excl.split(","));
330             }
331             return exc;
332         }
333 
334         @Override
335         public Charset encoding() {
336             return StandardCharsets.UTF_8;
337         }
338     }
339 }