| Index: lib/protobuf/plugin/MessageGenerator.dart
|
| diff --git a/lib/protobuf/plugin/MessageGenerator.dart b/lib/protobuf/plugin/MessageGenerator.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d9d5e71df2489a9cdce2f539bccda77f64727c93
|
| --- /dev/null
|
| +++ b/lib/protobuf/plugin/MessageGenerator.dart
|
| @@ -0,0 +1,311 @@
|
| +// 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.
|
| +
|
| +class MessageGenerator implements ProtobufContainer {
|
| +
|
| + // Variables controlling output prettiness
|
| + static final String sp = " ";
|
| + static final bool _blankLines = true;
|
| + static final bool _fieldComments = true;
|
| +
|
| + String _builderClassname;
|
| + String _classname;
|
| + GenerationContext _context;
|
| + final GoogleProtobuf_DescriptorProto _descriptor;
|
| + List<EnumGenerator> _enumGenerators;
|
| + List<ProtobufField> _fieldList;
|
| + String _fqname;
|
| + List<MessageGenerator> _messageGenerators;
|
| + List<ExtensionGenerator> _extensionGenerators;
|
| + GoogleProtobuf_FileOptions_OptimizeMode _optimizeFor;
|
| + ProtobufContainer _parent;
|
| +
|
| + String get classname() => _classname;
|
| + String get fqname() => _fqname;
|
| + GoogleProtobuf_FileOptions_OptimizeMode get optimizeFor() => _optimizeFor;
|
| +
|
| + MessageGenerator(GoogleProtobuf_DescriptorProto this._descriptor,
|
| + ProtobufContainer this._parent, GenerationContext this._context) {
|
| + String name = _descriptor.name;
|
| + _classname = (_parent === null || _parent.classname == "") ?
|
| + name :
|
| + "${_parent.classname}_${name}";
|
| +
|
| + _builderClassname = "${_classname}_Builder";
|
| + _fqname = (_parent === null || _parent.fqname === null) ? _descriptor.name :
|
| + ( _parent.fqname == "." ?
|
| + ".${_descriptor.name}" :
|
| + "${_parent.fqname}.${_descriptor.name}" );
|
| + _optimizeFor = _parent != null ? _parent.optimizeFor : null;
|
| + _context.register(this);
|
| +
|
| + _fieldList = [];
|
| +
|
| + _enumGenerators = new List<EnumGenerator>();
|
| + for (GoogleProtobuf_EnumDescriptorProto e in _descriptor.enumType) {
|
| + _enumGenerators.add(new EnumGenerator(e, this, _context));
|
| + }
|
| +
|
| + _messageGenerators = new List<MessageGenerator>();
|
| + for (GoogleProtobuf_DescriptorProto n in _descriptor.nestedType) {
|
| + _messageGenerators.add(new MessageGenerator(n, this, _context));
|
| + }
|
| +
|
| + _extensionGenerators = new List<ExtensionGenerator>();
|
| + for (GoogleProtobuf_FieldDescriptorProto x in _descriptor.extension) {
|
| + _extensionGenerators.add(new ExtensionGenerator(x, this, _context));
|
| + }
|
| + }
|
| +
|
| + void initializeFields() {
|
| + _fieldList = [];
|
| + ProtobufField._resetIndices();
|
| + for (GoogleProtobuf_FieldDescriptorProto field in _descriptor.field) {
|
| + _fieldList.add(new ProtobufField(field, _context));
|
| + }
|
| + for (MessageGenerator m in _messageGenerators) {
|
| + m.initializeFields();
|
| + }
|
| + }
|
| +
|
| + void generate(IndentingWriter out,
|
| + [List<ExtensionGenerator> allExtensions = null]) {
|
| + for (EnumGenerator e in _enumGenerators) {
|
| + e.generate(out);
|
| + }
|
| +
|
| + for (MessageGenerator m in _messageGenerators) {
|
| + m.generate(out, allExtensions);
|
| + }
|
| +
|
| + out.addBlock("class ${_classname} extends GeneratedMessage${sp}{", "}", (){
|
| + for (ExtensionGenerator x in _extensionGenerators) {
|
| + if (allExtensions != null) {
|
| + allExtensions.add(x);
|
| + }
|
| + x.generate(out);
|
| + }
|
| +
|
| + out.println("static ${_classname} _d;");
|
| + out.println("static ${_builderClassname} newBuilder()${sp}=>"
|
| + "${sp}new ${_builderClassname}();");
|
| + out.println("static ${_classname} get defaultInstance()${sp}=>"
|
| + "${sp}null${sp}==${sp}_d${sp}?${sp}(_d${sp}=${sp}newBuilder()."
|
| + "buildPartial())${sp}:${sp}_d;");
|
| + out.println("static ${_classname} parseFromBuffer(List<int> i,"
|
| + "${sp}[ExtensionRegistry r])"
|
| + "${sp}=>${sp}GeneratedMessage.parseBuffer(newBuilder(),"
|
| + "${sp}i,${sp}r);");
|
| + out.println("static Future<${_classname}> parseFromStream("
|
| + "InputStream i,${sp}[ExtensionRegistry r])${sp}=>${sp}"
|
| + "GeneratedMessage.parseStream(newBuilder(),${sp}i,${sp}r);");
|
| + out.println("static ${_classname} parseFromJson("
|
| + "String i,"
|
| + "${sp}[ExtensionRegistry r])"
|
| + "${sp}=>${sp}GeneratedMessage.parseJson(newBuilder(),${sp}i,${sp}r);");
|
| + out.println("${_classname}._fromBuilder(${_builderClassname} b)"
|
| + "${sp}:${sp}super(b);");
|
| + out.println("${_builderClassname} toBuilder()"
|
| + "${sp}=>${sp}newBuilder().mergeFromMessage(this);");
|
| +
|
| + generateFields(out);
|
| + });
|
| +
|
| + // -------------------------- BUILDER --------------------------
|
| + out.blankLine();
|
| + out.addBlock("class ${_builderClassname} extends Builder${sp}{",
|
| + "}", ()
|
| + {
|
| + out.println("static BuilderInfo _i;");
|
| +
|
| + if (_blankLines) {
|
| + out.blankLine();
|
| + }
|
| + out.addBlock("void initialize_()${sp}{","}",(){
|
| + out.addBlock("if${sp}(null${sp}==${sp}_i)${sp}{", "}", () {
|
| + out.println("_i${sp}=${sp}new BuilderInfo(this,${sp}"
|
| + "\"${_classname}\");");
|
| +
|
| + for (ProtobufField field in _fieldList) {
|
| + String type = field.shortTypeName;
|
| +
|
| + String makeDefault = null;
|
| + if (field.hasInitialization) {
|
| + makeDefault = "${field.initialization}";
|
| + }
|
| + String subBuilder = null;
|
| + if (field.message || field.group) {
|
| + subBuilder = "()${sp}=>${sp}new ${field.baseType}_Builder()";
|
| + }
|
| + String valueOf = null;
|
| + if (field.enum) {
|
| + valueOf = "(var v)${sp}=>${sp}${field.baseType}.valueOf(v)";
|
| + }
|
| + if ("PM" == type) {
|
| + // Repeated message: default is an empty list
|
| + out.println("_i.m(${field.number},${sp}'"
|
| + "${field.externalFieldName}',${sp}$subBuilder,"
|
| + "${sp}()${sp}=>${sp}new PbList<${field.baseType}>(this));");
|
| + } else if (type[0] == "P" && type != "PG" && type != "PE") {
|
| + // Repeated, not a message or enum: default is an empty list,
|
| + // subBuilder is null, valueOf is null
|
| + out.println("_i.p(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type);");
|
| + } else if (type == "OE" || type == "QE") {
|
| + out.println("_i.e(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type,"
|
| + "${sp}$makeDefault,${sp}$valueOf);");
|
| + } else {
|
| + if (makeDefault == null && subBuilder == null && valueOf == null) {
|
| + out.println("_i.a(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type);");
|
| + } else if (subBuilder == null && valueOf == null) {
|
| + out.println("_i.a(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type,"
|
| + "${sp}$makeDefault);");
|
| + } else if (valueOf == null) {
|
| + out.println("_i.a(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type,"
|
| + "${sp}$makeDefault,${sp}$subBuilder);");
|
| + } else {
|
| + out.println("_i.a(${field.number},${sp}"
|
| + "'${field.externalFieldName}',${sp}Builder.$type,"
|
| + "${sp}$makeDefault,${sp}$subBuilder,${sp}$valueOf);");
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (_descriptor.extensionRange.length > 0) {
|
| + out.println("_i.hasExtensions${sp}=${sp}true;");
|
| + }
|
| + if (!_hasRequiredFields(this, new Set())) {
|
| + out.println("_i.hasRequiredFields${sp}=${sp}false;");
|
| + }
|
| + });
|
| + });
|
| +
|
| + if (_blankLines) {
|
| + out.blankLine();
|
| + }
|
| + out.println("$_classname build()${sp}=>${sp}super.build();");
|
| + out.println("$_classname buildPartial()${sp}=>${sp}"
|
| + "partial(new ${_classname}._fromBuilder(this));");
|
| + out.println("BuilderInfo get info_()${sp}=>${sp}_i;");
|
| +
|
| + generateFieldsAccessorsMutators(out);
|
| + });
|
| + out.blankLine();
|
| + }
|
| +
|
| + void generateFields(IndentingWriter out) {
|
| +
|
| +//
|
| +// Disable 'noSuchMethod' dependency for now until dart2js code generation
|
| +// improves
|
| +//
|
| +
|
| +// if (optimizeFor == GoogleProtobuf_FileOptions_OptimizeMode.SPEED) {
|
| + for (ProtobufField field in _fieldList) {
|
| + if (_blankLines) {
|
| + out.blankLine();
|
| + }
|
| +
|
| + String label = field.label.name.substring(6).toLowerCase();
|
| + String type = field.type.name.substring(5).toLowerCase();
|
| + if ("message" == type || "group" == type) {
|
| + type = field.typeName.substring(1);
|
| + }
|
| + String options = "";
|
| + if ((field.options != null) && field.options.packed) {
|
| + if (options.length == 0) {
|
| + options = " [";
|
| + }
|
| + options = "${options}packed=true";
|
| + }
|
| + if (options.length > 0) {
|
| + options = "${options}]";
|
| + }
|
| + if (_fieldComments) {
|
| + out.println("// $label $type ${field.name}${sp}=${sp}"
|
| + "${field.number}${options};");
|
| + }
|
| + out.println(
|
| + "${field.typeString} get ${field.externalFieldName}()${sp}=>"
|
| + "${sp}g_(${field.number});");
|
| + if (field.single) {
|
| + out.println("bool has${field.titlecaseFieldName}()${sp}=>"
|
| + "${sp}h_(${field.number});");
|
| + }
|
| +// }
|
| + }
|
| + }
|
| +
|
| + // Returns true if the message type has any required fields. If it doesn't,
|
| + // we can optimize out calls to its isInitialized()/_findInvalidFields()
|
| + // methods.
|
| + //
|
| + // already_seen is used to avoid checking the same type multiple times
|
| + // (and also to protect against unbounded recursion).
|
| + bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) {
|
| + if (alreadySeen.contains(type._fqname)) {
|
| + // The type is already in cache. This means that either:
|
| + // a. The type has no required fields.
|
| + // b. We are in the midst of checking if the type has required fields,
|
| + // somewhere up the stack. In this case, we know that if the type
|
| + // has any required fields, they'll be found when we return to it,
|
| + // and the whole call to HasRequiredFields() will return true.
|
| + // Therefore, we don't have to check if this type has required fields
|
| + // here.
|
| + return false;
|
| + }
|
| + alreadySeen.add(type._fqname);
|
| + // If the type has extensions, an extension with message type could contain
|
| + // required fields, so we have to be conservative and assume such an
|
| + // extension exists.
|
| + if (type._descriptor.extensionRange.length > 0) {
|
| + return true;
|
| + }
|
| +
|
| + for (ProtobufField field in type._fieldList) {
|
| + if (field.required) {
|
| + return true;
|
| + }
|
| + if (field.message) {
|
| + ProtobufContainer messageType = _context[field.typeName];
|
| + if (messageType != null && messageType is MessageGenerator) {
|
| + if (_hasRequiredFields(messageType, alreadySeen)) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + void generateFieldsAccessorsMutators(IndentingWriter out) {
|
| + for (ProtobufField field in _fieldList) {
|
| +//
|
| +// Disable code size optimization for now until dart2js code generation
|
| +// is better able to handle 'noSuchMethod'
|
| +//
|
| +
|
| +// if (optimizeFor == GoogleProtobuf_FileOptions_OptimizeMode.SPEED) {
|
| + if (_blankLines) {
|
| + out.blankLine();
|
| + }
|
| + out.println("${field.typeString} get ${field.externalFieldName}()"
|
| + "${sp}=>${sp}g_(${field.number});");
|
| + if (field.single) {
|
| + out.println("void set ${field.externalFieldName}"
|
| + "(${field.typeString} v)${sp}"
|
| + "{${sp}s_(${field.number},${sp}v);${sp}}");
|
| + out.println("bool has${field.titlecaseFieldName}()${sp}=>"
|
| + "${sp}h_(${field.number});");
|
| + out.println("void clear${field.titlecaseFieldName}()${sp}=>"
|
| + "${sp}c_(${field.number});");
|
| + }
|
| +// }
|
| + }
|
| + }
|
| +}
|
|
|