View Javadoc
1   /*
2    * SPDX-License-Identifier: LGPL-2.1-or-later
3    * See LICENSE file for details.
4    *
5    * Copyright 2012-2026 Hazendaz.
6    */
7   package net.alchim31.maven.yuicompressor;
8   
9   import java.io.File;
10  import java.io.IOException;
11  import java.nio.file.Path;
12  import java.util.List;
13  
14  import javax.inject.Inject;
15  
16  import org.apache.maven.model.Resource;
17  import org.apache.maven.plugin.AbstractMojo;
18  import org.apache.maven.plugin.MojoExecutionException;
19  import org.apache.maven.plugin.MojoFailureException;
20  import org.apache.maven.plugins.annotations.Parameter;
21  import org.apache.maven.project.MavenProject;
22  import org.codehaus.plexus.build.BuildContext;
23  import org.codehaus.plexus.util.DirectoryScanner;
24  import org.codehaus.plexus.util.Scanner;
25  
26  /**
27   * Common class for mojos.
28   */
29  public abstract class MojoSupport extends AbstractMojo {
30  
31      /** The Constant EMPTY_STRING_ARRAY. */
32      private static final String[] EMPTY_STRING_ARRAY = {};
33  
34      /**
35       * Javascript source directory. (result will be put to outputDirectory).
36       */
37      @Parameter(defaultValue = "${project.basedir}/src/main/js")
38      private File sourceDirectory;
39  
40      /**
41       * Single directory for extra files to include in the WAR.
42       */
43      @Parameter(defaultValue = "${project.basedir}/src/main/webapp")
44      private File warSourceDirectory;
45  
46      /**
47       * The directory where the webapp is built.
48       */
49      @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}")
50      private File webappDirectory;
51  
52      /**
53       * The output directory into which to copy the resources.
54       */
55      @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
56      private File outputDirectory;
57  
58      /**
59       * The list of resources we want to transfer.
60       */
61      @Parameter(defaultValue = "${project.resources}", required = true, readonly = true)
62      private List<Resource> resources;
63  
64      /** list of additional excludes. */
65      @Parameter
66      private List<String> excludes;
67  
68      /** Use processed resources if available. */
69      @Parameter(defaultValue = "false")
70      private boolean useProcessedResources;
71  
72      /** list of additional includes. */
73      @Parameter
74      private List<String> includes;
75  
76      /** Excludes files from webapp directory. */
77      @Parameter
78      private boolean excludeWarSourceDirectory;
79  
80      /**
81       * Excludes files from resources directories.
82       */
83      @Parameter(defaultValue = "false")
84      private boolean excludeResources;
85  
86      /** Maven Project. */
87      @Parameter(defaultValue = "${project}", readonly = true, required = true)
88      protected MavenProject project;
89  
90      /** [js only] Display possible errors in the code. */
91      @Parameter(defaultValue = "true", property = "maven.yuicompressor.jswarn")
92      protected boolean jswarn;
93  
94      /**
95       * Whether to skip execution.
96       */
97      @Parameter(defaultValue = "false", property = "maven.yuicompressor.skip")
98      private boolean skip;
99  
100     /**
101      * Define if plugin must stop/fail on warnings.
102      */
103     @Parameter(defaultValue = "false", property = "maven.yuicompressor.failOnWarning")
104     protected boolean failOnWarning;
105 
106     /**
107      * Build Context.
108      */
109     @Inject
110     protected BuildContext buildContext;
111 
112     /** The js error reporter. */
113     protected ErrorReporter4Mojo jsErrorReporter;
114 
115     @Override
116     public void execute() throws MojoExecutionException, MojoFailureException {
117         if (skip) {
118             getLog().debug("run of yuicompressor-maven-plugin skipped");
119             return;
120         }
121 
122         if (failOnWarning) {
123             jswarn = true;
124         }
125 
126         jsErrorReporter = new ErrorReporter4Mojo(getLog(), jswarn, buildContext);
127 
128         try {
129             beforeProcess();
130             processDir(sourceDirectory, outputDirectory, null, useProcessedResources);
131             if (!excludeResources) {
132                 for (Resource resource : resources) {
133                     File destRoot = outputDirectory;
134                     if (resource.getTargetPath() != null) {
135                         destRoot = outputDirectory.toPath().resolve(resource.getTargetPath()).toFile();
136                     }
137                     processDir(Path.of(resource.getDirectory()).toFile(), destRoot, resource.getExcludes(),
138                             useProcessedResources);
139                 }
140             }
141             if (!excludeWarSourceDirectory) {
142                 processDir(warSourceDirectory, webappDirectory, null, useProcessedResources);
143             }
144             afterProcess();
145         } catch (Exception e) {
146             throw new MojoExecutionException("wrap: " + e.getMessage(), e);
147         }
148 
149         getLog().info(String.format("nb warnings: %d, nb errors: %d", jsErrorReporter.getWarningCnt(),
150                 jsErrorReporter.getErrorCnt()));
151         if (failOnWarning && jsErrorReporter.getWarningCnt() > 0) {
152             throw new MojoFailureException("warnings on " + this.getClass().getSimpleName() + "=> failure ! (see log)");
153         }
154     }
155 
156     /**
157      * Gets the default includes.
158      *
159      * @return the default includes
160      */
161     protected abstract String[] getDefaultIncludes();
162 
163     /**
164      * Before process.
165      *
166      * @throws IOException
167      *             the IO exception
168      */
169     protected abstract void beforeProcess() throws IOException;
170 
171     /**
172      * After process.
173      *
174      * @throws IOException
175      *             the IO exception
176      */
177     protected abstract void afterProcess() throws IOException;
178 
179     /**
180      * Force to use defaultIncludes (ignore srcIncludes) to avoid processing resources/includes from other type than
181      * *.css or *.js see https://github.com/davidB/yuicompressor-maven-plugin/issues/19
182      *
183      * @param srcRoot
184      *            the src root
185      * @param destRoot
186      *            the dest root
187      * @param srcExcludes
188      *            the src excludes
189      * @param destAsSource
190      *            the dest as source
191      *
192      * @throws IOException
193      *             the IO exception
194      * @throws MojoFailureException
195      *             the Mojo Failure Exception
196      * @throws MojoExecutionException
197      *             the Mojo Execution Exception
198      */
199     private void processDir(File srcRoot, File destRoot, List<String> srcExcludes, boolean destAsSource)
200             throws IOException, MojoFailureException, MojoExecutionException {
201         if (srcRoot == null) {
202             return;
203         }
204         if (!srcRoot.exists()) {
205             buildContext.addMessage(srcRoot, 0, 0, "Directory " + srcRoot.getPath() + " does not exist",
206                     BuildContext.SEVERITY_WARNING, null);
207             getLog().info("Directory " + srcRoot.getPath() + " does not exist");
208             return;
209         }
210         if (destRoot == null) {
211             throw new MojoFailureException("destination directory for " + srcRoot + " is null");
212         }
213         Scanner scanner;
214         if (!buildContext.isIncremental()) {
215             DirectoryScanner dScanner = new DirectoryScanner();
216             dScanner.setBasedir(srcRoot);
217             scanner = dScanner;
218         } else {
219             scanner = buildContext.newScanner(srcRoot);
220         }
221 
222         if (includes == null) {
223             scanner.setIncludes(getDefaultIncludes());
224         } else {
225             scanner.setIncludes(includes.toArray(new String[0]));
226         }
227 
228         if (srcExcludes != null && !srcExcludes.isEmpty()) {
229             scanner.setExcludes(srcExcludes.toArray(EMPTY_STRING_ARRAY));
230         }
231         if (excludes != null && !excludes.isEmpty()) {
232             scanner.setExcludes(excludes.toArray(EMPTY_STRING_ARRAY));
233         }
234         scanner.addDefaultExcludes();
235 
236         scanner.scan();
237 
238         String[] includedFiles = scanner.getIncludedFiles();
239         if (includedFiles == null || includedFiles.length == 0) {
240             if (buildContext.isIncremental()) {
241                 getLog().info("No files have changed, so skipping the processing");
242             } else {
243                 getLog().info("No files to be processed");
244             }
245             return;
246         }
247         for (String name : includedFiles) {
248             SourceFile src = new SourceFile(srcRoot, destRoot, name, destAsSource);
249             jsErrorReporter.setDefaultFileName("..."
250                     + src.toFile().getCanonicalPath().substring(src.toFile().getCanonicalPath().lastIndexOf('/') + 1));
251             jsErrorReporter.setFile(src.toFile());
252             processFile(src);
253         }
254     }
255 
256     /**
257      * Process file.
258      *
259      * @param src
260      *            the src
261      *
262      * @throws IOException
263      *             the IO exception
264      * @throws MojoExecutionException
265      *             the mojo execution exception
266      */
267     protected abstract void processFile(SourceFile src) throws IOException, MojoExecutionException;
268 }