@Override public String format(BindingDeclaration bindingDeclaration) { if (bindingDeclaration instanceof SubcomponentDeclaration) { return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration); } if (bindingDeclaration instanceof ContributionBinding) { ContributionBinding binding = (ContributionBinding) bindingDeclaration; switch (binding.bindingKind()) { case SYNTHETIC_RELEASABLE_REFERENCE_MANAGER: return String.format( "binding for %s from the scope declaration", stripCommonTypePrefixes(keyFormatter.format(binding.key()))); case SYNTHETIC_RELEASABLE_REFERENCE_MANAGERS: return String.format( "Dagger-generated binding for %s", stripCommonTypePrefixes(keyFormatter.format(binding.key()))); default: break; } } checkArgument( bindingDeclaration.bindingElement().isPresent(), "Cannot format bindings without source elements: %s", bindingDeclaration); Element bindingElement = bindingDeclaration.bindingElement().get(); switch (bindingElement.asType().getKind()) { case EXECUTABLE: return methodSignatureFormatter.format( MoreElements.asExecutable(bindingElement), bindingDeclaration .contributingModule() .map(module -> MoreTypes.asDeclared(module.asType()))); case DECLARED: return stripCommonTypePrefixes(bindingElement.asType().toString()); default: throw new IllegalArgumentException("Formatting unsupported for element: " + bindingElement); } }
@Override ImmutableSet<JavaWriter> write(ClassName generatedTypeName, ProductionBinding binding) { TypeMirror keyType = binding.productionType().equals(Type.MAP) ? Util.getProvidedValueTypeOfMap(MoreTypes.asDeclared(binding.key().type())) : binding.key().type(); TypeName providedTypeName = TypeNames.forTypeMirror(keyType); TypeName futureTypeName = ParameterizedTypeName.create(ClassName.fromClass(ListenableFuture.class), providedTypeName); JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName()); ClassWriter factoryWriter = writer.addClass(generatedTypeName.simpleName()); ConstructorWriter constructorWriter = factoryWriter.addConstructor(); constructorWriter.addModifiers(PUBLIC); factoryWriter.addField(binding.bindingTypeElement(), "module").addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(binding.bindingTypeElement(), "module"); constructorWriter .body() .addSnippet("assert module != null;") .addSnippet("this.module = module;"); factoryWriter.addField(Executor.class, "executor").addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(Executor.class, "executor"); constructorWriter .body() .addSnippet("assert executor != null;") .addSnippet("this.executor = executor;"); factoryWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName()); factoryWriter.addModifiers(PUBLIC); factoryWriter.addModifiers(FINAL); factoryWriter.setSuperclass( ParameterizedTypeName.create(AbstractProducer.class, providedTypeName)); MethodWriter getMethodWriter = factoryWriter.addMethod(futureTypeName, "compute"); getMethodWriter.annotate(Override.class); getMethodWriter.addModifiers(PROTECTED); final ImmutableMap<BindingKey, FrameworkField> fields = SourceFiles.generateBindingFieldsForDependencies( dependencyRequestMapper, binding.dependencies()); for (FrameworkField bindingField : fields.values()) { TypeName fieldType = bindingField.frameworkType(); FieldWriter field = factoryWriter.addField(fieldType, bindingField.name()); field.addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(field.type(), field.name()); constructorWriter .body() .addSnippet("assert %s != null;", field.name()) .addSnippet("this.%1$s = %1$s;", field.name()); } boolean returnsFuture = binding.bindingKind().equals(ProductionBinding.Kind.FUTURE_PRODUCTION); ImmutableList<DependencyRequest> asyncDependencies = FluentIterable.from(binding.dependencies()) .filter( new Predicate<DependencyRequest>() { @Override public boolean apply(DependencyRequest dependency) { return isAsyncDependency(dependency); } }) .toList(); for (DependencyRequest dependency : asyncDependencies) { ParameterizedTypeName futureType = ParameterizedTypeName.create( ClassName.fromClass(ListenableFuture.class), asyncDependencyType(dependency)); String name = fields.get(dependency.bindingKey()).name(); Snippet futureAccess = Snippet.format("%s.get()", name); getMethodWriter .body() .addSnippet( "%s %sFuture = %s;", futureType, name, dependency.kind().equals(DependencyRequest.Kind.PRODUCED) ? Snippet.format( "%s.createFutureProduced(%s)", ClassName.fromClass(Producers.class), futureAccess) : futureAccess); } if (asyncDependencies.isEmpty()) { ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); for (DependencyRequest dependency : binding.dependencies()) { parameterSnippets.add( frameworkTypeUsageStatement( Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind())); } final boolean wrapWithFuture = false; // since submitToExecutor will create the future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); TypeName callableReturnType = returnsFuture ? futureTypeName : providedTypeName; Snippet throwsClause = getThrowsClause(binding.thrownTypes()); Snippet callableSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s>() {", " @Override public %2$s call() %3$s{", " return %4$s;", " }", "}"), ClassName.fromClass(Callable.class), callableReturnType, throwsClause, invocationSnippet); getMethodWriter .body() .addSnippet( "%s future = %s.submitToExecutor(%s, executor);", ParameterizedTypeName.create( ClassName.fromClass(ListenableFuture.class), callableReturnType), ClassName.fromClass(Producers.class), callableSnippet); getMethodWriter .body() .addSnippet( "return %s;", returnsFuture ? Snippet.format("%s.dereference(future)", ClassName.fromClass(Futures.class)) : "future"); } else { final Snippet futureSnippet; final Snippet transformSnippet; if (asyncDependencies.size() == 1) { DependencyRequest asyncDependency = Iterables.getOnlyElement(asyncDependencies); futureSnippet = Snippet.format("%s", fields.get(asyncDependency.bindingKey()).name() + "Future"); String argName = asyncDependency.requestElement().getSimpleName().toString(); ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); for (DependencyRequest dependency : binding.dependencies()) { // We really want to compare instances here, because asyncDependency is an element in the // set binding.dependencies(). if (dependency == asyncDependency) { parameterSnippets.add(Snippet.format("%s", argName)); } else { parameterSnippets.add( frameworkTypeUsageStatement( Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind())); } } boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); Snippet throwsClause = getThrowsClause(binding.thrownTypes()); transformSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s, %3$s>() {", " @Override public %4$s apply(%2$s %5$s) %6$s{", " return %7$s;", " }", "}"), ClassName.fromClass(AsyncFunction.class), asyncDependencyType(asyncDependency), providedTypeName, futureTypeName, argName, throwsClause, invocationSnippet); } else { futureSnippet = Snippet.format( "%s.<%s>allAsList(%s)", ClassName.fromClass(Futures.class), ClassName.fromClass(Object.class), Joiner.on(",") .join( FluentIterable.from(asyncDependencies) .transform( new Function<DependencyRequest, String>() { @Override public String apply(DependencyRequest dependency) { return fields.get(dependency.bindingKey()).name() + "Future"; } }))); ImmutableList<Snippet> parameterSnippets = getParameterSnippets(binding, fields, "args"); boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets); ParameterizedTypeName listOfObject = ParameterizedTypeName.create( ClassName.fromClass(List.class), ClassName.fromClass(Object.class)); Snippet throwsClause = getThrowsClause(binding.thrownTypes()); transformSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s, %3$s>() {", " @SuppressWarnings(\"unchecked\") // safe by specification", " @Override public %4$s apply(%2$s args) %5$s{", " return %6$s;", " }", "}"), ClassName.fromClass(AsyncFunction.class), listOfObject, providedTypeName, futureTypeName, throwsClause, invocationSnippet); } getMethodWriter .body() .addSnippet( "return %s.%s(%s, %s, executor);", ClassName.fromClass(Futures.class), "transform", futureSnippet, transformSnippet); } // TODO(gak): write a sensible toString return ImmutableSet.of(writer); }
@Override public DeclaredType apply(TypeElement typeElement) { return MoreTypes.asDeclared(typeElement.asType()); }
@Override Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProvisionBinding binding) { // We don't want to write out resolved bindings -- we want to write out the generic version. checkState(!binding.unresolved().isPresent()); TypeMirror keyType = binding.contributionType().equals(ContributionType.MAP) ? MapType.from(binding.key().type()).unwrappedValueType(Provider.class) : binding.key().type(); TypeName providedTypeName = TypeName.get(keyType); ParameterizedTypeName parameterizedFactoryName = factoryOf(providedTypeName); Optional<ParameterizedTypeName> factoryOfRawTypeName = Optional.absent(); TypeSpec.Builder factoryBuilder; Optional<MethodSpec.Builder> constructorBuilder = Optional.absent(); ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding); ImmutableMap<BindingKey, FrameworkField> fields = generateBindingFieldsForDependencies(dependencyRequestMapper, binding); switch (binding.factoryCreationStrategy()) { case ENUM_INSTANCE: factoryBuilder = enumBuilder(generatedTypeName.simpleName()).addEnumConstant("INSTANCE"); // If we have type parameters, then remove the parameters from our providedTypeName, // since we'll be implementing an erased version of it. if (!typeParameters.isEmpty()) { factoryBuilder.addAnnotation(SUPPRESS_WARNINGS_RAWTYPES); // TODO(ronshapiro): instead of reassigning, introduce an optional/second parameter providedTypeName = ((ParameterizedTypeName) providedTypeName).rawType; factoryOfRawTypeName = Optional.of(factoryOf(providedTypeName)); } break; case CLASS_CONSTRUCTOR: factoryBuilder = classBuilder(generatedTypeName.simpleName()) .addTypeVariables(typeParameters) .addModifiers(FINAL); constructorBuilder = Optional.of(constructorBuilder().addModifiers(PUBLIC)); if (binding.bindingKind().equals(PROVISION) && !binding.bindingElement().getModifiers().contains(STATIC)) { addConstructorParameterAndTypeField( TypeName.get(binding.bindingTypeElement().asType()), "module", factoryBuilder, constructorBuilder.get()); } for (FrameworkField bindingField : fields.values()) { addConstructorParameterAndTypeField( bindingField.javapoetFrameworkType(), bindingField.name(), factoryBuilder, constructorBuilder.get()); } break; default: throw new AssertionError(); } factoryBuilder .addModifiers(PUBLIC) .addSuperinterface(factoryOfRawTypeName.or(parameterizedFactoryName)); // If constructing a factory for @Inject or @Provides bindings, we use a static create method // so that generated components can avoid having to refer to the generic types // of the factory. (Otherwise they may have visibility problems referring to the types.) Optional<MethodSpec> createMethod; switch (binding.bindingKind()) { case INJECTION: case PROVISION: // The return type is usually the same as the implementing type, except in the case // of enums with type variables (where we cast). MethodSpec.Builder createMethodBuilder = methodBuilder("create") .addModifiers(PUBLIC, STATIC) .addTypeVariables(typeParameters) .returns(parameterizedFactoryName); List<ParameterSpec> params = constructorBuilder.isPresent() ? constructorBuilder.get().build().parameters : ImmutableList.<ParameterSpec>of(); createMethodBuilder.addParameters(params); switch (binding.factoryCreationStrategy()) { case ENUM_INSTANCE: if (typeParameters.isEmpty()) { createMethodBuilder.addStatement("return INSTANCE"); } else { // We use an unsafe cast here because the types are different. // It's safe because the type is never referenced anywhere. createMethodBuilder.addStatement("return ($T) INSTANCE", TypeNames.FACTORY); createMethodBuilder.addAnnotation(SUPPRESS_WARNINGS_UNCHECKED); } break; case CLASS_CONSTRUCTOR: createMethodBuilder.addStatement( "return new $T($L)", javapoetParameterizedGeneratedTypeNameForBinding(binding), makeParametersCodeBlock(Lists.transform(params, CodeBlocks.PARAMETER_NAME))); break; default: throw new AssertionError(); } createMethod = Optional.of(createMethodBuilder.build()); break; default: createMethod = Optional.absent(); } if (constructorBuilder.isPresent()) { factoryBuilder.addMethod(constructorBuilder.get().build()); } List<CodeBlock> parameters = Lists.newArrayList(); for (DependencyRequest dependency : binding.dependencies()) { parameters.add( frameworkTypeUsageStatement( CodeBlocks.format("$L", fields.get(dependency.bindingKey()).name()), dependency.kind())); } CodeBlock parametersCodeBlock = makeParametersCodeBlock(parameters); MethodSpec.Builder getMethodBuilder = methodBuilder("get") .returns(providedTypeName) .addAnnotation(Override.class) .addModifiers(PUBLIC); if (binding.bindingKind().equals(PROVISION)) { CodeBlock.Builder providesMethodInvocationBuilder = CodeBlock.builder(); if (binding.bindingElement().getModifiers().contains(STATIC)) { providesMethodInvocationBuilder.add("$T", ClassName.get(binding.bindingTypeElement())); } else { providesMethodInvocationBuilder.add("module"); } providesMethodInvocationBuilder.add( ".$L($L)", binding.bindingElement().getSimpleName(), parametersCodeBlock); CodeBlock providesMethodInvocation = providesMethodInvocationBuilder.build(); if (binding.provisionType().equals(SET)) { TypeName paramTypeName = TypeName.get(MoreTypes.asDeclared(keyType).getTypeArguments().get(0)); // TODO(cgruber): only be explicit with the parameter if paramType contains wildcards. getMethodBuilder.addStatement( "return $T.<$T>singleton($L)", Collections.class, paramTypeName, providesMethodInvocation); } else if (binding.nullableType().isPresent() || nullableValidationType.equals(Diagnostic.Kind.WARNING)) { if (binding.nullableType().isPresent()) { getMethodBuilder.addAnnotation((ClassName) TypeName.get(binding.nullableType().get())); } getMethodBuilder.addStatement("return $L", providesMethodInvocation); } else { String failMsg = CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD; getMethodBuilder .addStatement( "$T provided = $L", getMethodBuilder.build().returnType, providesMethodInvocation) .addCode("if (provided == null) { ") .addStatement("throw new $T($S)", NullPointerException.class, failMsg) .addCode("}") .addStatement("return provided"); } } else if (binding.membersInjectionRequest().isPresent()) { getMethodBuilder.addStatement( "$1T instance = new $1T($2L)", providedTypeName, parametersCodeBlock); getMethodBuilder.addStatement( "$L.injectMembers(instance)", fields.get(binding.membersInjectionRequest().get().bindingKey()).name()); getMethodBuilder.addStatement("return instance"); } else { getMethodBuilder.addStatement("return new $T($L)", providedTypeName, parametersCodeBlock); } factoryBuilder.addMethod(getMethodBuilder.build()); if (createMethod.isPresent()) { factoryBuilder.addMethod(createMethod.get()); } // TODO(gak): write a sensible toString return Optional.of(factoryBuilder); }
String format(BindingMethodIdentifier bindingMethodIdentifier) { return format( MoreElements.asExecutable(bindingMethodIdentifier.bindingMethod()), Optional.of(MoreTypes.asDeclared(bindingMethodIdentifier.contributingModule().asType()))); }