View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.spi;
6   
7   import java.io.File;
8   import java.io.IOException;
9   import java.nio.charset.Charset;
10  import java.nio.charset.StandardCharsets;
11  import java.util.Arrays;
12  import java.util.Collection;
13  import java.util.Collections;
14  import java.util.HashMap;
15  import java.util.HashSet;
16  import java.util.LinkedList;
17  import java.util.Map;
18  import java.util.Set;
19  import org.apache.commons.io.FileUtils;
20  import org.apache.commons.io.filefilter.DirectoryFileFilter;
21  import org.apache.commons.io.filefilter.IOFileFilter;
22  import org.apache.commons.io.filefilter.WildcardFileFilter;
23  
24  /**
25   * Environment.
26   *
27   * @since 0.3
28   */
29  @SuppressWarnings("PMD.TooManyMethods")
30  public interface Environment {
31  
32      /**
33       * Get project's basedir.
34       * @return The directory
35       */
36      File basedir();
37  
38      /**
39       * Get directory to keep temporary files in.
40       * @return The directory
41       */
42      File tempdir();
43  
44      /**
45       * Get directory where <tt>.class</tt> files are stored.
46       * @return The directory
47       */
48      File outdir();
49  
50      /**
51       * Get parameter by name, and return default if it's not set.
52       * @param name The name of parameter
53       * @param value Default value to return as default
54       * @return The value
55       */
56      String param(String name, String value);
57  
58      /**
59       * Get classloader for this project.
60       * @return The classloader
61       */
62      ClassLoader classloader();
63  
64      /**
65       * Get list of paths in classpath.
66       * @return The collection of paths
67       */
68      Collection<String> classpath();
69  
70      /**
71       * Returns the files matching the specified pattern.
72       *
73       * <p>The pattern matching scheme used is wildcard matching. The characters
74       * '?' and '*' represents single or multiple wildcard characters,
75       * respectively. Pattern matching is case sensitive.
76       *
77       * @param pattern File name pattern.
78       * @return Collection of files, matching the specified pattern.
79       */
80      Collection<File> files(String pattern);
81  
82      /**
83       * Shall this item be excluded from report?
84       * @param check Name of the check that is asking
85       * @param name File or any other item, which is subject of validation
86       * @return TRUE if it should be ignored
87       */
88      boolean exclude(String check, String name);
89  
90      /**
91       * List of exclude patterns for given checker.
92       * Each list element will contain exactly one exclude pattern which,
93       * depending on the plugin that uses the excludes might be either wildcard
94       * (CodeNarc) pattern or regex pattern (FindBugs).
95       * @param checker Name of the checker that is asking (pmd, codenarc ...)
96       * @return Exclude patterns
97       */
98      Collection<String> excludes(String checker);
99  
100     /**
101      * Encoding for the files.
102      * @return Source files charset
103      */
104     Charset encoding();
105 
106     /**
107      * Mock of {@link Environment}.
108      *
109      * @since 0.1
110      */
111     final class Mock implements Environment {
112         /**
113          * The basedir.
114          */
115         private final File basedir;
116 
117         /**
118          * Files for classpath.
119          */
120         private final Set<String> classpath;
121 
122         /**
123          * Map of params.
124          */
125         private final Map<String, String> params;
126 
127         /**
128          * Exclude patterns.
129          */
130         private String excl;
131 
132         /**
133          * Public ctor.
134          * @throws IOException If some IO problem
135          */
136         @SuppressWarnings(
137             "PMD.ConstructorOnlyInitializesOrCallOtherConstructors"
138             )
139         public Mock() throws IOException {
140             this.params = new HashMap<>();
141             this.classpath = new HashSet<>(1);
142             final File temp = File.createTempFile(
143                 "mock", ".qulice",
144                 new File(System.getProperty("java.io.tmpdir"))
145             );
146             if (!temp.delete()) {
147                 throw new IllegalStateException("files collision");
148             }
149             if (!temp.mkdirs()) {
150                 throw new IllegalStateException("mkdir failed");
151             }
152             FileUtils.forceDeleteOnExit(temp);
153             this.basedir = new File(temp, "basedir");
154             if (this.basedir.mkdirs()) {
155                 assert this.basedir != null;
156             }
157             this.classpath.add(
158                 this.outdir().getAbsolutePath().replace(File.separatorChar, '/')
159             );
160         }
161 
162         /**
163          * With this param and its value.
164          * @param name Param name
165          * @param value Param value
166          * @return This object
167          */
168         public Environment.Mock withParam(final String name,
169             final String value) {
170             this.params.put(name, value);
171             return this;
172         }
173 
174         /**
175          * With this file on board.
176          * @param name File name related to basedir
177          * @param content File content to write
178          * @return This object
179          * @throws IOException If some IO problem
180          */
181         public Environment.Mock withFile(final String name,
182             final String content) throws IOException {
183             final File file = new File(this.basedir, name);
184             FileUtils.writeStringToFile(
185                 file,
186                 content,
187                 StandardCharsets.UTF_8
188             );
189             return this;
190         }
191 
192         /**
193          * With this file on board.
194          * @param name File name related to basedir
195          * @param bytes File content to write
196          * @return This object
197          * @throws IOException If some IO problem
198          */
199         public Environment.Mock withFile(final String name,
200             final byte[] bytes) throws IOException {
201             final File file = new File(this.basedir, name);
202             FileUtils.writeByteArrayToFile(file, bytes);
203             return this;
204         }
205 
206         /**
207          * With exclude patterns.
208          * @param excludes Exclude patterns
209          * @return This object
210          */
211         public Environment.Mock withExcludes(final String excludes) {
212             this.excl = excludes;
213             return this;
214         }
215 
216         /**
217          * With default classpath.
218          * @return This object
219          */
220         @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
221         public Environment.Mock withDefaultClasspath() {
222             Collections.addAll(
223                 this.classpath,
224                 System.getProperty("java.class.path")
225                     .split(System.getProperty("path.separator"))
226             );
227             return this;
228         }
229 
230         @Override
231         public File basedir() {
232             return this.basedir;
233         }
234 
235         @Override
236         public File tempdir() {
237             final File file = new File(this.basedir, "target/tempdir");
238             if (file.mkdirs()) {
239                 assert file != null;
240             }
241             return file;
242         }
243 
244         @Override
245         public File outdir() {
246             final File file = new File(this.basedir, "target/classes");
247             if (file.mkdirs()) {
248                 assert file != null;
249             }
250             return file;
251         }
252 
253         @Override
254         public String param(final String name, final String value) {
255             String val = this.params.get(name);
256             if (val == null) {
257                 val = value;
258             }
259             return val;
260         }
261 
262         @Override
263         public ClassLoader classloader() {
264             return Thread.currentThread().getContextClassLoader();
265         }
266 
267         @Override
268         public Collection<String> classpath() {
269             return Collections.unmodifiableCollection(this.classpath);
270         }
271 
272         @Override
273         @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
274         public Collection<File> files(final String pattern) {
275             final Collection<File> files = new LinkedList<>();
276             final IOFileFilter filter = WildcardFileFilter.builder().setWildcards(pattern).get();
277             if (this.basedir().exists()) {
278                 files.addAll(
279                     FileUtils.listFiles(
280                         this.basedir(),
281                         filter,
282                         DirectoryFileFilter.INSTANCE
283                     )
284                 );
285             }
286             return files;
287         }
288 
289         @Override
290         public boolean exclude(final String check, final String name) {
291             return false;
292         }
293 
294         @Override
295         public Collection<String> excludes(final String checker) {
296             final Collection<String> exc;
297             if (this.excl == null) {
298                 exc = Collections.emptyList();
299             } else {
300                 exc = Arrays.asList(this.excl.split(","));
301             }
302             return exc;
303         }
304 
305         @Override
306         public Charset encoding() {
307             return StandardCharsets.UTF_8;
308         }
309     }
310 }