• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / smartsprites / 266

03 May 2025 07:20PM UTC coverage: 84.619%. Remained the same
266

push

github

hazendaz
Use path over file and fix test that would now break with modern usage (had extra /)

533 of 658 branches covered (81.0%)

Branch coverage included in aggregate %.

1277 of 1481 relevant lines covered (86.23%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.1
/src/main/java/org/carrot2/labs/smartsprites/SmartSpritesParameters.java
1
/*
2
 * SmartSprites Project
3
 *
4
 * Copyright (C) 2007-2009, Stanisław Osiński.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without modification,
8
 * are permitted provided that the following conditions are met:
9
 *
10
 * - Redistributions of  source code must  retain the above  copyright notice, this
11
 *   list of conditions and the following disclaimer.
12
 *
13
 * - Redistributions in binary form must reproduce the above copyright notice, this
14
 *   list of conditions and the following  disclaimer in  the documentation  and/or
15
 *   other materials provided with the distribution.
16
 *
17
 * - Neither the name of the SmartSprites Project nor the names of its contributors
18
 *   may  be used  to endorse  or  promote  products derived   from  this  software
19
 *   without specific prior written permission.
20
 *
21
 * - We kindly request that you include in the end-user documentation provided with
22
 *   the redistribution and/or in the software itself an acknowledgement equivalent
23
 *   to  the  following: "This product includes software developed by the SmartSprites
24
 *   Project."
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  AND
27
 * ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED  TO, THE IMPLIED
28
 * WARRANTIES  OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR  PURPOSE   ARE
29
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE  FOR
30
 * ANY DIRECT, INDIRECT, INCIDENTAL,  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
31
 * (INCLUDING, BUT  NOT LIMITED  TO, PROCUREMENT  OF SUBSTITUTE  GOODS OR SERVICES;
32
 * LOSS OF USE, DATA, OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  ON
33
 * ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY,  OR TORT
34
 * (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY  OUT OF THE USE  OF THIS
35
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 */
37
package org.carrot2.labs.smartsprites;
38

39
import java.io.File;
40
import java.io.IOException;
41
import java.nio.charset.StandardCharsets;
42
import java.nio.file.Path;
43
import java.util.List;
44

45
import org.carrot2.labs.smartsprites.message.Message.MessageLevel;
46
import org.carrot2.labs.smartsprites.message.Message.MessageType;
47
import org.carrot2.labs.smartsprites.message.MessageLog;
48
import org.carrot2.util.FileUtils;
49
import org.carrot2.util.StringUtils;
50
import org.kohsuke.args4j.Argument;
51
import org.kohsuke.args4j.Option;
52

53
/**
54
 * Contains invocation parameters for SmartSprites, provides methods for validating the parameters.
55
 */
56
public final class SmartSpritesParameters {
57
    /**
58
     * Path to the directory that contains the css files to be processed. Directories containing CSS and image files
59
     * must be writable, unless output.dir.path is provided. The root.dir.path can be either absolute, e.g.
60
     * c:/myproject/web or relative to the directory in which this script is run.
61
     */
62
    @Option(name = "--root-dir-path", required = false, metaVar = "DIR")
63
    private String rootDir;
64

65
    /**
66
     * Paths to individual CSS files to process. Either {@link #rootDir} or {@link #cssFiles} must be not empty. If only
67
     * {@link #cssFiles} is not empty, {@link #outputDir} must be blank. If both {@link #rootDir} and {@link #cssFiles}
68
     * are not empty, {@link #outputDir} is supported but only {@link #cssFiles} from {@link #rootDir} are processed.
69
     */
70
    @Argument(metaVar = "CSS-FILES")
71
    @Option(name = "--css-files", required = false, metaVar = "FILES")
72
    private List<String> cssFiles;
73

74
    /**
75
     * Output directory for processed CSS files and CSS-relative sprite images. The directory structure relative to
76
     * root.dir.path will be preserved in the output directory. E.g. if CSS files are contained in the css/base
77
     * directory of root.dir.path, the processed results will be written to output.dir.path/css/base. Also, CSS-relative
78
     * sprite images will be written to the output directory. Sprite images with document-root-relative URLs will be
79
     * written relative to the document.root.dir.path.
80
     * <p>
81
     * If the output.dir.path directory does not exist, it will be created.
82
     * <p>
83
     * You can leave this property empty, in which case the CSS files will be written next to the original CSS files
84
     * with css.file.suffix, and sprite images will be written relative to CSS files.
85
     * <p>
86
     * If you are using a non-empty output.dir.path, you might want to use an empty css.file.suffix.
87
     */
88
    @Option(name = "--output-dir-path", metaVar = "DIR")
89
    private String outputDir;
90

91
    /**
92
     * Document root path for document-root-relative (starting with '/') image urls in CSS. All such image URLs will be
93
     * taken relative to document.root.dir.path. Also document-root-relative sprite URLs will be written relative to
94
     * document.root.dir.path. You can leave this property empty if your CSS uses only CSS-relative image URLs. *
95
     */
96
    @Option(name = "--document-root-dir-path", metaVar = "DIR")
97
    private String documentRootDir;
98

99
    /**
100
     * Message logging level. If you're getting lots of INFO messages and want to see only warnings, set this option to
101
     * WARN.
102
     */
103
    @Option(name = "--log-level")
104
    private MessageLevel logLevel;
105

106
    /**
107
     * The encoding to assume for input and output CSS files.
108
     */
109
    @Option(name = "--css-file-encoding")
110
    private String cssFileEncoding;
111

112
    /**
113
     * Suffix to be appended to the processed CSS file name.
114
     */
115
    @Option(name = "--css-file-suffix")
116
    private String cssFileSuffix;
117

118
    /**
119
     * The required depth of the generated PNG sprites.
120
     */
121
    @Option(name = "--sprite-png-depth")
122
    private PngDepth spritePngDepth;
123

124
    /**
125
     * If <code>true</code>, SmartSprites will generate the sprite directive indicating that the image is a sprite
126
     * image.
127
     */
128
    @Option(name = "--mark-sprite-images")
129
    private boolean markSpriteImages;
130

131
    /** The default suffix to be added to the generated CSS files. */
132
    public static final String DEFAULT_CSS_FILE_SUFFIX = "-sprite";
133

134
    /** By default, we use full color only when necessary. */
135
    public static final PngDepth DEFAULT_SPRITE_PNG_DEPTH = PngDepth.AUTO;
1✔
136

137
    /** By default, we'll assume CSS files are UTF-8 encoded. */
138
    public static final String DEFAULT_CSS_FILE_ENCODING = StandardCharsets.UTF_8.name();
1✔
139

140
    /** The default logging level. */
141
    public static final MessageLevel DEFAULT_LOGGING_LEVEL = MessageLevel.INFO;
1✔
142

143
    /** By default, we don't generate sprite directive in output css. */
144
    public static final boolean DEFAULT_MARK_SPRITE_IMAGES = false;
145

146
    /**
147
     * The Enum PngDepth.
148
     */
149
    public enum PngDepth {
1✔
150

151
        /** The auto. */
152
        AUTO,
1✔
153
        /** The indexed. */
154
        INDEXED,
1✔
155
        /** The direct. */
156
        DIRECT;
1✔
157
    }
158

159
    /**
160
     * Creates the parameters with default options and null root dir, before root dir is set, the parameters are
161
     * invalid.
162
     */
163
    public SmartSpritesParameters() {
164
        this(null);
×
165
    }
×
166

167
    /**
168
     * Creates the parameters with most default values.
169
     *
170
     * @param rootDir
171
     *            the root dir
172
     */
173
    public SmartSpritesParameters(String rootDir) {
174
        this(rootDir, null, null, null, MessageLevel.INFO, DEFAULT_CSS_FILE_SUFFIX, DEFAULT_SPRITE_PNG_DEPTH,
×
175
                DEFAULT_CSS_FILE_ENCODING, DEFAULT_MARK_SPRITE_IMAGES);
176
    }
×
177

178
    /**
179
     * Creates the parameters.
180
     *
181
     * @param rootDir
182
     *            the root dir
183
     * @param cssFiles
184
     *            the css files
185
     * @param outputDir
186
     *            the output dir
187
     * @param documentRootDir
188
     *            the document root dir
189
     * @param logLevel
190
     *            the log level
191
     * @param cssFileSuffix
192
     *            the css file suffix
193
     * @param spritePngDepth
194
     *            the sprite png depth
195
     * @param cssEncoding
196
     *            the css encoding
197
     */
198
    public SmartSpritesParameters(String rootDir, List<String> cssFiles, String outputDir, String documentRootDir,
199
            MessageLevel logLevel, String cssFileSuffix, PngDepth spritePngDepth, String cssEncoding) {
200
        this(rootDir, cssFiles, outputDir, documentRootDir, logLevel, cssFileSuffix, spritePngDepth, cssEncoding,
1✔
201
                DEFAULT_MARK_SPRITE_IMAGES);
202
    }
1✔
203

204
    /**
205
     * Creates the parameters.
206
     *
207
     * @param rootDir
208
     *            the root dir
209
     * @param cssFiles
210
     *            the css files
211
     * @param outputDir
212
     *            the output dir
213
     * @param documentRootDir
214
     *            the document root dir
215
     * @param logLevel
216
     *            the log level
217
     * @param cssFileSuffix
218
     *            the css file suffix
219
     * @param spritePngDepth
220
     *            the sprite png depth
221
     * @param cssEncoding
222
     *            the css encoding
223
     * @param markSpriteImages
224
     *            the mark sprite images
225
     */
226
    public SmartSpritesParameters(String rootDir, List<String> cssFiles, String outputDir, String documentRootDir,
227
            MessageLevel logLevel, String cssFileSuffix, PngDepth spritePngDepth, String cssEncoding,
228
            boolean markSpriteImages) {
1✔
229
        this.rootDir = rootDir;
1✔
230
        this.cssFiles = cssFiles;
1✔
231
        this.outputDir = outputDir;
1✔
232
        this.documentRootDir = documentRootDir;
1✔
233
        this.logLevel = logLevel;
1✔
234
        this.cssFileEncoding = cssEncoding;
1✔
235
        this.cssFileSuffix = getCssFileSuffix(cssFileSuffix);
1✔
236
        this.spritePngDepth = spritePngDepth;
1✔
237
        this.markSpriteImages = markSpriteImages;
1✔
238
    }
1✔
239

240
    /**
241
     * Validates the provided parameters. All resource paths are resolved against the local file system.
242
     *
243
     * @param log
244
     *            the log
245
     *
246
     * @return <code>true</code> if the parameters are valid
247
     */
248
    public boolean validate(MessageLog log) {
249
        boolean valid = true;
1✔
250

251
        // Either root dir or css files are required
252
        if (!hasRootDir() && !hasCssFiles()) {
1✔
253
            log.error(MessageType.EITHER_ROOT_DIR_OR_CSS_FILES_IS_REQUIRED);
1✔
254
            return false;
1✔
255
        }
256

257
        // If there is no output dir, we can't have both root dir or css files
258
        if (!hasOutputDir() && hasRootDir() && hasCssFiles()) {
1✔
259
            log.error(MessageType.ROOT_DIR_AND_CSS_FILES_CANNOT_BE_BOTH_SPECIFIED_UNLESS_WITH_OUTPUT_DIR);
1✔
260
            return false;
1✔
261
        }
262

263
        // Check root dir if provided
264
        if (hasRootDir()) {
1✔
265
            final File directory = FileUtils.getCanonicalOrAbsoluteFile(this.rootDir);
1✔
266
            if (!directory.exists() || !directory.isDirectory()) {
1!
267
                log.error(MessageType.ROOT_DIR_DOES_NOT_EXIST_OR_IS_NOT_DIRECTORY, this.rootDir);
1✔
268
                valid = false;
1✔
269
            }
270
        }
271

272
        // Check output dir if provided
273
        if (hasOutputDir()) {
1✔
274
            // For output dir, we need root dir
275
            if (!hasRootDir()) {
1✔
276
                log.error(MessageType.ROOT_DIR_IS_REQUIRED_FOR_OUTPUT_DIR);
1✔
277
                return false;
1✔
278
            }
279

280
            final File directory = FileUtils.getCanonicalOrAbsoluteFile(this.outputDir);
1✔
281
            if (directory.exists() && !directory.isDirectory()) {
1✔
282
                log.error(MessageType.OUTPUT_DIR_IS_NOT_DIRECTORY, this.outputDir);
1✔
283
                valid = false;
1✔
284
            }
285
        }
286

287
        if (!hasOutputDir() && StringUtils.isBlank(cssFileSuffix)) {
1✔
288
            log.error(MessageType.CSS_FILE_SUFFIX_IS_REQUIRED_IF_NO_OUTPUT_DIR);
1✔
289
            valid = false;
1✔
290
        }
291

292
        if (hasDocumentRootDir()) {
1✔
293
            final File directory = FileUtils.getCanonicalOrAbsoluteFile(this.documentRootDir);
1✔
294
            if (!directory.exists() || !directory.isDirectory()) {
1✔
295
                log.error(MessageType.DOCUMENT_ROOT_DIR_DOES_NOT_EXIST_OR_IS_NOT_DIRECTORY, this.documentRootDir);
1✔
296
                valid = false;
1✔
297
            }
298
        }
299

300
        return valid;
1✔
301
    }
302

303
    /**
304
     * Gets the css file suffix.
305
     *
306
     * @param suffix
307
     *            the suffix
308
     *
309
     * @return the css file suffix
310
     */
311
    private String getCssFileSuffix(String suffix) {
312
        if (suffix != null) {
1✔
313
            return suffix;
1✔
314
        }
315
        if (!hasOutputDir()) {
1✔
316
            // If there is no output dir, we must have some suffix
317
            return DEFAULT_CSS_FILE_SUFFIX;
1✔
318
        }
319
        // If we have an output dir, we can have an empty suffix
320
        return "";
1✔
321
    }
322

323
    /**
324
     * Gets the root dir.
325
     *
326
     * @return the root dir
327
     */
328
    public String getRootDir() {
329
        return rootDir;
1✔
330
    }
331

332
    /**
333
     * Gets the root dir file.
334
     *
335
     * @return the root dir file
336
     *
337
     * @throws IOException
338
     *             Signals that an I/O exception has occurred.
339
     */
340
    public File getRootDirFile() throws IOException {
341
        return rootDir.startsWith("..") ? Path.of(rootDir).toFile().getCanonicalFile() : Path.of(rootDir).toFile();
1!
342
    }
343

344
    /**
345
     * Checks for root dir.
346
     *
347
     * @return true, if successful
348
     */
349
    public boolean hasRootDir() {
350
        return StringUtils.isNotBlank(rootDir);
1✔
351
    }
352

353
    /**
354
     * Gets the css files.
355
     *
356
     * @return the css files
357
     */
358
    public List<String> getCssFiles() {
359
        return cssFiles;
1✔
360
    }
361

362
    /**
363
     * Checks for css files.
364
     *
365
     * @return true, if successful
366
     */
367
    public boolean hasCssFiles() {
368
        return cssFiles != null && !cssFiles.isEmpty();
1!
369
    }
370

371
    /**
372
     * Gets the output dir.
373
     *
374
     * @return the output dir
375
     */
376
    public String getOutputDir() {
377
        return outputDir;
1✔
378
    }
379

380
    /**
381
     * Checks for output dir.
382
     *
383
     * @return true, if successful
384
     */
385
    public boolean hasOutputDir() {
386
        return StringUtils.isNotBlank(outputDir);
1✔
387
    }
388

389
    /**
390
     * Gets the document root dir.
391
     *
392
     * @return the document root dir
393
     */
394
    public String getDocumentRootDir() {
395
        return documentRootDir;
1✔
396
    }
397

398
    /**
399
     * Checks for document root dir.
400
     *
401
     * @return true, if successful
402
     */
403
    public boolean hasDocumentRootDir() {
404
        return StringUtils.isNotBlank(documentRootDir);
1✔
405
    }
406

407
    /**
408
     * Gets the log level.
409
     *
410
     * @return the log level
411
     */
412
    public MessageLevel getLogLevel() {
413
        return logLevel;
×
414
    }
415

416
    /**
417
     * Gets the css file suffix.
418
     *
419
     * @return the css file suffix
420
     */
421
    public String getCssFileSuffix() {
422
        return cssFileSuffix;
1✔
423
    }
424

425
    /**
426
     * Gets the sprite png depth.
427
     *
428
     * @return the sprite png depth
429
     */
430
    public PngDepth getSpritePngDepth() {
431
        return spritePngDepth;
1✔
432
    }
433

434
    /**
435
     * Checks if is mark sprite images.
436
     *
437
     * @return true, if is mark sprite images
438
     */
439
    public boolean isMarkSpriteImages() {
440
        return markSpriteImages;
1✔
441
    }
442

443
    /**
444
     * Gets the css file encoding.
445
     *
446
     * @return the css file encoding
447
     */
448
    public String getCssFileEncoding() {
449
        return cssFileEncoding;
1✔
450
    }
451
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc