blob: 2e30f3e414080d3c09e96d0fdfbcb992f8f65925 [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.
part of protoc;
class ExtensionGenerator {
final FieldDescriptorProto _descriptor;
final ProtobufContainer _parent;
// populated by resolve()
ProtobufField _field;
final String _extensionName;
String _extendedFullName = "";
List<int> _fieldPath;
final List<int> _fieldPathSegment;
/// See [[ProtobufContainer]
List<int> get fieldPath =>
_fieldPath ??= List.from(_parent.fieldPath)..addAll(_fieldPathSegment);
ExtensionGenerator._(this._descriptor, this._parent, Set<String> usedNames,
int repeatedFieldIndex, int fieldIdTag)
: _extensionName = extensionName(_descriptor, usedNames),
_fieldPathSegment = [fieldIdTag, repeatedFieldIndex];
static const _topLevelFieldTag = 7;
static const _nestedFieldTag = 6;
ExtensionGenerator.topLevel(FieldDescriptorProto descriptor,
ProtobufContainer parent, Set<String> usedNames, int repeatedFieldIndex)
: this._(descriptor, parent, usedNames, repeatedFieldIndex,
_topLevelFieldTag);
ExtensionGenerator.nested(FieldDescriptorProto descriptor,
ProtobufContainer parent, Set<String> usedNames, int repeatedFieldIndex)
: this._(
descriptor, parent, usedNames, repeatedFieldIndex, _nestedFieldTag);
void resolve(GenerationContext ctx) {
_field = ProtobufField.extension(_descriptor, _parent, ctx);
ProtobufContainer extendedType = ctx.getFieldType(_descriptor.extendee);
// TODO(skybrian) When would this be null?
if (extendedType != null) {
_extendedFullName = extendedType.fullName;
}
}
String get package => _parent.package;
/// The generator of the .pb.dart file where this extension will be defined.
FileGenerator get fileGen => _parent.fileGen;
String get name {
if (_field == null) throw StateError("resolve not called");
String name = _extensionName;
return _parent is MessageGenerator ? '${_parent.classname}.$name' : name;
}
bool get needsFixnumImport {
if (_field == null) throw StateError("resolve not called");
return _field.needsFixnumImport;
}
/// Adds dependencies of [generate] to [imports].
///
/// For each .pb.dart file that the generated code needs to import,
/// add its generator.
void addImportsTo(
Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
if (_field == null) throw StateError("resolve not called");
var typeGen = _field.baseType.generator;
if (typeGen != null) {
// The type of this extension is defined in a different file,
// so we need to import it.
if (typeGen is EnumGenerator) {
// Enums are always in a different file.
enumImports.add(typeGen.fileGen);
} else if (typeGen.fileGen != fileGen) {
imports.add(typeGen.fileGen);
}
}
}
/// Adds dependencies of [generateConstants] to [imports].
///
/// For each .pb.dart file that the generated code needs to import,
/// add its generator.
void addConstantImportsTo(Set<FileGenerator> imports) {
if (_field == null) throw StateError("resolve not called");
// No dependencies - nothing to do.
}
void generate(IndentingWriter out) {
if (_field == null) throw StateError("resolve not called");
String name = _extensionName;
var type = _field.baseType;
var dartType = type.getDartType(fileGen);
String invocation;
List<String> positionals = <String>[];
positionals.add("'$_extendedFullName'");
positionals.add("'${_descriptor.jsonName}'");
positionals.add('${_field.number}');
positionals.add(_field.typeConstant);
Map<String, String> named = <String, String>{};
named['protoName'] = _field.quotedProtoName;
if (_field.isRepeated) {
invocation = '$_protobufImportPrefix.Extension<$dartType>.repeated';
named['check'] =
'$_protobufImportPrefix.getCheckFunction(${_field.typeConstant})';
if (type.isMessage || type.isGroup) {
named['subBuilder'] = '$dartType.create';
} else if (type.isEnum) {
named['valueOf'] = '$dartType.valueOf';
named['enumValues'] = '$dartType.values';
}
} else {
invocation = '$_protobufImportPrefix.Extension<$dartType>';
named['defaultOrMaker'] = _field.generateDefaultFunction(fileGen);
if (type.isMessage || type.isGroup) {
named['subBuilder'] = '$dartType.create';
} else if (type.isEnum) {
var dartEnum = type.getDartType(fileGen);
named['valueOf'] = '$dartEnum.valueOf';
named['enumValues'] = '$dartEnum.values';
}
}
assert(invocation != null);
out.printAnnotated(
'static final $_protobufImportPrefix.Extension $name = '
'$invocation(${ProtobufField._formatArguments(positionals, named)});\n',
[
NamedLocation(
name: name,
fieldPathSegment: List.from(fieldPath),
start: 'static final $_protobufImportPrefix.Extension '.length)
]);
}
}