1 #include "Errors.h" 2 #include "stream_proto_utils.h" 3 #include "string_utils.h" 4 5 #include <stdio.h> 6 #include <iomanip> 7 #include <iostream> 8 #include <sstream> 9 #include <map> 10 11 using namespace android::stream_proto; 12 using namespace google::protobuf::io; 13 using namespace std; 14 15 /** 16 * If the descriptor gives us a class name, use that. Otherwise make one up from 17 * the filename of the .proto file. 18 */ 19 static string 20 make_outer_class_name(const FileDescriptorProto& file_descriptor) 21 { 22 string name = file_descriptor.options().java_outer_classname(); 23 if (name.size() == 0) { 24 name = to_camel_case(file_base_name(file_descriptor.name())); 25 if (name.size() == 0) { 26 ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE, 27 "Unable to make an outer class name for file: %s", 28 file_descriptor.name().c_str()); 29 name = "Unknown"; 30 } 31 } 32 return name; 33 } 34 35 /** 36 * Figure out the package name that we are generating. 37 */ 38 static string 39 make_java_package(const FileDescriptorProto& file_descriptor) { 40 if (file_descriptor.options().has_java_package()) { 41 return file_descriptor.options().java_package(); 42 } else { 43 return file_descriptor.package(); 44 } 45 } 46 47 /** 48 * Figure out the name of the file we are generating. 49 */ 50 static string 51 make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name) 52 { 53 string const package = make_java_package(file_descriptor); 54 string result; 55 if (package.size() > 0) { 56 result = replace_string(package, '.', '/'); 57 result += '/'; 58 } 59 60 result += class_name; 61 result += ".java"; 62 63 return result; 64 } 65 66 static string 67 indent_more(const string& indent) 68 { 69 return indent + INDENT; 70 } 71 72 /** 73 * Write the constants for an enum. 74 */ 75 static void 76 write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent) 77 { 78 const int N = enu.value_size(); 79 text << indent << "// enum " << enu.name() << endl; 80 for (int i=0; i<N; i++) { 81 const EnumValueDescriptorProto& value = enu.value(i); 82 text << indent << "public static final int " 83 << make_constant_name(value.name()) 84 << " = " << value.number() << ";" << endl; 85 } 86 text << endl; 87 } 88 89 /** 90 * Write a field. 91 */ 92 static void 93 write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent) 94 { 95 string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL 96 ? "optional " : ""; 97 string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED 98 ? "repeated " : ""; 99 string proto_type = get_proto_type(field); 100 string packed_comment = field.options().packed() 101 ? " [packed=true]" : ""; 102 text << indent << "// " << optional_comment << repeated_comment << proto_type << ' ' 103 << field.name() << " = " << field.number() << packed_comment << ';' << endl; 104 105 text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x"; 106 107 ios::fmtflags fmt(text.flags()); 108 text << setfill('0') << setw(16) << hex << get_field_id(field); 109 text.flags(fmt); 110 111 text << "L;" << endl; 112 113 text << endl; 114 } 115 116 /** 117 * Write a Message constants class. 118 */ 119 static void 120 write_message(stringstream& text, const DescriptorProto& message, const string& indent) 121 { 122 int N; 123 const string indented = indent_more(indent); 124 125 text << indent << "// message " << message.name() << endl; 126 text << indent << "public final class " << message.name() << " {" << endl; 127 text << endl; 128 129 // Enums 130 N = message.enum_type_size(); 131 for (int i=0; i<N; i++) { 132 write_enum(text, message.enum_type(i), indented); 133 } 134 135 // Nested classes 136 N = message.nested_type_size(); 137 for (int i=0; i<N; i++) { 138 write_message(text, message.nested_type(i), indented); 139 } 140 141 // Fields 142 N = message.field_size(); 143 for (int i=0; i<N; i++) { 144 write_field(text, message.field(i), indented); 145 } 146 147 text << indent << "}" << endl; 148 text << endl; 149 } 150 151 /** 152 * Write the contents of a file. 153 * 154 * If there are enums and generate_outer is false, invalid java code will be generated. 155 */ 156 static void 157 write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor, 158 const string& filename, bool generate_outer, 159 const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages) 160 { 161 stringstream text; 162 163 string const package_name = make_java_package(file_descriptor); 164 string const outer_class_name = make_outer_class_name(file_descriptor); 165 166 text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl; 167 text << "// source: " << file_descriptor.name() << endl << endl; 168 169 if (package_name.size() > 0) { 170 if (package_name.size() > 0) { 171 text << "package " << package_name << ";" << endl; 172 text << endl; 173 } 174 } 175 176 // This bit of policy is android api rules specific: Raw proto classes 177 // must never be in the API 178 text << "/** @hide */" << endl; 179 // text << "@android.annotation.TestApi" << endl; 180 181 if (generate_outer) { 182 text << "public final class " << outer_class_name << " {" << endl; 183 text << endl; 184 } 185 186 size_t N; 187 const string indented = generate_outer ? indent_more("") : string(); 188 189 N = enums.size(); 190 for (size_t i=0; i<N; i++) { 191 write_enum(text, enums[i], indented); 192 } 193 194 N = messages.size(); 195 for (size_t i=0; i<N; i++) { 196 write_message(text, messages[i], indented); 197 } 198 199 if (generate_outer) { 200 text << "}" << endl; 201 } 202 203 CodeGeneratorResponse::File* file_response = response->add_file(); 204 file_response->set_name(filename); 205 file_response->set_content(text.str()); 206 } 207 208 /** 209 * Write one file per class. Put all of the enums into the "outer" class. 210 */ 211 static void 212 write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) 213 { 214 // If there is anything to put in the outer class file, create one 215 if (file_descriptor.enum_type_size() > 0) { 216 vector<EnumDescriptorProto> enums; 217 int N = file_descriptor.enum_type_size(); 218 for (int i=0; i<N; i++) { 219 enums.push_back(file_descriptor.enum_type(i)); 220 } 221 222 vector<DescriptorProto> messages; 223 224 write_file(response, file_descriptor, 225 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), 226 true, enums, messages); 227 } 228 229 // For each of the message types, make a file 230 int N = file_descriptor.message_type_size(); 231 for (int i=0; i<N; i++) { 232 vector<EnumDescriptorProto> enums; 233 234 vector<DescriptorProto> messages; 235 messages.push_back(file_descriptor.message_type(i)); 236 237 write_file(response, file_descriptor, 238 make_file_name(file_descriptor, file_descriptor.message_type(i).name()), 239 false, enums, messages); 240 } 241 } 242 243 static void 244 write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) 245 { 246 int N; 247 248 vector<EnumDescriptorProto> enums; 249 N = file_descriptor.enum_type_size(); 250 for (int i=0; i<N; i++) { 251 enums.push_back(file_descriptor.enum_type(i)); 252 } 253 254 vector<DescriptorProto> messages; 255 N = file_descriptor.message_type_size(); 256 for (int i=0; i<N; i++) { 257 messages.push_back(file_descriptor.message_type(i)); 258 } 259 260 write_file(response, file_descriptor, 261 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), 262 true, enums, messages); 263 } 264 265 /** 266 * Main. 267 */ 268 int 269 main(int argc, char const*const* argv) 270 { 271 (void)argc; 272 (void)argv; 273 274 GOOGLE_PROTOBUF_VERIFY_VERSION; 275 276 CodeGeneratorRequest request; 277 CodeGeneratorResponse response; 278 279 // Read the request 280 request.ParseFromIstream(&cin); 281 282 // Build the files we need. 283 const int N = request.proto_file_size(); 284 for (int i=0; i<N; i++) { 285 const FileDescriptorProto& file_descriptor = request.proto_file(i); 286 if (should_generate_for_file(request, file_descriptor.name())) { 287 if (file_descriptor.options().java_multiple_files()) { 288 write_multiple_files(&response, file_descriptor); 289 } else { 290 write_single_file(&response, file_descriptor); 291 } 292 } 293 } 294 295 // If we had errors, don't write the response. Print the errors and exit. 296 if (ERRORS.HasErrors()) { 297 ERRORS.Print(); 298 return 1; 299 } 300 301 // If we didn't have errors, write the response and exit happily. 302 response.SerializeToOstream(&cout); 303 return 0; 304 } 305