1 /* 2 * Copyright (C) 2009 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.collect.Lists.transform; 20 import static com.google.common.collect.Sets.difference; 21 import static com.google.common.collect.Sets.newHashSet; 22 import static java.lang.reflect.Modifier.isPublic; 23 import static java.lang.reflect.Modifier.isStatic; 24 25 import com.google.common.base.Function; 26 import com.google.common.base.Joiner; 27 import com.google.common.base.Objects; 28 import java.lang.reflect.Method; 29 import java.lang.reflect.Type; 30 import java.lang.reflect.TypeVariable; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.Locale; 34 import java.util.Map; 35 import java.util.Set; 36 import junit.framework.TestCase; 37 38 /** 39 * Tests that all {@code public static} methods "inherited" from superclasses are "overridden" in 40 * each immutable-collection class. This ensures, for example, that a call written "{@code 41 * ImmutableSortedSet.copyOf()}" cannot secretly be a call to {@code ImmutableSet.copyOf()}. 42 * 43 * @author Chris Povirk 44 */ 45 public class FauxveridesTest extends TestCase { testImmutableBiMap()46 public void testImmutableBiMap() { 47 doHasAllFauxveridesTest(ImmutableBiMap.class, ImmutableMap.class); 48 } 49 testImmutableListMultimap()50 public void testImmutableListMultimap() { 51 doHasAllFauxveridesTest(ImmutableListMultimap.class, ImmutableMultimap.class); 52 } 53 testImmutableSetMultimap()54 public void testImmutableSetMultimap() { 55 doHasAllFauxveridesTest(ImmutableSetMultimap.class, ImmutableMultimap.class); 56 } 57 testImmutableSortedMap()58 public void testImmutableSortedMap() { 59 doHasAllFauxveridesTest(ImmutableSortedMap.class, ImmutableMap.class); 60 } 61 testImmutableSortedSet()62 public void testImmutableSortedSet() { 63 doHasAllFauxveridesTest(ImmutableSortedSet.class, ImmutableSet.class); 64 } 65 testImmutableSortedMultiset()66 public void testImmutableSortedMultiset() { 67 doHasAllFauxveridesTest(ImmutableSortedMultiset.class, ImmutableMultiset.class); 68 } 69 70 /* 71 * Demonstrate that ClassCastException is possible when calling 72 * ImmutableSorted{Set,Map}.copyOf(), whose type parameters we are unable to 73 * restrict (see ImmutableSortedSetFauxverideShim). 74 */ 75 testImmutableSortedMapCopyOfMap()76 public void testImmutableSortedMapCopyOfMap() { 77 Map<Object, Object> original = 78 ImmutableMap.of(new Object(), new Object(), new Object(), new Object()); 79 80 try { 81 ImmutableSortedMap.copyOf(original); 82 fail(); 83 } catch (ClassCastException expected) { 84 } 85 } 86 testImmutableSortedSetCopyOfIterable()87 public void testImmutableSortedSetCopyOfIterable() { 88 Set<Object> original = ImmutableSet.of(new Object(), new Object()); 89 90 try { 91 ImmutableSortedSet.copyOf(original); 92 fail(); 93 } catch (ClassCastException expected) { 94 } 95 } 96 testImmutableSortedSetCopyOfIterator()97 public void testImmutableSortedSetCopyOfIterator() { 98 Set<Object> original = ImmutableSet.of(new Object(), new Object()); 99 100 try { 101 ImmutableSortedSet.copyOf(original.iterator()); 102 fail(); 103 } catch (ClassCastException expected) { 104 } 105 } 106 doHasAllFauxveridesTest(Class<?> descendant, Class<?> ancestor)107 private void doHasAllFauxveridesTest(Class<?> descendant, Class<?> ancestor) { 108 Set<MethodSignature> required = getAllRequiredToFauxveride(ancestor); 109 Set<MethodSignature> found = getAllFauxveridden(descendant, ancestor); 110 Set<MethodSignature> missing = ImmutableSortedSet.copyOf(difference(required, found)); 111 if (!missing.isEmpty()) { 112 fail( 113 rootLocaleFormat( 114 "%s should hide the public static methods declared in %s: %s", 115 descendant.getSimpleName(), ancestor.getSimpleName(), missing)); 116 } 117 } 118 getAllRequiredToFauxveride(Class<?> ancestor)119 private static Set<MethodSignature> getAllRequiredToFauxveride(Class<?> ancestor) { 120 return getPublicStaticMethodsBetween(ancestor, Object.class); 121 } 122 getAllFauxveridden(Class<?> descendant, Class<?> ancestor)123 private static Set<MethodSignature> getAllFauxveridden(Class<?> descendant, Class<?> ancestor) { 124 return getPublicStaticMethodsBetween(descendant, ancestor); 125 } 126 getPublicStaticMethodsBetween( Class<?> descendant, Class<?> ancestor)127 private static Set<MethodSignature> getPublicStaticMethodsBetween( 128 Class<?> descendant, Class<?> ancestor) { 129 Set<MethodSignature> methods = newHashSet(); 130 for (Class<?> clazz : getClassesBetween(descendant, ancestor)) { 131 methods.addAll(getPublicStaticMethods(clazz)); 132 } 133 return methods; 134 } 135 getPublicStaticMethods(Class<?> clazz)136 private static Set<MethodSignature> getPublicStaticMethods(Class<?> clazz) { 137 Set<MethodSignature> publicStaticMethods = newHashSet(); 138 139 for (Method method : clazz.getDeclaredMethods()) { 140 int modifiers = method.getModifiers(); 141 if (isPublic(modifiers) && isStatic(modifiers)) { 142 publicStaticMethods.add(new MethodSignature(method)); 143 } 144 } 145 146 return publicStaticMethods; 147 } 148 149 /** [descendant, ancestor) */ getClassesBetween(Class<?> descendant, Class<?> ancestor)150 private static Set<Class<?>> getClassesBetween(Class<?> descendant, Class<?> ancestor) { 151 Set<Class<?>> classes = newHashSet(); 152 153 while (!descendant.equals(ancestor)) { 154 classes.add(descendant); 155 descendant = descendant.getSuperclass(); 156 } 157 158 return classes; 159 } 160 161 /** 162 * Not really a signature -- just the parts that affect whether one method is a fauxveride of a 163 * method from an ancestor class. 164 * 165 * <p>See JLS 8.4.2 for the definition of the related "override-equivalent." 166 */ 167 private static final class MethodSignature implements Comparable<MethodSignature> { 168 final String name; 169 final List<Class<?>> parameterTypes; 170 final TypeSignature typeSignature; 171 MethodSignature(Method method)172 MethodSignature(Method method) { 173 name = method.getName(); 174 parameterTypes = Arrays.asList(method.getParameterTypes()); 175 typeSignature = new TypeSignature(method.getTypeParameters()); 176 } 177 178 @Override equals(Object obj)179 public boolean equals(Object obj) { 180 if (obj instanceof MethodSignature) { 181 MethodSignature other = (MethodSignature) obj; 182 return name.equals(other.name) 183 && parameterTypes.equals(other.parameterTypes) 184 && typeSignature.equals(other.typeSignature); 185 } 186 187 return false; 188 } 189 190 @Override hashCode()191 public int hashCode() { 192 return Objects.hashCode(name, parameterTypes, typeSignature); 193 } 194 195 @Override toString()196 public String toString() { 197 return rootLocaleFormat("%s%s(%s)", typeSignature, name, getTypesString(parameterTypes)); 198 } 199 200 @Override compareTo(MethodSignature o)201 public int compareTo(MethodSignature o) { 202 return toString().compareTo(o.toString()); 203 } 204 } 205 206 private static final class TypeSignature { 207 final List<TypeParameterSignature> parameterSignatures; 208 TypeSignature(TypeVariable<Method>[] parameters)209 TypeSignature(TypeVariable<Method>[] parameters) { 210 parameterSignatures = 211 transform( 212 Arrays.asList(parameters), 213 new Function<TypeVariable<?>, TypeParameterSignature>() { 214 @Override 215 public TypeParameterSignature apply(TypeVariable<?> from) { 216 return new TypeParameterSignature(from); 217 } 218 }); 219 } 220 221 @Override equals(Object obj)222 public boolean equals(Object obj) { 223 if (obj instanceof TypeSignature) { 224 TypeSignature other = (TypeSignature) obj; 225 return parameterSignatures.equals(other.parameterSignatures); 226 } 227 228 return false; 229 } 230 231 @Override hashCode()232 public int hashCode() { 233 return parameterSignatures.hashCode(); 234 } 235 236 @Override toString()237 public String toString() { 238 return (parameterSignatures.isEmpty()) 239 ? "" 240 : "<" + Joiner.on(", ").join(parameterSignatures) + "> "; 241 } 242 } 243 244 private static final class TypeParameterSignature { 245 final String name; 246 final List<Type> bounds; 247 TypeParameterSignature(TypeVariable<?> typeParameter)248 TypeParameterSignature(TypeVariable<?> typeParameter) { 249 name = typeParameter.getName(); 250 bounds = Arrays.asList(typeParameter.getBounds()); 251 } 252 253 @Override equals(Object obj)254 public boolean equals(Object obj) { 255 if (obj instanceof TypeParameterSignature) { 256 TypeParameterSignature other = (TypeParameterSignature) obj; 257 /* 258 * The name is here only for display purposes; <E extends Number> and <T 259 * extends Number> are equivalent. 260 */ 261 return bounds.equals(other.bounds); 262 } 263 264 return false; 265 } 266 267 @Override hashCode()268 public int hashCode() { 269 return bounds.hashCode(); 270 } 271 272 @Override toString()273 public String toString() { 274 return (bounds.equals(ImmutableList.of(Object.class))) 275 ? name 276 : name + " extends " + getTypesString(bounds); 277 } 278 } 279 getTypesString(List<? extends Type> types)280 private static String getTypesString(List<? extends Type> types) { 281 List<String> names = transform(types, SIMPLE_NAME_GETTER); 282 return Joiner.on(", ").join(names); 283 } 284 285 private static final Function<Type, String> SIMPLE_NAME_GETTER = 286 new Function<Type, String>() { 287 @Override 288 public String apply(Type from) { 289 if (from instanceof Class) { 290 return ((Class<?>) from).getSimpleName(); 291 } 292 return from.toString(); 293 } 294 }; 295 rootLocaleFormat(String format, Object... args)296 private static String rootLocaleFormat(String format, Object... args) { 297 return String.format(Locale.ROOT, format, args); 298 } 299 } 300