| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """protoc plugin to create c++ reader/writer for json-encoded protobufs | |
| 7 | |
| 8 The reader/writer use Chrome's base::Values. | |
| 9 """ | |
| 10 | |
| 11 import optparse | |
| 12 import os | |
| 13 | |
| 14 from util import plugin, plugin_protos, writer | |
| 15 from util.plugin import Indented | |
| 16 | |
| 17 class CppConverterWriter(writer.CodeWriter): | |
| 18 def WriteProtoFile(self, proto_file, output_dir): | |
| 19 err = proto_file.CheckSupported() | |
| 20 if err: | |
| 21 self.AddError(err) | |
| 22 return | |
| 23 | |
| 24 self.WriteCStyleHeader() | |
| 25 | |
| 26 self.Output('#include "{output_dir}{generated_pb_h}"', | |
| 27 output_dir=output_dir + '/' if output_dir else '', | |
| 28 generated_pb_h=proto_file.CppBaseHeader()) | |
| 29 self.Output('') | |
| 30 | |
| 31 self.Output('// proto dependencies') | |
| 32 | |
| 33 for dep in proto_file.GetDependencies(): | |
| 34 self.WriteIncludeForProtoFile(dep) | |
| 35 | |
| 36 self.Output('') | |
| 37 self.Output('// base dependencies') | |
| 38 self.Output('#include "base/values.h"') | |
| 39 self.Output('') | |
| 40 self.Output('#include <memory>') | |
| 41 self.Output('#include <string>') | |
| 42 self.Output('#include <utility>') | |
| 43 self.Output('') | |
| 44 | |
| 45 namespaces = proto_file.ProtoNamespaces() + ['json'] | |
| 46 for name in namespaces: | |
| 47 self.Output('namespace {name} {{', name=name) | |
| 48 self.IncreaseIndent() | |
| 49 | |
| 50 for message in proto_file.GetMessages(): | |
| 51 self.WriteMessage(message) | |
| 52 | |
| 53 # Nothing to do for enums | |
| 54 | |
| 55 for name in namespaces: | |
| 56 self.DecreaseIndent() | |
| 57 self.Output('}}') | |
| 58 | |
| 59 def WriteMessage(self, message): | |
| 60 self.Output('class {class_name} {{', | |
| 61 class_name=message.CppConverterClassName()) | |
| 62 self.Output(' public:') | |
| 63 with self.AddIndent(): | |
| 64 for nested_class in message.GetMessages(): | |
| 65 self.WriteMessage(nested_class) | |
| 66 | |
| 67 generated_class_name = message.QualifiedTypes().cpp_base | |
| 68 # Nothing to write for enums. | |
| 69 | |
| 70 self.Output( | |
| 71 'static bool ReadFromValue(const base::Value* json, {generated_class_n
ame}* message) {{\n' | |
| 72 #' {generated_class_name} message;\n' | |
| 73 ' const base::DictionaryValue* dict;\n' | |
| 74 ' if (!json->GetAsDictionary(&dict)) goto error;\n' | |
| 75 '', | |
| 76 generated_class_name=generated_class_name) | |
| 77 | |
| 78 with self.AddIndent(): | |
| 79 for field_proto in message.GetFields(): | |
| 80 self.WriteFieldRead(field_proto) | |
| 81 | |
| 82 self.Output( | |
| 83 ' return true;\n' | |
| 84 '\n' | |
| 85 'error:\n' | |
| 86 ' return false;\n' | |
| 87 '}}\n' | |
| 88 '\n' | |
| 89 'static std::unique_ptr<base::DictionaryValue> WriteToValue(const {gen
erated_class_name}& message) {{\n' | |
| 90 ' std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryVal
ue());\n' | |
| 91 '', | |
| 92 generated_class_name=generated_class_name) | |
| 93 | |
| 94 with self.AddIndent(): | |
| 95 for field_proto in message.GetFields(): | |
| 96 self.FieldWriteToValue(field_proto) | |
| 97 | |
| 98 self.Output( | |
| 99 ' return dict;\n' | |
| 100 '', | |
| 101 generated_class_name=generated_class_name) | |
| 102 self.Output('}}') | |
| 103 | |
| 104 self.Output('}};') | |
| 105 self.Output('') | |
| 106 | |
| 107 def FieldWriteToValue(self, field): | |
| 108 if field.IsRepeated(): | |
| 109 self.Output('{{') | |
| 110 else: | |
| 111 self.Output('if (message.has_{field_name}()) {{\n', field_name=field.name) | |
| 112 | |
| 113 with self.AddIndent(): | |
| 114 if field.IsRepeated(): | |
| 115 self.RepeatedMemberFieldWriteToValue(field) | |
| 116 else: | |
| 117 self.OptionalMemberFieldWriteToValue(field) | |
| 118 | |
| 119 self.Output('}}') | |
| 120 | |
| 121 def RepeatedMemberFieldWriteToValue(self, field): | |
| 122 prologue = ( | |
| 123 'base::ListValue* field_list = new base::ListValue();\n' | |
| 124 'dict->Set("{field_number}", field_list);\n' | |
| 125 'for (int i = 0; i < message.{field_name}_size(); ++i) {{\n' | |
| 126 ) | |
| 127 | |
| 128 if field.IsClassType(): | |
| 129 middle = ( | |
| 130 'std::unique_ptr<base::Value> inner_message_value = \n' | |
| 131 ' {inner_class_converter}::WriteToValue(message.{field_name}(i));\n
' | |
| 132 'field_list->Append(std::move(inner_message_value));\n' | |
| 133 ) | |
| 134 else: | |
| 135 middle = ( | |
| 136 'field_list->Append{value_type}(message.{field_name}(i));\n' | |
| 137 ) | |
| 138 self.Output( | |
| 139 prologue + Indented(middle) + '\n}}', | |
| 140 field_number=field.JavascriptIndex(), | |
| 141 field_name=field.name, | |
| 142 value_type=field.CppValueType() if not field.IsClassType() else None, | |
| 143 inner_class_converter=field.CppConverterType() | |
| 144 ) | |
| 145 | |
| 146 def OptionalMemberFieldWriteToValue(self, field): | |
| 147 if field.IsClassType(): | |
| 148 body = ( | |
| 149 'std::unique_ptr<base::Value> inner_message_value = \n' | |
| 150 ' {inner_class_converter}::WriteToValue(message.{field_name}());\n' | |
| 151 'dict->Set("{field_number}", std::move(inner_message_value));\n' | |
| 152 ) | |
| 153 else: | |
| 154 body = ( | |
| 155 'dict->Set{value_type}("{field_number}", message.{field_name}());\n' | |
| 156 ) | |
| 157 | |
| 158 self.Output( | |
| 159 body, | |
| 160 field_number=field.JavascriptIndex(), | |
| 161 field_name=field.name, | |
| 162 value_type=field.CppValueType() if not field.IsClassType() else None, | |
| 163 inner_class_converter=field.CppConverterType(), | |
| 164 ) | |
| 165 | |
| 166 def WriteFieldRead(self, field): | |
| 167 self.Output('if (dict->HasKey("{field_number}")) {{', | |
| 168 field_number=field.JavascriptIndex()) | |
| 169 | |
| 170 with self.AddIndent(): | |
| 171 if field.IsRepeated(): | |
| 172 self.RepeatedMemberFieldRead(field) | |
| 173 else: | |
| 174 self.OptionalMemberFieldRead(field) | |
| 175 | |
| 176 self.Output('}}') | |
| 177 | |
| 178 def RepeatedMemberFieldRead(self, field): | |
| 179 prologue = ( | |
| 180 'const base::ListValue* field_list;\n' | |
| 181 'if (!dict->GetList("{field_number}", &field_list)) {{\n' | |
| 182 ' goto error;\n' | |
| 183 '}}\n' | |
| 184 'for (size_t i = 0; i < field_list->GetSize(); ++i) {{\n' | |
| 185 ) | |
| 186 | |
| 187 if field.IsClassType(): | |
| 188 middle = ( | |
| 189 'const base::Value* inner_message_value;\n' | |
| 190 'if (!field_list->Get(i, &inner_message_value)) {{\n' | |
| 191 ' goto error;\n' | |
| 192 '}}\n' | |
| 193 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message
->add_{field_name}())) {{\n' | |
| 194 ' goto error;\n' | |
| 195 '}}\n' | |
| 196 ) | |
| 197 else: | |
| 198 middle = ( | |
| 199 '{cpp_type} field_value;\n' | |
| 200 'if (!field_list->Get{value_type}(i, &field_value)) {{\n' | |
| 201 ' goto error;\n' | |
| 202 '}}\n' | |
| 203 'message->add_{field_name}(field_value);\n' | |
| 204 ) | |
| 205 | |
| 206 self.Output( | |
| 207 prologue + Indented(middle) + '\n}}', | |
| 208 field_number=field.JavascriptIndex(), | |
| 209 field_name=field.name, | |
| 210 cpp_type=field.CppPrimitiveType() if not field.IsClassType() else None, | |
| 211 value_type=field.CppValueType() if not field.IsClassType() else None, | |
| 212 inner_class_parser=field.CppConverterType() | |
| 213 ) | |
| 214 | |
| 215 def OptionalMemberFieldRead(self, field): | |
| 216 if field.IsClassType(): | |
| 217 self.Output( | |
| 218 'const base::Value* inner_message_value;\n' | |
| 219 'if (!dict->Get("{field_number}", &inner_message_value)) {{\n' | |
| 220 ' goto error;\n' | |
| 221 '}}\n' | |
| 222 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message
->mutable_{field_name}())) {{\n' | |
| 223 ' goto error;\n' | |
| 224 '}}\n' | |
| 225 '', | |
| 226 field_number=field.JavascriptIndex(), | |
| 227 field_name=field.name, | |
| 228 inner_class_parser=field.CppConverterType() | |
| 229 ) | |
| 230 else: | |
| 231 self.Output( | |
| 232 '{cpp_type} field_value;\n' | |
| 233 'if (!dict->Get{value_type}("{field_number}", &field_value)) {{\n' | |
| 234 ' goto error;\n' | |
| 235 '}}\n' | |
| 236 'message->set_{field_name}(field_value);\n' | |
| 237 '', | |
| 238 field_number=field.JavascriptIndex(), | |
| 239 field_name=field.name, | |
| 240 cpp_type=field.CppPrimitiveType(), | |
| 241 value_type=field.CppValueType() | |
| 242 ) | |
| 243 | |
| 244 | |
| 245 def main(): | |
| 246 request = plugin.ReadRequestFromStdin() | |
| 247 response = plugin_protos.PluginResponse() | |
| 248 | |
| 249 output_dir = request.GetArgs().get('output_dir', '') | |
| 250 | |
| 251 # TODO(cjhopman): This should only be generating files for files listed in | |
| 252 # request.file_to_generate. Since we don't actually support dependencies, | |
| 253 # only files in file_to_generate should be here, anyway. | |
| 254 for proto_file in request.GetAllFiles(): | |
| 255 plugin.RegisterProtoFile(proto_file) | |
| 256 | |
| 257 writer = CppConverterWriter() | |
| 258 writer.WriteProtoFile(proto_file, output_dir) | |
| 259 | |
| 260 converter_filename = proto_file.CppConverterFilename() | |
| 261 if output_dir: | |
| 262 converter_filename = os.path.join(output_dir, | |
| 263 os.path.split(converter_filename)[1]) | |
| 264 | |
| 265 response.AddFileWithContent(converter_filename, writer.GetValue()) | |
| 266 if writer.GetErrors(): | |
| 267 response.AddError('\n'.join(writer.GetErrors())) | |
| 268 | |
| 269 response.WriteToStdout() | |
| 270 | |
| 271 | |
| 272 if __name__ == '__main__': | |
| 273 main() | |
| OLD | NEW |