Fixing the lack of ClassLoader consideration in the compareTo methods of enum.Enum and enums.Enum, along with unit tests, as mentioned on Bugzilla entry #32619 by Kathy Van Stone.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@398812 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2006-05-02 05:21:00 +00:00
parent 56541a7485
commit c803367cd0
4 changed files with 88 additions and 33 deletions

View File

@ -543,18 +543,10 @@ public abstract class Enum implements Comparable, Serializable {
return iName.equals(((Enum) other).iName); return iName.equals(((Enum) other).iName);
} else { } else {
// This and other are in different class loaders, we must use reflection. // This and other are in different class loaders, we must use reflection.
try { if (other.getClass().getName().equals(this.getClass().getName()) == false) {
Method mth = other.getClass().getMethod("getName", null); return false;
String name = (String) mth.invoke(other, null);
return iName.equals(name);
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
} }
return false; return iName.equals( getNameInOtherClassLoader(other) );
} }
} }
@ -573,6 +565,9 @@ public abstract class Enum implements Comparable, Serializable {
* <p>The default ordering is alphabetic by name, but this * <p>The default ordering is alphabetic by name, but this
* can be overridden by subclasses.</p> * can be overridden by subclasses.</p>
* *
* <p>If the parameter is in a different class loader than this instance,
* reflection is used to compare the names.</p>
*
* @see java.lang.Comparable#compareTo(Object) * @see java.lang.Comparable#compareTo(Object)
* @param other the other object to compare to * @param other the other object to compare to
* @return -ve if this is less than the other object, +ve if greater * @return -ve if this is less than the other object, +ve if greater
@ -584,9 +579,29 @@ public abstract class Enum implements Comparable, Serializable {
if (other == this) { if (other == this) {
return 0; return 0;
} }
if (other.getClass() != this.getClass()) {
if (other.getClass().getName().equals(this.getClass().getName())) {
return iName.compareTo( getNameInOtherClassLoader(other) );
}
}
return iName.compareTo(((Enum) other).iName); return iName.compareTo(((Enum) other).iName);
} }
private String getNameInOtherClassLoader(Object other) {
try {
Method mth = other.getClass().getMethod("getName", null);
String name = (String) mth.invoke(other, null);
return name;
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
}
throw new IllegalStateException("This should not happen");
}
/** /**
* <p>Human readable description of this Enum item.</p> * <p>Human readable description of this Enum item.</p>
* *

View File

@ -543,18 +543,7 @@ public abstract class Enum implements Comparable, Serializable {
if (other.getClass().getName().equals(this.getClass().getName()) == false) { if (other.getClass().getName().equals(this.getClass().getName()) == false) {
return false; return false;
} }
try { return iName.equals( getNameInOtherClassLoader(other) );
Method mth = other.getClass().getMethod("getName", null);
String name = (String) mth.invoke(other, null);
return iName.equals(name);
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
}
return false;
} }
} }
@ -573,6 +562,9 @@ public abstract class Enum implements Comparable, Serializable {
* <p>The default ordering is alphabetic by name, but this * <p>The default ordering is alphabetic by name, but this
* can be overridden by subclasses.</p> * can be overridden by subclasses.</p>
* *
* <p>If the parameter is in a different class loader than this instance,
* reflection is used to compare the names.</p>
*
* @see java.lang.Comparable#compareTo(Object) * @see java.lang.Comparable#compareTo(Object)
* @param other the other object to compare to * @param other the other object to compare to
* @return -ve if this is less than the other object, +ve if greater * @return -ve if this is less than the other object, +ve if greater
@ -584,9 +576,29 @@ public abstract class Enum implements Comparable, Serializable {
if (other == this) { if (other == this) {
return 0; return 0;
} }
if (other.getClass() != this.getClass()) {
if (other.getClass().getName().equals(this.getClass().getName())) {
return iName.compareTo( getNameInOtherClassLoader(other) );
}
}
return iName.compareTo(((Enum) other).iName); return iName.compareTo(((Enum) other).iName);
} }
private String getNameInOtherClassLoader(Object other) {
try {
Method mth = other.getClass().getMethod("getName", null);
String name = (String) mth.invoke(other, null);
return name;
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
}
throw new IllegalStateException("This should not happen");
}
/** /**
* <p>Human readable description of this Enum item.</p> * <p>Human readable description of this Enum item.</p>
* *

View File

@ -444,12 +444,12 @@ public final class EnumTest extends TestCase {
public void testColorEnumEqualsWithDifferentClassLoaders() throws SecurityException, IllegalArgumentException, public void testColorEnumEqualsWithDifferentClassLoaders() throws SecurityException, IllegalArgumentException,
ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.BLUE); this.testWithDifferentClassLoaders(ColorEnum.BLUE);
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.GREEN); this.testWithDifferentClassLoaders(ColorEnum.GREEN);
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.RED); this.testWithDifferentClassLoaders(ColorEnum.RED);
} }
void testEqualsTrueWithDifferentClassLoaders(ColorEnum colorEnum) throws ClassNotFoundException, SecurityException, void testWithDifferentClassLoaders(ColorEnum colorEnum) throws ClassNotFoundException, SecurityException,
NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
// Sanity checks: // Sanity checks:
assertTrue(colorEnum.equals(colorEnum)); assertTrue(colorEnum.equals(colorEnum));
@ -457,6 +457,7 @@ public final class EnumTest extends TestCase {
// set up: // set up:
ClassLoader classLoader = ClassUtilsTest.newSystemClassLoader(); ClassLoader classLoader = ClassUtilsTest.newSystemClassLoader();
Object enumObjectFromOtherClassLoader = this.getColorEnum(classLoader, colorEnum.getName()); Object enumObjectFromOtherClassLoader = this.getColorEnum(classLoader, colorEnum.getName());
// the real test, part 1. // the real test, part 1.
try { try {
ColorEnum testCase = (ColorEnum) enumObjectFromOtherClassLoader; ColorEnum testCase = (ColorEnum) enumObjectFromOtherClassLoader;
@ -464,10 +465,12 @@ public final class EnumTest extends TestCase {
} catch (ClassCastException e) { } catch (ClassCastException e) {
// normal. // normal.
} }
// the real test, part 2. // the real test, part 2.
assertEquals("The two objects should match even though they are from different class loaders", colorEnum, assertEquals("The two objects should match even though they are from different class loaders", colorEnum,
enumObjectFromOtherClassLoader); enumObjectFromOtherClassLoader);
// the real test, part 3.
// the real test, part 3 - testing equals(Object)
int falseCount = 0; int falseCount = 0;
for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) { for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) {
ColorEnum element = (ColorEnum) iter.next(); ColorEnum element = (ColorEnum) iter.next();
@ -477,6 +480,17 @@ public final class EnumTest extends TestCase {
} }
} }
assertEquals(ColorEnum.getEnumList().size() - 1, falseCount); assertEquals(ColorEnum.getEnumList().size() - 1, falseCount);
// the real test, part 4 - testing compareTo(Object) == 0
falseCount = 0;
for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) {
ColorEnum element = (ColorEnum) iter.next();
if (!colorEnum.equals(element)) {
falseCount++;
assertFalse( ((Comparable)enumObjectFromOtherClassLoader).compareTo(element) == 0);
}
}
assertEquals(ColorEnum.getEnumList().size() - 1, falseCount);
} }
Object getColorEnum(ClassLoader classLoader, String color) throws ClassNotFoundException, SecurityException, Object getColorEnum(ClassLoader classLoader, String color) throws ClassNotFoundException, SecurityException,

View File

@ -442,12 +442,12 @@ public final class EnumTest extends TestCase {
public void testColorEnumEqualsWithDifferentClassLoaders() throws SecurityException, IllegalArgumentException, public void testColorEnumEqualsWithDifferentClassLoaders() throws SecurityException, IllegalArgumentException,
ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.BLUE); this.testWithDifferentClassLoaders(ColorEnum.BLUE);
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.GREEN); this.testWithDifferentClassLoaders(ColorEnum.GREEN);
this.testEqualsTrueWithDifferentClassLoaders(ColorEnum.RED); this.testWithDifferentClassLoaders(ColorEnum.RED);
} }
void testEqualsTrueWithDifferentClassLoaders(ColorEnum colorEnum) throws ClassNotFoundException, SecurityException, void testWithDifferentClassLoaders(ColorEnum colorEnum) throws ClassNotFoundException, SecurityException,
NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
// Sanity checks: // Sanity checks:
assertTrue(colorEnum.equals(colorEnum)); assertTrue(colorEnum.equals(colorEnum));
@ -455,6 +455,7 @@ public final class EnumTest extends TestCase {
// set up: // set up:
ClassLoader classLoader = ClassUtilsTest.newSystemClassLoader(); ClassLoader classLoader = ClassUtilsTest.newSystemClassLoader();
Object enumObjectFromOtherClassLoader = this.getColorEnum(classLoader, colorEnum.getName()); Object enumObjectFromOtherClassLoader = this.getColorEnum(classLoader, colorEnum.getName());
// the real test, part 1. // the real test, part 1.
try { try {
ColorEnum testCase = (ColorEnum) enumObjectFromOtherClassLoader; ColorEnum testCase = (ColorEnum) enumObjectFromOtherClassLoader;
@ -462,10 +463,12 @@ public final class EnumTest extends TestCase {
} catch (ClassCastException e) { } catch (ClassCastException e) {
// normal. // normal.
} }
// the real test, part 2. // the real test, part 2.
assertEquals("The two objects should match even though they are from different class loaders", colorEnum, assertEquals("The two objects should match even though they are from different class loaders", colorEnum,
enumObjectFromOtherClassLoader); enumObjectFromOtherClassLoader);
// the real test, part 3.
// the real test, part 3 - testing equals(Object)
int falseCount = 0; int falseCount = 0;
for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) { for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) {
ColorEnum element = (ColorEnum) iter.next(); ColorEnum element = (ColorEnum) iter.next();
@ -475,6 +478,17 @@ public final class EnumTest extends TestCase {
} }
} }
assertEquals(ColorEnum.getEnumList().size() - 1, falseCount); assertEquals(ColorEnum.getEnumList().size() - 1, falseCount);
// the real test, part 4 - testing compareTo(Object) == 0
falseCount = 0;
for (Iterator iter = ColorEnum.iterator(); iter.hasNext();) {
ColorEnum element = (ColorEnum) iter.next();
if (!colorEnum.equals(element)) {
falseCount++;
assertFalse( ((Comparable)enumObjectFromOtherClassLoader).compareTo(element) == 0);
}
}
assertEquals(ColorEnum.getEnumList().size() - 1, falseCount);
} }
Object getColorEnum(ClassLoader classLoader, String color) throws ClassNotFoundException, SecurityException, Object getColorEnum(ClassLoader classLoader, String color) throws ClassNotFoundException, SecurityException,