1 /* 2 * Copyright (C) 2018 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.internal.codegen; 18 19 import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom; 20 import static javax.lang.model.element.Modifier.ABSTRACT; 21 import static javax.lang.model.element.Modifier.PRIVATE; 22 import static javax.lang.model.element.Modifier.STATIC; 23 import static javax.lang.model.util.ElementFilter.constructorsIn; 24 25 import com.squareup.javapoet.ClassName; 26 import com.squareup.javapoet.CodeBlock; 27 import dagger.internal.codegen.langmodel.Accessibility; 28 import dagger.internal.codegen.langmodel.DaggerElements; 29 import java.util.Optional; 30 import javax.lang.model.element.ExecutableElement; 31 import javax.lang.model.element.TypeElement; 32 33 /** Convenience methods for generating and using module constructor proxy methods. */ 34 final class ModuleProxies { 35 /** The name of the class that hosts the module constructor proxy method. */ constructorProxyTypeName(TypeElement moduleElement)36 static ClassName constructorProxyTypeName(TypeElement moduleElement) { 37 ModuleKind.checkIsModule(moduleElement); 38 ClassName moduleClassName = ClassName.get(moduleElement); 39 return moduleClassName 40 .topLevelClassName() 41 .peerClass(SourceFiles.classFileName(moduleClassName) + "_Proxy"); 42 } 43 44 /** 45 * The module constructor being proxied. A proxy is generated if it is not publicly accessible and 46 * has no arguments. If an implicit reference to the enclosing class exists, or the module is 47 * abstract, no proxy method can be generated. 48 */ 49 // TODO(ronshapiro): make this an @Injectable class that injects DaggerElements nonPublicNullaryConstructor( TypeElement moduleElement, DaggerElements elements)50 static Optional<ExecutableElement> nonPublicNullaryConstructor( 51 TypeElement moduleElement, DaggerElements elements) { 52 ModuleKind.checkIsModule(moduleElement); 53 if (moduleElement.getModifiers().contains(ABSTRACT) 54 || (moduleElement.getNestingKind().isNested() 55 && !moduleElement.getModifiers().contains(STATIC))) { 56 return Optional.empty(); 57 } 58 return constructorsIn(elements.getAllMembers(moduleElement)).stream() 59 .filter(constructor -> !Accessibility.isElementPubliclyAccessible(constructor)) 60 .filter(constructor -> !constructor.getModifiers().contains(PRIVATE)) 61 .filter(constructor -> constructor.getParameters().isEmpty()) 62 .findAny(); 63 } 64 65 /** 66 * Returns a code block that creates a new module instance, either by invoking the nullary 67 * constructor if it's accessible from {@code requestingClass} or else by invoking the 68 * constructor's generated proxy method. 69 */ newModuleInstance( TypeElement moduleElement, ClassName requestingClass, DaggerElements elements)70 static CodeBlock newModuleInstance( 71 TypeElement moduleElement, ClassName requestingClass, DaggerElements elements) { 72 ModuleKind.checkIsModule(moduleElement); 73 String packageName = requestingClass.packageName(); 74 return nonPublicNullaryConstructor(moduleElement, elements) 75 .filter(constructor -> !isElementAccessibleFrom(constructor, packageName)) 76 .map( 77 constructor -> 78 CodeBlock.of("$T.newInstance()", constructorProxyTypeName(moduleElement))) 79 .orElse(CodeBlock.of("new $T()", moduleElement)); 80 } 81 } 82