blob: c06cce5f3d38eae38da72b64f9ae1261ba481d3a [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;
/// Per-message type setup.
class BuilderInfo {
/// Fully qualified name of this message.
final String qualifiedMessageName;
/// Mapping from [FieldInfo.index]s to [FieldInfo]s.
final List<FieldInfo> byIndex = <FieldInfo>[];
/// Mapping from [FieldInfo.tagNumber]s to [FieldInfo]s.
final Map<int, FieldInfo> fieldInfo = <int, FieldInfo>{};
/// Mapping from string representation of [FieldInfo.tagNumber]s to
/// [FieldInfo]s.
///
/// This map is used when parsing JSON messages generated by
/// [GeneratedMessage.writeToJson], which converts field tags to strings and
/// uses those strings as JSON map keys. With this field we avoid parsing
/// those strings to integers when parsing the generated maps.
final Map<String, FieldInfo> byTagAsString = <String, FieldInfo>{};
/// Mapping from [FieldInfo.name]s to [FieldInfo]s.
final Map<String, FieldInfo> byName = <String, FieldInfo>{};
/// Mapping from `oneof` field [FieldInfo.tagNumber]s to the their indices in
/// [_FieldSet._oneofCases].
final Map<int, int> oneofs = <int, int>{};
/// Whether the message has extension fields.
bool hasExtensions = false;
/// Whether the message has required fields.
///
/// Note that proto3 doesn't have required fields, only proto2 does.
bool hasRequiredFields = true;
List<FieldInfo>? _sortedByTag;
// For well-known types.
final Object? Function(GeneratedMessage message, TypeRegistry typeRegistry)?
toProto3Json;
final Function(GeneratedMessage targetMessage, Object json,
TypeRegistry typeRegistry, JsonParsingContext context)? fromProto3Json;
final CreateBuilderFunc? createEmptyInstance;
BuilderInfo(String? messageName,
{PackageName package = const PackageName(''),
this.createEmptyInstance,
this.toProto3Json,
this.fromProto3Json})
: qualifiedMessageName = '${package.prefix}$messageName';
void add<T>(
int tagNumber,
String name,
int? fieldType,
dynamic defaultOrMaker,
CreateBuilderFunc? subBuilder,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
{String? protoName}) {
final index = byIndex.length;
final fieldInfo = (tagNumber == 0)
? FieldInfo.dummy(index)
: FieldInfo<T>(name, tagNumber, index, fieldType!,
defaultOrMaker: defaultOrMaker,
subBuilder: subBuilder,
valueOf: valueOf,
enumValues: enumValues,
protoName: protoName);
_addField(fieldInfo);
}
void addMapField<K, V>(
int tagNumber,
String name,
int keyFieldType,
int valueFieldType,
BuilderInfo mapEntryBuilderInfo,
CreateBuilderFunc? valueCreator,
{ProtobufEnum? defaultEnumValue,
String? protoName}) {
final index = byIndex.length;
_addField(MapFieldInfo<K, V>(name, tagNumber, index, PbFieldType.M,
keyFieldType, valueFieldType, mapEntryBuilderInfo, valueCreator,
defaultEnumValue: defaultEnumValue, protoName: protoName));
}
void addRepeated<T>(
int tagNumber,
String name,
int fieldType,
CheckFunc<T> check,
CreateBuilderFunc? subBuilder,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
{ProtobufEnum? defaultEnumValue,
String? protoName}) {
final index = byIndex.length;
_addField(FieldInfo<T>.repeated(
name, tagNumber, index, fieldType, check, subBuilder,
valueOf: valueOf,
enumValues: enumValues,
defaultEnumValue: defaultEnumValue,
protoName: protoName));
}
void _addField(FieldInfo fi) {
byIndex.add(fi);
assert(byIndex[fi.index!] == fi);
// Fields with tag number 0 are considered dummy fields added to avoid
// index calculations add up. They should not be reflected in the following
// maps.
if (!fi._isDummy) {
fieldInfo[fi.tagNumber] = fi;
byTagAsString['${fi.tagNumber}'] = fi;
byName[fi.name] = fi;
}
}
void a<T>(int tagNumber, String name, int fieldType,
{dynamic defaultOrMaker,
CreateBuilderFunc? subBuilder,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
String? protoName}) {
add<T>(tagNumber, name, fieldType, defaultOrMaker, subBuilder, valueOf,
enumValues,
protoName: protoName);
}
/// Adds PbFieldType.OS String with no default value to reduce generated
/// code size.
void aOS(int tagNumber, String name, {String? protoName}) {
add<String>(tagNumber, name, PbFieldType.OS, null, null, null, null,
protoName: protoName);
}
/// Adds PbFieldType.PS String with no default value.
void pPS(int tagNumber, String name, {String? protoName}) {
addRepeated<String>(tagNumber, name, PbFieldType.PS,
getCheckFunction(PbFieldType.PS), null, null, null,
protoName: protoName);
}
/// Adds PbFieldType.QS String with no default value.
void aQS(int tagNumber, String name, {String? protoName}) {
add<String>(tagNumber, name, PbFieldType.QS, null, null, null, null,
protoName: protoName);
}
/// Adds Int64 field with Int64.ZERO default.
void aInt64(int tagNumber, String name, {String? protoName}) {
add<Int64>(tagNumber, name, PbFieldType.O6, Int64.ZERO, null, null, null,
protoName: protoName);
}
/// Adds a boolean with no default value.
void aOB(int tagNumber, String name, {String? protoName}) {
add<bool>(tagNumber, name, PbFieldType.OB, null, null, null, null,
protoName: protoName);
}
// Enum.
void e<T>(int tagNumber, String name, int fieldType,
{dynamic defaultOrMaker,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
String? protoName}) {
add<T>(
tagNumber, name, fieldType, defaultOrMaker, null, valueOf, enumValues,
protoName: protoName);
}
// Repeated, not a message, group, or enum.
void p<T>(int tagNumber, String name, int fieldType, {String? protoName}) {
assert(!_isGroupOrMessage(fieldType) && !_isEnum(fieldType));
addRepeated<T>(tagNumber, name, fieldType, getCheckFunction(fieldType),
null, null, null,
protoName: protoName);
}
// Repeated message, group, or enum.
void pc<T>(int tagNumber, String name, int fieldType,
{CreateBuilderFunc? subBuilder,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
ProtobufEnum? defaultEnumValue,
String? protoName}) {
assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
valueOf, enumValues,
defaultEnumValue: defaultEnumValue, protoName: protoName);
}
void aOM<T extends GeneratedMessage>(int tagNumber, String name,
{required T Function() subBuilder, String? protoName}) {
add<T>(
tagNumber,
name,
PbFieldType.OM,
GeneratedMessage._defaultMakerFor<T>(subBuilder),
subBuilder,
null,
null,
protoName: protoName);
}
void aQM<T extends GeneratedMessage>(int tagNumber, String name,
{required T Function() subBuilder, String? protoName}) {
add<T>(
tagNumber,
name,
PbFieldType.QM,
GeneratedMessage._defaultMakerFor<T>(subBuilder),
subBuilder,
null,
null,
protoName: protoName);
}
// oneof declarations.
void oo(int oneofIndex, List<int> tags) {
for (final tag in tags) {
oneofs[tag] = oneofIndex;
}
}
// Map field.
void m<K, V>(int tagNumber, String name,
{String? entryClassName,
required int keyFieldType,
required int valueFieldType,
CreateBuilderFunc? valueCreator,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
ProtobufEnum? defaultEnumValue,
PackageName packageName = const PackageName(''),
String? protoName,
dynamic valueDefaultOrMaker}) {
final mapEntryBuilderInfo = BuilderInfo(entryClassName,
package: packageName)
..add(PbMap._keyFieldNumber, 'key', keyFieldType, null, null, null, null)
..add(PbMap._valueFieldNumber, 'value', valueFieldType,
valueDefaultOrMaker, valueCreator, valueOf, enumValues);
addMapField<K, V>(tagNumber, name, keyFieldType, valueFieldType,
mapEntryBuilderInfo, valueCreator,
defaultEnumValue: defaultEnumValue, protoName: protoName);
}
bool containsTagNumber(int tagNumber) => fieldInfo.containsKey(tagNumber);
dynamic defaultValue(int tagNumber) {
final func = makeDefault(tagNumber);
return func == null ? null : func();
}
// Returns the field name for a given tag number, for debugging purposes.
String? fieldName(int tagNumber) {
final i = fieldInfo[tagNumber];
return i?.name;
}
int? fieldType(int tagNumber) {
final i = fieldInfo[tagNumber];
return i?.type;
}
MakeDefaultFunc? makeDefault(int tagNumber) {
final i = fieldInfo[tagNumber];
return i?.makeDefault;
}
CreateBuilderFunc? subBuilder(int tagNumber) {
final i = fieldInfo[tagNumber];
return i?.subBuilder;
}
int? tagNumber(String fieldName) {
final i = byName[fieldName];
return i?.tagNumber;
}
ValueOfFunc? valueOfFunc(int tagNumber) {
final i = fieldInfo[tagNumber];
return i?.valueOf;
}
/// The [FieldInfo] for each field in tag number order.
List<FieldInfo> get sortedByTag => _sortedByTag ??= _computeSortedByTag();
/// The message name. Also see [qualifiedMessageName].
String get messageName {
final lastDot = qualifiedMessageName.lastIndexOf('.');
return lastDot == -1
? qualifiedMessageName
: qualifiedMessageName.substring(lastDot + 1);
}
List<FieldInfo> _computeSortedByTag() =>
// Code generator inserts fields in tag order, but it's possible for
// user-written code to insert unordered.
List<FieldInfo>.from(fieldInfo.values, growable: false)
..sort(
(FieldInfo a, FieldInfo b) => a.tagNumber.compareTo(b.tagNumber));
GeneratedMessage _makeEmptyMessage(
int tagNumber, ExtensionRegistry? extensionRegistry) {
var subBuilderFunc = subBuilder(tagNumber);
if (subBuilderFunc == null && extensionRegistry != null) {
subBuilderFunc = extensionRegistry
.getExtension(qualifiedMessageName, tagNumber)!
.subBuilder;
}
return subBuilderFunc!();
}
ProtobufEnum? _decodeEnum(
int tagNumber, ExtensionRegistry? registry, int rawValue) {
var f = valueOfFunc(tagNumber);
if (f == null && registry != null) {
f = registry.getExtension(qualifiedMessageName, tagNumber)!.valueOf;
}
return f!(rawValue);
}
}