private static void printJavaNativeStub(
     ProcessingEnvironment env,
     PrintWriter writer,
     ExecutableElement method,
     Mode mode,
     boolean generate_error_checks,
     boolean context_specific) {
   if (Utils.isMethodIndirect(generate_error_checks, context_specific, method)) {
     writer.print("\tstatic native ");
   } else {
     Utils.printDocComment(writer, method, env);
     writer.print("\tpublic static native ");
   }
   writer.print(getResultType(method, true));
   writer.print(
       " " + Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
   if (mode == Mode.BUFFEROBJECT) {
     writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
   }
   writer.print("(");
   boolean first_parameter =
       generateParametersJava(
           writer, method, TypeInfo.getDefaultTypeInfoMap(method), true, true, mode);
   if (context_specific) {
     if (!first_parameter) {
       writer.print(", ");
     }
     writer.print("long " + Utils.FUNCTION_POINTER_VAR_NAME);
   }
   writer.println(");");
 }
  /** TODO : fix info multi-type methods print. */
  private static void generateMethodJava(
      ProcessingEnvironment env,
      TypeMap type_map,
      PrintWriter writer,
      TypeElement interface_decl,
      ExecutableElement method,
      boolean generate_error_checks,
      boolean context_specific) {
    writer.println();
    if (Utils.isMethodIndirect(generate_error_checks, context_specific, method)) {
      if (method.getAnnotation(GenerateAutos.class) != null) {
        printMethodWithMultiType(
            env,
            type_map,
            writer,
            interface_decl,
            method,
            TypeInfo.getDefaultTypeInfoMap(method),
            Mode.AUTOS,
            generate_error_checks,
            context_specific);
      }
      Collection<Map<VariableElement, TypeInfo>> cross_product =
          TypeInfo.getTypeInfoCrossProduct(type_map, method);
      for (Map<VariableElement, TypeInfo> typeinfos_instance : cross_product) {
        printMethodWithMultiType(
            env,
            type_map,
            writer,
            interface_decl,
            method,
            typeinfos_instance,
            Mode.NORMAL,
            generate_error_checks,
            context_specific);
      }
    }
    if (method.getAnnotation(CachedResult.class) != null
        && !method.getAnnotation(CachedResult.class).isRange()) {
      printMethodWithMultiType(
          env,
          type_map,
          writer,
          interface_decl,
          method,
          TypeInfo.getDefaultTypeInfoMap(method),
          Mode.CACHEDRESULT,
          generate_error_checks,
          context_specific);
    }

    Reuse reuse_annotation = method.getAnnotation(Reuse.class);
    Alternate alt_annotation = method.getAnnotation(Alternate.class);
    if (alt_annotation == null || (alt_annotation.nativeAlt() && !alt_annotation.skipNative())) {
      if (alt_annotation != null
          && method.getSimpleName().toString().equals(alt_annotation.value())) {
        throw new RuntimeException(
            "An alternate function with native code should have a different name than the main function.");
      }

      if (reuse_annotation == null) {
        printJavaNativeStub(
            env, writer, method, Mode.NORMAL, generate_error_checks, context_specific);
      }

      if (Utils.hasMethodBufferObjectParameter(method)) {
        printMethodWithMultiType(
            env,
            type_map,
            writer,
            interface_decl,
            method,
            TypeInfo.getDefaultTypeInfoMap(method),
            Mode.BUFFEROBJECT,
            generate_error_checks,
            context_specific);
        if (reuse_annotation == null) {
          printJavaNativeStub(
              env, writer, method, Mode.BUFFEROBJECT, generate_error_checks, context_specific);
        }
      }
    }
  }