Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(666)

Unified Diff: third_party/dom_distiller_js/protoc_plugins/json_values_converter.py

Issue 2034373002: Generate the proto JSON converter for DOM distiller (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
diff --git a/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py b/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
new file mode 100755
index 0000000000000000000000000000000000000000..533e21d78ef8a91e400d9c8e03539df95b4b78a0
--- /dev/null
+++ b/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""protoc plugin to create c++ reader/writer for json-encoded protobufs
nyquist 2016/06/14 00:48:11 Nit: C++, JSON, etc.
wychen 2016/08/07 09:16:27 Done.
+
+The reader/writer use Chrome's base::Values.
+"""
+
+import optparse
+import os
+
+from util import plugin, plugin_protos, writer
+from util.plugin import Indented
+
+class CppConverterWriter(writer.CodeWriter):
nyquist 2016/06/14 00:48:11 Could we have a sanity-check style test for this t
wychen 2016/08/07 09:16:27 Done.
+ def WriteProtoFile(self, proto_file, output_dir):
+ err = proto_file.CheckSupported()
+ if err:
+ self.AddError(err)
+ return
+
+ self.WriteCStyleHeader()
+
+ self.Output('#include "{output_dir}{generated_pb_h}"',
+ output_dir=output_dir + '/' if output_dir else '',
+ generated_pb_h=proto_file.CppBaseHeader())
+ self.Output('')
+
+ self.Output('// proto dependencies')
+
+ for dep in proto_file.GetDependencies():
+ self.WriteIncludeForProtoFile(dep)
+
+ self.Output('')
+ self.Output('// base dependencies')
+ self.Output('#include "base/values.h"')
+ self.Output('')
+ self.Output('#include <memory>')
+ self.Output('#include <string>')
+ self.Output('#include <utility>')
+ self.Output('')
+
+ namespaces = proto_file.ProtoNamespaces() + ['json']
+ for name in namespaces:
+ self.Output('namespace {name} {{', name=name)
+ self.IncreaseIndent()
+
+ for message in proto_file.GetMessages():
+ self.WriteMessage(message)
+
+ # Nothing to do for enums
+
+ for name in namespaces:
+ self.DecreaseIndent()
+ self.Output('}}')
+
+ def WriteMessage(self, message):
+ self.Output('class {class_name} {{',
+ class_name=message.CppConverterClassName())
+ self.Output(' public:')
+ with self.AddIndent():
+ for nested_class in message.GetMessages():
+ self.WriteMessage(nested_class)
+
+ generated_class_name = message.QualifiedTypes().cpp_base
+ # Nothing to write for enums.
+
+ self.Output(
+ 'static bool ReadFromValue(const base::Value* json, {generated_class_name}* message) {{\n'
+ #' {generated_class_name} message;\n'
+ ' const base::DictionaryValue* dict;\n'
+ ' if (!json->GetAsDictionary(&dict)) goto error;\n'
+ '',
+ generated_class_name=generated_class_name)
+
+ with self.AddIndent():
+ for field_proto in message.GetFields():
+ self.WriteFieldRead(field_proto)
+
+ self.Output(
+ ' return true;\n'
+ '\n'
+ 'error:\n'
+ ' return false;\n'
+ '}}\n'
+ '\n'
+ 'static std::unique_ptr<base::DictionaryValue> WriteToValue(const {generated_class_name}& message) {{\n'
+ ' std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());\n'
+ '',
+ generated_class_name=generated_class_name)
+
+ with self.AddIndent():
+ for field_proto in message.GetFields():
+ self.FieldWriteToValue(field_proto)
+
+ self.Output(
+ ' return dict;\n'
+ '',
+ generated_class_name=generated_class_name)
+ self.Output('}}')
+
+ self.Output('}};')
+ self.Output('')
+
+ def FieldWriteToValue(self, field):
+ if field.IsRepeated():
+ self.Output('{{')
+ else:
+ self.Output('if (message.has_{field_name}()) {{\n', field_name=field.name)
+
+ with self.AddIndent():
+ if field.IsRepeated():
+ self.RepeatedMemberFieldWriteToValue(field)
+ else:
+ self.OptionalMemberFieldWriteToValue(field)
+
+ self.Output('}}')
+
+ def RepeatedMemberFieldWriteToValue(self, field):
+ prologue = (
+ 'base::ListValue* field_list = new base::ListValue();\n'
+ 'dict->Set("{field_number}", field_list);\n'
+ 'for (int i = 0; i < message.{field_name}_size(); ++i) {{\n'
+ )
+
+ if field.IsClassType():
+ middle = (
+ 'std::unique_ptr<base::Value> inner_message_value = \n'
+ ' {inner_class_converter}::WriteToValue(message.{field_name}(i));\n'
+ 'field_list->Append(std::move(inner_message_value));\n'
+ )
+ else:
+ middle = (
+ 'field_list->Append{value_type}(message.{field_name}(i));\n'
+ )
+ self.Output(
+ prologue + Indented(middle) + '\n}}',
+ field_number=field.JavascriptIndex(),
+ field_name=field.name,
+ value_type=field.CppValueType() if not field.IsClassType() else None,
+ inner_class_converter=field.CppConverterType()
+ )
+
+ def OptionalMemberFieldWriteToValue(self, field):
+ if field.IsClassType():
+ body = (
+ 'std::unique_ptr<base::Value> inner_message_value = \n'
+ ' {inner_class_converter}::WriteToValue(message.{field_name}());\n'
+ 'dict->Set("{field_number}", std::move(inner_message_value));\n'
+ )
+ else:
+ body = (
+ 'dict->Set{value_type}("{field_number}", message.{field_name}());\n'
+ )
+
+ self.Output(
+ body,
+ field_number=field.JavascriptIndex(),
+ field_name=field.name,
+ value_type=field.CppValueType() if not field.IsClassType() else None,
+ inner_class_converter=field.CppConverterType(),
+ )
+
+ def WriteFieldRead(self, field):
+ self.Output('if (dict->HasKey("{field_number}")) {{',
+ field_number=field.JavascriptIndex())
+
+ with self.AddIndent():
+ if field.IsRepeated():
+ self.RepeatedMemberFieldRead(field)
+ else:
+ self.OptionalMemberFieldRead(field)
+
+ self.Output('}}')
+
+ def RepeatedMemberFieldRead(self, field):
+ prologue = (
+ 'const base::ListValue* field_list;\n'
+ 'if (!dict->GetList("{field_number}", &field_list)) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ 'for (size_t i = 0; i < field_list->GetSize(); ++i) {{\n'
+ )
+
+ if field.IsClassType():
+ middle = (
+ 'const base::Value* inner_message_value;\n'
+ 'if (!field_list->Get(i, &inner_message_value)) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message->add_{field_name}())) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ )
+ else:
+ middle = (
+ '{cpp_type} field_value;\n'
+ 'if (!field_list->Get{value_type}(i, &field_value)) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ 'message->add_{field_name}(field_value);\n'
+ )
+
+ self.Output(
+ prologue + Indented(middle) + '\n}}',
+ field_number=field.JavascriptIndex(),
+ field_name=field.name,
+ cpp_type=field.CppPrimitiveType() if not field.IsClassType() else None,
+ value_type=field.CppValueType() if not field.IsClassType() else None,
+ inner_class_parser=field.CppConverterType()
+ )
+
+ def OptionalMemberFieldRead(self, field):
+ if field.IsClassType():
+ self.Output(
+ 'const base::Value* inner_message_value;\n'
+ 'if (!dict->Get("{field_number}", &inner_message_value)) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ 'if (!{inner_class_parser}::ReadFromValue(inner_message_value, message->mutable_{field_name}())) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ '',
+ field_number=field.JavascriptIndex(),
+ field_name=field.name,
+ inner_class_parser=field.CppConverterType()
+ )
+ else:
+ self.Output(
+ '{cpp_type} field_value;\n'
+ 'if (!dict->Get{value_type}("{field_number}", &field_value)) {{\n'
+ ' goto error;\n'
+ '}}\n'
+ 'message->set_{field_name}(field_value);\n'
+ '',
+ field_number=field.JavascriptIndex(),
+ field_name=field.name,
+ cpp_type=field.CppPrimitiveType(),
+ value_type=field.CppValueType()
+ )
+
+
+def main():
+ request = plugin.ReadRequestFromStdin()
+ response = plugin_protos.PluginResponse()
+
+ output_dir = request.GetArgs().get('output_dir', '')
+
+ # TODO(cjhopman): This should only be generating files for files listed in
nyquist 2016/06/14 00:48:11 I don't think cjhopman is planning on doing this.
wychen 2016/08/07 09:16:27 Done.
+ # request.file_to_generate. Since we don't actually support dependencies,
+ # only files in file_to_generate should be here, anyway.
+ for proto_file in request.GetAllFiles():
+ plugin.RegisterProtoFile(proto_file)
+
+ writer = CppConverterWriter()
+ writer.WriteProtoFile(proto_file, output_dir)
+
+ converter_filename = proto_file.CppConverterFilename()
+ if output_dir:
+ converter_filename = os.path.join(output_dir,
+ os.path.split(converter_filename)[1])
+
+ response.AddFileWithContent(converter_filename, writer.GetValue())
+ if writer.GetErrors():
+ response.AddError('\n'.join(writer.GetErrors()))
+
+ response.WriteToStdout()
+
+
+if __name__ == '__main__':
+ main()

Powered by Google App Engine
This is Rietveld 408576698