From 30d873bd66f50a6021de5d44d7ee5b8a809b0524 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Wed, 11 Feb 2015 13:12:14 -0800 Subject: 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 --- Android.mk | 8 ++++- java/README.txt | 24 ++++++++++++- .../protobuf/compiler/javanano/javanano_enum.cc | 40 +++++++++++++++++++--- .../compiler/javanano/javanano_enum_field.cc | 24 ++++++++++--- .../compiler/javanano/javanano_generator.cc | 2 ++ .../protobuf/compiler/javanano/javanano_params.h | 11 +++++- 6 files changed, 98 insertions(+), 11 deletions(-) diff --git a/Android.mk b/Android.mk index fcf9a21..5dc957c 100644 --- a/Android.mk +++ b/Android.mk @@ -462,6 +462,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := android-nano-test-parcelable LOCAL_MODULE_TAGS := tests LOCAL_SDK_VERSION := current +# Only needed at compile-time. +LOCAL_JAVA_LIBRARIES := android-support-annotations LOCAL_PROTOC_OPTIMIZE_TYPE := nano @@ -470,7 +472,8 @@ LOCAL_SRC_FILES := src/google/protobuf/unittest_simple_nano.proto LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ - parcelable_messages = true + parcelable_messages = true, \ + generate_intdefs = true include $(BUILD_STATIC_JAVA_LIBRARY) @@ -480,6 +483,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := android-nano-test-parcelable-extendable LOCAL_MODULE_TAGS := tests LOCAL_SDK_VERSION := current +# Only needed at compile-time. +LOCAL_JAVA_LIBRARIES := android-support-annotations LOCAL_PROTOC_OPTIMIZE_TYPE := nano @@ -489,6 +494,7 @@ LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ parcelable_messages = true, \ + generate_intdefs = true, \ store_unknown_fields = true include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/java/README.txt b/java/README.txt index 7081a84..b02e88b 100644 --- a/java/README.txt +++ b/java/README.txt @@ -593,6 +593,28 @@ ignore_services={true,false} (default: false) parcelable_messages={true,false} (default: 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: @@ -646,7 +668,7 @@ Please run the following steps to test: - "make -j12 aprotoc libprotobuf-java-nano aprotoc-test-nano-params NanoAndroidTest" and check for build errors. - Plug in an Android device or start an emulator. -- adb install -r out/target/product/generic/data/app/NanoAndroidTest.apk +- adb install -r out/target/product/generic/data/app/NanoAndroidTest/NanoAndroidTest.apk - Run: "adb shell am instrument -w com.google.protobuf.nano.test/android.test.InstrumentationTestRunner" and verify all tests pass. diff --git a/src/google/protobuf/compiler/javanano/javanano_enum.cc b/src/google/protobuf/compiler/javanano/javanano_enum.cc index f934b05..c6e8dfe 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum.cc +++ b/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 diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc index 99b316b..7666db3 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc +++ b/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" diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc index 9ca06cd..96e3e80 100644 --- a/src/google/protobuf/compiler/javanano/javanano_generator.cc +++ b/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; } diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h index 77bc717..e3b4bb9 100644 --- a/src/google/protobuf/compiler/javanano/javanano_params.h +++ b/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 -- cgit v1.1