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

hazendaz / smartsprites / 555

21 May 2026 03:26PM UTC coverage: 87.799%. Remained the same
555

push

github

hazendaz
[tests] Fix tests that were looking at size of original license before spdx change

557 of 670 branches covered (83.13%)

Branch coverage included in aggregate %.

1350 of 1502 relevant lines covered (89.88%)

0.9 hits per line

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

91.6
/src/main/java/org/carrot2/labs/smartsprites/SpriteDirectiveOccurrenceCollector.java
1
/*
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2021-2026 Hazendaz
6
 * Copyright (C) 2007-2009, Stanisław Osiński.
7
 */
8
package org.carrot2.labs.smartsprites;
9

10
import com.google.common.collect.LinkedListMultimap;
11
import com.google.common.collect.Multimap;
12

13
import java.io.BufferedReader;
14
import java.io.IOException;
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.LinkedHashMap;
18
import java.util.Map;
19
import java.util.regex.Matcher;
20
import java.util.regex.Pattern;
21

22
import org.carrot2.labs.smartsprites.css.CssProperty;
23
import org.carrot2.labs.smartsprites.css.CssSyntaxUtils;
24
import org.carrot2.labs.smartsprites.message.Message.MessageType;
25
import org.carrot2.labs.smartsprites.message.MessageLog;
26
import org.carrot2.labs.smartsprites.resource.ResourceHandler;
27

28
/**
29
 * Methods for collecting SmartSprites directives from CSS files.
30
 */
31
public class SpriteDirectiveOccurrenceCollector {
32

33
    /** A regular expression for extracting sprite image directives. */
34
    private static final Pattern SPRITE_IMAGE_DIRECTIVE = Pattern.compile("/\\*+\\s+(sprite:[^*]*)\\*+/");
1✔
35

36
    /** A regular expression for extracting sprite reference directives. */
37
    private static final Pattern SPRITE_REFERENCE_DIRECTIVE = Pattern.compile("/\\*+\\s+(sprite-ref:[^*]*)\\*+/");
1✔
38

39
    /** This builder's message log. */
40
    private final MessageLog messageLog;
41

42
    /** The resource handler. */
43
    private final ResourceHandler resourceHandler;
44

45
    /**
46
     * Creates a {@link SpriteDirectiveOccurrenceCollector} with the provided parameters and log.
47
     *
48
     * @param messageLog
49
     *            the message log
50
     * @param resourceHandler
51
     *            the resource handler
52
     */
53
    SpriteDirectiveOccurrenceCollector(MessageLog messageLog, ResourceHandler resourceHandler) {
1✔
54
        this.resourceHandler = resourceHandler;
1✔
55
        this.messageLog = messageLog;
1✔
56
    }
1✔
57

58
    /**
59
     * Collects {@link SpriteImageOccurrence}s from a single CSS file.
60
     *
61
     * @param cssFile
62
     *            the css file
63
     *
64
     * @return the collection
65
     *
66
     * @throws IOException
67
     *             Signals that an I/O exception has occurred.
68
     */
69
    Collection<SpriteImageOccurrence> collectSpriteImageOccurrences(String cssFile) throws IOException {
70
        final Collection<SpriteImageOccurrence> occurrences = new ArrayList<>();
1✔
71
        messageLog.setCssFile(null);
1✔
72
        messageLog.info(MessageType.READING_SPRITE_IMAGE_DIRECTIVES, cssFile);
1✔
73
        messageLog.setCssFile(cssFile);
1✔
74

75
        int lineNumber = -1;
1✔
76
        String line;
77

78
        try (BufferedReader reader = new BufferedReader(resourceHandler.getResourceAsReader(cssFile))) {
1✔
79
            while ((line = reader.readLine()) != null) {
1✔
80
                lineNumber++;
1✔
81
                messageLog.setLine(lineNumber);
1✔
82

83
                final String spriteImageDirectiveString = extractSpriteImageDirectiveString(line);
1✔
84
                if (spriteImageDirectiveString == null) {
1✔
85
                    continue;
1✔
86
                }
87

88
                final SpriteImageDirective directive = SpriteImageDirective.parse(spriteImageDirectiveString,
1✔
89
                        messageLog);
90
                if (directive == null) {
1!
91
                    continue;
×
92
                }
93

94
                occurrences.add(new SpriteImageOccurrence(directive, cssFile, lineNumber));
1✔
95
            }
1✔
96
        }
97

98
        return occurrences;
1✔
99
    }
100

101
    /**
102
     * Collects {@link SpriteReferenceOccurrence}s from a single CSS file.
103
     *
104
     * @param cssFile
105
     *            the css file
106
     * @param spriteImageDirectives
107
     *            the sprite image directives
108
     *
109
     * @return the collection
110
     *
111
     * @throws IOException
112
     *             Signals that an I/O exception has occurred.
113
     */
114
    Collection<SpriteReferenceOccurrence> collectSpriteReferenceOccurrences(String cssFile,
115
            Map<String, SpriteImageDirective> spriteImageDirectives) throws IOException {
116
        final Collection<SpriteReferenceOccurrence> directives = new ArrayList<>();
1✔
117

118
        messageLog.setCssFile(null);
1✔
119
        messageLog.info(MessageType.READING_SPRITE_REFERENCE_DIRECTIVES, cssFile);
1✔
120
        messageLog.setCssFile(cssFile);
1✔
121

122
        int lineNumber = -1;
1✔
123
        String line;
124

125
        try (BufferedReader reader = new BufferedReader(resourceHandler.getResourceAsReader(cssFile))) {
1✔
126
            while ((line = reader.readLine()) != null) {
1✔
127
                lineNumber++;
1✔
128
                messageLog.setLine(lineNumber);
1✔
129

130
                final String directiveString = extractSpriteReferenceDirectiveString(line);
1✔
131
                if (directiveString == null) {
1✔
132
                    continue;
1✔
133
                }
134

135
                final CssProperty backgroundProperty = extractSpriteReferenceCssProperty(line);
1✔
136
                final String imageUrl = CssSyntaxUtils.unpackUrl(backgroundProperty.value, messageLog);
1✔
137
                if (imageUrl == null) {
1!
138
                    continue;
×
139
                }
140

141
                final SpriteReferenceDirective directive = SpriteReferenceDirective.parse(directiveString,
1✔
142
                        spriteImageDirectives, messageLog);
143
                if (directive == null) {
1!
144
                    continue;
×
145
                }
146

147
                directives.add(new SpriteReferenceOccurrence(directive, imageUrl, cssFile, lineNumber,
1✔
148
                        backgroundProperty.important));
149
            }
1✔
150
        }
151

152
        return directives;
1✔
153
    }
154

155
    /**
156
     * Collects {@link SpriteImageOccurrence}s from the provided CSS files.
157
     *
158
     * @param filePaths
159
     *            the file paths
160
     *
161
     * @return the multimap
162
     *
163
     * @throws IOException
164
     *             Signals that an I/O exception has occurred.
165
     */
166
    Multimap<String, SpriteImageOccurrence> collectSpriteImageOccurrences(Collection<String> filePaths)
167
            throws IOException {
168
        final Multimap<String, SpriteImageOccurrence> spriteImageOccurrencesByFile = LinkedListMultimap.create();
1✔
169
        for (final String cssFile : filePaths) {
1✔
170
            messageLog.setCssFile(cssFile);
1✔
171

172
            final Collection<SpriteImageOccurrence> spriteImageOccurrences = collectSpriteImageOccurrences(cssFile);
1✔
173

174
            spriteImageOccurrencesByFile.putAll(cssFile, spriteImageOccurrences);
1✔
175
        }
1✔
176
        return spriteImageOccurrencesByFile;
1✔
177
    }
178

179
    /**
180
     * Collects {@link SpriteReferenceOccurrence}s from the provided CSS files.
181
     *
182
     * @param files
183
     *            the files
184
     * @param spriteImageDirectivesBySpriteId
185
     *            the sprite image directives by sprite id
186
     *
187
     * @return the multimap
188
     *
189
     * @throws IOException
190
     *             Signals that an I/O exception has occurred.
191
     */
192
    Multimap<String, SpriteReferenceOccurrence> collectSpriteReferenceOccurrences(Collection<String> files,
193
            final Map<String, SpriteImageDirective> spriteImageDirectivesBySpriteId) throws IOException {
194
        final Multimap<String, SpriteReferenceOccurrence> spriteEntriesByFile = LinkedListMultimap.create();
1✔
195
        for (final String cssFile : files) {
1✔
196
            messageLog.setCssFile(cssFile);
1✔
197

198
            final Collection<SpriteReferenceOccurrence> spriteReferenceOccurrences = collectSpriteReferenceOccurrences(
1✔
199
                    cssFile, spriteImageDirectivesBySpriteId);
200

201
            spriteEntriesByFile.putAll(cssFile, spriteReferenceOccurrences);
1✔
202
        }
1✔
203
        return spriteEntriesByFile;
1✔
204
    }
205

206
    /**
207
     * Groups {@link SpriteImageDirective}s by sprite id.
208
     *
209
     * @param spriteImageOccurrencesByFile
210
     *            the sprite image occurrences by file
211
     *
212
     * @return the map
213
     */
214
    Map<String, SpriteImageOccurrence> mergeSpriteImageOccurrences(
215
            final Multimap<String, SpriteImageOccurrence> spriteImageOccurrencesByFile) {
216
        final Map<String, SpriteImageOccurrence> spriteImageDirectivesBySpriteId = new LinkedHashMap<>();
1✔
217
        for (final Map.Entry<String, SpriteImageOccurrence> entry : spriteImageOccurrencesByFile.entries()) {
1✔
218
            final String cssFile = entry.getKey();
1✔
219
            final SpriteImageOccurrence spriteImageOccurrence = entry.getValue();
1✔
220

221
            messageLog.setCssFile(cssFile);
1✔
222

223
            // Add to the global map, checking for duplicates
224
            if (spriteImageDirectivesBySpriteId.containsKey(spriteImageOccurrence.spriteImageDirective.spriteId)) {
1!
225
                messageLog.warning(MessageType.IGNORING_SPRITE_IMAGE_REDEFINITION);
×
226
            } else {
227
                spriteImageDirectivesBySpriteId.put(spriteImageOccurrence.spriteImageDirective.spriteId,
1✔
228
                        spriteImageOccurrence);
229
            }
230
        }
1✔
231
        return spriteImageDirectivesBySpriteId;
1✔
232
    }
233

234
    /**
235
     * Groups {@link SpriteReferenceOccurrence}s by sprite id.
236
     *
237
     * @param spriteEntriesByFile
238
     *            the sprite entries by file
239
     *
240
     * @return the multimap
241
     */
242
    static Multimap<String, SpriteReferenceOccurrence> mergeSpriteReferenceOccurrences(
243
            final Multimap<String, SpriteReferenceOccurrence> spriteEntriesByFile) {
244
        final Multimap<String, SpriteReferenceOccurrence> spriteReferenceOccurrencesBySpriteId = LinkedListMultimap
245
                .create();
1✔
246
        for (final SpriteReferenceOccurrence spriteReferenceOccurrence : spriteEntriesByFile.values()) {
1✔
247
            spriteReferenceOccurrencesBySpriteId.put(spriteReferenceOccurrence.spriteReferenceDirective.spriteRef,
1✔
248
                    spriteReferenceOccurrence);
249
        }
1✔
250
        return spriteReferenceOccurrencesBySpriteId;
1✔
251
    }
252

253
    /**
254
     * Extract the sprite image directive string to be parsed.
255
     *
256
     * @param cssLine
257
     *            the css line
258
     *
259
     * @return the string
260
     */
261
    static String extractSpriteImageDirectiveString(String cssLine) {
262
        final Matcher matcher = SPRITE_IMAGE_DIRECTIVE.matcher(cssLine);
1✔
263

264
        if (matcher.find()) {
1✔
265
            return matcher.group(1).trim();
1✔
266
        }
267
        return null;
1✔
268
    }
269

270
    /**
271
     * Extract the sprite reference directive string to be parsed.
272
     *
273
     * @param css
274
     *            the css
275
     *
276
     * @return the string
277
     */
278
    static String extractSpriteReferenceDirectiveString(String css) {
279
        final Matcher matcher = SPRITE_REFERENCE_DIRECTIVE.matcher(css);
1✔
280

281
        if (matcher.find()) {
1✔
282
            return matcher.group(1).trim();
1✔
283
        }
284
        return null;
1✔
285
    }
286

287
    /**
288
     * Extract the url to the image to be added to a sprite.
289
     *
290
     * @param css
291
     *            the css
292
     *
293
     * @return the css property
294
     */
295
    CssProperty extractSpriteReferenceCssProperty(String css) {
296
        final Matcher matcher = SPRITE_REFERENCE_DIRECTIVE.matcher(css);
1✔
297

298
        // Remove the directive
299
        final String noDirective = matcher.replaceAll("").trim();
1✔
300

301
        final Collection<CssProperty> rules = CssSyntaxUtils.extractProperties(noDirective);
1✔
302
        if (rules.isEmpty()) {
1!
303
            messageLog.warning(MessageType.NO_BACKGROUND_IMAGE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE, css);
×
304
            return null;
×
305
        }
306

307
        if (rules.size() > 1) {
1✔
308
            messageLog.warning(MessageType.MORE_THAN_ONE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE, css);
1✔
309
            return null;
1✔
310
        }
311

312
        final CssProperty backgroundImageRule = rules.iterator().next();
1✔
313
        if (!backgroundImageRule.rule.equals("background-image")) {
1✔
314
            messageLog.warning(MessageType.NO_BACKGROUND_IMAGE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE, css);
1✔
315
            return null;
1✔
316
        }
317

318
        return backgroundImageRule;
1✔
319
    }
320
}
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