diff --git a/src/main/java/org/springframework/classify/SubclassClassifier.java b/src/main/java/org/springframework/classify/SubclassClassifier.java index f5ae984..900b98d 100644 --- a/src/main/java/org/springframework/classify/SubclassClassifier.java +++ b/src/main/java/org/springframework/classify/SubclassClassifier.java @@ -21,16 +21,16 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** - * A {@link Classifier} for a parameterised object type based on a map. - * Classifies objects according to their inheritance relation with the supplied - * type map. If the object to be classified is one of the keys of the provided - * map, or is a subclass of one of the keys, then the map entry value for that - * key is returned. Otherwise returns the default value which is null by - * default. + * A {@link Classifier} for a parameterised object type based on a map. Classifies objects + * according to their inheritance relation with the supplied type map. If the object to be + * classified is one of the keys of the provided map, or is a subclass of one of the keys, + * then the map entry value for that key is returned. Otherwise returns the default value + * which is null by default. * * @author Dave Syer * @author Gary Russell - * + * @param the type of the thing to classify + * @param the output of the classifier */ @SuppressWarnings("serial") public class SubclassClassifier implements Classifier { @@ -49,7 +49,6 @@ public class SubclassClassifier implements Classifier { /** * Create a {@link SubclassClassifier} with supplied default value. - * * @param defaultValue the default value */ public SubclassClassifier(C defaultValue) { @@ -58,7 +57,6 @@ public class SubclassClassifier implements Classifier { /** * Create a {@link SubclassClassifier} with supplied default value. - * * @param defaultValue the default value * @param typeMap the map of types */ @@ -69,9 +67,8 @@ public class SubclassClassifier implements Classifier { } /** - * Public setter for the default value for mapping keys that are not found - * in the map (or their subclasses). Defaults to false. - * + * Public setter for the default value for mapping keys that are not found in the map + * (or their subclasses). Defaults to false. * @param defaultValue the default value to set */ public void setDefaultValue(C defaultValue) { @@ -79,10 +76,9 @@ public class SubclassClassifier implements Classifier { } /** - * Set the classifications up as a map. The keys are types and these will be - * mapped along with all their subclasses to the corresponding value. The - * most specific types will match first. - * + * Set the classifications up as a map. The keys are types and these will be mapped + * along with all their subclasses to the corresponding value. The most specific types + * will match first. * @param map a map from type to class */ public void setTypeMap(Map, C> map) { @@ -90,37 +86,51 @@ public class SubclassClassifier implements Classifier { } /** - * Return the value from the type map whose key is the class of the given - * Throwable, or its nearest ancestor if a subclass. - * + * Return the value from the type map whose key is the class of the given Throwable, + * or its nearest ancestor if a subclass. * @return C the classified value * @param classifiable the classifiable thing */ + @Override public C classify(T classifiable) { if (classifiable == null) { - return defaultValue; + return this.defaultValue; } @SuppressWarnings("unchecked") Class exceptionClass = (Class) classifiable.getClass(); - if (classified.containsKey(exceptionClass)) { - return classified.get(exceptionClass); + if (this.classified.containsKey(exceptionClass)) { + return this.classified.get(exceptionClass); } // check for subclasses C value = null; - for (Class cls = exceptionClass; !cls.equals(Object.class) && value == null; cls = cls.getSuperclass()) { - value = classified.get(cls); + for (Class cls = exceptionClass; !cls.equals(Object.class) + && value == null; cls = cls.getSuperclass()) { + value = this.classified.get(cls); } - //ConcurrentHashMap doesn't allow nulls + // check for interfaces subclasses + if (value == null) { + for (Class cls = exceptionClass; !cls.equals(Object.class) + && value == null; cls = cls.getSuperclass()) { + for (Class ifc : cls.getInterfaces()) { + value = this.classified.get(ifc); + if (value != null) { + break; + } + } + } + } + + // ConcurrentHashMap doesn't allow nulls if (value != null) { this.classified.put(exceptionClass, value); } if (value == null) { - value = defaultValue; + value = this.defaultValue; } return value; @@ -128,14 +138,14 @@ public class SubclassClassifier implements Classifier { /** * Return the default value supplied in the constructor (default false). - * * @return C the default value */ final public C getDefault() { - return defaultValue; + return this.defaultValue; } protected Map, C> getClassified() { - return classified; + return this.classified; } + } diff --git a/src/test/java/org/springframework/classify/SubclassClassifierTests.java b/src/test/java/org/springframework/classify/SubclassClassifierTests.java new file mode 100644 index 0000000..88efea9 --- /dev/null +++ b/src/test/java/org/springframework/classify/SubclassClassifierTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2006-2007 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 + * + * https://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.classify; + +import java.util.Collections; +import java.util.function.Supplier; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SubclassClassifierTests { + + @Test + public void testClassifyInterface() { + SubclassClassifier classifier = new SubclassClassifier(); + classifier.setTypeMap( + Collections., String>singletonMap(Supplier.class, "foo")); + assertEquals("foo", classifier.classify(new Foo())); + } + + @Test + public void testClassifyInterfaceOfParent() { + SubclassClassifier classifier = new SubclassClassifier(); + classifier.setTypeMap( + Collections., String>singletonMap(Supplier.class, "foo")); + assertEquals("foo", classifier.classify(new Bar())); + } + + public class Bar extends Foo { + + } + + public static class Foo implements Supplier { + + @Override + public String get() { + return "foo"; + } + + } + +} diff --git a/src/test/java/org/springframework/classify/SubclassExceptionClassifierTests.java b/src/test/java/org/springframework/classify/SubclassExceptionClassifierTests.java index be87f43..46d02e7 100644 --- a/src/test/java/org/springframework/classify/SubclassExceptionClassifierTests.java +++ b/src/test/java/org/springframework/classify/SubclassExceptionClassifierTests.java @@ -16,8 +16,6 @@ package org.springframework.classify; -import org.junit.Test; - import java.io.FileNotFoundException; import java.net.ConnectException; import java.net.SocketException; @@ -25,6 +23,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.NoSuchElementException; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -34,64 +34,68 @@ public class SubclassExceptionClassifierTests { @Test public void testClassifyNullIsDefault() { - assertEquals(classifier.classify(null), classifier.getDefault()); + assertEquals(this.classifier.classify(null), this.classifier.getDefault()); } @Test public void testClassifyNull() { - assertNull(classifier.classify(null)); + assertNull(this.classifier.classify(null)); } @Test public void testClassifyNullNonDefault() { - classifier = new SubclassClassifier("foo"); - assertEquals("foo", classifier.classify(null)); + this.classifier = new SubclassClassifier("foo"); + assertEquals("foo", this.classifier.classify(null)); } @Test public void testClassifyRandomException() { - assertNull(classifier.classify(new IllegalStateException("Foo"))); + assertNull(this.classifier.classify(new IllegalStateException("Foo"))); } @Test public void testClassifyExactMatch() { - classifier.setTypeMap(Collections., String> singletonMap( - IllegalStateException.class, "foo")); - assertEquals("foo", classifier.classify(new IllegalStateException("Foo"))); + this.classifier + .setTypeMap(Collections., String>singletonMap( + IllegalStateException.class, "foo")); + assertEquals("foo", this.classifier.classify(new IllegalStateException("Foo"))); } @Test public void testClassifySubclassMatch() { - classifier.setTypeMap(Collections., String> singletonMap(RuntimeException.class, - "foo")); - assertEquals("foo", classifier.classify(new IllegalStateException("Foo"))); + this.classifier + .setTypeMap(Collections., String>singletonMap( + RuntimeException.class, "foo")); + assertEquals("foo", this.classifier.classify(new IllegalStateException("Foo"))); } @Test public void testClassifySuperclassDoesNotMatch() { - classifier.setTypeMap(Collections., String> singletonMap( - IllegalStateException.class, "foo")); - assertEquals(classifier.getDefault(), classifier.classify(new RuntimeException("Foo"))); + this.classifier + .setTypeMap(Collections., String>singletonMap( + IllegalStateException.class, "foo")); + assertEquals(this.classifier.getDefault(), + this.classifier.classify(new RuntimeException("Foo"))); } @SuppressWarnings("serial") @Test public void testClassifyAncestorMatch() { - classifier.setTypeMap(new HashMap, String>() { + this.classifier.setTypeMap(new HashMap, String>() { { put(Exception.class, "foo"); put(IllegalArgumentException.class, "bar"); put(RuntimeException.class, "spam"); } }); - assertEquals("spam", classifier.classify(new IllegalStateException("Foo"))); + assertEquals("spam", this.classifier.classify(new IllegalStateException("Foo"))); } @SuppressWarnings("serial") @Test public void testClassifyAncestorMatch2() { - classifier = new SubclassClassifier(); - classifier.setTypeMap(new HashMap, String>() { + this.classifier = new SubclassClassifier(); + this.classifier.setTypeMap(new HashMap, String>() { { put(SocketException.class, "1"); put(FileNotFoundException.class, "buz"); @@ -102,10 +106,11 @@ public class SubclassExceptionClassifierTests { put(ConnectException.class, "2"); } }); - assertEquals("2", classifier.classify(new SubConnectException())); + assertEquals("2", this.classifier.classify(new SubConnectException())); } public static class SubConnectException extends ConnectException { } + }