blob: 13f4dfb6b71bf3426783dfbc9485d1b059b1185b [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of protobuf;
typedef CreateBuilderFunc = GeneratedMessage Function();
typedef MakeDefaultFunc = Function();
typedef ValueOfFunc = ProtobufEnum Function(int value);
/// The base class for all protobuf message types.
///
/// The protoc plugin generates subclasses providing type-specific
/// properties and methods.
///
/// Public properties and methods added here should also be added to
/// GeneratedMessage_reservedNames and should be unlikely to be used in
/// a proto file.
abstract class GeneratedMessage {
_FieldSet _fieldSet;
GeneratedMessage() {
_fieldSet = _FieldSet(this, info_, eventPlugin);
if (eventPlugin != null) eventPlugin.attach(this);
}
GeneratedMessage.fromBuffer(
List<int> input, ExtensionRegistry extensionRegistry) {
_fieldSet = _FieldSet(this, info_, eventPlugin);
if (eventPlugin != null) eventPlugin.attach(this);
mergeFromBuffer(input, extensionRegistry);
}
GeneratedMessage.fromJson(String input, ExtensionRegistry extensionRegistry) {
_fieldSet = _FieldSet(this, info_, eventPlugin);
if (eventPlugin != null) eventPlugin.attach(this);
mergeFromJson(input, extensionRegistry);
}
// Overridden by subclasses.
BuilderInfo get info_;
/// Subclasses can override this getter to be notified of changes
/// to protobuf fields.
EventPlugin get eventPlugin => null;
/// Creates a deep copy of the fields in this message.
/// (The generated code uses [mergeFromMessage].)
GeneratedMessage clone();
/// Creates an empty instance of the same message type as this.
GeneratedMessage createEmptyInstance();
UnknownFieldSet get unknownFields => _fieldSet._ensureUnknownFields();
/// Make this message read-only.
///
/// Marks this message, and any sub-messages, as read-only.
GeneratedMessage freeze() {
_fieldSet._markReadOnly();
return this;
}
/// Returns `true` if this message is marked read-only. Otherwise `false`.
///
/// Even when `false`, some sub-message could be read-only.
///
/// If `true` all sub-messages are frozen.
bool get isFrozen => _fieldSet._isReadOnly;
/// Returns a writable, shallow copy of this message.
///
/// Sub messages will be shared with [this] and will still be frozen if [this]
/// is frozen.
///
/// The lists representing repeated fields are copied. But their elements will
/// be shared with the corresponding list in [this].
///
/// Similarly for map fields, the maps will be copied, but share the elements.
// TODO(nichite, sigurdm): Consider returning an actual builder object that
// lazily creates builders.
GeneratedMessage toBuilder() {
final result = createEmptyInstance();
result._fieldSet._shallowCopyValues(_fieldSet);
return result;
}
/// Apply [updates] to a copy of this message.
///
/// Makes a writable copy of this message, applies the [updates] to it, and
/// marks the copy read-only before returning it.
GeneratedMessage copyWith(void Function(GeneratedMessage) updates) {
final builder = toBuilder();
updates(builder);
return builder.freeze();
}
bool hasRequiredFields() => info_.hasRequiredFields;
/// Returns [:true:] if all required fields in the message and all embedded
/// messages are set, false otherwise.
bool isInitialized() => _fieldSet._hasRequiredValues();
/// Clears all data that was set in this message.
///
/// After calling [clear], [getField] will still return default values for
/// unset fields.
void clear() => _fieldSet._clear();
// TODO(antonm): move to getters.
int getTagNumber(String fieldName) => info_.tagNumber(fieldName);
@override
bool operator ==(other) {
if (identical(this, other)) return true;
return other is GeneratedMessage
? _fieldSet._equals(other._fieldSet)
: false;
}
/// Calculates a hash code based on the contents of the protobuf.
///
/// The hash may change when any field changes (recursively).
/// Therefore, protobufs used as map keys shouldn't be changed.
@override
int get hashCode => _fieldSet._hashCode;
/// Returns a String representation of this message.
///
/// This representation is similar to, but not quite, the Protocol Buffer
/// TextFormat. Each field is printed on its own line. Sub-messages are
/// indented two spaces farther than their parent messages.
///
/// Note that this format is absolutely subject to change, and should only
/// ever be used for debugging.
@override
String toString() => toDebugString();
/// Returns a String representation of this message.
///
/// This generates the same output as [toString], but can be used by mixins
/// to compose debug strings with additional information.
String toDebugString() {
var out = StringBuffer();
_fieldSet.writeString(out, '');
return out.toString();
}
void check() {
if (!isInitialized()) {
var invalidFields = <String>[];
_fieldSet._appendInvalidFields(invalidFields, '');
var missingFields = (invalidFields..sort()).join(', ');
throw StateError('Message missing required fields: $missingFields');
}
}
Uint8List writeToBuffer() {
var out = CodedBufferWriter();
writeToCodedBufferWriter(out);
return out.toBuffer();
}
void writeToCodedBufferWriter(CodedBufferWriter output) =>
_writeToCodedBufferWriter(_fieldSet, output);
void mergeFromCodedBufferReader(CodedBufferReader input,
[ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]) =>
_mergeFromCodedBufferReader(_fieldSet, input, extensionRegistry);
/// Merges serialized protocol buffer data into this message.
///
/// For each field in [input] that is already present in this message:
///
/// * If it's a repeated field, this appends to the end of our list.
/// * Else, if it's a scalar, this overwrites our field.
/// * Else, (it's a non-repeated sub-message), this recursively merges into
/// the existing sub-message.
void mergeFromBuffer(List<int> input,
[ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]) {
var codedInput = CodedBufferReader(input);
_mergeFromCodedBufferReader(_fieldSet, codedInput, extensionRegistry);
codedInput.checkLastTagWas(0);
}
// JSON support.
/// Returns the JSON encoding of this message as a Dart [Map].
///
/// The encoding is described in [GeneratedMessage.writeToJson].
Map<String, dynamic> writeToJsonMap() => _writeToJsonMap(_fieldSet);
/// Returns a JSON string that encodes this message.
///
/// Each message (top level or nested) is represented as an object delimited
/// by curly braces. Within a message, elements are indexed by tag number
/// (surrounded by quotes). Repeated elements are represented as arrays.
///
/// Boolean values, strings, and floating-point values are represented as
/// literals. Values with a 32-bit integer datatype are represented as integer
/// literals; values with a 64-bit integer datatype (regardless of their
/// actual runtime value) are represented as strings. Enumerated values are
/// represented as their integer value.
///
/// For the proto3 JSON format use: [toProto3JSON].
String writeToJson() => jsonEncode(writeToJsonMap());
/// Returns an Object representing Proto3 JSON serialization of [this].
///
/// The key for each field is be the camel-cased name of the field.
///
/// Well-known types and their special JSON encoding are supported.
/// If a well-known type cannot be encoded (eg. a `google.protobuf.Timestamp`
/// with negative `nanoseconds`) an error is thrown.
///
/// Extensions and unknown fields are not encoded.
///
/// The [typeRegistry] is be used for encoding `Any` messages. If an `Any`
/// message encoding a type not in [typeRegistry] is encountered, an
/// error is thrown.
Object toProto3Json(
{TypeRegistry typeRegistry = const TypeRegistry.empty()}) =>
_writeToProto3Json(_fieldSet, typeRegistry);
/// Merges field values from [json], a JSON object using proto3 encoding.
///
/// Well-known types and their special JSON encoding are supported.
///
/// If [ignoreUnknownFields] is `false` (the default) an
/// [FormatException] is be thrown if an unknown field name
/// is encountered. Otherwise the unknown field is ignored.
///
/// If [supportNamesWithUnderscores] is `true` (the default) field names in
/// the JSON can be represented as either camel-case JSON-names or names with
/// underscores.
/// If `false` only the JSON names are supported.
///
/// If [permissiveEnums] is `true` (default `false`) enum values in the
/// JSON will be matched without case insensitivity and ignoring `-`s and `_`.
/// This allows JSON values like `camelCase` and `kebab-case` to match the
/// enum values `CAMEL_CASE` and `KEBAB_CASE`.
/// In case of ambiguities between the enum values, the first matching value
/// will be found.
///
/// The [typeRegistry] is be used for decoding `Any` messages. If an `Any`
/// message encoding a type not in [typeRegistry] is encountered, a
/// [FormatException] is thrown.
///
/// If the JSON is otherwise not formatted correctly (a String where a
/// number was expected etc.) a [FormatException] is thrown.
void mergeFromProto3Json(Object json,
{TypeRegistry typeRegistry = const TypeRegistry.empty(),
bool ignoreUnknownFields = false,
bool supportNamesWithUnderscores = true,
bool permissiveEnums = false}) =>
_mergeFromProto3Json(json, _fieldSet, typeRegistry, ignoreUnknownFields,
supportNamesWithUnderscores, permissiveEnums);
/// Merges field values from [data], a JSON object, encoded as described by
/// [GeneratedMessage.writeToJson].
///
/// For the proto3 JSON format use: [mergeFromProto3JSON].
void mergeFromJson(String data,
[ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]) {
/// Disable lazy creation of Dart objects for a dart2js speedup.
/// This is a slight regression on the Dart VM.
/// TODO(skybrian) we could skip the reviver if we're running
/// on the Dart VM for a slight speedup.
final jsonMap =
jsonDecode(data, reviver: _emptyReviver) as Map<String, dynamic>;
_mergeFromJsonMap(_fieldSet, jsonMap, extensionRegistry);
}
static dynamic _emptyReviver(k, v) => v;
/// Merges field values from a JSON object represented as a Dart map.
///
/// The encoding is described in [GeneratedMessage.writeToJson].
void mergeFromJsonMap(Map<String, dynamic> json,
[ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]) {
_mergeFromJsonMap(_fieldSet, json, extensionRegistry);
}
/// Adds an extension field value to a repeated field.
///
/// The backing [List] will be created if necessary.
/// If the list already exists, the old extension won't be overwritten.
void addExtension(Extension extension, var value) {
if (!extension.isRepeated) {
throw ArgumentError(
'Cannot add to a non-repeated field (use setExtension())');
}
_fieldSet._ensureExtensions().._ensureRepeatedField(extension).add(value);
}
/// Clears an extension field and also removes the extension.
void clearExtension(Extension extension) {
if (_fieldSet._hasExtensions) {
_fieldSet._extensions._clearFieldAndInfo(extension);
}
}
/// Clears the contents of a given field.
///
/// If it's an extension field, the Extension will be kept.
/// The tagNumber should be a valid tag or extension.
void clearField(int tagNumber) => _fieldSet._clearField(tagNumber);
int $_whichOneof(int oneofIndex) => _fieldSet._oneofCases[oneofIndex] ?? 0;
bool extensionsAreInitialized() => _fieldSet._hasRequiredExtensionValues();
/// Returns the value of [extension].
///
/// If not set, returns the extension's default value.
dynamic getExtension(Extension extension) {
return _fieldSet._ensureExtensions()._getFieldOrDefault(extension);
}
/// Returns the value of the field associated with [tagNumber], or the
/// default value if it is not set.
dynamic getField(int tagNumber) => _fieldSet._getField(tagNumber);
/// Creates List implementing a mutable repeated field.
///
/// Mixins may override this method to change the List type. To ensure
/// that the protobuf can be encoded correctly, the returned List must
/// validate all items added to it. This can most easily be done
/// using the FieldInfo.check function.
List<T> createRepeatedField<T>(int tagNumber, FieldInfo<T> fi) {
return PbList<T>(check: fi.check);
}
/// Creates a Map representing a map field.
Map<K, V> createMapField<K, V>(int tagNumber, MapFieldInfo<K, V> fi) {
return PbMap<K, V>(
fi.keyFieldType, fi.valueFieldType, fi.mapEntryBuilderInfo);
}
/// Returns the value of a field, ignoring any defaults.
///
/// For unset or cleared fields, returns null.
/// Also returns null for unknown tag numbers.
dynamic getFieldOrNull(int tagNumber) =>
_fieldSet._getFieldOrNullByTag(tagNumber);
/// Returns the default value for the given field.
///
/// For repeated fields, returns an immutable empty list
/// (unlike [getField]). For all other fields, returns
/// the same thing that getField() would for a cleared field.
dynamic getDefaultForField(int tagNumber) =>
_fieldSet._ensureInfo(tagNumber).readonlyDefault;
/// Returns [:true:] if a value of [extension] is present.
bool hasExtension(Extension extension) =>
_fieldSet._hasExtensions &&
_fieldSet._extensions._getFieldOrNull(extension) != null;
/// Returns [:true:] if this message has a field associated with [tagNumber].
bool hasField(int tagNumber) => _fieldSet._hasField(tagNumber);
/// Merges the contents of the [other] into this message.
///
/// Singular fields that are set in [other] overwrite the corresponding fields
/// in this message. Repeated fields are appended. Singular sub-messages are
/// recursively merged.
void mergeFromMessage(GeneratedMessage other) =>
_fieldSet._mergeFromMessage(other._fieldSet);
void mergeUnknownFields(UnknownFieldSet unknownFieldSet) => _fieldSet
._ensureUnknownFields()
.mergeFromUnknownFieldSet(unknownFieldSet);
/// Sets the value of a non-repeated extension field to [value].
void setExtension(Extension extension, value) {
if (value == null) throw ArgumentError('value is null');
if (_isRepeated(extension.type)) {
throw ArgumentError(_fieldSet._setFieldFailedMessage(
extension, value, 'repeating field (use get + .add())'));
}
_fieldSet._ensureExtensions()._setFieldAndInfo(extension, value);
}
/// Sets the value of a field by its [tagNumber].
///
/// Throws an [:ArgumentError:] if [value] does not match the type
/// associated with [tagNumber].
///
/// Throws an [:ArgumentError:] if [value] is [:null:]. To clear a field of
/// it's current value, use [clearField] instead.
void setField(int tagNumber, value) {
_fieldSet._setField(tagNumber, value);
return; // ignore: dead_code
return; // ignore: dead_code
}
/// For generated code only.
T $_get<T>(int index, T defaultValue) =>
_fieldSet._$get<T>(index, defaultValue);
/// For generated code only.
T $_getN<T>(int index) {
// The implicit downcast at the return is always correct by construction
// from the protoc generator. dart2js will omit the implicit downcast when
// compiling with `-O3` or higher. We should introduce some way to
// communicate that the downcast cannot fail to the other compilers.
//
// TODO(sra): With NNDB we will need to add 'as T', and a dart2js annotation
// (to be implemented) to omit the 'as' check.
return _fieldSet._$getND(index);
}
/// For generated code only.
T $_ensure<T>(int index) {
return _fieldSet._$ensure<T>(index);
}
/// For generated code only.
List<T> $_getList<T>(int index) => _fieldSet._$getList<T>(index);
/// For generated code only.
Map<K, V> $_getMap<K, V>(int index) => _fieldSet._$getMap<K, V>(index);
/// For generated code only.
bool $_getB(int index, bool defaultValue) =>
_fieldSet._$getB(index, defaultValue);
/// For generated code only.
bool $_getBF(int index) => _fieldSet._$getBF(index);
/// For generated code only.
int $_getI(int index, int defaultValue) =>
_fieldSet._$getI(index, defaultValue);
/// For generated code only.
int $_getIZ(int index) => _fieldSet._$getIZ(index);
/// For generated code only.
String $_getS(int index, String defaultValue) =>
_fieldSet._$getS(index, defaultValue);
/// For generated code only.
String $_getSZ(int index) => _fieldSet._$getSZ(index);
/// For generated code only.
Int64 $_getI64(int index) => _fieldSet._$getI64(index);
/// For generated code only.
bool $_has(int index) => _fieldSet._$has(index);
/// For generated code only.
void $_setBool(int index, bool value) => _fieldSet._$set(index, value);
/// For generated code only.
void $_setBytes(int index, List<int> value) => _fieldSet._$set(index, value);
/// For generated code only.
void $_setString(int index, String value) => _fieldSet._$set(index, value);
/// For generated code only.
void $_setFloat(int index, double value) {
if (value == null || !_isFloat32(value)) {
_fieldSet._$check(index, value);
}
_fieldSet._$set(index, value);
}
/// For generated code only.
void $_setDouble(int index, double value) => _fieldSet._$set(index, value);
/// For generated code only.
void $_setSignedInt32(int index, int value) {
if (value == null || !_isSigned32(value)) {
_fieldSet._$check(index, value);
}
_fieldSet._$set(index, value);
}
/// For generated code only.
void $_setUnsignedInt32(int index, int value) {
if (value == null || !_isUnsigned32(value)) {
_fieldSet._$check(index, value);
}
_fieldSet._$set(index, value);
}
/// For generated code only.
void $_setInt64(int index, Int64 value) => _fieldSet._$set(index, value);
// Support for generating a read-only default singleton instance.
static final Map<Function, Function> _defaultMakers = {};
static T Function() _defaultMakerFor<T extends GeneratedMessage>(
T Function() createFn) {
return _defaultMakers[createFn] ??= _createDefaultMakerFor<T>(createFn);
}
static T Function() _createDefaultMakerFor<T extends GeneratedMessage>(
T Function() createFn) {
T defaultValue;
T defaultMaker() {
return defaultValue ??= createFn()..freeze();
}
return defaultMaker;
}
/// For generated code only.
static T $_defaultFor<T extends GeneratedMessage>(T Function() createFn) =>
_defaultMakerFor<T>(createFn)();
}
/// The package name of a protobuf message.
class PackageName {
final String name;
const PackageName(this.name);
String get prefix => name == '' ? '' : '$name.';
}