private void writeProvidesAdapter(
      JavaWriter writer,
      ExecutableElement providerMethod,
      Map<ExecutableElement, String> methodToClassName,
      Map<String, AtomicInteger> methodNameToNextId)
      throws IOException {
    String methodName = providerMethod.getSimpleName().toString();
    String moduleType = CodeGen.typeToString(providerMethod.getEnclosingElement().asType());
    String className = bindingClassName(providerMethod, methodToClassName, methodNameToNextId);
    String returnType = CodeGen.typeToString(providerMethod.getReturnType());
    List<? extends VariableElement> parameters = providerMethod.getParameters();
    boolean dependent = !parameters.isEmpty();

    writer.emitEmptyLine();
    writer.emitJavadoc(binderTypeDocs(returnType, false, false, dependent));
    writer.beginType(
        className,
        "class",
        PUBLIC | FINAL | STATIC,
        JavaWriter.type(Binding.class, returnType),
        JavaWriter.type(Provider.class, returnType));
    writer.emitField(moduleType, "module", PRIVATE | FINAL);
    for (Element parameter : parameters) {
      TypeMirror parameterType = parameter.asType();
      writer.emitField(
          JavaWriter.type(Binding.class, CodeGen.typeToString(parameterType)),
          parameterName(parameter),
          PRIVATE);
    }

    writer.emitEmptyLine();
    writer.beginMethod(null, className, PUBLIC, moduleType, "module");
    boolean singleton = providerMethod.getAnnotation(Singleton.class) != null;
    String key = JavaWriter.stringLiteral(GeneratorKeys.get(providerMethod));
    String membersKey = null;
    writer.emitStatement(
        "super(%s, %s, %s, %s.class)",
        key, membersKey, (singleton ? "IS_SINGLETON" : "NOT_SINGLETON"), moduleType);
    writer.emitStatement("this.module = module");
    writer.endMethod();

    if (dependent) {
      writer.emitEmptyLine();
      writer.emitJavadoc(ProcessorJavadocs.ATTACH_METHOD);
      writer.emitAnnotation(Override.class);
      writer.emitAnnotation(SuppressWarnings.class, JavaWriter.stringLiteral("unchecked"));
      writer.beginMethod("void", "attach", PUBLIC, Linker.class.getCanonicalName(), "linker");
      for (VariableElement parameter : parameters) {
        String parameterKey = GeneratorKeys.get(parameter);
        writer.emitStatement(
            "%s = (%s) linker.requestBinding(%s, %s.class)",
            parameterName(parameter),
            writer.compressType(
                JavaWriter.type(Binding.class, CodeGen.typeToString(parameter.asType()))),
            JavaWriter.stringLiteral(parameterKey),
            writer.compressType(moduleType));
      }
      writer.endMethod();

      writer.emitEmptyLine();
      writer.emitJavadoc(ProcessorJavadocs.GET_DEPENDENCIES_METHOD);
      writer.emitAnnotation(Override.class);
      String setOfBindings = JavaWriter.type(Set.class, "Binding<?>");
      writer.beginMethod(
          "void",
          "getDependencies",
          PUBLIC,
          setOfBindings,
          "getBindings",
          setOfBindings,
          "injectMembersBindings");
      for (Element parameter : parameters) {
        writer.emitStatement("getBindings.add(%s)", parameter.getSimpleName().toString());
      }
      writer.endMethod();
    }

    writer.emitEmptyLine();
    writer.emitJavadoc(ProcessorJavadocs.GET_METHOD, returnType);
    writer.emitAnnotation(Override.class);
    writer.beginMethod(returnType, "get", PUBLIC);
    StringBuilder args = new StringBuilder();
    boolean first = true;
    for (Element parameter : parameters) {
      if (!first) args.append(", ");
      else first = false;
      args.append(String.format("%s.get()", parameter.getSimpleName().toString()));
    }
    writer.emitStatement("return module.%s(%s)", methodName, args.toString());
    writer.endMethod();

    writer.endType();
  }
  /**
   * Write a companion class for {@code type} that implements {@link ModuleAdapter} to expose its
   * provider methods.
   */
  private void writeModuleAdapter(
      TypeElement type, Map<String, Object> module, List<ExecutableElement> providerMethods)
      throws IOException {
    if (module == null) {
      error(type + " has @Provides methods but no @Module annotation", type);
      return;
    }

    Object[] staticInjections = (Object[]) module.get("staticInjections");
    Object[] entryPoints = (Object[]) module.get("entryPoints");
    Object[] includes = (Object[]) module.get("includes");

    boolean overrides = (Boolean) module.get("overrides");
    boolean complete = (Boolean) module.get("complete");

    String adapterName = CodeGen.adapterName(type, MODULE_ADAPTER_SUFFIX);
    JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(adapterName, type);
    JavaWriter writer = new JavaWriter(sourceFile.openWriter());

    boolean multibindings = checkForMultibindings(providerMethods);
    boolean providerMethodDependencies = checkForDependencies(providerMethods);

    writer.emitEndOfLineComment(ProcessorJavadocs.GENERATED_BY_DAGGER);
    writer.emitPackage(CodeGen.getPackage(type).getQualifiedName().toString());
    writer.emitEmptyLine();
    writer.emitImports(
        getImports(multibindings, !providerMethods.isEmpty(), providerMethodDependencies));

    String typeName = type.getQualifiedName().toString();
    writer.emitEmptyLine();
    writer.emitJavadoc(ProcessorJavadocs.MODULE_TYPE);
    writer.beginType(
        adapterName, "class", PUBLIC | FINAL, JavaWriter.type(ModuleAdapter.class, typeName));

    StringBuilder entryPointsField = new StringBuilder().append("{ ");
    for (Object entryPoint : entryPoints) {
      TypeMirror typeMirror = (TypeMirror) entryPoint;
      String key = GeneratorKeys.rawMembersKey(typeMirror);
      entryPointsField.append(JavaWriter.stringLiteral(key)).append(", ");
    }
    entryPointsField.append("}");
    writer.emitField(
        "String[]", "ENTRY_POINTS", PRIVATE | STATIC | FINAL, entryPointsField.toString());

    StringBuilder staticInjectionsField = new StringBuilder().append("{ ");
    for (Object staticInjection : staticInjections) {
      TypeMirror typeMirror = (TypeMirror) staticInjection;
      staticInjectionsField.append(CodeGen.typeToString(typeMirror)).append(".class, ");
    }
    staticInjectionsField.append("}");
    writer.emitField(
        "Class<?>[]",
        "STATIC_INJECTIONS",
        PRIVATE | STATIC | FINAL,
        staticInjectionsField.toString());

    StringBuilder includesField = new StringBuilder().append("{ ");
    for (Object include : includes) {
      if (!(include instanceof TypeMirror)) {
        // TODO(tbroyer): pass annotation information
        processingEnv
            .getMessager()
            .printMessage(
                Diagnostic.Kind.WARNING,
                "Unexpected value: " + include + " in includes of " + type,
                type);
        continue;
      }
      TypeMirror typeMirror = (TypeMirror) include;
      includesField.append(CodeGen.typeToString(typeMirror)).append(".class, ");
    }
    includesField.append("}");
    writer.emitField("Class<?>[]", "INCLUDES", PRIVATE | STATIC | FINAL, includesField.toString());

    writer.emitEmptyLine();
    writer.beginMethod(null, adapterName, PUBLIC);
    writer.emitStatement(
        "super(ENTRY_POINTS, STATIC_INJECTIONS, %s /*overrides*/, " + "INCLUDES, %s /*complete*/)",
        overrides, complete);
    writer.endMethod();

    ExecutableElement noArgsConstructor = CodeGen.getNoArgsConstructor(type);
    if (noArgsConstructor != null && CodeGen.isCallableConstructor(noArgsConstructor)) {
      writer.emitEmptyLine();
      writer.emitAnnotation(Override.class);
      writer.beginMethod(typeName, "newModule", PROTECTED);
      writer.emitStatement("return new %s()", typeName);
      writer.endMethod();
    }
    // caches
    Map<ExecutableElement, String> methodToClassName =
        new LinkedHashMap<ExecutableElement, String>();
    Map<String, AtomicInteger> methodNameToNextId = new LinkedHashMap<String, AtomicInteger>();

    if (!providerMethods.isEmpty()) {
      writer.emitEmptyLine();
      writer.emitJavadoc(ProcessorJavadocs.GET_DEPENDENCIES_METHOD);
      writer.emitAnnotation(Override.class);
      writer.beginMethod("void", "getBindings", PUBLIC, BINDINGS_MAP, "map");

      for (ExecutableElement providerMethod : providerMethods) {
        Provides provides = providerMethod.getAnnotation(Provides.class);
        switch (provides.type()) {
          case UNIQUE:
            {
              String key = GeneratorKeys.get(providerMethod);
              writer.emitStatement(
                  "map.put(%s, new %s(module))",
                  JavaWriter.stringLiteral(key),
                  bindingClassName(providerMethod, methodToClassName, methodNameToNextId));
              break;
            }
          case SET:
            {
              String key = GeneratorKeys.getElementKey(providerMethod);
              writer.emitStatement(
                  "SetBinding.add(map, %s, new %s(module))",
                  JavaWriter.stringLiteral(key),
                  bindingClassName(providerMethod, methodToClassName, methodNameToNextId));
              break;
            }
          default:
            throw new AssertionError("Unknown @Provides type " + provides.type());
        }
      }
      writer.endMethod();
    }

    for (ExecutableElement providerMethod : providerMethods) {
      writeProvidesAdapter(writer, providerMethod, methodToClassName, methodNameToNextId);
    }

    writer.endType();
    writer.close();
  }