blob: 95fa3b003ae025c1354d9e6342730624f6a2f2e5 [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,
{String protoName}) {
var index = byIndex.length;
_addField(MapFieldInfo<K, V>(name, tagNumber, index, PbFieldType.M,
keyFieldType, valueFieldType, mapEntryBuilderInfo, valueCreator,
protoName: protoName));
}
void addRepeated<T>(
int tagNumber,
String name,
int fieldType,
CheckFunc<T> check,
CreateBuilderFunc subBuilder,
ValueOfFunc valueOf,
List<ProtobufEnum> enumValues,
{String protoName}) {
var index = byIndex.length;
_addField(FieldInfo<T>.repeated(
name, tagNumber, index, fieldType, check, subBuilder,
valueOf: valueOf, enumValues: enumValues, 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,
String protoName}) {
assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
valueOf, enumValues,
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) {
tags.forEach((int tag) => 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,
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,
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 != null ? i.name : null;
}
int fieldType(int tagNumber) {
var i = fieldInfo[tagNumber];
return i != null ? i.type : null;
}
MakeDefaultFunc makeDefault(int tagNumber) {
var i = fieldInfo[tagNumber];
return i != null ? i.makeDefault : null;
}
CreateBuilderFunc subBuilder(int tagNumber) {
var i = fieldInfo[tagNumber];
return i != null ? i.subBuilder : null;
}
int tagNumber(String fieldName) {
var i = byName[fieldName];
return i != null ? i.tagNumber : null;
}
ValueOfFunc valueOfFunc(int tagNumber) {
var i = fieldInfo[tagNumber];
return i != null ? i.valueOf : null;
}
/// 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);
}