1 /* 2 * Copyright (C) 2019 The Dagger 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 dagger.hilt; 18 19 import dagger.hilt.internal.GeneratedComponent; 20 import dagger.hilt.internal.GeneratedComponentManager; 21 import dagger.hilt.internal.Preconditions; 22 import dagger.hilt.internal.TestSingletonComponent; 23 import java.lang.annotation.Annotation; 24 import javax.annotation.Nonnull; 25 26 /** Static utility methods for accessing objects through entry points. */ 27 public final class EntryPoints { 28 private static final String EARLY_ENTRY_POINT = "dagger.hilt.android.EarlyEntryPoint"; 29 30 /** 31 * Returns the entry point interface given a component or component manager. Note that this 32 * performs an unsafe cast and so callers should be sure that the given component/component 33 * manager matches the entry point interface that is given. 34 * 35 * @param component The Hilt-generated component instance. For convenience, also takes component 36 * manager instances as well. 37 * @param entryPoint The interface marked with {@link dagger.hilt.EntryPoint}. The {@link 38 * dagger.hilt.InstallIn} annotation on this entry point should match the component argument 39 * above. 40 */ 41 // Note that the input is not statically declared to be a Component or ComponentManager to make 42 // this method easier to use, since most code will use this with an Application or Activity type. 43 @Nonnull get(Object component, Class<T> entryPoint)44 public static <T> T get(Object component, Class<T> entryPoint) { 45 if (component instanceof GeneratedComponent) { 46 if (component instanceof TestSingletonComponent) { 47 // @EarlyEntryPoint only has an effect in test environment, so we shouldn't fail in 48 // non-test cases. In addition, some of the validation requires the use of reflection, which 49 // we don't want to do in non-test cases anyway. 50 Preconditions.checkState( 51 !hasAnnotationReflection(entryPoint, EARLY_ENTRY_POINT), 52 "Interface, %s, annotated with @EarlyEntryPoint should be called with " 53 + "EarlyEntryPoints.get() rather than EntryPoints.get()", 54 entryPoint.getCanonicalName()); 55 } 56 // Unsafe cast. There is no way for this method to know that the correct component was used. 57 return entryPoint.cast(component); 58 } else if (component instanceof GeneratedComponentManager) { 59 return get(((GeneratedComponentManager<?>) component).generatedComponent(), entryPoint); 60 } else { 61 throw new IllegalStateException( 62 String.format( 63 "Given component holder %s does not implement %s or %s", 64 component.getClass(), GeneratedComponent.class, GeneratedComponentManager.class)); 65 } 66 } 67 68 // Note: This method uses reflection but it should only be called in test environments. hasAnnotationReflection(Class<?> clazz, String annotationName)69 private static boolean hasAnnotationReflection(Class<?> clazz, String annotationName) { 70 for (Annotation annotation : clazz.getAnnotations()) { 71 if (annotation.annotationType().getCanonicalName().contentEquals(annotationName)) { 72 return true; 73 } 74 } 75 return false; 76 } 77 EntryPoints()78 private EntryPoints() {} 79 } 80