OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2016 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 # import is not supported |
| 32 assert [] == proto_file.GetDependencies() |
| 33 |
| 34 self.Output('// base dependencies') |
| 35 self.Output('#include "base/values.h"') |
| 36 self.Output('') |
| 37 self.Output('#include <memory>') |
| 38 self.Output('#include <string>') |
| 39 self.Output('#include <utility>') |
| 40 self.Output('') |
| 41 |
| 42 namespaces = proto_file.ProtoNamespaces() + ['json'] |
| 43 for name in namespaces: |
| 44 self.Output('namespace {name} {{', name=name) |
| 45 self.IncreaseIndent() |
| 46 |
| 47 for message in proto_file.GetMessages(): |
| 48 self.WriteMessage(message) |
| 49 |
| 50 # Nothing to do for enums |
| 51 |
| 52 for name in namespaces: |
| 53 self.DecreaseIndent() |
| 54 self.Output('}}') |
| 55 |
| 56 def WriteMessage(self, message): |
| 57 self.Output('class {class_name} {{', |
| 58 class_name=message.CppConverterClassName()) |
| 59 self.Output(' public:') |
| 60 with self.AddIndent(): |
| 61 for nested_class in message.GetMessages(): |
| 62 self.WriteMessage(nested_class) |
| 63 |
| 64 generated_class_name = message.QualifiedTypes().cpp_base |
| 65 # Nothing to write for enums. |
| 66 |
| 67 self.Output( |
| 68 'static bool ReadFromValue(const base::Value* json, {generated_class_n
ame}* message) {{\n' |
| 69 #' {generated_class_name} message;\n' |
| 70 ' const base::DictionaryValue* dict;\n' |
| 71 ' if (!json->GetAsDictionary(&dict)) goto error;\n' |
| 72 '', |
| 73 generated_class_name=generated_class_name) |
| 74 |
| 75 with self.AddIndent(): |
| 76 for field_proto in message.GetFields(): |
| 77 self.WriteFieldRead(field_proto) |
| 78 |
| 79 self.Output( |
| 80 ' return true;\n' |
| 81 '\n' |
| 82 'error:\n' |
| 83 ' return false;\n' |
| 84 '}}\n' |
| 85 '\n' |
| 86 'static std::unique_ptr<base::DictionaryValue> WriteToValue(const {gen
erated_class_name}& message) {{\n' |
| 87 ' std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryVal
ue());\n' |
| 88 '', |
| 89 generated_class_name=generated_class_name) |
| 90 |
| 91 with self.AddIndent(): |
| 92 for field_proto in message.GetFields(): |
| 93 self.FieldWriteToValue(field_proto) |
| 94 |
| 95 self.Output( |
| 96 ' return dict;\n' |
| 97 '', |
| 98 generated_class_name=generated_class_name) |
| 99 self.Output('}}') |
| 100 |
| 101 self.Output('}};') |
| 102 self.Output('') |
| 103 |
| 104 def FieldWriteToValue(self, field): |
| 105 if field.IsRepeated(): |
| 106 self.Output('{{') |
| 107 else: |
| 108 self.Output('if (message.has_{field_name}()) {{\n', field_name=field.name) |
| 109 |
| 110 with self.AddIndent(): |
| 111 if field.IsRepeated(): |
| 112 self.RepeatedMemberFieldWriteToValue(field) |
| 113 else: |
| 114 self.OptionalMemberFieldWriteToValue(field) |
| 115 |
| 116 self.Output('}}') |
| 117 |
| 118 def RepeatedMemberFieldWriteToValue(self, field): |
| 119 prologue = ( |
| 120 'base::ListValue* field_list = new base::ListValue();\n' |
| 121 'dict->Set("{field_number}", field_list);\n' |
| 122 'for (int i = 0; i < message.{field_name}_size(); ++i) {{\n' |
| 123 ) |
| 124 |
| 125 if field.IsClassType(): |
| 126 middle = ( |
| 127 'std::unique_ptr<base::Value> inner_message_value = \n' |
| 128 ' {inner_class_converter}::WriteToValue(message.{field_name}(i));\n
' |
| 129 'field_list->Append(std::move(inner_message_value));\n' |
| 130 ) |
| 131 else: |
| 132 middle = ( |
| 133 'field_list->Append{value_type}(message.{field_name}(i));\n' |
| 134 ) |
| 135 self.Output( |
| 136 prologue + Indented(middle) + '\n}}', |
| 137 field_number=field.JavascriptIndex(), |
| 138 field_name=field.name, |
| 139 value_type=field.CppValueType() if not field.IsClassType() else None, |
| 140 inner_class_converter=field.CppConverterType() |
| 141 ) |
| 142 |
| 143 def OptionalMemberFieldWriteToValue(self, field): |
| 144 if field.IsClassType(): |
| 145 body = ( |
| 146 'std::unique_ptr<base::Value> inner_message_value = \n' |
| 147 ' {inner_class_converter}::WriteToValue(message.{field_name}());\n' |
| 148 'dict->Set("{field_number}", std::move(inner_message_value));\n' |
| 149 ) |
| 150 else: |
| 151 body = ( |
| 152 'dict->Set{value_type}("{field_number}", message.{field_name}());\n' |
| 153 ) |
| 154 |
| 155 self.Output( |
| 156 body, |
| 157 field_number=field.JavascriptIndex(), |
| 158 field_name=field.name, |
| 159 value_type=field.CppValueType() if not field.IsClassType() else None, |
| 160 inner_class_converter=field.CppConverterType(), |
| 161 ) |
| 162 |
| 163 def WriteFieldRead(self, field): |
| 164 self.Output('if (dict->HasKey("{field_number}")) {{', |
| 165 field_number=field.JavascriptIndex()) |
| 166 |
| 167 with self.AddIndent(): |
| 168 if field.IsRepeated(): |
| 169 self.RepeatedMemberFieldRead(field) |
| 170 else: |
| 171 self.OptionalMemberFieldRead(field) |
| 172 |
| 173 self.Output('}}') |
| 174 |
| 175 def RepeatedMemberFieldRead(self, field): |
| 176 prologue = ( |
| 177 'const base::ListValue* field_list;\n' |
| 178 'if (!dict->GetList("{field_number}", &field_list)) {{\n' |
| 179 ' goto error;\n' |
| 180 '}}\n' |
| 181 'for (size_t i = 0; i < field_list->GetSize(); ++i) {{\n' |
| 182 ) |
| 183 |
| 184 if field.IsClassType(): |
| 185 middle = ( |
| 186 'const base::Value* inner_message_value;\n' |
| 187 'if (!field_list->Get(i, &inner_message_value)) {{\n' |
| 188 ' goto error;\n' |
| 189 '}}\n' |
| 190 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message
->add_{field_name}())) {{\n' |
| 191 ' goto error;\n' |
| 192 '}}\n' |
| 193 ) |
| 194 else: |
| 195 middle = ( |
| 196 '{cpp_type} field_value;\n' |
| 197 'if (!field_list->Get{value_type}(i, &field_value)) {{\n' |
| 198 ' goto error;\n' |
| 199 '}}\n' |
| 200 'message->add_{field_name}(field_value);\n' |
| 201 ) |
| 202 |
| 203 self.Output( |
| 204 prologue + Indented(middle) + '\n}}', |
| 205 field_number=field.JavascriptIndex(), |
| 206 field_name=field.name, |
| 207 cpp_type=field.CppPrimitiveType() if not field.IsClassType() else None, |
| 208 value_type=field.CppValueType() if not field.IsClassType() else None, |
| 209 inner_class_parser=field.CppConverterType() |
| 210 ) |
| 211 |
| 212 def OptionalMemberFieldRead(self, field): |
| 213 if field.IsClassType(): |
| 214 self.Output( |
| 215 'const base::Value* inner_message_value;\n' |
| 216 'if (!dict->Get("{field_number}", &inner_message_value)) {{\n' |
| 217 ' goto error;\n' |
| 218 '}}\n' |
| 219 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message
->mutable_{field_name}())) {{\n' |
| 220 ' goto error;\n' |
| 221 '}}\n' |
| 222 '', |
| 223 field_number=field.JavascriptIndex(), |
| 224 field_name=field.name, |
| 225 inner_class_parser=field.CppConverterType() |
| 226 ) |
| 227 else: |
| 228 self.Output( |
| 229 '{cpp_type} field_value;\n' |
| 230 'if (!dict->Get{value_type}("{field_number}", &field_value)) {{\n' |
| 231 ' goto error;\n' |
| 232 '}}\n' |
| 233 'message->set_{field_name}(field_value);\n' |
| 234 '', |
| 235 field_number=field.JavascriptIndex(), |
| 236 field_name=field.name, |
| 237 cpp_type=field.CppPrimitiveType(), |
| 238 value_type=field.CppValueType() |
| 239 ) |
| 240 |
| 241 |
| 242 def main(): |
| 243 request = plugin.ReadRequestFromStdin() |
| 244 response = plugin_protos.PluginResponse() |
| 245 |
| 246 output_dir = request.GetArgs().get('output_dir', '') |
| 247 |
| 248 for proto_file in request.GetAllFiles(): |
| 249 plugin.RegisterProtoFile(proto_file) |
| 250 |
| 251 writer = CppConverterWriter() |
| 252 writer.WriteProtoFile(proto_file, output_dir) |
| 253 |
| 254 converter_filename = proto_file.CppConverterFilename() |
| 255 if output_dir: |
| 256 converter_filename = os.path.join(output_dir, |
| 257 os.path.split(converter_filename)[1]) |
| 258 |
| 259 response.AddFileWithContent(converter_filename, writer.GetValue()) |
| 260 if writer.GetErrors(): |
| 261 response.AddError('\n'.join(writer.GetErrors())) |
| 262 |
| 263 plugin.SetBinaryStdio() |
| 264 response.WriteToStdout() |
| 265 |
| 266 |
| 267 if __name__ == '__main__': |
| 268 main() |
OLD | NEW |