1 /* 2 * Copyright (C) 2006 Google Inc. 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.inject.internal; 18 19 import static java.lang.annotation.RetentionPolicy.RUNTIME; 20 21 import com.google.common.base.Function; 22 import com.google.common.base.Joiner; 23 import com.google.common.base.Joiner.MapJoiner; 24 import com.google.common.base.Preconditions; 25 import com.google.common.cache.CacheBuilder; 26 import com.google.common.cache.CacheLoader; 27 import com.google.common.cache.LoadingCache; 28 import com.google.common.collect.ImmutableMap; 29 import com.google.common.collect.Maps; 30 import com.google.inject.BindingAnnotation; 31 import com.google.inject.Key; 32 import com.google.inject.ScopeAnnotation; 33 import com.google.inject.TypeLiteral; 34 import com.google.inject.internal.util.Classes; 35 import com.google.inject.name.Named; 36 import com.google.inject.name.Names; 37 import java.lang.annotation.Annotation; 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.lang.reflect.InvocationHandler; 41 import java.lang.reflect.Member; 42 import java.lang.reflect.Method; 43 import java.lang.reflect.Proxy; 44 import java.util.Arrays; 45 import java.util.Collection; 46 import java.util.Map; 47 import javax.inject.Qualifier; 48 49 /** 50 * Annotation utilities. 51 * 52 * @author crazybob@google.com (Bob Lee) 53 */ 54 public class Annotations { 55 56 /** Returns {@code true} if the given annotation type has no attributes. */ isMarker(Class<? extends Annotation> annotationType)57 public static boolean isMarker(Class<? extends Annotation> annotationType) { 58 return annotationType.getDeclaredMethods().length == 0; 59 } 60 isAllDefaultMethods(Class<? extends Annotation> annotationType)61 public static boolean isAllDefaultMethods(Class<? extends Annotation> annotationType) { 62 boolean hasMethods = false; 63 for (Method m : annotationType.getDeclaredMethods()) { 64 hasMethods = true; 65 if (m.getDefaultValue() == null) { 66 return false; 67 } 68 } 69 return hasMethods; 70 } 71 72 private static final LoadingCache<Class<? extends Annotation>, Annotation> cache = 73 CacheBuilder.newBuilder() 74 .weakKeys() 75 .build( 76 new CacheLoader<Class<? extends Annotation>, Annotation>() { 77 @Override 78 public Annotation load(Class<? extends Annotation> input) { 79 return generateAnnotationImpl(input); 80 } 81 }); 82 83 /** 84 * Generates an Annotation for the annotation class. Requires that the annotation is all 85 * optionals. 86 */ generateAnnotation(Class<T> annotationType)87 public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) { 88 Preconditions.checkState( 89 isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType); 90 return (T) cache.getUnchecked(annotationType); 91 } 92 generateAnnotationImpl(final Class<T> annotationType)93 private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) { 94 final Map<String, Object> members = resolveMembers(annotationType); 95 return annotationType.cast( 96 Proxy.newProxyInstance( 97 annotationType.getClassLoader(), 98 new Class<?>[] {annotationType}, 99 new InvocationHandler() { 100 @Override 101 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { 102 String name = method.getName(); 103 if (name.equals("annotationType")) { 104 return annotationType; 105 } else if (name.equals("toString")) { 106 return annotationToString(annotationType, members); 107 } else if (name.equals("hashCode")) { 108 return annotationHashCode(annotationType, members); 109 } else if (name.equals("equals")) { 110 return annotationEquals(annotationType, members, args[0]); 111 } else { 112 return members.get(name); 113 } 114 } 115 })); 116 } 117 118 private static ImmutableMap<String, Object> resolveMembers( 119 Class<? extends Annotation> annotationType) { 120 ImmutableMap.Builder<String, Object> result = ImmutableMap.builder(); 121 for (Method method : annotationType.getDeclaredMethods()) { 122 result.put(method.getName(), method.getDefaultValue()); 123 } 124 return result.build(); 125 } 126 127 /** Implements {@link Annotation#equals}. */ 128 private static boolean annotationEquals( 129 Class<? extends Annotation> type, Map<String, Object> members, Object other) 130 throws Exception { 131 if (!type.isInstance(other)) { 132 return false; 133 } 134 for (Method method : type.getDeclaredMethods()) { 135 String name = method.getName(); 136 if (!Arrays.deepEquals( 137 new Object[] {method.invoke(other)}, new Object[] {members.get(name)})) { 138 return false; 139 } 140 } 141 return true; 142 } 143 144 /** Implements {@link Annotation#hashCode}. */ 145 private static int annotationHashCode( 146 Class<? extends Annotation> type, Map<String, Object> members) throws Exception { 147 int result = 0; 148 for (Method method : type.getDeclaredMethods()) { 149 String name = method.getName(); 150 Object value = members.get(name); 151 result += (127 * name.hashCode()) ^ (Arrays.deepHashCode(new Object[] {value}) - 31); 152 } 153 return result; 154 } 155 156 private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("="); 157 158 private static final Function<Object, String> DEEP_TO_STRING_FN = 159 new Function<Object, String>() { 160 @Override 161 public String apply(Object arg) { 162 String s = Arrays.deepToString(new Object[] {arg}); 163 return s.substring(1, s.length() - 1); // cut off brackets 164 } 165 }; 166 167 /** Implements {@link Annotation#toString}. */ 168 private static String annotationToString( 169 Class<? extends Annotation> type, Map<String, Object> members) throws Exception { 170 StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("("); 171 JOINER.appendTo(sb, Maps.transformValues(members, DEEP_TO_STRING_FN)); 172 return sb.append(")").toString(); 173 } 174 175 /** Returns true if the given annotation is retained at runtime. */ 176 public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) { 177 Retention retention = annotationType.getAnnotation(Retention.class); 178 return retention != null && retention.value() == RetentionPolicy.RUNTIME; 179 } 180 181 /** Returns the scope annotation on {@code type}, or null if none is specified. */ 182 public static Class<? extends Annotation> findScopeAnnotation( 183 Errors errors, Class<?> implementation) { 184 return findScopeAnnotation(errors, implementation.getAnnotations()); 185 } 186 187 /** Returns the scoping annotation, or null if there isn't one. */ 188 public static Class<? extends Annotation> findScopeAnnotation( 189 Errors errors, Annotation[] annotations) { 190 Class<? extends Annotation> found = null; 191 192 for (Annotation annotation : annotations) { 193 Class<? extends Annotation> annotationType = annotation.annotationType(); 194 if (isScopeAnnotation(annotationType)) { 195 if (found != null) { 196 errors.duplicateScopeAnnotations(found, annotationType); 197 } else { 198 found = annotationType; 199 } 200 } 201 } 202 203 return found; 204 } 205 206 static boolean containsComponentAnnotation(Annotation[] annotations) { 207 for (Annotation annotation : annotations) { 208 // TODO(user): Should we scope this down to dagger.Component? 209 if (annotation.annotationType().getSimpleName().equals("Component")) { 210 return true; 211 } 212 } 213 214 return false; 215 } 216 217 private static final boolean QUOTE_MEMBER_VALUES = determineWhetherToQuote(); 218 219 /** 220 * Returns {@code value}, quoted if annotation implementations quote their member values. In Java 221 * 9, annotations quote their string members. 222 */ 223 public static String memberValueString(String value) { 224 return QUOTE_MEMBER_VALUES ? "\"" + value + "\"" : value; 225 } 226 227 @Retention(RUNTIME) 228 private @interface TestAnnotation { 229 String value(); 230 } 231 232 @TestAnnotation("determineWhetherToQuote") 233 private static boolean determineWhetherToQuote() { 234 try { 235 String annotation = 236 Annotations.class 237 .getDeclaredMethod("determineWhetherToQuote") 238 .getAnnotation(TestAnnotation.class) 239 .toString(); 240 return annotation.contains("\"determineWhetherToQuote\""); 241 } catch (NoSuchMethodException e) { 242 throw new AssertionError(e); 243 } 244 } 245 246 /** Checks for the presence of annotations. Caches results because Android doesn't. */ 247 static class AnnotationChecker { 248 private final Collection<Class<? extends Annotation>> annotationTypes; 249 250 /** Returns true if the given class has one of the desired annotations. */ 251 private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations = 252 new CacheLoader<Class<? extends Annotation>, Boolean>() { 253 @Override 254 public Boolean load(Class<? extends Annotation> annotationType) { 255 for (Annotation annotation : annotationType.getAnnotations()) { 256 if (annotationTypes.contains(annotation.annotationType())) { 257 return true; 258 } 259 } 260 return false; 261 } 262 }; 263 264 final LoadingCache<Class<? extends Annotation>, Boolean> cache = 265 CacheBuilder.newBuilder().weakKeys().build(hasAnnotations); 266 267 /** Constructs a new checker that looks for annotations of the given types. */ 268 AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) { 269 this.annotationTypes = annotationTypes; 270 } 271 272 /** Returns true if the given type has one of the desired annotations. */ 273 boolean hasAnnotations(Class<? extends Annotation> annotated) { 274 return cache.getUnchecked(annotated); 275 } 276 } 277 278 private static final AnnotationChecker scopeChecker = 279 new AnnotationChecker(Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class)); 280 281 public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) { 282 return scopeChecker.hasAnnotations(annotationType); 283 } 284 285 /** 286 * Adds an error if there is a misplaced annotations on {@code type}. Scoping annotations are not 287 * allowed on abstract classes or interfaces. 288 */ 289 public static void checkForMisplacedScopeAnnotations( 290 Class<?> type, Object source, Errors errors) { 291 if (Classes.isConcrete(type)) { 292 return; 293 } 294 295 Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type); 296 if (scopeAnnotation != null 297 // We let Dagger Components through to aid migrations. 298 && !containsComponentAnnotation(type.getAnnotations())) { 299 errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source); 300 } 301 } 302 303 // NOTE: getKey/findBindingAnnotation are used by Gin which is abandoned. So changing this API 304 // will prevent Gin users from upgrading Guice version. 305 306 /** Gets a key for the given type, member and annotations. */ 307 public static Key<?> getKey( 308 TypeLiteral<?> type, Member member, Annotation[] annotations, Errors errors) 309 throws ErrorsException { 310 int numErrorsBefore = errors.size(); 311 Annotation found = findBindingAnnotation(errors, member, annotations); 312 errors.throwIfNewErrors(numErrorsBefore); 313 return found == null ? Key.get(type) : Key.get(type, found); 314 } 315 316 /** Returns the binding annotation on {@code member}, or null if there isn't one. */ 317 public static Annotation findBindingAnnotation( 318 Errors errors, Member member, Annotation[] annotations) { 319 Annotation found = null; 320 321 for (Annotation annotation : annotations) { 322 Class<? extends Annotation> annotationType = annotation.annotationType(); 323 if (isBindingAnnotation(annotationType)) { 324 if (found != null) { 325 errors.duplicateBindingAnnotations(member, found.annotationType(), annotationType); 326 } else { 327 found = annotation; 328 } 329 } 330 } 331 332 return found; 333 } 334 335 private static final AnnotationChecker bindingAnnotationChecker = 336 new AnnotationChecker(Arrays.asList(BindingAnnotation.class, Qualifier.class)); 337 338 /** Returns true if annotations of the specified type are binding annotations. */ 339 public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) { 340 return bindingAnnotationChecker.hasAnnotations(annotationType); 341 } 342 343 /** 344 * If the annotation is an instance of {@code javax.inject.Named}, canonicalizes to 345 * com.google.guice.name.Named. Returns the given annotation otherwise. 346 */ 347 public static Annotation canonicalizeIfNamed(Annotation annotation) { 348 if (annotation instanceof javax.inject.Named) { 349 return Names.named(((javax.inject.Named) annotation).value()); 350 } else { 351 return annotation; 352 } 353 } 354 355 /** 356 * If the annotation is the class {@code javax.inject.Named}, canonicalizes to 357 * com.google.guice.name.Named. Returns the given annotation class otherwise. 358 */ 359 public static Class<? extends Annotation> canonicalizeIfNamed( 360 Class<? extends Annotation> annotationType) { 361 if (annotationType == javax.inject.Named.class) { 362 return Named.class; 363 } else { 364 return annotationType; 365 } 366 } 367 368 /** 369 * Returns the name the binding should use. This is based on the annotation. If the annotation has 370 * an instance and is not a marker annotation, we ask the annotation for its toString. If it was a 371 * marker annotation or just an annotation type, we use the annotation's name. Otherwise, the name 372 * is the empty string. 373 */ 374 public static String nameOf(Key<?> key) { 375 Annotation annotation = key.getAnnotation(); 376 Class<? extends Annotation> annotationType = key.getAnnotationType(); 377 if (annotation != null && !isMarker(annotationType)) { 378 return key.getAnnotation().toString(); 379 } else if (key.getAnnotationType() != null) { 380 return "@" + key.getAnnotationType().getName(); 381 } else { 382 return ""; 383 } 384 } 385 } 386