Allow SubclassClassifier to work with interfaces
Up to now it only traversed superclasses. Now it looks at the interfaces as well.
This commit is contained in:
@@ -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 <T> the type of the thing to classify
|
||||
* @param <C> the output of the classifier
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
@@ -49,7 +49,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
|
||||
/**
|
||||
* Create a {@link SubclassClassifier} with supplied default value.
|
||||
*
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public SubclassClassifier(C defaultValue) {
|
||||
@@ -58,7 +57,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
|
||||
/**
|
||||
* 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<T, C> implements Classifier<T, C> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T, C> implements Classifier<T, C> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Class<? extends T>, C> map) {
|
||||
@@ -90,37 +86,51 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<? extends T> exceptionClass = (Class<? extends T>) 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<T, C> implements Classifier<T, C> {
|
||||
|
||||
/**
|
||||
* 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<Class<? extends T>, C> getClassified() {
|
||||
return classified;
|
||||
return this.classified;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object, String> classifier = new SubclassClassifier<Object, String>();
|
||||
classifier.setTypeMap(
|
||||
Collections.<Class<?>, String>singletonMap(Supplier.class, "foo"));
|
||||
assertEquals("foo", classifier.classify(new Foo()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifyInterfaceOfParent() {
|
||||
SubclassClassifier<Object, String> classifier = new SubclassClassifier<Object, String>();
|
||||
classifier.setTypeMap(
|
||||
Collections.<Class<?>, String>singletonMap(Supplier.class, "foo"));
|
||||
assertEquals("foo", classifier.classify(new Bar()));
|
||||
}
|
||||
|
||||
public class Bar extends Foo {
|
||||
|
||||
}
|
||||
|
||||
public static class Foo implements Supplier<String> {
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Throwable, String>("foo");
|
||||
assertEquals("foo", classifier.classify(null));
|
||||
this.classifier = new SubclassClassifier<Throwable, String>("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.<Class<? extends Throwable>, String> singletonMap(
|
||||
IllegalStateException.class, "foo"));
|
||||
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
|
||||
this.classifier
|
||||
.setTypeMap(Collections.<Class<? extends Throwable>, String>singletonMap(
|
||||
IllegalStateException.class, "foo"));
|
||||
assertEquals("foo", this.classifier.classify(new IllegalStateException("Foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifySubclassMatch() {
|
||||
classifier.setTypeMap(Collections.<Class<? extends Throwable>, String> singletonMap(RuntimeException.class,
|
||||
"foo"));
|
||||
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
|
||||
this.classifier
|
||||
.setTypeMap(Collections.<Class<? extends Throwable>, String>singletonMap(
|
||||
RuntimeException.class, "foo"));
|
||||
assertEquals("foo", this.classifier.classify(new IllegalStateException("Foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifySuperclassDoesNotMatch() {
|
||||
classifier.setTypeMap(Collections.<Class<? extends Throwable>, String> singletonMap(
|
||||
IllegalStateException.class, "foo"));
|
||||
assertEquals(classifier.getDefault(), classifier.classify(new RuntimeException("Foo")));
|
||||
this.classifier
|
||||
.setTypeMap(Collections.<Class<? extends Throwable>, 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<Class<? extends Throwable>, String>() {
|
||||
this.classifier.setTypeMap(new HashMap<Class<? extends Throwable>, 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<Throwable, String>();
|
||||
classifier.setTypeMap(new HashMap<Class<? extends Throwable>, String>() {
|
||||
this.classifier = new SubclassClassifier<Throwable, String>();
|
||||
this.classifier.setTypeMap(new HashMap<Class<? extends Throwable>, 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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user