blob: 2b3da0498d7d11a1b86bd931be1a29e08fc3c37f [file] [log] [blame]
// Copyright (c) 2013, 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.
// @dart=2.11
part of '../protoc.dart';
class EnumAlias {
final EnumValueDescriptorProto value;
final EnumValueDescriptorProto canonicalValue;
EnumAlias(this.value, this.canonicalValue);
}
class EnumGenerator extends ProtobufContainer {
@override
final ProtobufContainer parent;
@override
final String classname;
@override
final String fullName;
final EnumDescriptorProto _descriptor;
final List<EnumValueDescriptorProto> _canonicalValues =
<EnumValueDescriptorProto>[];
final List<int> _originalCanonicalIndices = <int>[];
final List<EnumAlias> _aliases = <EnumAlias>[];
/// Maps the name of an enum value to the Dart name we will use for it.
final Map<String, String> dartNames = <String, String>{};
final List<int> _originalAliasIndices = <int>[];
List<int> _fieldPath;
final List<int> _fieldPathSegment;
/// See [[ProtobufContainer]
@override
List<int> get fieldPath =>
_fieldPath ??= List.from(parent.fieldPath)..addAll(_fieldPathSegment);
EnumGenerator._(EnumDescriptorProto descriptor, this.parent,
Set<String> usedClassNames, int repeatedFieldIndex, int fieldIdTag)
: assert(parent != null),
_fieldPathSegment = [fieldIdTag, repeatedFieldIndex],
classname = messageOrEnumClassName(descriptor.name, usedClassNames,
parent: parent?.classname ?? ''),
fullName = parent.fullName == ''
? descriptor.name
: '${parent.fullName}.${descriptor.name}',
_descriptor = descriptor {
final usedNames = {...reservedEnumNames};
for (var i = 0; i < descriptor.value.length; i++) {
var value = descriptor.value[i];
var canonicalValue =
descriptor.value.firstWhere((v) => v.number == value.number);
if (value == canonicalValue) {
_canonicalValues.add(value);
_originalCanonicalIndices.add(i);
} else {
_aliases.add(EnumAlias(value, canonicalValue));
_originalAliasIndices.add(i);
}
dartNames[value.name] = disambiguateName(
avoidInitialUnderscore(value.name), usedNames, enumSuffixes());
}
}
static const _topLevelFieldTag = 5;
static const _nestedFieldTag = 4;
EnumGenerator.topLevel(
EnumDescriptorProto descriptor,
ProtobufContainer parent,
Set<String> usedClassNames,
int repeatedFieldIndex)
: this._(descriptor, parent, usedClassNames, repeatedFieldIndex,
_topLevelFieldTag);
EnumGenerator.nested(EnumDescriptorProto descriptor, ProtobufContainer parent,
Set<String> usedClassNames, int repeatedFieldIndex)
: this._(descriptor, parent, usedClassNames, repeatedFieldIndex,
_nestedFieldTag);
@override
String get package => parent.package;
@override
FileGenerator get fileGen => parent.fileGen;
/// Make this enum available as a field type.
void register(GenerationContext ctx) {
ctx.registerFieldType(this);
}
/// Returns a const expression that evaluates to the JSON for this message.
/// [usage] represents the .pb.dart file where the expression will be used.
String getJsonConstant(FileGenerator usage) {
var name = '$classname\$json';
if (usage.protoFileUri == fileGen.protoFileUri) {
return name;
}
return '$fileImportPrefix.$name';
}
static const int _enumValueTag = 2;
void generate(IndentingWriter out) {
out.addAnnotatedBlock(
'class $classname extends $protobufImportPrefix.ProtobufEnum {',
'}\n', [
NamedLocation(
name: classname, fieldPathSegment: fieldPath, start: 'class '.length)
], () {
// -----------------------------------------------------------------
// Define enum types.
for (var i = 0; i < _canonicalValues.length; i++) {
var val = _canonicalValues[i];
final name = dartNames[val.name];
final conditionalValName = configurationDependent(
'protobuf.omit_enum_names', quoted(val.name));
out.printlnAnnotated(
'static const $classname $name = '
'$classname._(${val.number}, $conditionalValName);',
[
NamedLocation(
name: name,
fieldPathSegment: List.from(fieldPath)
..addAll([_enumValueTag, _originalCanonicalIndices[i]]),
start: 'static const $classname '.length)
]);
}
if (_aliases.isNotEmpty) {
out.println();
for (var i = 0; i < _aliases.length; i++) {
var alias = _aliases[i];
final name = dartNames[alias.value.name];
out.printlnAnnotated(
'static const $classname $name ='
' ${dartNames[alias.canonicalValue.name]};',
[
NamedLocation(
name: name,
fieldPathSegment: List.from(fieldPath)
..addAll([_enumValueTag, _originalAliasIndices[i]]),
start: 'static const $classname '.length)
]);
}
}
out.println();
out.println('static const $coreImportPrefix.List<$classname> values ='
' <$classname> [');
for (var val in _canonicalValues) {
final name = dartNames[val.name];
out.println(' $name,');
}
out.println('];');
out.println();
out.println(
'static final $coreImportPrefix.Map<$coreImportPrefix.int, $classname> _byValue ='
' $protobufImportPrefix.ProtobufEnum.initByValue(values);');
out.println('static $classname? valueOf($coreImportPrefix.int value) =>'
' _byValue[value];');
out.println();
out.println(
'const $classname._($coreImportPrefix.int v, $coreImportPrefix.String n) '
': super(v, n);');
});
}
/// Writes a Dart constant containing the JSON for the EnumProtoDescriptor.
void generateConstants(IndentingWriter out) {
var name = getJsonConstant(fileGen);
var json = _descriptor.writeToJsonMap();
out.println('@$coreImportPrefix.Deprecated'
'(\'Use ${toplevelParent.binaryDescriptorName} instead\')');
out.print('const $name = ');
writeJsonConst(out, json);
out.println(';');
out.println();
}
}