View Javadoc
1   /*
2    * Copyright (c) 2012-2024 Hazendaz.
3    *
4    * All rights reserved. This program and the accompanying materials
5    * are made available under the terms of The Apache Software License,
6    * Version 2.0 which accompanies this distribution, and is available at
7    * http://www.apache.org/licenses/LICENSE-2.0.txt
8    *
9    * Contributors:
10   *     Hazendaz (Jeremy Landis).
11   */
12  package net.jangaroo.smartsprites.maven;
13  
14  import java.io.File;
15  import java.io.IOException;
16  import java.util.ArrayList;
17  import java.util.List;
18  import java.util.Locale;
19  
20  import org.apache.maven.plugin.AbstractMojo;
21  import org.apache.maven.plugin.MojoExecutionException;
22  import org.apache.maven.plugin.MojoFailureException;
23  import org.apache.maven.plugins.annotations.LifecyclePhase;
24  import org.apache.maven.plugins.annotations.Mojo;
25  import org.apache.maven.plugins.annotations.Parameter;
26  import org.carrot2.labs.smartsprites.SmartSpritesParameters;
27  import org.carrot2.labs.smartsprites.SpriteBuilder;
28  import org.carrot2.labs.smartsprites.message.Message;
29  import org.carrot2.labs.smartsprites.message.MessageLog;
30  import org.carrot2.labs.smartsprites.message.PrintStreamMessageSink;
31  
32  /**
33   * Goal which creates Spritesheets from given css and image files Explanation of every variable is taken directly from
34   * the smartsprites documentation (https://www.w3schools.com/css/css_image_sprites.asp)
35   * <p>
36   * For further information on the use of smartsprites please refer to:
37   * https://www.w3schools.com/css/css_image_sprites.asp
38   */
39  @Mojo(name = "smartsprites", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, requiresProject = false, threadSafe = true)
40  public class SmartSpritesMojo extends AbstractMojo {
41  
42      /** The static root directory mode. */
43      private static final String ROOT_DIR_MODE = "rootDirMode";
44  
45      /** The static css files mode. */
46      private static final String CSS_FILES_MODE = "cssFilesMode";
47  
48      /**
49       * Directory in which SmartSprites processing should be done, required if css-files not specified or if
50       * output-dir-path specified, default: not specified.
51       * <p>
52       * SmartSprites will process all files with the *.css extension found in root-dir-path or any subdirectory of it.
53       * For more fine-grained control over the processed CSS files, see the css-files option.
54       * <p>
55       * If the provided root directory path is relative, it will be resolved against the current working directory.
56       */
57      @Parameter(defaultValue = "${project.basedir}/src/main/sprites", property = "rootDirPath")
58      private File rootDirPath;
59  
60      /**
61       * Paths of CSS files to process, required if root-dir-path not specified, default: not specified.
62       * <p>
63       * SmartSprites will process all CSS files listed using this option. If css-files is to be used together with
64       * output-dir-path, root-dir-path must also be specified so that SmartSprites can preserve the directory structure
65       * found in root-dir-path in output-dir-path. If root-dir-path and output-dir-path are used, css-files outside of
66       * root-dir-path will be ignored.
67       * <p>
68       * Relative CSS file paths provided using this option will be resolved against the current working directory. Please
69       * note that SmartSprites will not expand any wildcards (like style/*.css), it assumes the expansion is performed at
70       * the command line shell level.
71       * <p>
72       * To specify the list of CSS files to process in the SmartSprites Ant task, use one or more nested fileset
73       * elements. Please see the build.xml file in the distribution archive for an example.
74       */
75      @Parameter(property = "cssFiles")
76      private List<File> cssFiles;
77  
78      /**
79       * Output directory for processed CSS files and CSS-relative sprite images, optional, default: not specified.
80       * <p>
81       * If a non-empty output-dir-path is specified, a non-empty root-dir-path must also be provided. The directory
82       * structure relative to the root-dir-path will be preserved in the output directory. E.g. if CSS files are
83       * contained in the css/base directory of root-dir-path, the processed results will be written to
84       * output-dir-path/css/base. Also, CSS-relative sprite images will be written to the output directory. Sprite images
85       * with document-root-relative URLs will be written relative to the document-root-dir-path. If the output-dir-path
86       * directory does not exist, it will be created. If the provided output directory path is relative, it will be
87       * resolved against the current working directory.
88       * <p>
89       * You can leave the output-dir-path empty, in which case the CSS files will be written next to the original CSS
90       * files with the css-file-suffix, and sprite images will be written relative to the original CSS files. If you are
91       * using a non-empty output-dir-path, you might want to use an empty css-file-suffix.
92       */
93      @Parameter(defaultValue = "${project.build.directory}/generated-resources/META-INF/resources/spritesheets", property = "outPutDirPath")
94      private File outputDirPath;
95  
96      /**
97       * Document root path for document-root-relative (starting with /) image urls in CSS, optional, default: not
98       * specified.
99       * <p>
100      * All document-root-relative image and sprite URLs will be taken relative to document-root-dir-path. Also
101      * document-root-relative sprite URLs will be written relative to document-root-dir-path. You can leave this
102      * property empty if your CSS uses only CSS-relative image URLs. If the provided document root directory path is
103      * relative, it will be resolved against the current working directory.
104      */
105     @Parameter(property = "documentRootDirPath")
106     private File documentRootDirPath;
107 
108     /**
109      * Message logging level, optional, default: WARN.
110      * <p>
111      * Messages less important than log-level will not be shown.
112      * <p>
113      * SmartSprites has 5 levels of log messages (in the increasing order of importance).
114      * <p>
115      * INFO: information messages, can be safely ignored.
116      * <p>
117      * DEPRECATION: Notice messages related to deprecated features.
118      * <p>
119      * WARN: Warning messages, ignoring can lead to the converted designs looking broken.
120      * <p>
121      * ERROR: Error messages, SmartSprites cannot perform processing.
122      * <p>
123      * STATUS: Status messages displayed at the end of processing.
124      */
125     @Parameter(defaultValue = "WARN", property = "logLevel")
126     private String logLevel;
127 
128     /**
129      * Color depth of sprites in the PNG format, optional, default: AUTO. AUTO: PNG color depth will be chosen
130      * automatically. If the sprite image does not contain partial transparencies (alpha channel) and has less than 256
131      * colors, PNG8 will be used. Otherwise, the sprite will be saved in PNG24. DIRECT: PNG sprites will always be saved
132      * in the PNG24 format. INDEXED: PNG sprites will always be saved in the PNG8 format. If the sprite image contains
133      * partial transparencies (alpha channel) or has more than 255 colors, image quality loss may occur and appropriate
134      * warnings will be issued. See also the sprite-matte-color property.
135      */
136     @Parameter(defaultValue = "AUTO", property = "spritePngDepth")
137     private String spritePngDepth;
138 
139     /**
140      * The encoding to assume for input and output CSS files, default: UTF-8. For the list of allowed values, please see
141      * the list of encodings supported in Java.
142      */
143     @Parameter(defaultValue = "UTF-8", property = "cssFileEncoding")
144     private String cssFileEncoding;
145 
146     /**
147      * Suffix to be appended to the processed CSS file name, optional, default: .
148      */
149     @Parameter(defaultValue = "", property = "cssFileSuffix")
150     private String cssFileSuffix;
151 
152     /**
153      * To make sure the different modes are working correctly a mode has to be specified. Modes: - rootDirMode (rootDir
154      * is set, optionally outputdir! No css-Files!) - cssFilesMode (only cssFiles is set, no rootDir, no OutputDir) -
155      * cssFilesWithOutputDirMode (cssFiles are set, as well as rootDir and outputDir)
156      */
157     @Parameter(defaultValue = SmartSpritesMojo.ROOT_DIR_MODE, property = "workingMode")
158     private String workingMode;
159 
160     /**
161      * To enable skipping run of plugin.
162      */
163     @Parameter(defaultValue = "false", alias = "skip", property = "skip")
164     private boolean skip;
165 
166     @Override
167     public void execute() throws MojoExecutionException, MojoFailureException {
168         // Check if plugin run should be skipped
169         if (this.skip) {
170             this.getLog().info("Smartsprites is skipped");
171             return;
172         }
173 
174         // Check for the correct log-level
175         Message.MessageLevel msgLogLevel;
176         try {
177             msgLogLevel = Message.MessageLevel.valueOf(this.logLevel.toUpperCase(Locale.ENGLISH));
178         } catch (final Exception e) {
179             throw new MojoExecutionException("LogLevel Error - please select a valid value! (INFO, WARN) ", e);
180         }
181 
182         // Check for the correct PNG-Depth
183         SmartSpritesParameters.PngDepth pngDepth;
184         try {
185             pngDepth = SmartSpritesParameters.PngDepth.valueOf(this.spritePngDepth);
186         } catch (final Exception e) {
187             throw new MojoExecutionException("PngDepth Error - please select a valid value! (AUTO, DIRECT, INDEXED)  ",
188                     e);
189         }
190 
191         // Make sure we are using only correct workingModes
192         if (!(this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE)
193                 || this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)
194                 || this.workingMode.equals("cssFilesWithOutputDirMode"))) {
195             throw new MojoExecutionException(
196                     "workingMode Error - plese select a valid value! (rootDirMode, cssFilesMode, cssFilesWithOutputDirMode)");
197         }
198 
199         // Variables for paths
200         String rootDirPathTemp = "";
201         String outputDirPathTemp = "";
202         String documentRootDirPathTemp = "";
203         List<String> cssFilesTemp = new ArrayList<>();
204 
205         // Check if we should set cssFiles to null, or if we are in a working mode
206         // where we want to use cssFiles
207         if (!this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE)) {
208             for (final File cssFile : this.cssFiles) {
209                 if (cssFile.exists()) {
210                     cssFilesTemp.add(cssFile.toString());
211                 } else {
212                     throw new MojoExecutionException("The following css-file doesn't exist: " + cssFile);
213                 }
214             }
215         } else {
216             this.cssFiles = null;
217         }
218 
219         // Check if the folders exist or are at least configured
220         if (this.rootDirPath != null && !this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) {
221             if (this.rootDirPath.exists()) {
222                 rootDirPathTemp = this.rootDirPath.toString();
223             } else {
224                 throw new MojoExecutionException("The rootDirPath doesn't exist. " + this.rootDirPath.toString());
225             }
226         }
227 
228         // Check if the outputDirPath is set
229         if (this.outputDirPath != null && !this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) {
230             outputDirPathTemp = this.outputDirPath.toString();
231         }
232 
233         // Check if the documentRootDirPath is set
234         if (this.documentRootDirPath != null) {
235             documentRootDirPathTemp = this.documentRootDirPath.toString();
236         }
237 
238         // Determine which workingMode we are in and check if all conditions for that
239         // specific mode are matched
240         if (this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE)) {
241             if (this.rootDirPath == null) {
242                 throw new MojoExecutionException("Please configure a rootDirPath.");
243             }
244             cssFilesTemp = null;
245         } else if (this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) {
246             if (this.cssFiles == null) {
247                 throw new MojoExecutionException("Please configure some cssFiles.");
248             }
249             rootDirPathTemp = null;
250             outputDirPathTemp = null;
251         } else if ((this.workingMode.equals("cssFilesWithOutputDirMode") && this.cssFiles == null)
252                 || this.rootDirPath == null || this.outputDirPath == null) {
253             throw new MojoExecutionException("Please configure cssFiles and/or a rootDirPath and/or an outputDirPath");
254         }
255 
256         // Configure the SmartSpritesParameters for execution
257         SmartSpritesParameters smartParameters = new SmartSpritesParameters(rootDirPathTemp, cssFilesTemp,
258                 outputDirPathTemp, documentRootDirPathTemp, msgLogLevel, this.cssFileSuffix, pngDepth,
259                 this.cssFileEncoding);
260 
261         final MessageLog messageLog = new MessageLog(
262                 new PrintStreamMessageSink(System.out, smartParameters.getLogLevel()));
263         SpriteBuilder spriteBuilder;
264 
265         // Try to execute SmartSprites with the configured parameters and the defined messageLog
266         try {
267             spriteBuilder = new SpriteBuilder(smartParameters, messageLog);
268             spriteBuilder.buildSprites();
269         } catch (final IOException e) {
270             throw new MojoExecutionException("Smartsprites error: ", e);
271         }
272     }
273 
274 }