Skip to content

Commit

Permalink
Generate @IntDef annotations for nanoproto enums.
Browse files Browse the repository at this point in the history
@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
  • Loading branch information
jpd236 authored and brianduff committed Apr 28, 2015
1 parent 41f0294 commit ec19be2
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 9 deletions.
23 changes: 23 additions & 0 deletions javanano/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
----------------------------------------------
Expand Down
40 changes: 36 additions & 4 deletions src/google/protobuf/compiler/javanano/javanano_enum.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 20 additions & 4 deletions src/google/protobuf/compiler/javanano/javanano_enum_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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_,
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/google/protobuf/compiler/javanano/javanano_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
11 changes: 10 additions & 1 deletion src/google/protobuf/compiler/javanano/javanano_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ec19be2

Please sign in to comment.