diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java index 0c4439c7e..e24b8286b 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java @@ -58,9 +58,11 @@ public class SqlPagingQueryProviderFactoryBean implements FactoryBean { private String sortKey; + private boolean ascending = true; + private Map providers = new HashMap(); - private boolean ascending; + { providers.put(DB2, new Db2PagingQueryProvider()); providers.put(DB2ZOS, new Db2PagingQueryProvider()); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapper.java index 55fd0f7d7..f7dac303d 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapper.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapper.java @@ -39,7 +39,7 @@ import org.springframework.util.Assert; * with the most specific, and the first match always succeeds. * * @see PrefixMatchingCompositeLineTokenizer - * @see PatternMatcher#matchPrefix(String, Map) + * @see PatternMatcher#match(String, Map) * * @author Dan Garrette * @since 2.0 @@ -56,7 +56,7 @@ public class PrefixMatchingCompositeLineMapper implements LineMapper, Init * int) */ public T mapLine(String line, int lineNumber) throws Exception { - return PatternMatcher.matchPrefix(line, this.fieldSetMappers).mapFieldSet(this.tokenizer.tokenize(line)); + return PatternMatcher.match(line, this.fieldSetMappers).mapFieldSet(this.tokenizer.tokenize(line)); } /* @@ -75,6 +75,13 @@ public class PrefixMatchingCompositeLineMapper implements LineMapper, Init } public void setFieldSetMappers(Map> fieldSetMappers) { - this.fieldSetMappers = new LinkedHashMap>(fieldSetMappers); + this.fieldSetMappers = new LinkedHashMap>(); + for (String key : fieldSetMappers.keySet()) { + FieldSetMapper value = fieldSetMappers.get(key); + if (!key.endsWith("*")) { + key = key + "*"; + } + this.fieldSetMappers.put(key, value); + } } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PrefixMatchingCompositeLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PrefixMatchingCompositeLineTokenizer.java index 8642c64cf..6c2f7d2a8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PrefixMatchingCompositeLineTokenizer.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PrefixMatchingCompositeLineTokenizer.java @@ -31,10 +31,11 @@ import org.springframework.util.Assert; * are sorted starting with the most specific, and the first match always * succeeds. * - * @see PatternMatcher#matchPrefix(String, Map) + * @see PatternMatcher#match(String, Map) * * @author Ben Hale * @author Dan Garrette + * @author Dave Syer */ public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer, InitializingBean { @@ -46,7 +47,7 @@ public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer, Init * @see org.springframework.batch.item.file.transform.LineTokenizer#tokenize(java.lang.String) */ public FieldSet tokenize(String line) { - return PatternMatcher.matchPrefix(line, this.tokenizers).tokenize(line); + return PatternMatcher.match(line, this.tokenizers).tokenize(line); } /* @@ -60,6 +61,13 @@ public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer, Init } public void setTokenizers(Map tokenizers) { - this.tokenizers = new LinkedHashMap(tokenizers); + this.tokenizers = new LinkedHashMap(); + for (String key : tokenizers.keySet()) { + LineTokenizer value = tokenizers.get(key); + if (!key.endsWith("*")) { + key = key + "*"; + } + this.tokenizers.put(key, value); + } } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PatternMatcher.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PatternMatcher.java index 36e5f7602..12cc51056 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PatternMatcher.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PatternMatcher.java @@ -21,21 +21,14 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import org.springframework.util.Assert; + /** * @author Dave Syer * @author Dan Garrette */ public class PatternMatcher { - /** - * Used for reverse-sorting strings - */ - private static Comparator STRING_REVERSE_COMPARATOR = new Comparator() { - public int compare(String o1, String o2) { - return o2.compareTo(o1); - } - }; - /** * Lifted from AntPathMatcher in Spring Core. Tests whether or not a string * matches against a pattern. The pattern may contain two special @@ -43,13 +36,11 @@ public class PatternMatcher { * '*' means zero or more characters
* '?' means one and only one character * - * @param pattern - * pattern to match against. Must not be null. - * @param str - * string which must be matched against the pattern. Must not be - * null. + * @param pattern pattern to match against. Must not be null. + * @param str string which must be matched against the pattern. Must not be + * null. * @return true if the string matches against the pattern, or - * false otherwise. + * false otherwise. */ public static boolean match(String pattern, String str) { char[] patArr = pattern.toCharArray(); @@ -179,12 +170,12 @@ public class PatternMatcher { /** *

- * This method takes a String line and a map from String prefixes to values - * of any type. During processing, the method will identify the most - * specific prefix in the map that matches the beginning of the line. Once - * the correct is identified, its value is returned. Note that if the map - * contains the empty string as a prefix, then it will serve as the - * "default" case, matching every non-null line given. + * This method takes a String key and a map from Strings to values of any + * type. During processing, the method will identify the most specific key + * in the map that matches the line. Once the correct is identified, its + * value is returned. Note that if the map contains the wildcard string "*" + * as a key, then it will serve as the "default" case, matching every + * line that does not match anything else. * *

* If no matching prefix is found, a {@link IllegalStateException} will be @@ -193,32 +184,34 @@ public class PatternMatcher { *

* Null keys are not allowed in the map. * - * @param line - * An input string - * @param map - * A map with String prefixes as keys. + * @param line An input string + * @param map A map with String prefixes as keys. * @return the value whose prefix matches the given line */ - public static S matchPrefix(String line, Map map) { + public static S match(String line, Map map) { S value = null; - - if (line == null) { - throw new IllegalStateException("Could not handle a null line"); - } + Assert.notNull(line, "A non-null key must be provided."); // Sort keys to start with the most specific List sorted = new ArrayList(map.keySet()); - Collections.sort(sorted, STRING_REVERSE_COMPARATOR); + Collections.sort(sorted, new Comparator() { + public int compare(String o1, String o2) { + String s1 = o1; // .replace('?', '{'); + String s2 = o2; // .replace('*', '}'); + return s2.compareTo(s1); + } + }); for (String key : sorted) { - if (line.startsWith(key)) { + if (PatternMatcher.match(key, line)) { value = map.get(key); break; } } if (value == null) { - throw new IllegalStateException("Could not find a matching prefix for line=[" + line + "]"); + throw new IllegalStateException("Could not find a matching pattern for key=[" + line + "]"); } return value; } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java index 57a924250..7388119b5 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBeanTests.java @@ -83,6 +83,13 @@ public class SqlPagingQueryProviderFactoryBeanTests { assertTrue("Wrong query: "+query, query.contains("x=y")); } + @Test + public void testAscending() throws Exception { + PagingQueryProvider provider = (PagingQueryProvider) factory.getObject(); + String query = provider.generateFirstPageQuery(100); + assertTrue("Wrong query: "+query, query.contains("ASC")); + } + @Test(expected=IllegalArgumentException.class) public void testWrongDatabaseType() throws Exception { factory.setDatabaseType("NoSuchDb"); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapperTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapperTests.java index d80bee284..2b2b574cd 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapperTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/PrefixMatchingCompositeLineMapperTests.java @@ -31,6 +31,7 @@ import org.springframework.batch.item.file.transform.Name; /** * @author Dan Garrette + * @author Dave Syer * @since 2.0 */ public class PrefixMatchingCompositeLineMapperTests { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PatternMatcherTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PatternMatcherTests.java index deab3a72a..48df26683 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PatternMatcherTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PatternMatcherTests.java @@ -32,81 +32,87 @@ public class PatternMatcherTests { private static Map map = new HashMap(); static { - map.put("an", 3); - map.put("a", 2); - map.put("big", 4); + map.put("an*", 3); + map.put("a*", 2); + map.put("big*", 4); } private static Map defaultMap = new HashMap(); static { defaultMap.put("an", 3); defaultMap.put("a", 2); - defaultMap.put("big", 4); - defaultMap.put("", 1); + defaultMap.put("big*", 4); + defaultMap.put("big?*", 5); + defaultMap.put("*", 1); } @Test - public void testMatch_noWildcard_yes() { + public void testMatchNoWildcardYes() { assertTrue(PatternMatcher.match("abc", "abc")); } @Test - public void testMatch_noWildcard_no() { + public void testMatchNoWildcardNo() { assertFalse(PatternMatcher.match("abc", "ab")); } @Test - public void testMatch_qMark_yes() { + public void testMatchSingleYes() { assertTrue(PatternMatcher.match("a?c", "abc")); } @Test - public void testMatch_qMark_no() { + public void testMatchSingleNo() { assertFalse(PatternMatcher.match("a?c", "ab")); } @Test - public void testMatch_star_yes() { + public void testMatchSingleWildcardNo() { + assertTrue(PatternMatcher.match("a?*", "abc")); + } + + @Test + public void testMatchStarYes() { assertTrue(PatternMatcher.match("a*c", "abdegc")); } @Test - public void testMatch_star_no() { + public void testMatchStarNo() { assertFalse(PatternMatcher.match("a*c", "abdeg")); } @Test - public void testMatchPrefix_subsumed() { - assertEquals(2, PatternMatcher.matchPrefix("apple", map).intValue()); + public void testMatchPrefixSubsumed() { + assertEquals(2, PatternMatcher.match("apple", map).intValue()); } @Test - public void testMatchPrefix_subsuming() { - assertEquals(3, PatternMatcher.matchPrefix("animal", map).intValue()); + public void testMatchPrefixSubsuming() { + assertEquals(3, PatternMatcher.match("animal", map).intValue()); } @Test - public void testMatchPrefix_unrelated() { - assertEquals(4, PatternMatcher.matchPrefix("biggest", map).intValue()); + public void testMatchPrefixUnrelated() { + assertEquals(4, PatternMatcher.match("biggest", map).intValue()); } @Test(expected = IllegalStateException.class) public void testMatchPrefix_noMatch() { - PatternMatcher.matchPrefix("bat", map); + PatternMatcher.match("bat", map); } @Test - public void testMatchPrefix_defaultValue_unrelated() { - assertEquals(4, PatternMatcher.matchPrefix("biggest", defaultMap).intValue()); + public void testMatchPrefixDefaultValueUnrelated() { + assertEquals(5, PatternMatcher.match("biggest", defaultMap).intValue()); } @Test - public void testMatchPrefix_defaultValue_emptyString() { - assertEquals(1, PatternMatcher.matchPrefix("", defaultMap).intValue()); + public void testMatchPrefixDefaultValueEmptyString() { + assertEquals(1, PatternMatcher.match("", defaultMap).intValue()); } @Test - public void testMatchPrefix_defaultValue_noMatch() { - assertEquals(1, PatternMatcher.matchPrefix("bat", defaultMap).intValue()); + public void testMatchPrefixDefaultValueNoMatch() { + assertEquals(1, PatternMatcher.match("bat", defaultMap).intValue()); } }