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 | |
nyquist
2016/06/14 00:48:11
Nit: C++, JSON, etc.
wychen
2016/08/07 09:16:27
Done.
| |
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): | |
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.
| |
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 | |
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.
| |
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 |