EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.support]

COVERAGE SUMMARY FOR SOURCE FILE [PatternMatcher.java]

nameclass, %method, %block, %line, %
PatternMatcher.java100% (2/2)100% (5/5)94%  (325/347)91%  (81/89)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class PatternMatcher100% (1/1)100% (3/3)93%  (311/333)91%  (78/86)
match (String, String): boolean 100% (1/1)91%  (236/258)88%  (61/69)
PatternMatcher (Map): void 100% (1/1)100% (30/30)100% (7/7)
match (String): Object 100% (1/1)100% (45/45)100% (10/10)
     
class PatternMatcher$1100% (1/1)100% (2/2)100% (14/14)100% (4/4)
PatternMatcher$1 (PatternMatcher): void 100% (1/1)100% (6/6)100% (1/1)
compare (String, String): int 100% (1/1)100% (8/8)100% (3/3)

1/*
2 * Copyright 2006-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.springframework.batch.support;
17 
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.Comparator;
21import java.util.HashMap;
22import java.util.List;
23import java.util.Map;
24 
25import org.springframework.util.Assert;
26 
27/**
28 * @author Dave Syer
29 * @author Dan Garrette
30 */
31public class PatternMatcher<S> {
32 
33        private Map<String, S> map = new HashMap<String, S>();
34        private List<String> sorted = new ArrayList<String>();
35 
36        /**
37         * Initialize a new {@link PatternMatcher} with a map of patterns to values
38         * @param map a map from String patterns to values
39         */
40        public PatternMatcher(Map<String, S> map) {
41                super();
42                this.map = map;
43                // Sort keys to start with the most specific
44                sorted = new ArrayList<String>(map.keySet());
45                Collections.sort(sorted, new Comparator<String>() {
46            @Override
47                        public int compare(String o1, String o2) {
48                                String s1 = o1; // .replace('?', '{');
49                                String s2 = o2; // .replace('*', '}');
50                                return s2.compareTo(s1);
51                        }
52                });
53        }
54 
55        /**
56         * Lifted from AntPathMatcher in Spring Core. Tests whether or not a string
57         * matches against a pattern. The pattern may contain two special
58         * characters:<br>
59         * '*' means zero or more characters<br>
60         * '?' means one and only one character
61         * 
62         * @param pattern pattern to match against. Must not be <code>null</code>.
63         * @param str string which must be matched against the pattern. Must not be
64         * <code>null</code>.
65         * @return <code>true</code> if the string matches against the pattern, or
66         * <code>false</code> otherwise.
67         */
68        public static boolean match(String pattern, String str) {
69                char[] patArr = pattern.toCharArray();
70                char[] strArr = str.toCharArray();
71                int patIdxStart = 0;
72                int patIdxEnd = patArr.length - 1;
73                int strIdxStart = 0;
74                int strIdxEnd = strArr.length - 1;
75                char ch;
76 
77                boolean containsStar = pattern.contains("*");
78 
79                if (!containsStar) {
80                        // No '*'s, so we make a shortcut
81                        if (patIdxEnd != strIdxEnd) {
82                                return false; // Pattern and string do not have the same size
83                        }
84                        for (int i = 0; i <= patIdxEnd; i++) {
85                                ch = patArr[i];
86                                if (ch != '?') {
87                                        if (ch != strArr[i]) {
88                                                return false;// Character mismatch
89                                        }
90                                }
91                        }
92                        return true; // String matches against pattern
93                }
94 
95                if (patIdxEnd == 0) {
96                        return true; // Pattern contains only '*', which matches anything
97                }
98 
99                // Process characters before first star
100                while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
101                        if (ch != '?') {
102                                if (ch != strArr[strIdxStart]) {
103                                        return false;// Character mismatch
104                                }
105                        }
106                        patIdxStart++;
107                        strIdxStart++;
108                }
109                if (strIdxStart > strIdxEnd) {
110                        // All characters in the string are used. Check if only '*'s are
111                        // left in the pattern. If so, we succeeded. Otherwise failure.
112                        for (int i = patIdxStart; i <= patIdxEnd; i++) {
113                                if (patArr[i] != '*') {
114                                        return false;
115                                }
116                        }
117                        return true;
118                }
119 
120                // Process characters after last star
121                while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
122                        if (ch != '?') {
123                                if (ch != strArr[strIdxEnd]) {
124                                        return false;// Character mismatch
125                                }
126                        }
127                        patIdxEnd--;
128                        strIdxEnd--;
129                }
130                if (strIdxStart > strIdxEnd) {
131                        // All characters in the string are used. Check if only '*'s are
132                        // left in the pattern. If so, we succeeded. Otherwise failure.
133                        for (int i = patIdxStart; i <= patIdxEnd; i++) {
134                                if (patArr[i] != '*') {
135                                        return false;
136                                }
137                        }
138                        return true;
139                }
140 
141                // process pattern between stars. padIdxStart and patIdxEnd point
142                // always to a '*'.
143                while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
144                        int patIdxTmp = -1;
145                        for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
146                                if (patArr[i] == '*') {
147                                        patIdxTmp = i;
148                                        break;
149                                }
150                        }
151                        if (patIdxTmp == patIdxStart + 1) {
152                                // Two stars next to each other, skip the first one.
153                                patIdxStart++;
154                                continue;
155                        }
156                        // Find the pattern between padIdxStart & padIdxTmp in str between
157                        // strIdxStart & strIdxEnd
158                        int patLength = (patIdxTmp - patIdxStart - 1);
159                        int strLength = (strIdxEnd - strIdxStart + 1);
160                        int foundIdx = -1;
161                        strLoop: for (int i = 0; i <= strLength - patLength; i++) {
162                                for (int j = 0; j < patLength; j++) {
163                                        ch = patArr[patIdxStart + j + 1];
164                                        if (ch != '?') {
165                                                if (ch != strArr[strIdxStart + i + j]) {
166                                                        continue strLoop;
167                                                }
168                                        }
169                                }
170 
171                                foundIdx = strIdxStart + i;
172                                break;
173                        }
174 
175                        if (foundIdx == -1) {
176                                return false;
177                        }
178 
179                        patIdxStart = patIdxTmp;
180                        strIdxStart = foundIdx + patLength;
181                }
182 
183                // All characters in the string are used. Check if only '*'s are left
184                // in the pattern. If so, we succeeded. Otherwise failure.
185                for (int i = patIdxStart; i <= patIdxEnd; i++) {
186                        if (patArr[i] != '*') {
187                                return false;
188                        }
189                }
190 
191                return true;
192        }
193 
194        /**
195         * <p>
196         * This method takes a String key and a map from Strings to values of any
197         * type. During processing, the method will identify the most specific key
198         * in the map that matches the line. Once the correct is identified, its
199         * value is returned. Note that if the map contains the wildcard string "*"
200         * as a key, then it will serve as the "default" case, matching every line
201         * that does not match anything else.
202         * 
203         * <p>
204         * If no matching prefix is found, a {@link IllegalStateException} will be
205         * thrown.
206         * 
207         * <p>
208         * Null keys are not allowed in the map.
209         * 
210         * @param line An input string
211         * @return the value whose prefix matches the given line
212         */
213        public S match(String line) {
214 
215                S value = null;
216                Assert.notNull(line, "A non-null key must be provided to match against.");
217 
218                for (String key : sorted) {
219                        if (PatternMatcher.match(key, line)) {
220                                value = map.get(key);
221                                break;
222                        }
223                }
224 
225                if (value == null) {
226                        throw new IllegalStateException("Could not find a matching pattern for key=[" + line + "]");
227                }
228                return value;
229 
230        }
231 
232}

[all classes][org.springframework.batch.support]
EMMA 2.0.5312 (C) Vladimir Roubtsov