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

hazendaz / smartsprites / #43

11 Nov 2023 07:47PM UTC coverage: 88.431%. Remained the same
#43

push

github

hazendaz
[tests] Fix tests given they expected order and now license on everything for +36

1437 of 1625 relevant lines covered (88.43%)

0.88 hits per line

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

94.39
/src/main/java/org/carrot2/labs/smartsprites/SpriteDirectiveOccurrenceCollector.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.BufferedReader;
40
import java.io.IOException;
41
import java.util.ArrayList;
42
import java.util.Collection;
43
import java.util.LinkedHashMap;
44
import java.util.Map;
45
import java.util.regex.Matcher;
46
import java.util.regex.Pattern;
47

48
import org.carrot2.labs.smartsprites.css.CssProperty;
49
import org.carrot2.labs.smartsprites.css.CssSyntaxUtils;
50
import org.carrot2.labs.smartsprites.message.Message.MessageType;
51
import org.carrot2.labs.smartsprites.message.MessageLog;
52
import org.carrot2.labs.smartsprites.resource.ResourceHandler;
53

54
import com.google.common.collect.LinkedListMultimap;
55
import com.google.common.collect.Multimap;
56
import com.google.common.io.Closeables;
57

58
/**
59
 * Methods for collecting SmartSprites directives from CSS files.
60
 */
61
public class SpriteDirectiveOccurrenceCollector
62
{
63
    /** A regular expression for extracting sprite image directives */
64
    private static final Pattern SPRITE_IMAGE_DIRECTIVE = Pattern
1✔
65
        .compile("/\\*+\\s+(sprite:[^*]*)\\*+/");
1✔
66

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

71
    /** This builder's message log */
72
    private final MessageLog messageLog;
73

74
    /** The resource handler */
75
    private final ResourceHandler resourceHandler;
76

77
    /**
78
     * Creates a {@link SpriteDirectiveOccurrenceCollector} with the provided parameters
79
     * and log.
80
     */
81
    SpriteDirectiveOccurrenceCollector(MessageLog messageLog,
82
        ResourceHandler resourceHandler)
83
    {
1✔
84
        this.resourceHandler = resourceHandler;
1✔
85
        this.messageLog = messageLog;
1✔
86
    }
1✔
87

88
    /**
89
     * Collects {@link SpriteImageOccurrence}s from a single CSS file.
90
     */
91
    Collection<SpriteImageOccurrence> collectSpriteImageOccurrences(String cssFile)
92
        throws IOException
93
    {
94
        final Collection<SpriteImageOccurrence> occurrences = new ArrayList<>();
1✔
95
        final BufferedReader reader = new BufferedReader(resourceHandler
1✔
96
            .getResourceAsReader(cssFile));
1✔
97
        messageLog.setCssFile(null);
1✔
98
        messageLog.info(MessageType.READING_SPRITE_IMAGE_DIRECTIVES, cssFile);
1✔
99
        messageLog.setCssFile(cssFile);
1✔
100

101
        int lineNumber = -1;
1✔
102
        String line;
103

104
        try
105
        {
106
            while ((line = reader.readLine()) != null)
1✔
107
            {
108
                messageLog.setLine(++lineNumber);
1✔
109

110
                final String spriteImageDirectiveString = extractSpriteImageDirectiveString(line);
1✔
111
                if (spriteImageDirectiveString == null)
1✔
112
                {
113
                    continue;
1✔
114
                }
115

116
                final SpriteImageDirective directive = SpriteImageDirective.parse(
1✔
117
                    spriteImageDirectiveString, messageLog);
118
                if (directive == null)
1✔
119
                {
120
                    continue;
×
121
                }
122

123
                occurrences
1✔
124
                    .add(new SpriteImageOccurrence(directive, cssFile, lineNumber));
1✔
125
            }
1✔
126
        }
127
        finally
128
        {
129
            Closeables.close(reader, true);
1✔
130
        }
131

132
        return occurrences;
1✔
133
    }
134

135
    /**
136
     * Collects {@link SpriteReferenceOccurrence}s from a single CSS file.
137
     */
138
    Collection<SpriteReferenceOccurrence> collectSpriteReferenceOccurrences(
139
        String cssFile, Map<String, SpriteImageDirective> spriteImageDirectives)
140
        throws IOException
141
    {
142
        final Collection<SpriteReferenceOccurrence> directives = new ArrayList<>();
1✔
143

144
        final BufferedReader reader = new BufferedReader(resourceHandler
1✔
145
            .getResourceAsReader(cssFile));
1✔
146
        messageLog.setCssFile(null);
1✔
147
        messageLog.info(MessageType.READING_SPRITE_REFERENCE_DIRECTIVES, cssFile);
1✔
148
        messageLog.setCssFile(cssFile);
1✔
149

150
        int lineNumber = -1;
1✔
151
        String line;
152

153
        try
154
        {
155
            while ((line = reader.readLine()) != null)
1✔
156
            {
157
                messageLog.setLine(++lineNumber);
1✔
158

159
                final String directiveString = extractSpriteReferenceDirectiveString(line);
1✔
160
                if (directiveString == null)
1✔
161
                {
162
                    continue;
1✔
163
                }
164

165
                final CssProperty backgroundProperty = extractSpriteReferenceCssProperty(line);
1✔
166
                final String imageUrl = CssSyntaxUtils.unpackUrl(
1✔
167
                    backgroundProperty.value, messageLog);
168
                if (imageUrl == null)
1✔
169
                {
170
                    continue;
×
171
                }
172

173
                final SpriteReferenceDirective directive = SpriteReferenceDirective
1✔
174
                    .parse(directiveString, spriteImageDirectives, messageLog);
1✔
175
                if (directive == null)
1✔
176
                {
177
                    continue;
×
178
                }
179

180
                directives.add(new SpriteReferenceOccurrence(directive, imageUrl,
1✔
181
                    cssFile, lineNumber, backgroundProperty.important));
182
            }
1✔
183
        }
184
        finally
185
        {
186
            Closeables.close(reader, false);
1✔
187
        }
188

189
        return directives;
1✔
190
    }
191

192
    /**
193
     * Collects {@link SpriteImageOccurrence}s from the provided CSS files.
194
     */
195
    Multimap<String, SpriteImageOccurrence> collectSpriteImageOccurrences(
196
        Collection<String> filePaths) throws IOException
197
    {
198
        final Multimap<String, SpriteImageOccurrence> spriteImageOccurrencesByFile = LinkedListMultimap
199
            .create();
1✔
200
        for (final String cssFile : filePaths)
1✔
201
        {
202
            messageLog.setCssFile(cssFile);
1✔
203

204
            final Collection<SpriteImageOccurrence> spriteImageOccurrences = collectSpriteImageOccurrences(cssFile);
1✔
205

206
            spriteImageOccurrencesByFile.putAll(cssFile, spriteImageOccurrences);
1✔
207
        }
1✔
208
        return spriteImageOccurrencesByFile;
1✔
209
    }
210

211
    /**
212
     * Collects {@link SpriteReferenceOccurrence}s from the provided CSS files.
213
     */
214
    Multimap<String, SpriteReferenceOccurrence> collectSpriteReferenceOccurrences(
215
        Collection<String> files,
216
        final Map<String, SpriteImageDirective> spriteImageDirectivesBySpriteId)
217
        throws IOException
218
    {
219
        final Multimap<String, SpriteReferenceOccurrence> spriteEntriesByFile = LinkedListMultimap
220
            .create();
1✔
221
        for (final String cssFile : files)
1✔
222
        {
223
            messageLog.setCssFile(cssFile);
1✔
224

225
            final Collection<SpriteReferenceOccurrence> spriteReferenceOccurrences = collectSpriteReferenceOccurrences(
1✔
226
                cssFile, spriteImageDirectivesBySpriteId);
227

228
            spriteEntriesByFile.putAll(cssFile, spriteReferenceOccurrences);
1✔
229
        }
1✔
230
        return spriteEntriesByFile;
1✔
231
    }
232

233
    /**
234
     * Groups {@link SpriteImageDirective}s by sprite id.
235
     */
236
    Map<String, SpriteImageOccurrence> mergeSpriteImageOccurrences(
237
        final Multimap<String, SpriteImageOccurrence> spriteImageOccurrencesByFile)
238
    {
239
        final Map<String, SpriteImageOccurrence> spriteImageDirectivesBySpriteId = new LinkedHashMap<>();
1✔
240
        for (final Map.Entry<String, SpriteImageOccurrence> entry : spriteImageOccurrencesByFile
1✔
241
            .entries())
1✔
242
        {
243
            final String cssFile = entry.getKey();
1✔
244
            final SpriteImageOccurrence spriteImageOccurrence = entry.getValue();
1✔
245

246
            messageLog.setCssFile(cssFile);
1✔
247

248
            // Add to the global map, checking for duplicates
249
            if (spriteImageDirectivesBySpriteId
1✔
250
                .containsKey(spriteImageOccurrence.spriteImageDirective.spriteId))
1✔
251
            {
252
                messageLog.warning(MessageType.IGNORING_SPRITE_IMAGE_REDEFINITION);
×
253
            }
254
            else
255
            {
256
                spriteImageDirectivesBySpriteId.put(
1✔
257
                    spriteImageOccurrence.spriteImageDirective.spriteId,
258
                    spriteImageOccurrence);
259
            }
260
        }
1✔
261
        return spriteImageDirectivesBySpriteId;
1✔
262
    }
263

264
    /**
265
     * Groups {@link SpriteReferenceOccurrence}s by sprite id.
266
     */
267
    static Multimap<String, SpriteReferenceOccurrence> mergeSpriteReferenceOccurrences(
268
        final Multimap<String, SpriteReferenceOccurrence> spriteEntriesByFile)
269
    {
270
        final Multimap<String, SpriteReferenceOccurrence> spriteReferenceOccurrencesBySpriteId = LinkedListMultimap
271
            .create();
1✔
272
        for (final SpriteReferenceOccurrence spriteReferenceOccurrence : spriteEntriesByFile
1✔
273
            .values())
1✔
274
        {
275
            spriteReferenceOccurrencesBySpriteId.put(
1✔
276
                spriteReferenceOccurrence.spriteReferenceDirective.spriteRef,
277
                spriteReferenceOccurrence);
278
        }
1✔
279
        return spriteReferenceOccurrencesBySpriteId;
1✔
280
    }
281

282
    /**
283
     * Extract the sprite image directive string to be parsed.
284
     */
285
    static String extractSpriteImageDirectiveString(String cssLine)
286
    {
287
        final Matcher matcher = SPRITE_IMAGE_DIRECTIVE.matcher(cssLine);
1✔
288

289
        if (matcher.find())
1✔
290
        {
291
            return matcher.group(1).trim();
1✔
292
        }
293
        else
294
        {
295
            return null;
1✔
296
        }
297
    }
298

299
    /**
300
     * Extract the sprite reference directive string to be parsed.
301
     */
302
    static String extractSpriteReferenceDirectiveString(String css)
303
    {
304
        final Matcher matcher = SPRITE_REFERENCE_DIRECTIVE.matcher(css);
1✔
305

306
        if (matcher.find())
1✔
307
        {
308
            return matcher.group(1).trim();
1✔
309
        }
310
        else
311
        {
312
            return null;
1✔
313
        }
314
    }
315

316
    /**
317
     * Extract the url to the image to be added to a sprite.
318
     */
319
    CssProperty extractSpriteReferenceCssProperty(String css)
320
    {
321
        final Matcher matcher = SPRITE_REFERENCE_DIRECTIVE.matcher(css);
1✔
322

323
        // Remove the directive
324
        final String noDirective = matcher.replaceAll("").trim();
1✔
325

326
        final Collection<CssProperty> rules = CssSyntaxUtils
1✔
327
            .extractProperties(noDirective);
1✔
328
        if (rules.isEmpty())
1✔
329
        {
330
            messageLog.warning(
×
331
                MessageType.NO_BACKGROUND_IMAGE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE,
332
                css);
333
            return null;
×
334
        }
335

336
        if (rules.size() > 1)
1✔
337
        {
338
            messageLog.warning(
1✔
339
                MessageType.MORE_THAN_ONE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE, css);
340
            return null;
1✔
341
        }
342

343
        final CssProperty backgroundImageRule = rules.iterator().next();
1✔
344
        if (!backgroundImageRule.rule.equals("background-image"))
1✔
345
        {
346
            messageLog.warning(
1✔
347
                MessageType.NO_BACKGROUND_IMAGE_RULE_NEXT_TO_SPRITE_REFERENCE_DIRECTIVE,
348
                css);
349
            return null;
1✔
350
        }
351

352
        return backgroundImageRule;
1✔
353
    }
354
}
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