Procházet zdrojové kódy

Generate @IntDef annotations for nanoproto enums.

@IntDef is a support library annotation which allows build tools to
determine the valid set of values for a given integer field when that
field is intended to be restricted like an enum. This avoids the
overhead of enums while still allowing for compile-time type checking
in most circumstances.

Change-Id: Iee02e0b49a8e069f6456572f538e0a0d301fdfd5
Jeff Davidson před 10 roky
rodič
revize
ec19be2f3c

+ 23 - 0
javanano/README.md

@@ -145,6 +145,7 @@ optional_field_style   -> default or accessors
 enum_style             -> c or java
 ignore_services        -> true or false
 parcelable_messages    -> true or false
+generate_intdefs       -> true or false
 ```
 
 **java_package=\<file-name\>|\<package-name\>** (no default)
@@ -302,6 +303,28 @@ parcelable_messages    -> true or false
 
   Android-specific option to generate Parcelable messages.
 
+**generate_intdefs={true,false}** (default: false)
+  Android-specific option to generate @IntDef annotations for enums.
+
+  If turned on, an '@IntDef' annotation (a public @interface) will be
+  generated for each enum, and every integer parameter and return
+  value in the generated code meant for this enum will be annotated
+  with it. This interface is generated with the same name and at the
+  same place as the enum members' container interfaces described
+  above under 'enum_style=java', regardless of the enum_style option
+  used. When this is combined with enum_style=java, the interface
+  will be both the '@IntDef' annotation and the container of the enum
+  members; otherwise the interface has an empty body.
+
+  Your app must declare a compile-time dependency on the
+  android-support-annotations library.
+
+  For more information on how these @IntDef annotations help with
+  compile-time type safety, see:
+  https://sites.google.com/a/android.com/tools/tech-docs/support-annotations
+  and
+  https://developer.android.com/reference/android/support/annotation/IntDef.html
+
 
 To use nano protobufs within the Android repo:
 ----------------------------------------------

+ 36 - 4
src/google/protobuf/compiler/javanano/javanano_enum.cc

@@ -73,13 +73,45 @@ void EnumGenerator::Generate(io::Printer* printer) {
       "// enum $classname$\n",
       "classname", descriptor_->name());
 
+  const string classname = RenameJavaKeywords(descriptor_->name());
+
   // Start of container interface
+  // If generating intdefs, we use the container interface as the intdef if
+  // present. Otherwise, we just make an empty @interface parallel to the
+  // constants.
+  bool use_intdef = params_.generate_intdefs();
   bool use_shell_class = params_.java_enum_style();
-  if (use_shell_class) {
-    printer->Print(
-      "public interface $classname$ {\n",
-      "classname", RenameJavaKeywords(descriptor_->name()));
+  if (use_intdef) {
+    // @IntDef annotation so tools can enforce correctness
+    // Annotations will be discarded by the compiler
+    printer->Print("@java.lang.annotation.Retention("
+      "java.lang.annotation.RetentionPolicy.SOURCE)\n"
+      "@android.support.annotation.IntDef({\n");
     printer->Indent();
+    for (int i = 0; i < canonical_values_.size(); i++) {
+      const string constant_name =
+          RenameJavaKeywords(canonical_values_[i]->name());
+      if (use_shell_class) {
+        printer->Print("$classname$.$name$,\n",
+          "classname", classname,
+          "name", constant_name);
+      } else {
+        printer->Print("$name$,\n", "name", constant_name);
+      }
+    }
+    printer->Outdent();
+    printer->Print("})\n");
+  }
+  if (use_shell_class || use_intdef) {
+    printer->Print(
+      "public $at_for_intdef$interface $classname$ {\n",
+      "classname", classname,
+      "at_for_intdef", use_intdef ? "@" : "");
+    if (use_shell_class) {
+        printer->Indent();
+    } else {
+        printer->Print("}\n\n");
+    }
   }
 
   // Canonical values

+ 20 - 4
src/google/protobuf/compiler/javanano/javanano_enum_field.cc

@@ -76,6 +76,10 @@ void SetEnumVariables(const Params& params,
       internal::WireFormatLite::MakeTag(descriptor->number(),
           internal::WireFormat::WireTypeForFieldType(descriptor->type())));
   (*variables)["message_name"] = descriptor->containing_type()->name();
+  const EnumDescriptor* enum_type = descriptor->enum_type();
+  (*variables)["message_type_intdef"] = "@"
+      + ToJavaName(params, enum_type->name(), true,
+          enum_type->containing_type(), enum_type->file());
 }
 
 void LoadEnumValues(const Params& params,
@@ -116,8 +120,10 @@ EnumFieldGenerator::~EnumFieldGenerator() {}
 
 void EnumFieldGenerator::
 GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
-  printer->Print(variables_,
-    "public $type$ $name$;\n");
+  if (params_.generate_intdefs()) {
+    printer->Print(variables_, "$message_type_intdef$\n");
+  }
+  printer->Print(variables_, "public $type$ $name$;\n");
 
   if (params_.generate_has()) {
     printer->Print(variables_,
@@ -256,12 +262,22 @@ AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {}
 
 void AccessorEnumFieldGenerator::
 GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
+  printer->Print(variables_, "private int $name$_;\n");
+  if (params_.generate_intdefs()) {
+    printer->Print(variables_, "$message_type_intdef$\n");
+  }
   printer->Print(variables_,
-    "private int $name$_;\n"
     "public int get$capitalized_name$() {\n"
     "  return $name$_;\n"
     "}\n"
-    "public $message_name$ set$capitalized_name$(int value) {\n"
+    "public $message_name$ set$capitalized_name$(");
+  if (params_.generate_intdefs()) {
+    printer->Print(variables_,
+      "\n"
+      "    $message_type_intdef$ ");
+  }
+  printer->Print(variables_,
+    "int value) {\n"
     "  $name$_ = value;\n"
     "  $set_has$;\n"
     "  return this;\n"

+ 2 - 0
src/google/protobuf/compiler/javanano/javanano_generator.cc

@@ -154,6 +154,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
       params.set_parcelable_messages(option_value == "true");
     } else if (option_name == "generate_clone") {
       params.set_generate_clone(option_value == "true");
+    } else if (option_name == "generate_intdefs") {
+      params.set_generate_intdefs(option_value == "true");
     } else {
       *error = "Ignore unknown javanano generator option: " + option_name;
     }

+ 10 - 1
src/google/protobuf/compiler/javanano/javanano_params.h

@@ -67,6 +67,7 @@ class Params {
   bool reftypes_primitive_enums_;
   bool generate_clear_;
   bool generate_clone_;
+  bool generate_intdefs_;
 
  public:
   Params(const string & base_name) :
@@ -83,7 +84,8 @@ class Params {
     parcelable_messages_(false),
     reftypes_primitive_enums_(false),
     generate_clear_(true),
-    generate_clone_(false) {
+    generate_clone_(false),
+    generate_intdefs_(false) {
   }
 
   const string& base_name() const {
@@ -240,6 +242,13 @@ class Params {
   bool generate_clone() const {
     return generate_clone_;
   }
+
+  void set_generate_intdefs(bool value) {
+    generate_intdefs_ = value;
+  }
+  bool generate_intdefs() const {
+    return generate_intdefs_;
+  }
 };
 
 }  // namespace javanano