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

hazendaz / sitemesh2 / 59

22 Mar 2026 02:30AM UTC coverage: 40.347%. Remained the same
59

push

github

hazendaz
[mvn] Update maven wrapper

698 of 1891 branches covered (36.91%)

Branch coverage included in aggregate %.

1555 of 3693 relevant lines covered (42.11%)

0.42 hits per line

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

73.71
/src/main/java/com/opensymphony/module/sitemesh/mapper/PathMapper.java
1
/*
2
 * SPDX-License-Identifier: Apache-2.0
3
 * Copyright 2011-2026 Hazendaz
4
 */
5
/*
6
 * Title:        PathMapper
7
 * Description:
8
 *
9
 * This software is published under the terms of the OpenSymphony Software
10
 * License version 1.1, of which a copy has been included with this
11
 * distribution in the LICENSE.txt file.
12
 */
13

14
package com.opensymphony.module.sitemesh.mapper;
15

16
import java.util.HashMap;
17
import java.util.Iterator;
18
import java.util.Map;
19

20
/**
21
 * The PathMapper is used to map file patterns to keys, and find an approriate key for a given file path. The pattern
22
 * rules are consistent with those defined in the Servlet 2.3 API on the whole. Wildcard patterns are also supported,
23
 * using any combination of * and ?.
24
 * <h2>Example</h2> <blockquote><code>
25
 * PathMapper pm = new PathMapper();<br>
26
 * <br>
27
 * pm.put("one","/");<br>
28
 * pm.put("two","/mydir/*");<br>
29
 * pm.put("three","*.xml");<br>
30
 * pm.put("four","/myexactfile.html");<br>
31
 * pm.put("five","/*\/admin/*.??ml");<br>
32
 * <br>
33
 * String result1 = pm.get("/mydir/myfile.xml"); // returns "two";<br>
34
 * String result2 = pm.get("/mydir/otherdir/admin/myfile.html"); // returns "five";<br>
35
 * </code></blockquote>
36
 *
37
 * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
38
 * @author <a href="mailto:mcannon@internet.com">Mike Cannon-Brookes</a>
39
 * @author <a href="mailto:hani@formicary.net">Hani Suleiman</a>
40
 */
41
public class PathMapper {
1✔
42

43
    /** The mappings. */
44
    private Map<String, String> mappings = new HashMap<String, String>();
1✔
45

46
    /**
47
     * Add a key and appropriate matching pattern.
48
     *
49
     * @param key
50
     *            the key
51
     * @param pattern
52
     *            the pattern
53
     */
54
    public void put(String key, String pattern) {
55
        if (key != null) {
1✔
56
            mappings.put(pattern, key);
1✔
57
        }
58
    }
1✔
59

60
    /**
61
     * Retrieve appropriate key by matching patterns with supplied path.
62
     *
63
     * @param path
64
     *            the path
65
     *
66
     * @return the string
67
     */
68
    public String get(String path) {
69
        if (path == null) {
1✔
70
            path = "/";
1✔
71
        }
72
        String mapped = findKey(path, mappings);
1✔
73
        if (mapped == null) {
1✔
74
            return null;
1✔
75
        }
76
        return (String) mappings.get(mapped);
1✔
77
    }
78

79
    /**
80
     * Find exact key in mappings.
81
     *
82
     * @param path
83
     *            the path
84
     * @param mappings
85
     *            the mappings
86
     *
87
     * @return the string
88
     */
89
    private static String findKey(String path, Map<String, String> mappings) {
90
        String result = findExactKey(path, mappings);
1✔
91
        if (result == null) {
1✔
92
            result = findComplexKey(path, mappings);
1✔
93
        }
94
        if (result == null) {
1✔
95
            result = findDefaultKey(mappings);
1✔
96
        }
97
        return result;
1✔
98
    }
99

100
    /**
101
     * Check if path matches exact pattern ( /blah/blah.jsp ).
102
     *
103
     * @param path
104
     *            the path
105
     * @param mappings
106
     *            the mappings
107
     *
108
     * @return the string
109
     */
110
    private static String findExactKey(String path, Map<String, String> mappings) {
111
        if (mappings.containsKey(path)) {
1✔
112
            return path;
1✔
113
        }
114
        return null;
1✔
115
    }
116

117
    /**
118
     * Find complex key.
119
     *
120
     * @param path
121
     *            the path
122
     * @param mappings
123
     *            the mappings
124
     *
125
     * @return the string
126
     */
127
    private static String findComplexKey(String path, Map<String, String> mappings) {
128
        Iterator<String> i = mappings.keySet().iterator();
1✔
129
        String result = null, key = null;
1✔
130
        while (i.hasNext()) {
1✔
131
            key = (String) i.next();
1✔
132
            if (key.length() > 1 && (key.indexOf('?') != -1 || key.indexOf('*') != -1) && match(key, path, false)) {
1✔
133
                if (result == null || key.length() > result.length()) {
1✔
134
                    // longest key wins
135
                    result = key;
1✔
136
                }
137
            }
138
        }
139
        return result;
1✔
140
    }
141

142
    /**
143
     * Look for root pattern ( / ).
144
     *
145
     * @param mappings
146
     *            the mappings
147
     *
148
     * @return the string
149
     */
150
    private static String findDefaultKey(Map<String, String> mappings) {
151
        String[] defaultKeys = { "/", "*", "/*" };
1✔
152
        for (String defaultKey : defaultKeys) {
1✔
153
            if (mappings.containsKey(defaultKey)) {
1✔
154
                return defaultKey;
1✔
155
            }
156
        }
157
        return null;
1✔
158
    }
159

160
    /**
161
     * Match.
162
     *
163
     * @param pattern
164
     *            the pattern
165
     * @param str
166
     *            the str
167
     * @param isCaseSensitive
168
     *            the is case sensitive
169
     *
170
     * @return true, if successful
171
     */
172
    private static boolean match(String pattern, String str, boolean isCaseSensitive) {
173
        char[] patArr = pattern.toCharArray();
1✔
174
        char[] strArr = str.toCharArray();
1✔
175
        int patIdxStart = 0;
1✔
176
        int patIdxEnd = patArr.length - 1;
1✔
177
        int strIdxStart = 0;
1✔
178
        int strIdxEnd = strArr.length - 1;
1✔
179
        char ch;
180

181
        boolean containsStar = false;
1✔
182
        for (char element : patArr) {
1!
183
            if (element == '*') {
1✔
184
                containsStar = true;
1✔
185
                break;
1✔
186
            }
187
        }
188

189
        if (!containsStar) {
1!
190
            // No '*'s, so we make a shortcut
191
            if (patIdxEnd != strIdxEnd) {
×
192
                return false; // Pattern and string do not have the same size
×
193
            }
194
            for (int i = 0; i <= patIdxEnd; i++) {
×
195
                ch = patArr[i];
×
196
                if (ch != '?') {
×
197
                    if ((isCaseSensitive && ch != strArr[i])
×
198
                            || (!isCaseSensitive && Character.toUpperCase(ch) != Character.toUpperCase(strArr[i]))) {
×
199
                        return false; // Character mismatch
×
200
                    }
201
                }
202
            }
203
            return true; // String matches against pattern
×
204
        }
205

206
        if (patIdxEnd == 0) {
1!
207
            return true; // Pattern contains only '*', which matches anything
×
208
        }
209

210
        // Process characters before first star
211
        while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
1✔
212
            if (ch != '?') {
1!
213
                if ((isCaseSensitive && ch != strArr[strIdxStart]) || (!isCaseSensitive
1!
214
                        && Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxStart]))) {
1✔
215
                    return false; // Character mismatch
1✔
216
                }
217
            }
218
            patIdxStart++;
1✔
219
            strIdxStart++;
1✔
220
        }
221
        if (strIdxStart > strIdxEnd) {
1✔
222
            // All characters in the string are used. Check if only '*'s are
223
            // left in the pattern. If so, we succeeded. Otherwise failure.
224
            for (int i = patIdxStart; i <= patIdxEnd; i++) {
1✔
225
                if (patArr[i] != '*') {
1✔
226
                    return false;
1✔
227
                }
228
            }
229
            return true;
1✔
230
        }
231

232
        // Process characters after last star
233
        while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
1!
234
            if (ch != '?') {
1✔
235
                if ((isCaseSensitive && ch != strArr[strIdxEnd]) || (!isCaseSensitive
1!
236
                        && Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxEnd]))) {
1✔
237
                    return false; // Character mismatch
1✔
238
                }
239
            }
240
            patIdxEnd--;
1✔
241
            strIdxEnd--;
1✔
242
        }
243
        if (strIdxStart > strIdxEnd) {
1!
244
            // All characters in the string are used. Check if only '*'s are
245
            // left in the pattern. If so, we succeeded. Otherwise failure.
246
            for (int i = patIdxStart; i <= patIdxEnd; i++) {
×
247
                if (patArr[i] != '*') {
×
248
                    return false;
×
249
                }
250
            }
251
            return true;
×
252
        }
253

254
        // process pattern between stars. padIdxStart and patIdxEnd point
255
        // always to a '*'.
256
        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
1!
257
            int patIdxTmp = -1;
1✔
258
            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
1!
259
                if (patArr[i] == '*') {
1✔
260
                    patIdxTmp = i;
1✔
261
                    break;
1✔
262
                }
263
            }
264
            if (patIdxTmp == patIdxStart + 1) {
1!
265
                // Two stars next to each other, skip the first one.
266
                patIdxStart++;
×
267
                continue;
×
268
            }
269
            // Find the pattern between padIdxStart & padIdxTmp in str between
270
            // strIdxStart & strIdxEnd
271
            int patLength = patIdxTmp - patIdxStart - 1;
1✔
272
            int strLength = strIdxEnd - strIdxStart + 1;
1✔
273
            int foundIdx = -1;
1✔
274
            strLoop: for (int i = 0; i <= strLength - patLength; i++) {
1!
275
                for (int j = 0; j < patLength; j++) {
1✔
276
                    ch = patArr[patIdxStart + j + 1];
1✔
277
                    if (ch != '?') {
1!
278
                        if ((isCaseSensitive && ch != strArr[strIdxStart + i + j]) || (!isCaseSensitive
1!
279
                                && Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxStart + i + j]))) {
1✔
280
                            continue strLoop;
1✔
281
                        }
282
                    }
283
                }
284

285
                foundIdx = strIdxStart + i;
1✔
286
                break;
1✔
287
            }
288

289
            if (foundIdx == -1) {
1!
290
                return false;
×
291
            }
292

293
            patIdxStart = patIdxTmp;
1✔
294
            strIdxStart = foundIdx + patLength;
1✔
295
        }
1✔
296

297
        // All characters in the string are used. Check if only '*'s are left
298
        // in the pattern. If so, we succeeded. Otherwise failure.
299
        for (int i = patIdxStart; i <= patIdxEnd; i++) {
1✔
300
            if (patArr[i] != '*') {
1!
301
                return false;
×
302
            }
303
        }
304
        return true;
1✔
305
    }
306
}
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