OLD | NEW |
1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
4 // | 4 // |
5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
7 // met: | 7 // met: |
8 // | 8 // |
9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 return fields_by_number_; | 98 return fields_by_number_; |
99 } | 99 } |
100 | 100 |
101 void MessageGenerator::Generate(io::Printer* printer) { | 101 void MessageGenerator::Generate(io::Printer* printer) { |
102 map<string, string> vars; | 102 map<string, string> vars; |
103 vars["class_name"] = class_name(); | 103 vars["class_name"] = class_name(); |
104 vars["access_level"] = class_access_level(); | 104 vars["access_level"] = class_access_level(); |
105 | 105 |
106 WriteMessageDocComment(printer, descriptor_); | 106 WriteMessageDocComment(printer, descriptor_); |
107 printer->Print( | 107 printer->Print( |
| 108 "[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n"); |
| 109 WriteGeneratedCodeAttributes(printer); |
| 110 printer->Print( |
108 vars, | 111 vars, |
109 "$access_level$ sealed partial class $class_name$ : pb::IMessage<$class_name
$> {\n"); | 112 "$access_level$ sealed partial class $class_name$ : pb::IMessage<$class_name
$> {\n"); |
110 printer->Indent(); | 113 printer->Indent(); |
111 | 114 |
112 // All static fields and properties | 115 // All static fields and properties |
113 printer->Print( | 116 printer->Print( |
114 » vars, | 117 vars, |
115 » "private static readonly pb::MessageParser<$class_name$> _parser = new
pb::MessageParser<$class_name$>(() => new $class_name$());\n"); | 118 "private static readonly pb::MessageParser<$class_name$> _parser = new pb:
:MessageParser<$class_name$>(() => new $class_name$());\n" |
116 | 119 "public static pb::MessageParser<$class_name$> Parser { get { return _pars
er; } }\n\n"); |
117 WriteGeneratedCodeAttributes(printer); | |
118 printer->Print( | |
119 » vars, | |
120 » "public static pb::MessageParser<$class_name$> Parser { get { return _
parser; } }\n\n"); | |
121 | 120 |
122 // Access the message descriptor via the relevant file descriptor or containin
g message descriptor. | 121 // Access the message descriptor via the relevant file descriptor or containin
g message descriptor. |
123 if (!descriptor_->containing_type()) { | 122 if (!descriptor_->containing_type()) { |
124 vars["descriptor_accessor"] = GetReflectionClassName(descriptor_->file()) | 123 vars["descriptor_accessor"] = GetReflectionClassName(descriptor_->file()) |
125 + ".Descriptor.MessageTypes[" + SimpleItoa(descriptor_->index()) + "]"; | 124 + ".Descriptor.MessageTypes[" + SimpleItoa(descriptor_->index()) + "]"; |
126 } else { | 125 } else { |
127 vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type()) | 126 vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type()) |
128 + ".Descriptor.NestedTypes[" + SimpleItoa(descriptor_->index()) + "]"; | 127 + ".Descriptor.NestedTypes[" + SimpleItoa(descriptor_->index()) + "]"; |
129 } | 128 } |
130 | 129 |
131 WriteGeneratedCodeAttributes(printer); | |
132 printer->Print( | 130 printer->Print( |
133 » vars, | 131 vars, |
134 » "public static pbr::MessageDescriptor Descriptor {\n" | 132 "public static pbr::MessageDescriptor Descriptor {\n" |
135 » " get { return $descriptor_accessor$; }\n" | 133 " get { return $descriptor_accessor$; }\n" |
136 » "}\n" | 134 "}\n" |
137 » "\n"); | 135 "\n" |
138 WriteGeneratedCodeAttributes(printer); | |
139 printer->Print( | |
140 » vars, | |
141 "pbr::MessageDescriptor pb::IMessage.Descriptor {\n" | 136 "pbr::MessageDescriptor pb::IMessage.Descriptor {\n" |
142 " get { return Descriptor; }\n" | 137 " get { return Descriptor; }\n" |
143 "}\n" | 138 "}\n" |
144 "\n"); | 139 "\n"); |
145 | 140 |
146 // Parameterless constructor and partial OnConstruction method. | 141 // Parameterless constructor and partial OnConstruction method. |
147 WriteGeneratedCodeAttributes(printer); | |
148 printer->Print( | 142 printer->Print( |
149 vars, | 143 vars, |
150 "public $class_name$() {\n" | 144 "public $class_name$() {\n" |
151 " OnConstruction();\n" | 145 " OnConstruction();\n" |
152 "}\n\n" | 146 "}\n\n" |
153 "partial void OnConstruction();\n\n"); | 147 "partial void OnConstruction();\n\n"); |
154 | 148 |
155 GenerateCloningCode(printer); | 149 GenerateCloningCode(printer); |
156 GenerateFreezingCode(printer); | 150 GenerateFreezingCode(printer); |
157 | 151 |
(...skipping 29 matching lines...) Expand all Loading... |
187 for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { | 181 for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { |
188 const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); | 182 const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); |
189 printer->Print("$field_property_name$ = $index$,\n", | 183 printer->Print("$field_property_name$ = $index$,\n", |
190 "field_property_name", GetPropertyName(field), | 184 "field_property_name", GetPropertyName(field), |
191 "index", SimpleItoa(field->number())); | 185 "index", SimpleItoa(field->number())); |
192 } | 186 } |
193 printer->Outdent(); | 187 printer->Outdent(); |
194 printer->Print("}\n"); | 188 printer->Print("}\n"); |
195 // TODO: Should we put the oneof .proto comments here? | 189 // TODO: Should we put the oneof .proto comments here? |
196 // It's unclear exactly where they should go. | 190 // It's unclear exactly where they should go. |
197 » printer->Print( | 191 printer->Print( |
198 » vars, | 192 vars, |
199 » "private $property_name$OneofCase $name$Case_ = $property_name$OneofCa
se.None;\n"); | 193 "private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.N
one;\n" |
200 » WriteGeneratedCodeAttributes(printer); | 194 "public $property_name$OneofCase $property_name$Case {\n" |
201 » printer->Print( | 195 " get { return $name$Case_; }\n" |
202 » vars, | 196 "}\n\n" |
203 » "public $property_name$OneofCase $property_name$Case {\n" | |
204 » " get { return $name$Case_; }\n" | |
205 » "}\n\n"); | |
206 » WriteGeneratedCodeAttributes(printer); | |
207 » printer->Print( | |
208 » vars, | |
209 "public void Clear$property_name$() {\n" | 197 "public void Clear$property_name$() {\n" |
210 " $name$Case_ = $property_name$OneofCase.None;\n" | 198 " $name$Case_ = $property_name$OneofCase.None;\n" |
211 " $name$_ = null;\n" | 199 " $name$_ = null;\n" |
212 "}\n\n"); | 200 "}\n\n"); |
213 } | 201 } |
214 | 202 |
215 // Standard methods | 203 // Standard methods |
216 GenerateFrameworkMethods(printer); | 204 GenerateFrameworkMethods(printer); |
217 GenerateMessageSerializationMethods(printer); | 205 GenerateMessageSerializationMethods(printer); |
218 GenerateMergingMethods(printer); | 206 GenerateMergingMethods(printer); |
219 | 207 |
220 // Nested messages and enums | 208 // Nested messages and enums |
221 if (HasNestedGeneratedTypes()) { | 209 if (HasNestedGeneratedTypes()) { |
222 printer->Print( | 210 printer->Print( |
223 vars, | 211 vars, |
224 "#region Nested types\n" | 212 "#region Nested types\n" |
225 "/// <summary>Container for nested types declared in the $class_name$ mess
age type.</summary>\n"); | 213 "/// <summary>Container for nested types declared in the $class_name$ mess
age type.</summary>\n" |
| 214 "[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n"); |
226 WriteGeneratedCodeAttributes(printer); | 215 WriteGeneratedCodeAttributes(printer); |
227 printer->Print("public static partial class Types {\n"); | 216 printer->Print("public static partial class Types {\n"); |
228 printer->Indent(); | 217 printer->Indent(); |
229 for (int i = 0; i < descriptor_->enum_type_count(); i++) { | 218 for (int i = 0; i < descriptor_->enum_type_count(); i++) { |
230 EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options()); | 219 EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options()); |
231 enumGenerator.Generate(printer); | 220 enumGenerator.Generate(printer); |
232 } | 221 } |
233 for (int i = 0; i < descriptor_->nested_type_count(); i++) { | 222 for (int i = 0; i < descriptor_->nested_type_count(); i++) { |
234 // Don't generate nested types for maps... | 223 // Don't generate nested types for maps... |
235 if (!IsMapEntryMessage(descriptor_->nested_type(i))) { | 224 if (!IsMapEntryMessage(descriptor_->nested_type(i))) { |
(...skipping 23 matching lines...) Expand all Loading... |
259 for (int i = 0; i < descriptor_->nested_type_count(); i++) { | 248 for (int i = 0; i < descriptor_->nested_type_count(); i++) { |
260 if (!IsMapEntryMessage(descriptor_->nested_type(i))) { | 249 if (!IsMapEntryMessage(descriptor_->nested_type(i))) { |
261 return true; | 250 return true; |
262 } | 251 } |
263 } | 252 } |
264 return false; | 253 return false; |
265 } | 254 } |
266 | 255 |
267 void MessageGenerator::GenerateCloningCode(io::Printer* printer) { | 256 void MessageGenerator::GenerateCloningCode(io::Printer* printer) { |
268 map<string, string> vars; | 257 map<string, string> vars; |
269 WriteGeneratedCodeAttributes(printer); | |
270 vars["class_name"] = class_name(); | 258 vars["class_name"] = class_name(); |
271 printer->Print( | 259 printer->Print( |
272 vars, | 260 vars, |
273 "public $class_name$($class_name$ other) : this() {\n"); | 261 "public $class_name$($class_name$ other) : this() {\n"); |
274 printer->Indent(); | 262 printer->Indent(); |
275 // Clone non-oneof fields first | 263 // Clone non-oneof fields first |
276 for (int i = 0; i < descriptor_->field_count(); i++) { | 264 for (int i = 0; i < descriptor_->field_count(); i++) { |
277 if (!descriptor_->field(i)->containing_oneof()) { | 265 if (!descriptor_->field(i)->containing_oneof()) { |
278 scoped_ptr<FieldGeneratorBase> generator( | 266 scoped_ptr<FieldGeneratorBase> generator( |
279 CreateFieldGeneratorInternal(descriptor_->field(i))); | 267 CreateFieldGeneratorInternal(descriptor_->field(i))); |
(...skipping 19 matching lines...) Expand all Loading... |
299 printer->Print("break;\n"); | 287 printer->Print("break;\n"); |
300 printer->Outdent(); | 288 printer->Outdent(); |
301 } | 289 } |
302 printer->Outdent(); | 290 printer->Outdent(); |
303 printer->Print("}\n\n"); | 291 printer->Print("}\n\n"); |
304 } | 292 } |
305 | 293 |
306 printer->Outdent(); | 294 printer->Outdent(); |
307 printer->Print("}\n\n"); | 295 printer->Print("}\n\n"); |
308 | 296 |
309 WriteGeneratedCodeAttributes(printer); | |
310 printer->Print( | 297 printer->Print( |
311 vars, | 298 vars, |
312 "public $class_name$ Clone() {\n" | 299 "public $class_name$ Clone() {\n" |
313 " return new $class_name$(this);\n" | 300 " return new $class_name$(this);\n" |
314 "}\n\n"); | 301 "}\n\n"); |
315 } | 302 } |
316 | 303 |
317 void MessageGenerator::GenerateFreezingCode(io::Printer* printer) { | 304 void MessageGenerator::GenerateFreezingCode(io::Printer* printer) { |
318 } | 305 } |
319 | 306 |
320 void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { | 307 void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { |
321 map<string, string> vars; | 308 map<string, string> vars; |
322 vars["class_name"] = class_name(); | 309 vars["class_name"] = class_name(); |
323 | 310 |
324 // Equality | 311 // Equality |
325 WriteGeneratedCodeAttributes(printer); | |
326 printer->Print( | 312 printer->Print( |
327 vars, | 313 vars, |
328 "public override bool Equals(object other) {\n" | 314 "public override bool Equals(object other) {\n" |
329 " return Equals(other as $class_name$);\n" | 315 " return Equals(other as $class_name$);\n" |
330 "}\n\n"); | 316 "}\n\n" |
331 » WriteGeneratedCodeAttributes(printer); | |
332 » printer->Print( | |
333 » vars, | |
334 "public bool Equals($class_name$ other) {\n" | 317 "public bool Equals($class_name$ other) {\n" |
335 " if (ReferenceEquals(other, null)) {\n" | 318 " if (ReferenceEquals(other, null)) {\n" |
336 " return false;\n" | 319 " return false;\n" |
337 " }\n" | 320 " }\n" |
338 " if (ReferenceEquals(other, this)) {\n" | 321 " if (ReferenceEquals(other, this)) {\n" |
339 " return true;\n" | 322 " return true;\n" |
340 " }\n"); | 323 " }\n"); |
341 printer->Indent(); | 324 printer->Indent(); |
342 for (int i = 0; i < descriptor_->field_count(); i++) { | 325 for (int i = 0; i < descriptor_->field_count(); i++) { |
343 scoped_ptr<FieldGeneratorBase> generator( | 326 scoped_ptr<FieldGeneratorBase> generator( |
344 CreateFieldGeneratorInternal(descriptor_->field(i))); | 327 CreateFieldGeneratorInternal(descriptor_->field(i))); |
345 generator->WriteEquals(printer); | 328 generator->WriteEquals(printer); |
346 } | 329 } |
347 for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { | 330 for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { |
348 printer->Print("if ($property_name$Case != other.$property_name$Case) re
turn false;\n", | 331 printer->Print("if ($property_name$Case != other.$property_name$Case) re
turn false;\n", |
349 "property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->
name(), true)); | 332 "property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->
name(), true)); |
350 } | 333 } |
351 printer->Outdent(); | 334 printer->Outdent(); |
352 printer->Print( | 335 printer->Print( |
353 " return true;\n" | 336 " return true;\n" |
354 "}\n\n"); | 337 "}\n\n"); |
355 | 338 |
356 // GetHashCode | 339 // GetHashCode |
357 // Start with a non-zero value to easily distinguish between null and "empty
" messages. | 340 // Start with a non-zero value to easily distinguish between null and "empty
" messages. |
358 » WriteGeneratedCodeAttributes(printer); | 341 printer->Print( |
359 » printer->Print( | |
360 "public override int GetHashCode() {\n" | 342 "public override int GetHashCode() {\n" |
361 " int hash = 1;\n"); | 343 " int hash = 1;\n"); |
362 printer->Indent(); | 344 printer->Indent(); |
363 for (int i = 0; i < descriptor_->field_count(); i++) { | 345 for (int i = 0; i < descriptor_->field_count(); i++) { |
364 scoped_ptr<FieldGeneratorBase> generator( | 346 scoped_ptr<FieldGeneratorBase> generator( |
365 CreateFieldGeneratorInternal(descriptor_->field(i))); | 347 CreateFieldGeneratorInternal(descriptor_->field(i))); |
366 generator->WriteHash(printer); | 348 generator->WriteHash(printer); |
367 } | 349 } |
368 for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { | 350 for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { |
369 printer->Print("hash ^= (int) $name$Case_;\n", | 351 printer->Print("hash ^= (int) $name$Case_;\n", |
370 "name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), f
alse)); | 352 "name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), f
alse)); |
371 } | 353 } |
372 printer->Print("return hash;\n"); | 354 printer->Print("return hash;\n"); |
373 printer->Outdent(); | 355 printer->Outdent(); |
374 printer->Print("}\n\n"); | 356 printer->Print("}\n\n"); |
375 | 357 |
376 » WriteGeneratedCodeAttributes(printer); | 358 printer->Print( |
377 » printer->Print( | |
378 "public override string ToString() {\n" | 359 "public override string ToString() {\n" |
379 " return pb::JsonFormatter.ToDiagnosticString(this);\n" | 360 " return pb::JsonFormatter.ToDiagnosticString(this);\n" |
380 "}\n\n"); | 361 "}\n\n"); |
381 } | 362 } |
382 | 363 |
383 void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
{ | 364 void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
{ |
384 WriteGeneratedCodeAttributes(printer); | |
385 printer->Print( | 365 printer->Print( |
386 "public void WriteTo(pb::CodedOutputStream output) {\n"); | 366 "public void WriteTo(pb::CodedOutputStream output) {\n"); |
387 printer->Indent(); | 367 printer->Indent(); |
388 | 368 |
389 // Serialize all the fields | 369 // Serialize all the fields |
390 for (int i = 0; i < fields_by_number().size(); i++) { | 370 for (int i = 0; i < fields_by_number().size(); i++) { |
391 scoped_ptr<FieldGeneratorBase> generator( | 371 scoped_ptr<FieldGeneratorBase> generator( |
392 CreateFieldGeneratorInternal(fields_by_number()[i])); | 372 CreateFieldGeneratorInternal(fields_by_number()[i])); |
393 generator->GenerateSerializationCode(printer); | 373 generator->GenerateSerializationCode(printer); |
394 } | 374 } |
395 | 375 |
396 // TODO(jonskeet): Memoize size of frozen messages? | 376 // TODO(jonskeet): Memoize size of frozen messages? |
397 printer->Outdent(); | 377 printer->Outdent(); |
398 printer->Print( | 378 printer->Print( |
399 » "}\n" | 379 "}\n" |
400 » "\n"); | 380 "\n" |
401 WriteGeneratedCodeAttributes(printer); | |
402 printer->Print( | |
403 "public int CalculateSize() {\n"); | 381 "public int CalculateSize() {\n"); |
404 printer->Indent(); | 382 printer->Indent(); |
405 printer->Print("int size = 0;\n"); | 383 printer->Print("int size = 0;\n"); |
406 for (int i = 0; i < descriptor_->field_count(); i++) { | 384 for (int i = 0; i < descriptor_->field_count(); i++) { |
407 scoped_ptr<FieldGeneratorBase> generator( | 385 scoped_ptr<FieldGeneratorBase> generator( |
408 CreateFieldGeneratorInternal(descriptor_->field(i))); | 386 CreateFieldGeneratorInternal(descriptor_->field(i))); |
409 generator->GenerateSerializedSizeCode(printer); | 387 generator->GenerateSerializedSizeCode(printer); |
410 } | 388 } |
411 printer->Print("return size;\n"); | 389 printer->Print("return size;\n"); |
412 printer->Outdent(); | 390 printer->Outdent(); |
413 printer->Print("}\n\n"); | 391 printer->Print("}\n\n"); |
414 } | 392 } |
415 | 393 |
416 void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { | 394 void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { |
417 // Note: These are separate from GenerateMessageSerializationMethods() | 395 // Note: These are separate from GenerateMessageSerializationMethods() |
418 // because they need to be generated even for messages that are optimized | 396 // because they need to be generated even for messages that are optimized |
419 // for code size. | 397 // for code size. |
420 map<string, string> vars; | 398 map<string, string> vars; |
421 vars["class_name"] = class_name(); | 399 vars["class_name"] = class_name(); |
422 | 400 |
423 WriteGeneratedCodeAttributes(printer); | |
424 printer->Print( | 401 printer->Print( |
425 vars, | 402 vars, |
426 "public void MergeFrom($class_name$ other) {\n"); | 403 "public void MergeFrom($class_name$ other) {\n"); |
427 printer->Indent(); | 404 printer->Indent(); |
428 printer->Print( | 405 printer->Print( |
429 "if (other == null) {\n" | 406 "if (other == null) {\n" |
430 " return;\n" | 407 " return;\n" |
431 "}\n"); | 408 "}\n"); |
432 // Merge non-oneof fields | 409 // Merge non-oneof fields |
433 for (int i = 0; i < descriptor_->field_count(); i++) { | 410 for (int i = 0; i < descriptor_->field_count(); i++) { |
(...skipping 16 matching lines...) Expand all Loading... |
450 vars, | 427 vars, |
451 "case $property_name$OneofCase.$field_property_name$:\n" | 428 "case $property_name$OneofCase.$field_property_name$:\n" |
452 " $field_property_name$ = other.$field_property_name$;\n" | 429 " $field_property_name$ = other.$field_property_name$;\n" |
453 " break;\n"); | 430 " break;\n"); |
454 } | 431 } |
455 printer->Outdent(); | 432 printer->Outdent(); |
456 printer->Print("}\n\n"); | 433 printer->Print("}\n\n"); |
457 } | 434 } |
458 printer->Outdent(); | 435 printer->Outdent(); |
459 printer->Print("}\n\n"); | 436 printer->Print("}\n\n"); |
460 WriteGeneratedCodeAttributes(printer); | |
461 printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n"); | 437 printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n"); |
462 printer->Indent(); | 438 printer->Indent(); |
463 printer->Print( | 439 printer->Print( |
464 "uint tag;\n" | 440 "uint tag;\n" |
465 "while ((tag = input.ReadTag()) != 0) {\n" | 441 "while ((tag = input.ReadTag()) != 0) {\n" |
466 " switch(tag) {\n"); | 442 " switch(tag) {\n"); |
467 printer->Indent(); | 443 printer->Indent(); |
468 printer->Indent(); | 444 printer->Indent(); |
469 printer->Print( | 445 printer->Print( |
470 "default:\n" | 446 "default:\n" |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 | 495 |
520 FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal( | 496 FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal( |
521 const FieldDescriptor* descriptor) { | 497 const FieldDescriptor* descriptor) { |
522 return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor), this->opt
ions()); | 498 return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor), this->opt
ions()); |
523 } | 499 } |
524 | 500 |
525 } // namespace csharp | 501 } // namespace csharp |
526 } // namespace compiler | 502 } // namespace compiler |
527 } // namespace protobuf | 503 } // namespace protobuf |
528 } // namespace google | 504 } // namespace google |
OLD | NEW |