RESOLVED - issue BATCH-1109: Generalise PrefixMatching* to PatternMatching*

This commit is contained in:
dsyer
2009-03-03 17:11:49 +00:00
parent 7fd99c2344
commit f2f7a12eaa
7 changed files with 105 additions and 121 deletions

View File

@@ -16,36 +16,36 @@
package org.springframework.batch.item.file.mapping;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.batch.item.file.transform.PrefixMatchingCompositeLineTokenizer;
import org.springframework.batch.item.file.transform.PatternMatchingCompositeLineTokenizer;
import org.springframework.batch.support.PatternMatcher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* <p>
* A {@link LineMapper} implementation that stores a mapping of String prefixes
* to delegate {@link LineTokenizer}s as well as a mapping of String prefixes to
* A {@link LineMapper} implementation that stores a mapping of String patterns
* to delegate {@link LineTokenizer}s as well as a mapping of String patterns to
* delegate {@link FieldSetMapper}s. Each line received will be tokenized and
* then mapped to a field set.
*
* <p>
* Both the tokenizing and the mapping work in a similar way. The line will be
* checked for its prefix. If the prefix matches a key in the map of delegates,
* then the corresponding delegate will be used. Prefixes are sorted starting
* with the most specific, and the first match always succeeds.
* checked for its matching pattern. If the key matches a pattern in the map of
* delegates, then the corresponding delegate will be used. Patterns are sorted
* starting with the most specific, and the first match succeeds.
*
* @see PrefixMatchingCompositeLineTokenizer
* @see PatternMatchingCompositeLineTokenizer
*
* @author Dan Garrette
* @author Dave Syer
* @since 2.0
*/
public class PrefixMatchingCompositeLineMapper<T> implements LineMapper<T>, InitializingBean {
public class PatternMatchingCompositeLineMapper<T> implements LineMapper<T>, InitializingBean {
private PrefixMatchingCompositeLineTokenizer tokenizer = new PrefixMatchingCompositeLineTokenizer();
private PatternMatchingCompositeLineTokenizer tokenizer = new PatternMatchingCompositeLineTokenizer();
private PatternMatcher<FieldSetMapper<T>> patternMatcher;
@@ -77,14 +77,6 @@ public class PrefixMatchingCompositeLineMapper<T> implements LineMapper<T>, Init
public void setFieldSetMappers(Map<String, FieldSetMapper<T>> fieldSetMappers) {
Assert.isTrue(!fieldSetMappers.isEmpty(), "The 'fieldSetMappers' property must be non-empty");
LinkedHashMap<String, FieldSetMapper<T>> map = new LinkedHashMap<String, FieldSetMapper<T>>();
for (String key : fieldSetMappers.keySet()) {
FieldSetMapper<T> value = fieldSetMappers.get(key);
if (!key.endsWith("*")) {
key = key + "*";
}
map.put(key, value);
}
this.patternMatcher = new PatternMatcher<FieldSetMapper<T>>(map);
this.patternMatcher = new PatternMatcher<FieldSetMapper<T>>(fieldSetMappers);
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.batch.item.file.transform;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.batch.support.PatternMatcher;
@@ -25,17 +24,17 @@ import org.springframework.util.Assert;
/**
* A {@link LineTokenizer} implementation that stores a mapping of String
* prefixes to delegate {@link LineTokenizer}s. Each line tokenizied will be
* checked for its prefix. If the prefix matches a key in the map of delegates,
* then the corresponding delegate {@link LineTokenizer} will be used. Prefixes
* are sorted starting with the most specific, and the first match always
* succeeds.
* patterns to delegate {@link LineTokenizer}s. Each line tokenizied will be
* checked to see if it matches a pattern. If the line matches a key in the map
* of delegates, then the corresponding delegate {@link LineTokenizer} will be
* used. Patterns are sorted starting with the most specific, and the first
* match succeeds.
*
* @author Ben Hale
* @author Dan Garrette
* @author Dave Syer
*/
public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer, InitializingBean {
public class PatternMatchingCompositeLineTokenizer implements LineTokenizer, InitializingBean {
private PatternMatcher<LineTokenizer> tokenizers = null;
@@ -62,14 +61,6 @@ public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer, Init
public void setTokenizers(Map<String, LineTokenizer> tokenizers) {
Assert.isTrue(!tokenizers.isEmpty(), "The 'tokenizers' property must be non-empty");
LinkedHashMap<String, LineTokenizer> map = new LinkedHashMap<String, LineTokenizer>();
for (String key : tokenizers.keySet()) {
LineTokenizer value = tokenizers.get(key);
if (!key.endsWith("*")) {
key = key + "*";
}
map.put(key, value);
}
this.tokenizers = new PatternMatcher<LineTokenizer>(map);
this.tokenizers = new PatternMatcher<LineTokenizer>(tokenizers);
}
}

View File

@@ -34,9 +34,9 @@ import org.springframework.batch.item.file.transform.Name;
* @author Dave Syer
* @since 2.0
*/
public class PrefixMatchingCompositeLineMapperTests {
public class PatternMatchingCompositeLineMapperTests {
private PrefixMatchingCompositeLineMapper<Name> mapper = new PrefixMatchingCompositeLineMapper<Name>();
private PatternMatchingCompositeLineMapper<Name> mapper = new PatternMatchingCompositeLineMapper<Name>();
@Test(expected = IllegalArgumentException.class)
public void testNoMappers() throws Exception {
@@ -49,12 +49,12 @@ public class PrefixMatchingCompositeLineMapperTests {
@Test
public void testKeyFound() throws Exception {
Map<String, LineTokenizer> tokenizers = new HashMap<String, LineTokenizer>();
tokenizers.put("foo", new LineTokenizer() {
tokenizers.put("foo*", new LineTokenizer() {
public FieldSet tokenize(String line) {
return new DefaultFieldSet(new String[] { "a", "b" });
}
});
tokenizers.put("bar", new LineTokenizer() {
tokenizers.put("bar*", new LineTokenizer() {
public FieldSet tokenize(String line) {
return new DefaultFieldSet(new String[] { "c", "d" });
}
@@ -62,12 +62,12 @@ public class PrefixMatchingCompositeLineMapperTests {
mapper.setTokenizers(tokenizers);
Map<String, FieldSetMapper<Name>> fieldSetMappers = new HashMap<String, FieldSetMapper<Name>>();
fieldSetMappers.put("foo", new FieldSetMapper<Name>() {
fieldSetMappers.put("foo*", new FieldSetMapper<Name>() {
public Name mapFieldSet(FieldSet fs) {
return new Name(fs.readString(0), fs.readString(1), 0);
}
});
fieldSetMappers.put("bar", new FieldSetMapper<Name>() {
fieldSetMappers.put("bar*", new FieldSetMapper<Name>() {
public Name mapFieldSet(FieldSet fs) {
return new Name(fs.readString(1), fs.readString(0), 0);
}
@@ -81,12 +81,12 @@ public class PrefixMatchingCompositeLineMapperTests {
@Test(expected = IllegalStateException.class)
public void testMapperKeyNotFound() throws Exception {
Map<String, LineTokenizer> tokenizers = new HashMap<String, LineTokenizer>();
tokenizers.put("foo", new LineTokenizer() {
tokenizers.put("foo*", new LineTokenizer() {
public FieldSet tokenize(String line) {
return new DefaultFieldSet(new String[] { "a", "b" });
}
});
tokenizers.put("bar", new LineTokenizer() {
tokenizers.put("bar*", new LineTokenizer() {
public FieldSet tokenize(String line) {
return new DefaultFieldSet(new String[] { "c", "d" });
}
@@ -94,7 +94,7 @@ public class PrefixMatchingCompositeLineMapperTests {
mapper.setTokenizers(tokenizers);
Map<String, FieldSetMapper<Name>> fieldSetMappers = new HashMap<String, FieldSetMapper<Name>>();
fieldSetMappers.put("foo", new FieldSetMapper<Name>() {
fieldSetMappers.put("foo*", new FieldSetMapper<Name>() {
public Name mapFieldSet(FieldSet fs) {
return new Name(fs.readString(0), fs.readString(1), 0);
}

View File

@@ -28,10 +28,11 @@ import org.junit.Test;
/**
* @author Ben Hale
* @author Dan Garrette
* @author Dave Syer
*/
public class PrefixMatchingCompositeLineTokenizerTests {
public class PatternMatchingCompositeLineTokenizerTests {
private PrefixMatchingCompositeLineTokenizer tokenizer = new PrefixMatchingCompositeLineTokenizer();
private PatternMatchingCompositeLineTokenizer tokenizer = new PatternMatchingCompositeLineTokenizer();
@Test(expected = IllegalArgumentException.class)
public void testNoTokenizers() throws Exception {
@@ -42,7 +43,7 @@ public class PrefixMatchingCompositeLineTokenizerTests {
@Test
public void testEmptyKeyMatchesAnyLine() throws Exception {
Map<String, LineTokenizer> map = new HashMap<String, LineTokenizer>();
map.put("", new DelimitedLineTokenizer());
map.put("*", new DelimitedLineTokenizer());
map.put("foo", new LineTokenizer() {
public FieldSet tokenize(String line) {
return null;
@@ -58,12 +59,12 @@ public class PrefixMatchingCompositeLineTokenizerTests {
public void testEmptyKeyDoesNotMatchWhenAlternativeAvailable() throws Exception {
Map<String, LineTokenizer> map = new LinkedHashMap<String, LineTokenizer>();
map.put("", new LineTokenizer() {
map.put("*", new LineTokenizer() {
public FieldSet tokenize(String line) {
return null;
}
});
map.put("foo", new DelimitedLineTokenizer());
map.put("foo*", new DelimitedLineTokenizer());
tokenizer.setTokenizers(map);
tokenizer.afterPropertiesSet();
FieldSet fields = tokenizer.tokenize("foo,bar");
@@ -79,7 +80,7 @@ public class PrefixMatchingCompositeLineTokenizerTests {
@Test
public void testMatchWithPrefix() throws Exception {
tokenizer.setTokenizers(Collections.singletonMap("foo", (LineTokenizer) new LineTokenizer() {
tokenizer.setTokenizers(Collections.singletonMap("foo*", (LineTokenizer) new LineTokenizer() {
public FieldSet tokenize(String line) {
return new DefaultFieldSet(new String[] { line });
}

View File

@@ -25,17 +25,17 @@
</bean>
<bean id="prefixMatchingLineMapper"
class="org.springframework.batch.item.file.mapping.PrefixMatchingCompositeLineMapper">
class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
<property name="tokenizers">
<map>
<entry key="TRAD" value-ref="tradeLineTokenizer" />
<entry key="CUST" value-ref="customerLineTokenizer" />
<entry key="TRAD*" value-ref="tradeLineTokenizer" />
<entry key="CUST*" value-ref="customerLineTokenizer" />
</map>
</property>
<property name="fieldSetMappers">
<map>
<entry key="TRAD" value-ref="tradeFieldSetMapper" />
<entry key="CUST" value-ref="customerFieldSetMapper" />
<entry key="TRAD*" value-ref="tradeFieldSetMapper" />
<entry key="CUST*" value-ref="customerFieldSetMapper" />
</map>
</property>
</bean>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans"
<beans xmlns:batch="http://www.springframework.org/schema/batch"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
@@ -10,71 +10,71 @@
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<job id="multilineJob">
<step id="step1">
<tasklet reader="reader" writer="writer" commit-interval="1">
<streams>
<stream ref="fileItemReader"/>
</streams>
</tasklet>
</step>
</job>
<batch:job id="multilineJob">
<batch:step id="step1">
<batch:tasklet reader="reader" writer="writer" commit-interval="1">
<batch:streams>
<batch:stream ref="fileItemReader"/>
</batch:streams>
</batch:tasklet>
</batch:step>
</batch:job>
<beans:bean id="reader" class="org.springframework.batch.sample.domain.multiline.AggregateItemReader">
<beans:property name="itemReader" ref="fileItemReader" />
</beans:bean>
<bean id="reader" class="org.springframework.batch.sample.domain.multiline.AggregateItemReader">
<property name="itemReader" ref="fileItemReader" />
</bean>
<beans:bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
<beans:property name="resource" value="file:target/test-outputs/20070122.testStream.multilineStep.txt" />
<beans:property name="lineAggregator">
<beans:bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/>
</beans:property>
</beans:bean>
<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:target/test-outputs/20070122.testStream.multilineStep.txt" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/>
</property>
</bean>
<beans:bean id="fileItemReader"
<bean id="fileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader">
<beans:property name="resource"
<property name="resource"
value="classpath:data/multilineJob/input/20070122.teststream.multilineStep.txt" />
<beans:property name="lineMapper">
<beans:bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<beans:property name="lineTokenizer" ref="fixedFileDescriptor" />
<beans:property name="fieldSetMapper">
<beans:bean class="org.springframework.batch.sample.domain.multiline.AggregateItemFieldSetMapper">
<beans:property name="delegate" ref="tradeLineMapper" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:property>
</beans:bean>
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer" ref="fixedFileDescriptor" />
<property name="fieldSetMapper">
<bean class="org.springframework.batch.sample.domain.multiline.AggregateItemFieldSetMapper">
<property name="delegate" ref="tradeLineMapper" />
</bean>
</property>
</bean>
</property>
</bean>
<beans:bean id="tradeLineMapper"
<bean id="tradeLineMapper"
class="org.springframework.batch.sample.domain.trade.internal.TradeFieldSetMapper" />
<beans:bean id="fixedFileDescriptor"
class="org.springframework.batch.item.file.transform.PrefixMatchingCompositeLineTokenizer">
<beans:property name="tokenizers">
<beans:map>
<beans:entry key="BEGIN" value-ref="beginRecordTokenizer" />
<beans:entry key="END" value-ref="endRecordTokenizer" />
<beans:entry key="" value-ref="tradeRecordTokenizer" />
</beans:map>
</beans:property>
</beans:bean>
<bean id="fixedFileDescriptor"
class="org.springframework.batch.item.file.transform.PatternMatchingCompositeLineTokenizer">
<property name="tokenizers">
<map>
<entry key="BEGIN*" value-ref="beginRecordTokenizer" />
<entry key="END*" value-ref="endRecordTokenizer" />
<entry key="*" value-ref="tradeRecordTokenizer" />
</map>
</property>
</bean>
<beans:bean id="beginRecordTokenizer"
<bean id="beginRecordTokenizer"
class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
<beans:property name="columns" value="1-5" />
</beans:bean>
<property name="columns" value="1-5" />
</bean>
<beans:bean id="endRecordTokenizer"
<bean id="endRecordTokenizer"
class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
<beans:property name="columns" value="1-3" />
</beans:bean>
<property name="columns" value="1-3" />
</bean>
<beans:bean id="tradeRecordTokenizer"
<bean id="tradeRecordTokenizer"
class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
<beans:property name="names" value="ISIN,Quantity,Price,Customer" />
<beans:property name="columns" value="1-12, 13-15, 16-20, 21-29" />
</beans:bean>
<property name="names" value="ISIN,Quantity,Price,Customer" />
<property name="columns" value="1-12, 13-15, 16-20, 21-29" />
</bean>
</beans:beans>
</beans>

View File

@@ -4,19 +4,19 @@
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="orderFileTokenizer"
class="org.springframework.batch.item.file.transform.PrefixMatchingCompositeLineTokenizer">
class="org.springframework.batch.item.file.transform.PatternMatchingCompositeLineTokenizer">
<property name="tokenizers">
<map>
<entry key="HEA" value-ref="headerRecordTokenizer" />
<entry key="FOT" value-ref="footerRecordTokenizer" />
<entry key="BCU" value-ref="businessCustomerLineTokenizer" />
<entry key="NCU" value-ref="customerLineTokenizer" />
<entry key="BAD" value-ref="billingAddressLineTokenizer" />
<entry key="SAD" value-ref="shippingAddressLineTokenizer" />
<entry key="BIN" value-ref="billingLineTokenizer" />
<entry key="SIN" value-ref="shippingLineTokenizer" />
<entry key="LIT" value-ref="itemLineTokenizer" />
<entry key="" value-ref="defaultLineTokenizer" />
<entry key="HEA*" value-ref="headerRecordTokenizer" />
<entry key="FOT*" value-ref="footerRecordTokenizer" />
<entry key="BCU*" value-ref="businessCustomerLineTokenizer" />
<entry key="NCU*" value-ref="customerLineTokenizer" />
<entry key="BAD*" value-ref="billingAddressLineTokenizer" />
<entry key="SAD*" value-ref="shippingAddressLineTokenizer" />
<entry key="BIN*" value-ref="billingLineTokenizer" />
<entry key="SIN*" value-ref="shippingLineTokenizer" />
<entry key="LIT*" value-ref="itemLineTokenizer" />
<entry key="*" value-ref="defaultLineTokenizer" />
</map>
</property>
</bean>