Path: blob/trunk/third_party/closure/goog/proto2/message.js
2868 views
// Copyright 2008 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Protocol Buffer Message base class.16* @suppress {unusedPrivateMembers} For descriptor_ declaration.17*/1819goog.provide('goog.proto2.Message');2021goog.require('goog.asserts');22goog.require('goog.proto2.Descriptor');23goog.require('goog.proto2.FieldDescriptor');2425goog.forwardDeclare('goog.proto2.LazyDeserializer'); // circular reference26272829/**30* Abstract base class for all Protocol Buffer 2 messages. It will be31* subclassed in the code generated by the Protocol Compiler. Any other32* subclasses are prohibited.33* @constructor34*/35goog.proto2.Message = function() {36/**37* Stores the field values in this message. Keyed by the tag of the fields.38* @type {!Object}39* @private40*/41this.values_ = {};4243/**44* Stores the field information (i.e. metadata) about this message.45* @type {Object<number, !goog.proto2.FieldDescriptor>}46* @private47*/48this.fields_ = this.getDescriptor().getFieldsMap();4950/**51* The lazy deserializer for this message instance, if any.52* @type {goog.proto2.LazyDeserializer}53* @private54*/55this.lazyDeserializer_ = null;5657/**58* A map of those fields deserialized, from tag number to their deserialized59* value.60* @type {Object}61* @private62*/63this.deserializedFields_ = null;64};656667/**68* An enumeration defining the possible field types.69* Should be a mirror of that defined in descriptor.h.70*71* TODO(user): Remove this alias. The code generator generates code that72* references this enum, so it needs to exist until the code generator is73* changed. The enum was moved to from Message to FieldDescriptor to avoid a74* dependency cycle.75*76* Use goog.proto2.FieldDescriptor.FieldType instead.77*78* @enum {number}79*/80goog.proto2.Message.FieldType = {81DOUBLE: 1,82FLOAT: 2,83INT64: 3,84UINT64: 4,85INT32: 5,86FIXED64: 6,87FIXED32: 7,88BOOL: 8,89STRING: 9,90GROUP: 10,91MESSAGE: 11,92BYTES: 12,93UINT32: 13,94ENUM: 14,95SFIXED32: 15,96SFIXED64: 16,97SINT32: 17,98SINT64: 1899};100101102/**103* All instances of goog.proto2.Message should have a static descriptor_104* property. The Descriptor will be deserialized lazily in the getDescriptor()105* method.106*107* This declaration is just here for documentation purposes.108* goog.proto2.Message does not have its own descriptor.109*110* @type {undefined}111* @private112*/113goog.proto2.Message.descriptor_;114115116/**117* Initializes the message with a lazy deserializer and its associated data.118* This method should be called by internal methods ONLY.119*120* @param {goog.proto2.LazyDeserializer} deserializer The lazy deserializer to121* use to decode the data on the fly.122*123* @param {?} data The data to decode/deserialize.124*/125goog.proto2.Message.prototype.initializeForLazyDeserializer = function(126deserializer, data) {127128this.lazyDeserializer_ = deserializer;129this.values_ = data;130this.deserializedFields_ = {};131};132133134/**135* Sets the value of an unknown field, by tag.136*137* @param {number} tag The tag of an unknown field (must be >= 1).138* @param {*} value The value for that unknown field.139*/140goog.proto2.Message.prototype.setUnknown = function(tag, value) {141goog.asserts.assert(142!this.fields_[tag], 'Field is not unknown in this message');143goog.asserts.assert(144tag >= 1, 'Tag ' + tag + ' has value "' + value + '" in descriptor ' +145this.getDescriptor().getName());146147goog.asserts.assert(value !== null, 'Value cannot be null');148149this.values_[tag] = value;150if (this.deserializedFields_) {151delete this.deserializedFields_[tag];152}153};154155156/**157* Iterates over all the unknown fields in the message.158*159* @param {function(this:T, number, *)} callback A callback method160* which gets invoked for each unknown field.161* @param {T=} opt_scope The scope under which to execute the callback.162* If not given, the current message will be used.163* @template T164*/165goog.proto2.Message.prototype.forEachUnknown = function(callback, opt_scope) {166var scope = opt_scope || this;167for (var key in this.values_) {168var keyNum = Number(key);169if (!this.fields_[keyNum]) {170callback.call(scope, keyNum, this.values_[key]);171}172}173};174175176/**177* Returns the descriptor which describes the current message.178*179* This only works if we assume people never subclass protobufs.180*181* @return {!goog.proto2.Descriptor} The descriptor.182*/183goog.proto2.Message.prototype.getDescriptor = goog.abstractMethod;184185186/**187* Returns whether there is a value stored at the field specified by the188* given field descriptor.189*190* @param {goog.proto2.FieldDescriptor} field The field for which to check191* if there is a value.192*193* @return {boolean} True if a value was found.194*/195goog.proto2.Message.prototype.has = function(field) {196goog.asserts.assert(197field.getContainingType() == this.getDescriptor(),198'The current message does not contain the given field');199200return this.has$Value(field.getTag());201};202203204/**205* Returns the array of values found for the given repeated field.206*207* @param {goog.proto2.FieldDescriptor} field The field for which to208* return the values.209*210* @return {!Array<?>} The values found.211*/212goog.proto2.Message.prototype.arrayOf = function(field) {213goog.asserts.assert(214field.getContainingType() == this.getDescriptor(),215'The current message does not contain the given field');216217return this.array$Values(field.getTag());218};219220221/**222* Returns the number of values stored in the given field.223*224* @param {goog.proto2.FieldDescriptor} field The field for which to count225* the number of values.226*227* @return {number} The count of the values in the given field.228*/229goog.proto2.Message.prototype.countOf = function(field) {230goog.asserts.assert(231field.getContainingType() == this.getDescriptor(),232'The current message does not contain the given field');233234return this.count$Values(field.getTag());235};236237238/**239* Returns the value stored at the field specified by the240* given field descriptor.241*242* @param {goog.proto2.FieldDescriptor} field The field for which to get the243* value.244* @param {number=} opt_index If the field is repeated, the index to use when245* looking up the value.246*247* @return {?} The value found or null if none.248*/249goog.proto2.Message.prototype.get = function(field, opt_index) {250goog.asserts.assert(251field.getContainingType() == this.getDescriptor(),252'The current message does not contain the given field');253254return this.get$Value(field.getTag(), opt_index);255};256257258/**259* Returns the value stored at the field specified by the260* given field descriptor or the default value if none exists.261*262* @param {goog.proto2.FieldDescriptor} field The field for which to get the263* value.264* @param {number=} opt_index If the field is repeated, the index to use when265* looking up the value.266*267* @return {?} The value found or the default if none.268*/269goog.proto2.Message.prototype.getOrDefault = function(field, opt_index) {270goog.asserts.assert(271field.getContainingType() == this.getDescriptor(),272'The current message does not contain the given field');273274return this.get$ValueOrDefault(field.getTag(), opt_index);275};276277278/**279* Stores the given value to the field specified by the280* given field descriptor. Note that the field must not be repeated.281*282* @param {goog.proto2.FieldDescriptor} field The field for which to set283* the value.284* @param {*} value The new value for the field.285*/286goog.proto2.Message.prototype.set = function(field, value) {287goog.asserts.assert(288field.getContainingType() == this.getDescriptor(),289'The current message does not contain the given field');290291this.set$Value(field.getTag(), value);292};293294295/**296* Adds the given value to the field specified by the297* given field descriptor. Note that the field must be repeated.298*299* @param {goog.proto2.FieldDescriptor} field The field in which to add the300* the value.301* @param {*} value The new value to add to the field.302*/303goog.proto2.Message.prototype.add = function(field, value) {304goog.asserts.assert(305field.getContainingType() == this.getDescriptor(),306'The current message does not contain the given field');307308this.add$Value(field.getTag(), value);309};310311312/**313* Clears the field specified.314*315* @param {goog.proto2.FieldDescriptor} field The field to clear.316*/317goog.proto2.Message.prototype.clear = function(field) {318goog.asserts.assert(319field.getContainingType() == this.getDescriptor(),320'The current message does not contain the given field');321322this.clear$Field(field.getTag());323};324325326/**327* Compares this message with another one ignoring the unknown fields.328* @param {?} other The other message.329* @return {boolean} Whether they are equal. Returns false if the {@code other}330* argument is a different type of message or not a message.331*/332goog.proto2.Message.prototype.equals = function(other) {333if (!other || this.constructor != other.constructor) {334return false;335}336337var fields = this.getDescriptor().getFields();338for (var i = 0; i < fields.length; i++) {339var field = fields[i];340var tag = field.getTag();341if (this.has$Value(tag) != other.has$Value(tag)) {342return false;343}344345if (this.has$Value(tag)) {346var isComposite = field.isCompositeType();347348var fieldsEqual = function(value1, value2) {349return isComposite ? value1.equals(value2) : value1 == value2;350};351352var thisValue = this.getValueForTag_(tag);353var otherValue = other.getValueForTag_(tag);354355if (field.isRepeated()) {356// In this case thisValue and otherValue are arrays.357if (thisValue.length != otherValue.length) {358return false;359}360for (var j = 0; j < thisValue.length; j++) {361if (!fieldsEqual(thisValue[j], otherValue[j])) {362return false;363}364}365} else if (!fieldsEqual(thisValue, otherValue)) {366return false;367}368}369}370371return true;372};373374375/**376* Recursively copies the known fields from the given message to this message.377* Removes the fields which are not present in the source message.378* @param {!goog.proto2.Message} message The source message.379*/380goog.proto2.Message.prototype.copyFrom = function(message) {381goog.asserts.assert(382this.constructor == message.constructor,383'The source message must have the same type.');384385if (this != message) {386this.values_ = {};387if (this.deserializedFields_) {388this.deserializedFields_ = {};389}390this.mergeFrom(message);391}392};393394395/**396* Merges the given message into this message.397*398* Singular fields will be overwritten, except for embedded messages which will399* be merged. Repeated fields will be concatenated.400* @param {!goog.proto2.Message} message The source message.401*/402goog.proto2.Message.prototype.mergeFrom = function(message) {403goog.asserts.assert(404this.constructor == message.constructor,405'The source message must have the same type.');406var fields = this.getDescriptor().getFields();407408for (var i = 0; i < fields.length; i++) {409var field = fields[i];410var tag = field.getTag();411if (message.has$Value(tag)) {412if (this.deserializedFields_) {413delete this.deserializedFields_[field.getTag()];414}415416var isComposite = field.isCompositeType();417if (field.isRepeated()) {418var values = message.array$Values(tag);419for (var j = 0; j < values.length; j++) {420this.add$Value(tag, isComposite ? values[j].clone() : values[j]);421}422} else {423var value = message.getValueForTag_(tag);424if (isComposite) {425var child = this.getValueForTag_(tag);426if (child) {427child.mergeFrom(value);428} else {429this.set$Value(tag, value.clone());430}431} else {432this.set$Value(tag, value);433}434}435}436}437};438439440/**441* @return {!goog.proto2.Message} Recursive clone of the message only including442* the known fields.443*/444goog.proto2.Message.prototype.clone = function() {445/** @type {!goog.proto2.Message} */446var clone = new this.constructor;447clone.copyFrom(this);448return clone;449};450451452/**453* Fills in the protocol buffer with default values. Any fields that are454* already set will not be overridden.455* @param {boolean} simpleFieldsToo If true, all fields will be initialized;456* if false, only the nested messages and groups.457*/458goog.proto2.Message.prototype.initDefaults = function(simpleFieldsToo) {459var fields = this.getDescriptor().getFields();460for (var i = 0; i < fields.length; i++) {461var field = fields[i];462var tag = field.getTag();463var isComposite = field.isCompositeType();464465// Initialize missing fields.466if (!this.has$Value(tag) && !field.isRepeated()) {467if (isComposite) {468this.values_[tag] = new /** @type {Function} */ (field.getNativeType());469} else if (simpleFieldsToo) {470this.values_[tag] = field.getDefaultValue();471}472}473474// Fill in the existing composite fields recursively.475if (isComposite) {476if (field.isRepeated()) {477var values = this.array$Values(tag);478for (var j = 0; j < values.length; j++) {479values[j].initDefaults(simpleFieldsToo);480}481} else {482this.get$Value(tag).initDefaults(simpleFieldsToo);483}484}485}486};487488489/**490* Returns the whether or not the field indicated by the given tag491* has a value.492*493* GENERATED CODE USE ONLY. Basis of the has{Field} methods.494*495* @param {number} tag The tag.496*497* @return {boolean} Whether the message has a value for the field.498*/499goog.proto2.Message.prototype.has$Value = function(tag) {500return this.values_[tag] != null;501};502503504/**505* Returns the value for the given tag number. If a lazy deserializer is506* instantiated, lazily deserializes the field if required before returning the507* value.508*509* @param {number} tag The tag number.510* @return {?} The corresponding value, if any.511* @private512*/513goog.proto2.Message.prototype.getValueForTag_ = function(tag) {514// Retrieve the current value, which may still be serialized.515var value = this.values_[tag];516if (!goog.isDefAndNotNull(value)) {517return null;518}519520// If we have a lazy deserializer, then ensure that the field is521// properly deserialized.522if (this.lazyDeserializer_) {523// If the tag is not deserialized, then we must do so now. Deserialize524// the field's value via the deserializer.525if (!(tag in /** @type {!Object} */ (this.deserializedFields_))) {526var deserializedValue = this.lazyDeserializer_.deserializeField(527this, this.fields_[tag], value);528this.deserializedFields_[tag] = deserializedValue;529return deserializedValue;530}531532return this.deserializedFields_[tag];533}534535// Otherwise, just return the value.536return value;537};538539540/**541* Gets the value at the field indicated by the given tag.542*543* GENERATED CODE USE ONLY. Basis of the get{Field} methods.544*545* @param {number} tag The field's tag index.546* @param {number=} opt_index If the field is a repeated field, the index547* at which to get the value.548*549* @return {?} The value found or null for none.550* @protected551*/552goog.proto2.Message.prototype.get$Value = function(tag, opt_index) {553var value = this.getValueForTag_(tag);554555if (this.fields_[tag].isRepeated()) {556var index = opt_index || 0;557goog.asserts.assert(558index >= 0 && index < value.length,559'Given index %s is out of bounds. Repeated field length: %s', index,560value.length);561return value[index];562}563564return value;565};566567568/**569* Gets the value at the field indicated by the given tag or the default value570* if none.571*572* GENERATED CODE USE ONLY. Basis of the get{Field} methods.573*574* @param {number} tag The field's tag index.575* @param {number=} opt_index If the field is a repeated field, the index576* at which to get the value.577*578* @return {?} The value found or the default value if none set.579* @protected580*/581goog.proto2.Message.prototype.get$ValueOrDefault = function(tag, opt_index) {582if (!this.has$Value(tag)) {583// Return the default value.584var field = this.fields_[tag];585return field.getDefaultValue();586}587588return this.get$Value(tag, opt_index);589};590591592/**593* Gets the values at the field indicated by the given tag.594*595* GENERATED CODE USE ONLY. Basis of the {field}Array methods.596*597* @param {number} tag The field's tag index.598*599* @return {!Array<?>} The values found. If none, returns an empty array.600* @protected601*/602goog.proto2.Message.prototype.array$Values = function(tag) {603var value = this.getValueForTag_(tag);604return value || [];605};606607608/**609* Returns the number of values stored in the field by the given tag.610*611* GENERATED CODE USE ONLY. Basis of the {field}Count methods.612*613* @param {number} tag The tag.614*615* @return {number} The number of values.616* @protected617*/618goog.proto2.Message.prototype.count$Values = function(tag) {619var field = this.fields_[tag];620if (field.isRepeated()) {621return this.has$Value(tag) ? this.values_[tag].length : 0;622} else {623return this.has$Value(tag) ? 1 : 0;624}625};626627628/**629* Sets the value of the *non-repeating* field indicated by the given tag.630*631* GENERATED CODE USE ONLY. Basis of the set{Field} methods.632*633* @param {number} tag The field's tag index.634* @param {*} value The field's value.635* @protected636*/637goog.proto2.Message.prototype.set$Value = function(tag, value) {638if (goog.asserts.ENABLE_ASSERTS) {639var field = this.fields_[tag];640this.checkFieldType_(field, value);641}642643this.values_[tag] = value;644if (this.deserializedFields_) {645this.deserializedFields_[tag] = value;646}647};648649650/**651* Adds the value to the *repeating* field indicated by the given tag.652*653* GENERATED CODE USE ONLY. Basis of the add{Field} methods.654*655* @param {number} tag The field's tag index.656* @param {*} value The value to add.657* @protected658*/659goog.proto2.Message.prototype.add$Value = function(tag, value) {660if (goog.asserts.ENABLE_ASSERTS) {661var field = this.fields_[tag];662this.checkFieldType_(field, value);663}664665if (!this.values_[tag]) {666this.values_[tag] = [];667}668669this.values_[tag].push(value);670if (this.deserializedFields_) {671delete this.deserializedFields_[tag];672}673};674675676/**677* Ensures that the value being assigned to the given field678* is valid.679*680* @param {!goog.proto2.FieldDescriptor} field The field being assigned.681* @param {*} value The value being assigned.682* @private683*/684goog.proto2.Message.prototype.checkFieldType_ = function(field, value) {685if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) {686goog.asserts.assertNumber(value);687} else {688goog.asserts.assert(Object(value).constructor == field.getNativeType());689}690};691692693/**694* Clears the field specified by tag.695*696* GENERATED CODE USE ONLY. Basis of the clear{Field} methods.697*698* @param {number} tag The tag of the field to clear.699* @protected700*/701goog.proto2.Message.prototype.clear$Field = function(tag) {702delete this.values_[tag];703if (this.deserializedFields_) {704delete this.deserializedFields_[tag];705}706};707708709/**710* Creates the metadata descriptor representing the definition of this message.711*712* @param {function(new:goog.proto2.Message)} messageType Constructor for the713* message type to which this metadata applies.714* @param {!Object} metadataObj The object containing the metadata.715* @return {!goog.proto2.Descriptor} The new descriptor.716*/717goog.proto2.Message.createDescriptor = function(messageType, metadataObj) {718var fields = [];719var descriptorInfo = metadataObj[0];720721for (var key in metadataObj) {722if (key != 0) {723// Create the field descriptor.724fields.push(725new goog.proto2.FieldDescriptor(messageType, key, metadataObj[key]));726}727}728729return new goog.proto2.Descriptor(messageType, descriptorInfo, fields);730};731732733