Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/proto2/textformatserializer.js
2868 views
1
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Protocol Buffer 2 Serializer which serializes messages
17
* into a user-friendly text format. Note that this code can run a bit
18
* slowly (especially for parsing) and should therefore not be used for
19
* time or space-critical applications.
20
*
21
* @see http://goo.gl/QDmDr
22
*/
23
24
goog.provide('goog.proto2.TextFormatSerializer');
25
26
goog.require('goog.array');
27
goog.require('goog.asserts');
28
goog.require('goog.json');
29
goog.require('goog.math');
30
goog.require('goog.object');
31
goog.require('goog.proto2.FieldDescriptor');
32
goog.require('goog.proto2.Message');
33
goog.require('goog.proto2.Serializer');
34
goog.require('goog.string');
35
36
37
38
/**
39
* TextFormatSerializer, a serializer which turns Messages into the human
40
* readable text format.
41
* @param {boolean=} opt_ignoreMissingFields If true, then fields that cannot be
42
* found on the proto when parsing the text format will be ignored.
43
* @param {boolean=} opt_useEnumValues If true, serialization code for enums
44
* will use enum integer values instead of human-readable symbolic names.
45
* @constructor
46
* @extends {goog.proto2.Serializer}
47
* @final
48
*/
49
goog.proto2.TextFormatSerializer = function(
50
opt_ignoreMissingFields, opt_useEnumValues) {
51
/**
52
* Whether to ignore fields not defined on the proto when parsing the text
53
* format.
54
* @type {boolean}
55
* @private
56
*/
57
this.ignoreMissingFields_ = !!opt_ignoreMissingFields;
58
59
/**
60
* Whether to use integer enum values during enum serialization.
61
* If false, symbolic names will be used.
62
* @type {boolean}
63
* @private
64
*/
65
this.useEnumValues_ = !!opt_useEnumValues;
66
};
67
goog.inherits(goog.proto2.TextFormatSerializer, goog.proto2.Serializer);
68
69
70
/**
71
* Deserializes a message from text format and places the data in the message.
72
* @param {goog.proto2.Message} message The message in which to
73
* place the information.
74
* @param {*} data The text format data.
75
* @return {?string} The parse error or null on success.
76
* @override
77
*/
78
goog.proto2.TextFormatSerializer.prototype.deserializeTo = function(
79
message, data) {
80
var textData = data.toString();
81
var parser = new goog.proto2.TextFormatSerializer.Parser();
82
if (!parser.parse(message, textData, this.ignoreMissingFields_)) {
83
return parser.getError();
84
}
85
86
return null;
87
};
88
89
90
/**
91
* Serializes a message to a string.
92
* @param {goog.proto2.Message} message The message to be serialized.
93
* @return {string} The serialized form of the message.
94
* @override
95
*/
96
goog.proto2.TextFormatSerializer.prototype.serialize = function(message) {
97
var printer = new goog.proto2.TextFormatSerializer.Printer_();
98
this.serializeMessage_(message, printer);
99
return printer.toString();
100
};
101
102
103
/**
104
* Serializes the message and prints the text form into the given printer.
105
* @param {goog.proto2.Message} message The message to serialize.
106
* @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
107
* which the text format will be printed.
108
* @private
109
*/
110
goog.proto2.TextFormatSerializer.prototype.serializeMessage_ = function(
111
message, printer) {
112
var descriptor = message.getDescriptor();
113
var fields = descriptor.getFields();
114
115
// Add the defined fields, recursively.
116
goog.array.forEach(fields, function(field) {
117
this.printField_(message, field, printer);
118
}, this);
119
120
// Add the unknown fields, if any.
121
message.forEachUnknown(function(tag, value) {
122
this.serializeUnknown_(tag, value, goog.asserts.assert(printer));
123
}, this);
124
};
125
126
127
/**
128
* Serializes an unknown field. When parsed from the JsPb object format, this
129
* manifests as either a primitive type, an array, or a raw object with integer
130
* keys. There is no descriptor available to interpret the types of nested
131
* messages.
132
* @param {number} tag The tag for the field. Since it's unknown, this is a
133
* number rather than a string.
134
* @param {*} value The value of the field.
135
* @param {!goog.proto2.TextFormatSerializer.Printer_} printer The printer to
136
* which the text format will be serialized.
137
* @private
138
*/
139
goog.proto2.TextFormatSerializer.prototype.serializeUnknown_ = function(
140
tag, value, printer) {
141
if (!goog.isDefAndNotNull(value)) {
142
return;
143
}
144
145
if (goog.isArray(value)) {
146
goog.array.forEach(value, function(val) {
147
this.serializeUnknown_(tag, val, printer);
148
}, this);
149
return;
150
}
151
152
if (goog.isObject(value)) {
153
printer.append(tag);
154
printer.append(' {');
155
printer.appendLine();
156
printer.indent();
157
if (value instanceof goog.proto2.Message) {
158
// Note(user): This conditional is here to make the
159
// testSerializationOfUnknown unit test pass, but in practice we should
160
// never have a Message for an "unknown" field.
161
this.serializeMessage_(value, printer);
162
} else {
163
// For an unknown message, fields are keyed by positive integers. We
164
// don't have a 'length' property to use for enumeration, so go through
165
// all properties and ignore the ones that aren't legal keys.
166
for (var key in value) {
167
var keyAsNumber = goog.string.parseInt(key);
168
goog.asserts.assert(goog.math.isInt(keyAsNumber));
169
this.serializeUnknown_(keyAsNumber, value[key], printer);
170
}
171
}
172
printer.dedent();
173
printer.append('}');
174
printer.appendLine();
175
return;
176
}
177
178
if (goog.isString(value)) {
179
value = goog.string.quote(value);
180
}
181
printer.append(tag);
182
printer.append(': ');
183
printer.append(value.toString());
184
printer.appendLine();
185
};
186
187
188
/**
189
* Prints the serialized value for the given field to the printer.
190
* @param {*} value The field's value.
191
* @param {goog.proto2.FieldDescriptor} field The field whose value is being
192
* printed.
193
* @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
194
* which the value will be printed.
195
* @private
196
*/
197
goog.proto2.TextFormatSerializer.prototype.printFieldValue_ = function(
198
value, field, printer) {
199
switch (field.getFieldType()) {
200
case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
201
case goog.proto2.FieldDescriptor.FieldType.FLOAT:
202
case goog.proto2.FieldDescriptor.FieldType.INT64:
203
case goog.proto2.FieldDescriptor.FieldType.UINT64:
204
case goog.proto2.FieldDescriptor.FieldType.INT32:
205
case goog.proto2.FieldDescriptor.FieldType.UINT32:
206
case goog.proto2.FieldDescriptor.FieldType.FIXED64:
207
case goog.proto2.FieldDescriptor.FieldType.FIXED32:
208
case goog.proto2.FieldDescriptor.FieldType.BOOL:
209
case goog.proto2.FieldDescriptor.FieldType.SFIXED32:
210
case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
211
case goog.proto2.FieldDescriptor.FieldType.SINT32:
212
case goog.proto2.FieldDescriptor.FieldType.SINT64:
213
printer.append(value);
214
break;
215
216
case goog.proto2.FieldDescriptor.FieldType.BYTES:
217
case goog.proto2.FieldDescriptor.FieldType.STRING:
218
value = goog.string.quote(value.toString());
219
printer.append(value);
220
break;
221
222
case goog.proto2.FieldDescriptor.FieldType.ENUM:
223
if (!this.useEnumValues_) {
224
// Search the enum type for a matching key.
225
var found = false;
226
goog.object.forEach(field.getNativeType(), function(eValue, key) {
227
if (!found && eValue == value) {
228
printer.append(key);
229
found = true;
230
}
231
});
232
}
233
234
if (!found || this.useEnumValues_) {
235
// Otherwise, just print the numeric value.
236
printer.append(value.toString());
237
}
238
break;
239
240
case goog.proto2.FieldDescriptor.FieldType.GROUP:
241
case goog.proto2.FieldDescriptor.FieldType.MESSAGE:
242
this.serializeMessage_(
243
/** @type {goog.proto2.Message} */ (value), printer);
244
break;
245
}
246
};
247
248
249
/**
250
* Prints the serialized field to the printer.
251
* @param {goog.proto2.Message} message The parent message.
252
* @param {goog.proto2.FieldDescriptor} field The field to print.
253
* @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
254
* which the field will be printed.
255
* @private
256
*/
257
goog.proto2.TextFormatSerializer.prototype.printField_ = function(
258
message, field, printer) {
259
// Skip fields not present.
260
if (!message.has(field)) {
261
return;
262
}
263
264
var count = message.countOf(field);
265
for (var i = 0; i < count; ++i) {
266
// Field name.
267
printer.append(field.getName());
268
269
// Field delimiter.
270
if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
271
field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
272
printer.append(' {');
273
printer.appendLine();
274
printer.indent();
275
} else {
276
printer.append(': ');
277
}
278
279
// Write the field value.
280
this.printFieldValue_(message.get(field, i), field, printer);
281
282
// Close the field.
283
if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
284
field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
285
printer.dedent();
286
printer.append('}');
287
printer.appendLine();
288
} else {
289
printer.appendLine();
290
}
291
}
292
};
293
294
295
////////////////////////////////////////////////////////////////////////////////
296
297
298
299
/**
300
* Helper class used by the text format serializer for pretty-printing text.
301
* @constructor
302
* @private
303
*/
304
goog.proto2.TextFormatSerializer.Printer_ = function() {
305
/**
306
* The current indentation count.
307
* @type {number}
308
* @private
309
*/
310
this.indentation_ = 0;
311
312
/**
313
* The buffer of string pieces.
314
* @type {Array<string>}
315
* @private
316
*/
317
this.buffer_ = [];
318
319
/**
320
* Whether indentation is required before the next append of characters.
321
* @type {boolean}
322
* @private
323
*/
324
this.requiresIndentation_ = true;
325
};
326
327
328
/**
329
* @return {string} The contents of the printer.
330
* @override
331
*/
332
goog.proto2.TextFormatSerializer.Printer_.prototype.toString = function() {
333
return this.buffer_.join('');
334
};
335
336
337
/**
338
* Increases the indentation in the printer.
339
*/
340
goog.proto2.TextFormatSerializer.Printer_.prototype.indent = function() {
341
this.indentation_ += 2;
342
};
343
344
345
/**
346
* Decreases the indentation in the printer.
347
*/
348
goog.proto2.TextFormatSerializer.Printer_.prototype.dedent = function() {
349
this.indentation_ -= 2;
350
goog.asserts.assert(this.indentation_ >= 0);
351
};
352
353
354
/**
355
* Appends the given value to the printer.
356
* @param {*} value The value to append.
357
*/
358
goog.proto2.TextFormatSerializer.Printer_.prototype.append = function(value) {
359
if (this.requiresIndentation_) {
360
for (var i = 0; i < this.indentation_; ++i) {
361
this.buffer_.push(' ');
362
}
363
this.requiresIndentation_ = false;
364
}
365
366
this.buffer_.push(value.toString());
367
};
368
369
370
/**
371
* Appends a newline to the printer.
372
*/
373
goog.proto2.TextFormatSerializer.Printer_.prototype.appendLine = function() {
374
this.buffer_.push('\n');
375
this.requiresIndentation_ = true;
376
};
377
378
379
////////////////////////////////////////////////////////////////////////////////
380
381
382
383
/**
384
* Helper class for tokenizing the text format.
385
* @param {string} data The string data to tokenize.
386
* @param {boolean=} opt_ignoreWhitespace If true, whitespace tokens will not
387
* be reported by the tokenizer.
388
* @param {boolean=} opt_ignoreComments If true, comment tokens will not be
389
* reported by the tokenizer.
390
* @constructor
391
* @private
392
*/
393
goog.proto2.TextFormatSerializer.Tokenizer_ = function(
394
data, opt_ignoreWhitespace, opt_ignoreComments) {
395
396
/**
397
* Whether to skip whitespace tokens on output.
398
* @private {boolean}
399
*/
400
this.ignoreWhitespace_ = !!opt_ignoreWhitespace;
401
402
/**
403
* Whether to skip comment tokens on output.
404
* @private {boolean}
405
*/
406
this.ignoreComments_ = !!opt_ignoreComments;
407
408
/**
409
* The data being tokenized.
410
* @private {string}
411
*/
412
this.data_ = data;
413
414
/**
415
* The current index in the data.
416
* @private {number}
417
*/
418
this.index_ = 0;
419
420
/**
421
* The data string starting at the current index.
422
* @private {string}
423
*/
424
this.currentData_ = data;
425
426
/**
427
* The current token type.
428
* @private {goog.proto2.TextFormatSerializer.Tokenizer_.Token}
429
*/
430
this.current_ = {
431
type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes.END,
432
value: null
433
};
434
};
435
436
437
/**
438
* @typedef {{type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes,
439
* value: ?string}}
440
*/
441
goog.proto2.TextFormatSerializer.Tokenizer_.Token;
442
443
444
/**
445
* @return {goog.proto2.TextFormatSerializer.Tokenizer_.Token} The current
446
* token.
447
*/
448
goog.proto2.TextFormatSerializer.Tokenizer_.prototype.getCurrent = function() {
449
return this.current_;
450
};
451
452
453
/**
454
* An enumeration of all the token types.
455
* @enum {!RegExp}
456
*/
457
goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes = {
458
END: /---end---/,
459
// Leading "-" to identify "-infinity"."
460
IDENTIFIER: /^-?[a-zA-Z][a-zA-Z0-9_]*/,
461
NUMBER: /^(0x[0-9a-f]+)|(([-])?[0-9][0-9]*(\.?[0-9]+)?(e[+-]?[0-9]+|[f])?)/,
462
COMMENT: /^#.*/,
463
OPEN_BRACE: /^{/,
464
CLOSE_BRACE: /^}/,
465
OPEN_TAG: /^</,
466
CLOSE_TAG: /^>/,
467
OPEN_LIST: /^\[/,
468
CLOSE_LIST: /^\]/,
469
STRING: new RegExp('^"([^"\\\\]|\\\\.)*"'),
470
COLON: /^:/,
471
COMMA: /^,/,
472
SEMI: /^;/,
473
WHITESPACE: /^\s/
474
};
475
476
477
/**
478
* Advances to the next token.
479
* @return {boolean} True if a valid token was found, false if the end was
480
* reached or no valid token was found.
481
*/
482
goog.proto2.TextFormatSerializer.Tokenizer_.prototype.next = function() {
483
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
484
485
// Skip any whitespace if requested.
486
while (this.nextInternal_()) {
487
var type = this.getCurrent().type;
488
if ((type != types.WHITESPACE && type != types.COMMENT) ||
489
(type == types.WHITESPACE && !this.ignoreWhitespace_) ||
490
(type == types.COMMENT && !this.ignoreComments_)) {
491
return true;
492
}
493
}
494
495
// If we reach this point, set the current token to END.
496
this.current_ = {
497
type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes.END,
498
value: null
499
};
500
501
return false;
502
};
503
504
505
/**
506
* Internal method for determining the next token.
507
* @return {boolean} True if a next token was found, false otherwise.
508
* @private
509
*/
510
goog.proto2.TextFormatSerializer.Tokenizer_.prototype.nextInternal_ =
511
function() {
512
if (this.index_ >= this.data_.length) {
513
return false;
514
}
515
516
var data = this.currentData_;
517
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
518
var next = null;
519
520
// Loop through each token type and try to match the beginning of the string
521
// with the token's regular expression.
522
goog.object.some(types, function(type, id) {
523
if (next || type == types.END) {
524
return false;
525
}
526
527
// Note: This regular expression check is at, minimum, O(n).
528
var info = type.exec(data);
529
if (info && info.index == 0) {
530
next = {type: type, value: info[0]};
531
}
532
533
return !!next;
534
});
535
536
// Advance the index by the length of the token.
537
if (next) {
538
this.current_ =
539
/** @type {goog.proto2.TextFormatSerializer.Tokenizer_.Token} */ (next);
540
this.index_ += next.value.length;
541
this.currentData_ = this.currentData_.substring(next.value.length);
542
}
543
544
return !!next;
545
};
546
547
548
////////////////////////////////////////////////////////////////////////////////
549
550
551
552
/**
553
* Helper class for parsing the text format.
554
* @constructor
555
* @final
556
*/
557
goog.proto2.TextFormatSerializer.Parser = function() {
558
/**
559
* The error during parsing, if any.
560
* @type {?string}
561
* @private
562
*/
563
this.error_ = null;
564
565
/**
566
* The current tokenizer.
567
* @type {goog.proto2.TextFormatSerializer.Tokenizer_}
568
* @private
569
*/
570
this.tokenizer_ = null;
571
572
/**
573
* Whether to ignore missing fields in the proto when parsing.
574
* @type {boolean}
575
* @private
576
*/
577
this.ignoreMissingFields_ = false;
578
};
579
580
581
/**
582
* Parses the given data, filling the message as it goes.
583
* @param {goog.proto2.Message} message The message to fill.
584
* @param {string} data The text format data.
585
* @param {boolean=} opt_ignoreMissingFields If true, fields missing in the
586
* proto will be ignored.
587
* @return {boolean} True on success, false on failure. On failure, the
588
* getError method can be called to get the reason for failure.
589
*/
590
goog.proto2.TextFormatSerializer.Parser.prototype.parse = function(
591
message, data, opt_ignoreMissingFields) {
592
this.error_ = null;
593
this.ignoreMissingFields_ = !!opt_ignoreMissingFields;
594
this.tokenizer_ =
595
new goog.proto2.TextFormatSerializer.Tokenizer_(data, true, true);
596
this.tokenizer_.next();
597
return this.consumeMessage_(message, '');
598
};
599
600
601
/**
602
* @return {?string} The parse error, if any.
603
*/
604
goog.proto2.TextFormatSerializer.Parser.prototype.getError = function() {
605
return this.error_;
606
};
607
608
609
/**
610
* Reports a parse error.
611
* @param {string} msg The error message.
612
* @private
613
*/
614
goog.proto2.TextFormatSerializer.Parser.prototype.reportError_ = function(msg) {
615
this.error_ = msg;
616
};
617
618
619
/**
620
* Attempts to consume the given message.
621
* @param {goog.proto2.Message} message The message to consume and fill. If
622
* null, then the message contents will be consumed without ever being set
623
* to anything.
624
* @param {string} delimiter The delimiter expected at the end of the message.
625
* @return {boolean} True on success, false otherwise.
626
* @private
627
*/
628
goog.proto2.TextFormatSerializer.Parser.prototype.consumeMessage_ = function(
629
message, delimiter) {
630
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
631
while (!this.lookingAt_('>') && !this.lookingAt_('}') &&
632
!this.lookingAtType_(types.END)) {
633
if (!this.consumeField_(message)) {
634
return false;
635
}
636
}
637
638
if (delimiter) {
639
if (!this.consume_(delimiter)) {
640
return false;
641
}
642
} else {
643
if (!this.lookingAtType_(types.END)) {
644
this.reportError_('Expected END token');
645
}
646
}
647
648
return true;
649
};
650
651
652
/**
653
* Attempts to consume the value of the given field.
654
* @param {goog.proto2.Message} message The parent message.
655
* @param {goog.proto2.FieldDescriptor} field The field.
656
* @return {boolean} True on success, false otherwise.
657
* @private
658
*/
659
goog.proto2.TextFormatSerializer.Parser.prototype.consumeFieldValue_ = function(
660
message, field) {
661
var value = this.getFieldValue_(field);
662
if (goog.isNull(value)) {
663
return false;
664
}
665
666
if (field.isRepeated()) {
667
message.add(field, value);
668
} else {
669
message.set(field, value);
670
}
671
672
return true;
673
};
674
675
676
/**
677
* Attempts to convert a string to a number.
678
* @param {string} num in hexadecimal or float format.
679
* @return {number} The converted number or null on error.
680
* @private
681
*/
682
goog.proto2.TextFormatSerializer.Parser.getNumberFromString_ = function(num) {
683
684
var returnValue = goog.string.contains(num, '.') ?
685
parseFloat(num) : // num is a float.
686
goog.string.parseInt(num); // num is an int.
687
688
goog.asserts.assert(!isNaN(returnValue));
689
goog.asserts.assert(isFinite(returnValue));
690
691
return returnValue;
692
};
693
694
695
/**
696
* Parse NaN, positive infinity, or negative infinity from a string.
697
* @param {string} identifier An identifier string to check.
698
* @return {?number} Infinity, negative infinity, NaN, or null if none
699
* of the constants could be parsed.
700
* @private
701
*/
702
goog.proto2.TextFormatSerializer.Parser.parseNumericalConstant_ = function(
703
identifier) {
704
if (/^-?inf(?:inity)?f?$/i.test(identifier)) {
705
return Infinity * (goog.string.startsWith(identifier, '-') ? -1 : 1);
706
}
707
708
if (/^nanf?$/i.test(identifier)) {
709
return NaN;
710
}
711
712
return null;
713
};
714
715
716
/**
717
* Attempts to parse the given field's value from the stream.
718
* @param {goog.proto2.FieldDescriptor} field The field.
719
* @return {*} The field's value or null if none.
720
* @private
721
*/
722
goog.proto2.TextFormatSerializer.Parser.prototype.getFieldValue_ = function(
723
field) {
724
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
725
switch (field.getFieldType()) {
726
case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
727
case goog.proto2.FieldDescriptor.FieldType.FLOAT:
728
729
var identifier = this.consumeIdentifier_();
730
if (identifier) {
731
var numericalIdentifier =
732
goog.proto2.TextFormatSerializer.Parser.parseNumericalConstant_(
733
identifier);
734
// Use isDefAndNotNull since !!NaN is false.
735
if (goog.isDefAndNotNull(numericalIdentifier)) {
736
return numericalIdentifier;
737
}
738
}
739
740
case goog.proto2.FieldDescriptor.FieldType.INT32:
741
case goog.proto2.FieldDescriptor.FieldType.UINT32:
742
case goog.proto2.FieldDescriptor.FieldType.FIXED32:
743
case goog.proto2.FieldDescriptor.FieldType.SFIXED32:
744
case goog.proto2.FieldDescriptor.FieldType.SINT32:
745
var num = this.consumeNumber_();
746
if (!num) {
747
return null;
748
}
749
750
return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(num);
751
752
case goog.proto2.FieldDescriptor.FieldType.INT64:
753
case goog.proto2.FieldDescriptor.FieldType.UINT64:
754
case goog.proto2.FieldDescriptor.FieldType.FIXED64:
755
case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
756
case goog.proto2.FieldDescriptor.FieldType.SINT64:
757
var num = this.consumeNumber_();
758
if (!num) {
759
return null;
760
}
761
762
if (field.getNativeType() == Number) {
763
// 64-bit number stored as a number.
764
return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(
765
num);
766
}
767
768
return num; // 64-bit numbers are by default stored as strings.
769
770
case goog.proto2.FieldDescriptor.FieldType.BOOL:
771
var ident = this.consumeIdentifier_();
772
if (!ident) {
773
return null;
774
}
775
776
switch (ident) {
777
case 'true':
778
return true;
779
case 'false':
780
return false;
781
default:
782
this.reportError_('Unknown type for bool: ' + ident);
783
return null;
784
}
785
786
case goog.proto2.FieldDescriptor.FieldType.ENUM:
787
if (this.lookingAtType_(types.NUMBER)) {
788
var num = this.consumeNumber_();
789
if (!num) {
790
return null;
791
}
792
793
return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(
794
num);
795
} else {
796
// Search the enum type for a matching key.
797
var name = this.consumeIdentifier_();
798
if (!name) {
799
return null;
800
}
801
802
var enumValue = field.getNativeType()[name];
803
if (enumValue == null) {
804
this.reportError_('Unknown enum value: ' + name);
805
return null;
806
}
807
808
return enumValue;
809
}
810
811
case goog.proto2.FieldDescriptor.FieldType.BYTES:
812
case goog.proto2.FieldDescriptor.FieldType.STRING:
813
return this.consumeString_();
814
}
815
};
816
817
818
/**
819
* Attempts to consume a nested message.
820
* @param {goog.proto2.Message} message The parent message.
821
* @param {goog.proto2.FieldDescriptor} field The field.
822
* @return {boolean} True on success, false otherwise.
823
* @private
824
*/
825
goog.proto2.TextFormatSerializer.Parser.prototype.consumeNestedMessage_ =
826
function(message, field) {
827
var delimiter = '';
828
829
// Messages support both < > and { } as delimiters for legacy reasons.
830
if (this.tryConsume_('<')) {
831
delimiter = '>';
832
} else {
833
if (!this.consume_('{')) {
834
return false;
835
}
836
delimiter = '}';
837
}
838
839
var msg = field.getFieldMessageType().createMessageInstance();
840
var result = this.consumeMessage_(msg, delimiter);
841
if (!result) {
842
return false;
843
}
844
845
// Add the message to the parent message.
846
if (field.isRepeated()) {
847
message.add(field, msg);
848
} else {
849
message.set(field, msg);
850
}
851
852
return true;
853
};
854
855
856
/**
857
* Attempts to consume the value of an unknown field. This method uses
858
* heuristics to try to consume just the right tokens.
859
* @return {boolean} True on success, false otherwise.
860
* @private
861
*/
862
goog.proto2.TextFormatSerializer.Parser.prototype.consumeUnknownFieldValue_ =
863
function() {
864
// : is optional.
865
this.tryConsume_(':');
866
867
// Handle form: [.. , ... , ..]
868
if (this.tryConsume_('[')) {
869
while (true) {
870
this.tokenizer_.next();
871
if (this.tryConsume_(']')) {
872
break;
873
}
874
if (!this.consume_(',')) {
875
return false;
876
}
877
}
878
879
return true;
880
}
881
882
// Handle nested messages/groups.
883
if (this.tryConsume_('<')) {
884
return this.consumeMessage_(null /* unknown */, '>');
885
} else if (this.tryConsume_('{')) {
886
return this.consumeMessage_(null /* unknown */, '}');
887
} else {
888
// Otherwise, consume a single token for the field value.
889
this.tokenizer_.next();
890
}
891
892
return true;
893
};
894
895
896
/**
897
* Attempts to consume a field under a message.
898
* @param {goog.proto2.Message} message The parent message. If null, then the
899
* field value will be consumed without being assigned to anything.
900
* @return {boolean} True on success, false otherwise.
901
* @private
902
*/
903
goog.proto2.TextFormatSerializer.Parser.prototype.consumeField_ = function(
904
message) {
905
var fieldName = this.consumeIdentifier_();
906
if (!fieldName) {
907
this.reportError_('Missing field name');
908
return false;
909
}
910
911
var field = null;
912
if (message) {
913
field = message.getDescriptor().findFieldByName(fieldName.toString());
914
}
915
916
if (field == null) {
917
if (this.ignoreMissingFields_) {
918
return this.consumeUnknownFieldValue_();
919
} else {
920
this.reportError_('Unknown field: ' + fieldName);
921
return false;
922
}
923
}
924
925
if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
926
field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
927
// : is optional here.
928
this.tryConsume_(':');
929
if (!this.consumeNestedMessage_(message, field)) {
930
return false;
931
}
932
} else {
933
// Long Format: "someField: 123"
934
// Short Format: "someField: [123, 456, 789]"
935
if (!this.consume_(':')) {
936
return false;
937
}
938
939
if (field.isRepeated() && this.tryConsume_('[')) {
940
// Short repeated format, e.g. "foo: [1, 2, 3]"
941
while (true) {
942
if (!this.consumeFieldValue_(message, field)) {
943
return false;
944
}
945
if (this.tryConsume_(']')) {
946
break;
947
}
948
if (!this.consume_(',')) {
949
return false;
950
}
951
}
952
} else {
953
// Normal field format.
954
if (!this.consumeFieldValue_(message, field)) {
955
return false;
956
}
957
}
958
}
959
960
// For historical reasons, fields may optionally be separated by commas or
961
// semicolons.
962
this.tryConsume_(',') || this.tryConsume_(';');
963
return true;
964
};
965
966
967
/**
968
* Attempts to consume a token with the given string value.
969
* @param {string} value The string value for the token.
970
* @return {boolean} True if the token matches and was consumed, false
971
* otherwise.
972
* @private
973
*/
974
goog.proto2.TextFormatSerializer.Parser.prototype.tryConsume_ = function(
975
value) {
976
if (this.lookingAt_(value)) {
977
this.tokenizer_.next();
978
return true;
979
}
980
return false;
981
};
982
983
984
/**
985
* Consumes a token of the given type.
986
* @param {goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes} type The type
987
* of the token to consume.
988
* @return {?string} The string value of the token or null on error.
989
* @private
990
*/
991
goog.proto2.TextFormatSerializer.Parser.prototype.consumeToken_ = function(
992
type) {
993
if (!this.lookingAtType_(type)) {
994
this.reportError_('Expected token type: ' + type);
995
return null;
996
}
997
998
var value = this.tokenizer_.getCurrent().value;
999
this.tokenizer_.next();
1000
return value;
1001
};
1002
1003
1004
/**
1005
* Consumes an IDENTIFIER token.
1006
* @return {?string} The string value or null on error.
1007
* @private
1008
*/
1009
goog.proto2.TextFormatSerializer.Parser.prototype.consumeIdentifier_ =
1010
function() {
1011
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
1012
return this.consumeToken_(types.IDENTIFIER);
1013
};
1014
1015
1016
/**
1017
* Consumes a NUMBER token.
1018
* @return {?string} The string value or null on error.
1019
* @private
1020
*/
1021
goog.proto2.TextFormatSerializer.Parser.prototype.consumeNumber_ = function() {
1022
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
1023
return this.consumeToken_(types.NUMBER);
1024
};
1025
1026
1027
/**
1028
* Consumes a STRING token. Strings may come in multiple adjacent tokens which
1029
* are automatically concatenated, like in C or Python.
1030
* @return {?string} The *deescaped* string value or null on error.
1031
* @private
1032
*/
1033
goog.proto2.TextFormatSerializer.Parser.prototype.consumeString_ = function() {
1034
var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
1035
var value = this.consumeToken_(types.STRING);
1036
if (!value) {
1037
return null;
1038
}
1039
1040
var stringValue = goog.json.parse(value).toString();
1041
while (this.lookingAtType_(types.STRING)) {
1042
value = this.consumeToken_(types.STRING);
1043
stringValue += goog.json.parse(value).toString();
1044
}
1045
1046
return stringValue;
1047
};
1048
1049
1050
/**
1051
* Consumes a token with the given value. If not found, reports an error.
1052
* @param {string} value The string value expected for the token.
1053
* @return {boolean} True on success, false otherwise.
1054
* @private
1055
*/
1056
goog.proto2.TextFormatSerializer.Parser.prototype.consume_ = function(value) {
1057
if (!this.tryConsume_(value)) {
1058
this.reportError_('Expected token "' + value + '"');
1059
return false;
1060
}
1061
1062
return true;
1063
};
1064
1065
1066
/**
1067
* @param {string} value The value to check against.
1068
* @return {boolean} True if the current token has the given string value.
1069
* @private
1070
*/
1071
goog.proto2.TextFormatSerializer.Parser.prototype.lookingAt_ = function(value) {
1072
return this.tokenizer_.getCurrent().value == value;
1073
};
1074
1075
1076
/**
1077
* @param {goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes} type The
1078
* token type.
1079
* @return {boolean} True if the current token has the given type.
1080
* @private
1081
*/
1082
goog.proto2.TextFormatSerializer.Parser.prototype.lookingAtType_ = function(
1083
type) {
1084
return this.tokenizer_.getCurrent().type == type;
1085
};
1086
1087