It is very unlikely the protobuf changes the value in descriptor.h, and if defines an extra mapping, there are several places to maintain: 1. java-stream, 2. cpp-stream, 3. ProtoOutputStream.java 4. ProtoOutputStream.cpp 5. Privacy.h (GetFieldId) 6. StatsLog to generate field id (type << 32 + field number) Therefore use the current value in descriptor.h seems reasonable unless they change that, very very unlikely, they probably will just add new types, and deprect the existing ones like Group. Test: test output of dumpsys proto Change-Id: I6e150ab427851dd3b5dd55d3b273deeed7a0963c
305 lines
8.5 KiB
C++
305 lines
8.5 KiB
C++
#include "Errors.h"
|
|
#include "stream_proto_utils.h"
|
|
#include "string_utils.h"
|
|
|
|
#include <stdio.h>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <map>
|
|
|
|
using namespace android::stream_proto;
|
|
using namespace google::protobuf::io;
|
|
using namespace std;
|
|
|
|
/**
|
|
* If the descriptor gives us a class name, use that. Otherwise make one up from
|
|
* the filename of the .proto file.
|
|
*/
|
|
static string
|
|
make_outer_class_name(const FileDescriptorProto& file_descriptor)
|
|
{
|
|
string name = file_descriptor.options().java_outer_classname();
|
|
if (name.size() == 0) {
|
|
name = to_camel_case(file_base_name(file_descriptor.name()));
|
|
if (name.size() == 0) {
|
|
ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE,
|
|
"Unable to make an outer class name for file: %s",
|
|
file_descriptor.name().c_str());
|
|
name = "Unknown";
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Figure out the package name that we are generating.
|
|
*/
|
|
static string
|
|
make_java_package(const FileDescriptorProto& file_descriptor) {
|
|
if (file_descriptor.options().has_java_package()) {
|
|
return file_descriptor.options().java_package();
|
|
} else {
|
|
return file_descriptor.package();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Figure out the name of the file we are generating.
|
|
*/
|
|
static string
|
|
make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
|
|
{
|
|
string const package = make_java_package(file_descriptor);
|
|
string result;
|
|
if (package.size() > 0) {
|
|
result = replace_string(package, '.', '/');
|
|
result += '/';
|
|
}
|
|
|
|
result += class_name;
|
|
result += ".java";
|
|
|
|
return result;
|
|
}
|
|
|
|
static string
|
|
indent_more(const string& indent)
|
|
{
|
|
return indent + INDENT;
|
|
}
|
|
|
|
/**
|
|
* Write the constants for an enum.
|
|
*/
|
|
static void
|
|
write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
|
|
{
|
|
const int N = enu.value_size();
|
|
text << indent << "// enum " << enu.name() << endl;
|
|
for (int i=0; i<N; i++) {
|
|
const EnumValueDescriptorProto& value = enu.value(i);
|
|
text << indent << "public static final int "
|
|
<< make_constant_name(value.name())
|
|
<< " = " << value.number() << ";" << endl;
|
|
}
|
|
text << endl;
|
|
}
|
|
|
|
/**
|
|
* Write a field.
|
|
*/
|
|
static void
|
|
write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
|
|
{
|
|
string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
|
|
? "optional " : "";
|
|
string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
|
|
? "repeated " : "";
|
|
string proto_type = get_proto_type(field);
|
|
string packed_comment = field.options().packed()
|
|
? " [packed=true]" : "";
|
|
text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
|
|
<< field.name() << " = " << field.number() << packed_comment << ';' << endl;
|
|
|
|
text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
|
|
|
|
ios::fmtflags fmt(text.flags());
|
|
text << setfill('0') << setw(16) << hex << get_field_id(field);
|
|
text.flags(fmt);
|
|
|
|
text << "L;" << endl;
|
|
|
|
text << endl;
|
|
}
|
|
|
|
/**
|
|
* Write a Message constants class.
|
|
*/
|
|
static void
|
|
write_message(stringstream& text, const DescriptorProto& message, const string& indent)
|
|
{
|
|
int N;
|
|
const string indented = indent_more(indent);
|
|
|
|
text << indent << "// message " << message.name() << endl;
|
|
text << indent << "public final class " << message.name() << " {" << endl;
|
|
text << endl;
|
|
|
|
// Enums
|
|
N = message.enum_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
write_enum(text, message.enum_type(i), indented);
|
|
}
|
|
|
|
// Nested classes
|
|
N = message.nested_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
write_message(text, message.nested_type(i), indented);
|
|
}
|
|
|
|
// Fields
|
|
N = message.field_size();
|
|
for (int i=0; i<N; i++) {
|
|
write_field(text, message.field(i), indented);
|
|
}
|
|
|
|
text << indent << "}" << endl;
|
|
text << endl;
|
|
}
|
|
|
|
/**
|
|
* Write the contents of a file.
|
|
*
|
|
* If there are enums and generate_outer is false, invalid java code will be generated.
|
|
*/
|
|
static void
|
|
write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
|
|
const string& filename, bool generate_outer,
|
|
const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
|
|
{
|
|
stringstream text;
|
|
|
|
string const package_name = make_java_package(file_descriptor);
|
|
string const outer_class_name = make_outer_class_name(file_descriptor);
|
|
|
|
text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
|
|
text << "// source: " << file_descriptor.name() << endl << endl;
|
|
|
|
if (package_name.size() > 0) {
|
|
if (package_name.size() > 0) {
|
|
text << "package " << package_name << ";" << endl;
|
|
text << endl;
|
|
}
|
|
}
|
|
|
|
// This bit of policy is android api rules specific: Raw proto classes
|
|
// must never be in the API
|
|
text << "/** @hide */" << endl;
|
|
// text << "@android.annotation.TestApi" << endl;
|
|
|
|
if (generate_outer) {
|
|
text << "public final class " << outer_class_name << " {" << endl;
|
|
text << endl;
|
|
}
|
|
|
|
size_t N;
|
|
const string indented = generate_outer ? indent_more("") : string();
|
|
|
|
N = enums.size();
|
|
for (size_t i=0; i<N; i++) {
|
|
write_enum(text, enums[i], indented);
|
|
}
|
|
|
|
N = messages.size();
|
|
for (size_t i=0; i<N; i++) {
|
|
write_message(text, messages[i], indented);
|
|
}
|
|
|
|
if (generate_outer) {
|
|
text << "}" << endl;
|
|
}
|
|
|
|
CodeGeneratorResponse::File* file_response = response->add_file();
|
|
file_response->set_name(filename);
|
|
file_response->set_content(text.str());
|
|
}
|
|
|
|
/**
|
|
* Write one file per class. Put all of the enums into the "outer" class.
|
|
*/
|
|
static void
|
|
write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
|
|
{
|
|
// If there is anything to put in the outer class file, create one
|
|
if (file_descriptor.enum_type_size() > 0) {
|
|
vector<EnumDescriptorProto> enums;
|
|
int N = file_descriptor.enum_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
enums.push_back(file_descriptor.enum_type(i));
|
|
}
|
|
|
|
vector<DescriptorProto> messages;
|
|
|
|
write_file(response, file_descriptor,
|
|
make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
|
|
true, enums, messages);
|
|
}
|
|
|
|
// For each of the message types, make a file
|
|
int N = file_descriptor.message_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
vector<EnumDescriptorProto> enums;
|
|
|
|
vector<DescriptorProto> messages;
|
|
messages.push_back(file_descriptor.message_type(i));
|
|
|
|
write_file(response, file_descriptor,
|
|
make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
|
|
false, enums, messages);
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
|
|
{
|
|
int N;
|
|
|
|
vector<EnumDescriptorProto> enums;
|
|
N = file_descriptor.enum_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
enums.push_back(file_descriptor.enum_type(i));
|
|
}
|
|
|
|
vector<DescriptorProto> messages;
|
|
N = file_descriptor.message_type_size();
|
|
for (int i=0; i<N; i++) {
|
|
messages.push_back(file_descriptor.message_type(i));
|
|
}
|
|
|
|
write_file(response, file_descriptor,
|
|
make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
|
|
true, enums, messages);
|
|
}
|
|
|
|
/**
|
|
* Main.
|
|
*/
|
|
int
|
|
main(int argc, char const*const* argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
|
|
|
CodeGeneratorRequest request;
|
|
CodeGeneratorResponse response;
|
|
|
|
// Read the request
|
|
request.ParseFromIstream(&cin);
|
|
|
|
// Build the files we need.
|
|
const int N = request.proto_file_size();
|
|
for (int i=0; i<N; i++) {
|
|
const FileDescriptorProto& file_descriptor = request.proto_file(i);
|
|
if (should_generate_for_file(request, file_descriptor.name())) {
|
|
if (file_descriptor.options().java_multiple_files()) {
|
|
write_multiple_files(&response, file_descriptor);
|
|
} else {
|
|
write_single_file(&response, file_descriptor);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we had errors, don't write the response. Print the errors and exit.
|
|
if (ERRORS.HasErrors()) {
|
|
ERRORS.Print();
|
|
return 1;
|
|
}
|
|
|
|
// If we didn't have errors, write the response and exit happily.
|
|
response.SerializeToOstream(&cout);
|
|
return 0;
|
|
}
|