2 * Constructs a new Message Field.
\r
3 * @exports ProtoBuf.Reflect.Message.Field
\r
4 * @param {!ProtoBuf.Builder} builder Builder reference
\r
5 * @param {!ProtoBuf.Reflect.Message} message Message reference
\r
6 * @param {string} rule Rule, one of requried, optional, repeated
\r
7 * @param {string?} keytype Key data type, if any.
\r
8 * @param {string} type Data type, e.g. int32
\r
9 * @param {string} name Field name
\r
10 * @param {number} id Unique field id
\r
11 * @param {Object.<string,*>=} options Options
\r
12 * @param {!ProtoBuf.Reflect.Message.OneOf=} oneof Enclosing OneOf
\r
13 * @param {string?} syntax The syntax level of this definition (e.g., proto3)
\r
15 * @extends ProtoBuf.Reflect.T
\r
17 var Field = function(builder, message, rule, keytype, type, name, id, options, oneof, syntax) {
\r
18 T.call(this, builder, message, name);
\r
23 this.className = "Message.Field";
\r
26 * Message field required flag.
\r
30 this.required = rule === "required";
\r
33 * Message field repeated flag.
\r
37 this.repeated = rule === "repeated";
\r
40 * Message field map flag.
\r
44 this.map = rule === "map";
\r
47 * Message field key type. Type reference string if unresolved, protobuf
\r
48 * type if resolved. Valid only if this.map === true, null otherwise.
\r
49 * @type {string|{name: string, wireType: number}|null}
\r
52 this.keyType = keytype || null;
\r
55 * Message field type. Type reference string if unresolved, protobuf type if
\r
56 * resolved. In a map field, this is the value type.
\r
57 * @type {string|{name: string, wireType: number}}
\r
63 * Resolved type reference inside the global namespace.
\r
64 * @type {ProtoBuf.Reflect.T|null}
\r
67 this.resolvedType = null;
\r
70 * Unique message field id.
\r
77 * Message field options.
\r
78 * @type {!Object.<string,*>}
\r
82 this.options = options || {};
\r
89 this.defaultValue = null;
\r
93 * @type {?ProtoBuf.Reflect.Message.OneOf}
\r
96 this.oneof = oneof || null;
\r
99 * Syntax level of this definition (e.g., proto3).
\r
103 this.syntax = syntax || 'proto2';
\r
106 * Original field name.
\r
110 this.originalName = this.name; // Used to revert camelcase transformation on naming collisions
\r
113 * Element implementation. Created in build() after types are resolved.
\r
114 * @type {ProtoBuf.Element}
\r
117 this.element = null;
\r
120 * Key element implementation, for map fields. Created in build() after
\r
121 * types are resolved.
\r
122 * @type {ProtoBuf.Element}
\r
125 this.keyElement = null;
\r
127 // Convert field names to camel case notation if the override is set
\r
128 if (this.builder.options['convertFieldsToCamelCase'] && !(this instanceof Message.ExtensionField))
\r
129 this.name = ProtoBuf.Util.toCamelCase(this.name);
\r
133 * @alias ProtoBuf.Reflect.Message.Field.prototype
\r
136 var FieldPrototype = Field.prototype = Object.create(T.prototype);
\r
139 * Builds the field.
\r
143 FieldPrototype.build = function() {
\r
144 this.element = new Element(this.type, this.resolvedType, false, this.syntax, this.name);
\r
146 this.keyElement = new Element(this.keyType, undefined, true, this.syntax, this.name);
\r
148 // In proto3, fields do not have field presence, and every field is set to
\r
149 // its type's default value ("", 0, 0.0, or false).
\r
150 if (this.syntax === 'proto3' && !this.repeated && !this.map)
\r
151 this.defaultValue = Element.defaultFieldValue(this.type);
\r
153 // Otherwise, default values are present when explicitly specified
\r
154 else if (typeof this.options['default'] !== 'undefined')
\r
155 this.defaultValue = this.verifyValue(this.options['default']);
\r
159 * Checks if the given value can be set for this field.
\r
160 * @param {*} value Value to check
\r
161 * @param {boolean=} skipRepeated Whether to skip the repeated value check or not. Defaults to false.
\r
162 * @return {*} Verified, maybe adjusted, value
\r
163 * @throws {Error} If the value cannot be set for this field
\r
166 FieldPrototype.verifyValue = function(value, skipRepeated) {
\r
167 skipRepeated = skipRepeated || false;
\r
169 function fail(val, msg) {
\r
170 throw Error("Illegal value for "+self.toString(true)+" of type "+self.type.name+": "+val+" ("+msg+")");
\r
172 if (value === null) { // NULL values for optional fields
\r
174 fail(typeof value, "required");
\r
175 if (this.syntax === 'proto3' && this.type !== ProtoBuf.TYPES["message"])
\r
176 fail(typeof value, "proto3 field without field presence cannot be null");
\r
180 if (this.repeated && !skipRepeated) { // Repeated values as arrays
\r
181 if (!Array.isArray(value))
\r
184 for (i=0; i<value.length; i++)
\r
185 res.push(this.element.verifyValue(value[i]));
\r
188 if (this.map && !skipRepeated) { // Map values as objects
\r
189 if (!(value instanceof ProtoBuf.Map)) {
\r
190 // If not already a Map, attempt to convert.
\r
191 if (!(value instanceof Object)) {
\r
193 "expected ProtoBuf.Map or raw object for map field");
\r
195 return new ProtoBuf.Map(this, value);
\r
200 // All non-repeated fields expect no array
\r
201 if (!this.repeated && Array.isArray(value))
\r
202 fail(typeof value, "no array expected");
\r
204 return this.element.verifyValue(value);
\r
208 * Determines whether the field will have a presence on the wire given its
\r
210 * @param {*} value Verified field value
\r
211 * @param {!ProtoBuf.Builder.Message} message Runtime message
\r
212 * @return {boolean} Whether the field will be present on the wire
\r
214 FieldPrototype.hasWirePresence = function(value, message) {
\r
215 if (this.syntax !== 'proto3')
\r
216 return (value !== null);
\r
217 if (this.oneof && message[this.oneof.name] === this.name)
\r
219 switch (this.type) {
\r
220 case ProtoBuf.TYPES["int32"]:
\r
221 case ProtoBuf.TYPES["sint32"]:
\r
222 case ProtoBuf.TYPES["sfixed32"]:
\r
223 case ProtoBuf.TYPES["uint32"]:
\r
224 case ProtoBuf.TYPES["fixed32"]:
\r
225 return value !== 0;
\r
227 case ProtoBuf.TYPES["int64"]:
\r
228 case ProtoBuf.TYPES["sint64"]:
\r
229 case ProtoBuf.TYPES["sfixed64"]:
\r
230 case ProtoBuf.TYPES["uint64"]:
\r
231 case ProtoBuf.TYPES["fixed64"]:
\r
232 return value.low !== 0 || value.high !== 0;
\r
234 case ProtoBuf.TYPES["bool"]:
\r
237 case ProtoBuf.TYPES["float"]:
\r
238 case ProtoBuf.TYPES["double"]:
\r
239 return value !== 0.0;
\r
241 case ProtoBuf.TYPES["string"]:
\r
242 return value.length > 0;
\r
244 case ProtoBuf.TYPES["bytes"]:
\r
245 return value.remaining() > 0;
\r
247 case ProtoBuf.TYPES["enum"]:
\r
248 return value !== 0;
\r
250 case ProtoBuf.TYPES["message"]:
\r
251 return value !== null;
\r
258 * Encodes the specified field value to the specified buffer.
\r
259 * @param {*} value Verified field value
\r
260 * @param {ByteBuffer} buffer ByteBuffer to encode to
\r
261 * @param {!ProtoBuf.Builder.Message} message Runtime message
\r
262 * @return {ByteBuffer} The ByteBuffer for chaining
\r
263 * @throws {Error} If the field cannot be encoded
\r
266 FieldPrototype.encode = function(value, buffer, message) {
\r
267 if (this.type === null || typeof this.type !== 'object')
\r
268 throw Error("[INTERNAL] Unresolved type in "+this.toString(true)+": "+this.type);
\r
269 if (value === null || (this.repeated && value.length == 0))
\r
270 return buffer; // Optional omitted
\r
272 if (this.repeated) {
\r
274 // "Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire
\r
275 // types) can be declared 'packed'."
\r
276 if (this.options["packed"] && ProtoBuf.PACKABLE_WIRE_TYPES.indexOf(this.type.wireType) >= 0) {
\r
277 // "All of the elements of the field are packed into a single key-value pair with wire type 2
\r
278 // (length-delimited). Each element is encoded the same way it would be normally, except without a
\r
279 // tag preceding it."
\r
280 buffer.writeVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
\r
281 buffer.ensureCapacity(buffer.offset += 1); // We do not know the length yet, so let's assume a varint of length 1
\r
282 var start = buffer.offset; // Remember where the contents begin
\r
283 for (i=0; i<value.length; i++)
\r
284 this.element.encodeValue(this.id, value[i], buffer);
\r
285 var len = buffer.offset-start,
\r
286 varintLen = ByteBuffer.calculateVarint32(len);
\r
287 if (varintLen > 1) { // We need to move the contents
\r
288 var contents = buffer.slice(start, buffer.offset);
\r
289 start += varintLen-1;
\r
290 buffer.offset = start;
\r
291 buffer.append(contents);
\r
293 buffer.writeVarint32(len, start-varintLen);
\r
295 // "If your message definition has repeated elements (without the [packed=true] option), the encoded
\r
296 // message has zero or more key-value pairs with the same tag number"
\r
297 for (i=0; i<value.length; i++)
\r
298 buffer.writeVarint32((this.id << 3) | this.type.wireType),
\r
299 this.element.encodeValue(this.id, value[i], buffer);
\r
301 } else if (this.map) {
\r
302 // Write out each map entry as a submessage.
\r
303 value.forEach(function(val, key, m) {
\r
304 // Compute the length of the submessage (key, val) pair.
\r
306 ByteBuffer.calculateVarint32((1 << 3) | this.keyType.wireType) +
\r
307 this.keyElement.calculateLength(1, key) +
\r
308 ByteBuffer.calculateVarint32((2 << 3) | this.type.wireType) +
\r
309 this.element.calculateLength(2, val);
\r
311 // Submessage with wire type of length-delimited.
\r
312 buffer.writeVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
\r
313 buffer.writeVarint32(length);
\r
315 // Write out the key and val.
\r
316 buffer.writeVarint32((1 << 3) | this.keyType.wireType);
\r
317 this.keyElement.encodeValue(1, key, buffer);
\r
318 buffer.writeVarint32((2 << 3) | this.type.wireType);
\r
319 this.element.encodeValue(2, val, buffer);
\r
322 if (this.hasWirePresence(value, message)) {
\r
323 buffer.writeVarint32((this.id << 3) | this.type.wireType);
\r
324 this.element.encodeValue(this.id, value, buffer);
\r
328 throw Error("Illegal value for "+this.toString(true)+": "+value+" ("+e+")");
\r
334 * Calculates the length of this field's value on the network level.
\r
335 * @param {*} value Field value
\r
336 * @param {!ProtoBuf.Builder.Message} message Runtime message
\r
337 * @returns {number} Byte length
\r
340 FieldPrototype.calculate = function(value, message) {
\r
341 value = this.verifyValue(value); // May throw
\r
342 if (this.type === null || typeof this.type !== 'object')
\r
343 throw Error("[INTERNAL] Unresolved type in "+this.toString(true)+": "+this.type);
\r
344 if (value === null || (this.repeated && value.length == 0))
\r
345 return 0; // Optional omitted
\r
348 if (this.repeated) {
\r
350 if (this.options["packed"] && ProtoBuf.PACKABLE_WIRE_TYPES.indexOf(this.type.wireType) >= 0) {
\r
351 n += ByteBuffer.calculateVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
\r
353 for (i=0; i<value.length; i++)
\r
354 ni += this.element.calculateLength(this.id, value[i]);
\r
355 n += ByteBuffer.calculateVarint32(ni);
\r
358 for (i=0; i<value.length; i++)
\r
359 n += ByteBuffer.calculateVarint32((this.id << 3) | this.type.wireType),
\r
360 n += this.element.calculateLength(this.id, value[i]);
\r
362 } else if (this.map) {
\r
363 // Each map entry becomes a submessage.
\r
364 value.forEach(function(val, key, m) {
\r
365 // Compute the length of the submessage (key, val) pair.
\r
367 ByteBuffer.calculateVarint32((1 << 3) | this.keyType.wireType) +
\r
368 this.keyElement.calculateLength(1, key) +
\r
369 ByteBuffer.calculateVarint32((2 << 3) | this.type.wireType) +
\r
370 this.element.calculateLength(2, val);
\r
372 n += ByteBuffer.calculateVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
\r
373 n += ByteBuffer.calculateVarint32(length);
\r
377 if (this.hasWirePresence(value, message)) {
\r
378 n += ByteBuffer.calculateVarint32((this.id << 3) | this.type.wireType);
\r
379 n += this.element.calculateLength(this.id, value);
\r
383 throw Error("Illegal value for "+this.toString(true)+": "+value+" ("+e+")");
\r
389 * Decode the field value from the specified buffer.
\r
390 * @param {number} wireType Leading wire type
\r
391 * @param {ByteBuffer} buffer ByteBuffer to decode from
\r
392 * @param {boolean=} skipRepeated Whether to skip the repeated check or not. Defaults to false.
\r
393 * @return {*} Decoded value: array for packed repeated fields, [key, value] for
\r
394 * map fields, or an individual value otherwise.
\r
395 * @throws {Error} If the field cannot be decoded
\r
398 FieldPrototype.decode = function(wireType, buffer, skipRepeated) {
\r
401 // We expect wireType to match the underlying type's wireType unless we see
\r
402 // a packed repeated field, or unless this is a map field.
\r
404 (!this.map && wireType == this.type.wireType) ||
\r
405 (!skipRepeated && this.repeated && this.options["packed"] &&
\r
406 wireType == ProtoBuf.WIRE_TYPES.LDELIM) ||
\r
407 (this.map && wireType == ProtoBuf.WIRE_TYPES.LDELIM);
\r
409 throw Error("Illegal wire type for field "+this.toString(true)+": "+wireType+" ("+this.type.wireType+" expected)");
\r
411 // Handle packed repeated fields.
\r
412 if (wireType == ProtoBuf.WIRE_TYPES.LDELIM && this.repeated && this.options["packed"] && ProtoBuf.PACKABLE_WIRE_TYPES.indexOf(this.type.wireType) >= 0) {
\r
413 if (!skipRepeated) {
\r
414 nBytes = buffer.readVarint32();
\r
415 nBytes = buffer.offset + nBytes; // Limit
\r
417 while (buffer.offset < nBytes)
\r
418 values.push(this.decode(this.type.wireType, buffer, true));
\r
421 // Read the next value otherwise...
\r
426 // Read one (key, value) submessage, and return [key, value]
\r
427 var key = Element.defaultFieldValue(this.keyType);
\r
428 value = Element.defaultFieldValue(this.type);
\r
431 nBytes = buffer.readVarint32();
\r
432 if (buffer.remaining() < nBytes)
\r
433 throw Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining());
\r
435 // Get a sub-buffer of this key/value submessage
\r
436 var msgbuf = buffer.clone();
\r
437 msgbuf.limit = msgbuf.offset + nBytes;
\r
438 buffer.offset += nBytes;
\r
440 while (msgbuf.remaining() > 0) {
\r
441 var tag = msgbuf.readVarint32();
\r
442 wireType = tag & 0x07;
\r
443 var id = tag >>> 3;
\r
445 key = this.keyElement.decode(msgbuf, wireType, id);
\r
446 } else if (id === 2) {
\r
447 value = this.element.decode(msgbuf, wireType, id);
\r
449 throw Error("Unexpected tag in map field key/value submessage");
\r
453 return [key, value];
\r
456 // Handle singular and non-packed repeated field values.
\r
457 return this.element.decode(buffer, wireType, this.id);
\r