diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java new file mode 100644 index 000000000..a2ccc60c9 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.support; + +import org.springframework.batch.item.ItemProcessor; +import org.springframework.classify.Classifier; +import org.springframework.classify.ClassifierSupport; + +/** + * Calls one of a collection of ItemProcessors, based on a router + * pattern implemented through the provided {@link Classifier}. + * + * Note the user is responsible for injecting a {@link Classifier} + * that returns an ItemProcessor that conforms to the declared input and output types. + * + * @author Jimmy Praet + * @since 3.0 + */ +public class ClassifierCompositeItemProcessor implements ItemProcessor { + + private Classifier> classifier = + new ClassifierSupport> (null); + + /** + * @param classifier the classifier to set + */ + public void setClassifier(Classifier> classifier) { + this.classifier = classifier; + } + + /** + * Delegates to injected {@link ItemProcessor} instances according to the + * classification by the {@link Classifier}. + */ + @Override + public O process(I item) throws Exception { + return processItem(classifier.classify(item), item); + } + + /* + * Helper method to work around wildcard capture compiler error: see http://docs.oracle.com/javase/tutorial/java/generics/capture.html + * The method process(capture#4-of ?) in the type ItemProcessor is not applicable for the arguments (I) + */ + @SuppressWarnings("unchecked") + private O processItem(ItemProcessor processor, I input) throws Exception { + return processor.process((T) input); + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessorTests.java new file mode 100644 index 000000000..3a7f3751f --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessorTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.item.support; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.classify.PatternMatchingClassifier; +import org.springframework.classify.SubclassClassifier; + +/** + * @author Jimmy Praet + */ +public class ClassifierCompositeItemProcessorTests { + + @Test + public void testBasicClassifierCompositeItemProcessor() throws Exception { + ClassifierCompositeItemProcessor processor = new ClassifierCompositeItemProcessor(); + + ItemProcessor fooProcessor = new ItemProcessor() { + @Override + public String process(String item) throws Exception { + return "foo: " + item; + } + }; + ItemProcessor defaultProcessor = new ItemProcessor() { + @Override + public String process(String item) throws Exception { + return item; + } + }; + + Map> routingConfiguration = + new HashMap>(); + routingConfiguration.put("foo", fooProcessor); + routingConfiguration.put("*", defaultProcessor); + processor.setClassifier(new PatternMatchingClassifier>(routingConfiguration)); + + assertEquals("bar", processor.process("bar")); + assertEquals("foo: foo", processor.process("foo")); + assertEquals("baz", processor.process("baz")); + } + + /** + * Test the ClassifierCompositeItemProcessor with delegates that have more specific generic types for input as well as output. + */ + @Test + public void testGenericsClassifierCompositeItemProcessor() throws Exception { + ClassifierCompositeItemProcessor processor = new ClassifierCompositeItemProcessor(); + + ItemProcessor intProcessor = new ItemProcessor() { + @Override + public String process(Integer item) throws Exception { + return "int: " + item; + } + }; + ItemProcessor longProcessor = new ItemProcessor() { + @Override + public StringBuffer process(Long item) throws Exception { + return new StringBuffer("long: " + item); + } + }; + ItemProcessor defaultProcessor = new ItemProcessor() { + @Override + public StringBuilder process(Number item) throws Exception { + return new StringBuilder("number: " + item); + } + }; + + SubclassClassifier> classifier = + new SubclassClassifier>(); + Map, ItemProcessor> typeMap = + new HashMap, ItemProcessor>(); + typeMap.put(Integer.class, intProcessor); + typeMap.put(Long.class, longProcessor); + typeMap.put(Number.class, defaultProcessor); + classifier.setTypeMap(typeMap); + processor.setClassifier(classifier); + + assertEquals("int: 1", processor.process(Integer.valueOf(1)).toString()); + assertEquals("long: 2", processor.process(Long.valueOf(2)).toString()); + assertEquals("number: 3", processor.process(Byte.valueOf((byte) 3)).toString()); + } + +}