EMMA Coverage Report (generated Thu Jan 24 13:37:04 CST 2013)
[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%  (80/88)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class PatternMatcher100% (1/1)100% (3/3)93%  (311/333)91%  (77/85)
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% (9/9)
     
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                        public int compare(String o1, String o2) {
47                                String s1 = o1; // .replace('?', '{');
48                                String s2 = o2; // .replace('*', '}');
49                                return s2.compareTo(s1);
50                        }
51                });
52        }
53 
54        /**
55         * Lifted from AntPathMatcher in Spring Core. Tests whether or not a string
56         * matches against a pattern. The pattern may contain two special
57         * characters:<br>
58         * '*' means zero or more characters<br>
59         * '?' means one and only one character
60         * 
61         * @param pattern pattern to match against. Must not be <code>null</code>.
62         * @param str string which must be matched against the pattern. Must not be
63         * <code>null</code>.
64         * @return <code>true</code> if the string matches against the pattern, or
65         * <code>false</code> otherwise.
66         */
67        public static boolean match(String pattern, String str) {
68                char[] patArr = pattern.toCharArray();
69                char[] strArr = str.toCharArray();
70                int patIdxStart = 0;
71                int patIdxEnd = patArr.length - 1;
72                int strIdxStart = 0;
73                int strIdxEnd = strArr.length - 1;
74                char ch;
75 
76                boolean containsStar = pattern.contains("*");
77 
78                if (!containsStar) {
79                        // No '*'s, so we make a shortcut
80                        if (patIdxEnd != strIdxEnd) {
81                                return false; // Pattern and string do not have the same size
82                        }
83                        for (int i = 0; i <= patIdxEnd; i++) {
84                                ch = patArr[i];
85                                if (ch != '?') {
86                                        if (ch != strArr[i]) {
87                                                return false;// Character mismatch
88                                        }
89                                }
90                        }
91                        return true; // String matches against pattern
92                }
93 
94                if (patIdxEnd == 0) {
95                        return true; // Pattern contains only '*', which matches anything
96                }
97 
98                // Process characters before first star
99                while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
100                        if (ch != '?') {
101                                if (ch != strArr[strIdxStart]) {
102                                        return false;// Character mismatch
103                                }
104                        }
105                        patIdxStart++;
106                        strIdxStart++;
107                }
108                if (strIdxStart > strIdxEnd) {
109                        // All characters in the string are used. Check if only '*'s are
110                        // left in the pattern. If so, we succeeded. Otherwise failure.
111                        for (int i = patIdxStart; i <= patIdxEnd; i++) {
112                                if (patArr[i] != '*') {
113                                        return false;
114                                }
115                        }
116                        return true;
117                }
118 
119                // Process characters after last star
120                while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
121                        if (ch != '?') {
122                                if (ch != strArr[strIdxEnd]) {
123                                        return false;// Character mismatch
124                                }
125                        }
126                        patIdxEnd--;
127                        strIdxEnd--;
128                }
129                if (strIdxStart > strIdxEnd) {
130                        // All characters in the string are used. Check if only '*'s are
131                        // left in the pattern. If so, we succeeded. Otherwise failure.
132                        for (int i = patIdxStart; i <= patIdxEnd; i++) {
133                                if (patArr[i] != '*') {
134                                        return false;
135                                }
136                        }
137                        return true;
138                }
139 
140                // process pattern between stars. padIdxStart and patIdxEnd point
141                // always to a '*'.
142                while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
143                        int patIdxTmp = -1;
144                        for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
145                                if (patArr[i] == '*') {
146                                        patIdxTmp = i;
147                                        break;
148                                }
149                        }
150                        if (patIdxTmp == patIdxStart + 1) {
151                                // Two stars next to each other, skip the first one.
152                                patIdxStart++;
153                                continue;
154                        }
155                        // Find the pattern between padIdxStart & padIdxTmp in str between
156                        // strIdxStart & strIdxEnd
157                        int patLength = (patIdxTmp - patIdxStart - 1);
158                        int strLength = (strIdxEnd - strIdxStart + 1);
159                        int foundIdx = -1;
160                        strLoop: for (int i = 0; i <= strLength - patLength; i++) {
161                                for (int j = 0; j < patLength; j++) {
162                                        ch = patArr[patIdxStart + j + 1];
163                                        if (ch != '?') {
164                                                if (ch != strArr[strIdxStart + i + j]) {
165                                                        continue strLoop;
166                                                }
167                                        }
168                                }
169 
170                                foundIdx = strIdxStart + i;
171                                break;
172                        }
173 
174                        if (foundIdx == -1) {
175                                return false;
176                        }
177 
178                        patIdxStart = patIdxTmp;
179                        strIdxStart = foundIdx + patLength;
180                }
181 
182                // All characters in the string are used. Check if only '*'s are left
183                // in the pattern. If so, we succeeded. Otherwise failure.
184                for (int i = patIdxStart; i <= patIdxEnd; i++) {
185                        if (patArr[i] != '*') {
186                                return false;
187                        }
188                }
189 
190                return true;
191        }
192 
193        /**
194         * <p>
195         * This method takes a String key and a map from Strings to values of any
196         * type. During processing, the method will identify the most specific key
197         * in the map that matches the line. Once the correct is identified, its
198         * value is returned. Note that if the map contains the wildcard string "*"
199         * as a key, then it will serve as the "default" case, matching every line
200         * that does not match anything else.
201         * 
202         * <p>
203         * If no matching prefix is found, a {@link IllegalStateException} will be
204         * thrown.
205         * 
206         * <p>
207         * Null keys are not allowed in the map.
208         * 
209         * @param line An input string
210         * @return the value whose prefix matches the given line
211         */
212        public S match(String line) {
213 
214                S value = null;
215                Assert.notNull(line, "A non-null key must be provided to match against.");
216 
217                for (String key : sorted) {
218                        if (PatternMatcher.match(key, line)) {
219                                value = map.get(key);
220                                break;
221                        }
222                }
223 
224                if (value == null) {
225                        throw new IllegalStateException("Could not find a matching pattern for key=[" + line + "]");
226                }
227                return value;
228 
229        }
230 
231}

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