blob: ea5324ac6fdd301e28e8aeee8b1bf5ffd001fa91 [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 {
/// The fully qualified name of this message.
final String qualifiedMessageName;
final List<FieldInfo> byIndex = <FieldInfo>[];
final Map<int, FieldInfo> fieldInfo = <int, FieldInfo>{};
final Map<String, FieldInfo> byTagAsString = <String, FieldInfo>{};
final Map<String, FieldInfo> byName = <String, FieldInfo>{};
// Maps a tag number to the corresponding oneof index (if any).
final Map<int, int> oneofs = <int, int>{};
bool hasExtensions = false;
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}) {
var 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}) {
var 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}) {
var 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(
!isGroupOrMessageFieldType(fieldType) && !isEnumFieldType(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(isGroupOrMessageFieldType(fieldType) || isEnumFieldType(fieldType));
addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
valueOf, enumValues,
defaultEnumValue: defaultEnumValue, protoName: protoName);
}
void aOM<T extends GeneratedMessage>(int tagNumber, String name,
{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,
{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 (var tag in tags) {
oneofs[tag] = oneofIndex;
}
}
// Map field.
void m<K, V>(int tagNumber, String name,
{String? entryClassName,
int? keyFieldType,
int? valueFieldType,
CreateBuilderFunc? valueCreator,
ValueOfFunc? valueOf,
List<ProtobufEnum>? enumValues,
ProtobufEnum? defaultEnumValue,
PackageName packageName = const PackageName(''),
String? protoName}) {
var mapEntryBuilderInfo = BuilderInfo(entryClassName, package: packageName)
..add(PbMap.keyFieldNumber, 'key', keyFieldType, null, null, null, null)
..add(PbMap.valueFieldNumber, 'value', valueFieldType, null, 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) {
var func = makeDefault(tagNumber);
return func == null ? null : func();
}
// Returns the field name for a given tag number, for debugging purposes.
String? fieldName(int tagNumber) {
var i = fieldInfo[tagNumber];
return i?.name;
}
int? fieldType(int tagNumber) {
var i = fieldInfo[tagNumber];
return i?.type;
}
MakeDefaultFunc? makeDefault(int tagNumber) {
var i = fieldInfo[tagNumber];
return i?.makeDefault;
}
CreateBuilderFunc? subBuilder(int tagNumber) {
var i = fieldInfo[tagNumber];
return i?.subBuilder;
}
int? tagNumber(String fieldName) {
var i = byName[fieldName];
return i?.tagNumber;
}
ValueOfFunc? valueOfFunc(int tagNumber) {
var 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() {
// TODO(skybrian): perhaps the code generator should insert the FieldInfos
// in tag number order, to avoid sorting them?
return 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);
}
}
/// Annotation for marking accessors that belong together.
class TagNumber {
final int tagNumber;
/// Annotation for marking accessors that belong together.
///
/// Allows tooling to associate related accessors.
/// [tagNumber] is the protobuf tagnumber associated with the annotated
/// accessor.
const TagNumber(this.tagNumber);
}