OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 class MessageGenerator implements ProtobufContainer { |
| 6 |
| 7 // Variables controlling output prettiness |
| 8 static final String sp = " "; |
| 9 static final bool _blankLines = true; |
| 10 static final bool _fieldComments = true; |
| 11 |
| 12 String _builderClassname; |
| 13 String _classname; |
| 14 GenerationContext _context; |
| 15 final GoogleProtobuf_DescriptorProto _descriptor; |
| 16 List<EnumGenerator> _enumGenerators; |
| 17 List<ProtobufField> _fieldList; |
| 18 String _fqname; |
| 19 List<MessageGenerator> _messageGenerators; |
| 20 List<ExtensionGenerator> _extensionGenerators; |
| 21 GoogleProtobuf_FileOptions_OptimizeMode _optimizeFor; |
| 22 ProtobufContainer _parent; |
| 23 |
| 24 String get classname() => _classname; |
| 25 String get fqname() => _fqname; |
| 26 GoogleProtobuf_FileOptions_OptimizeMode get optimizeFor() => _optimizeFor; |
| 27 |
| 28 MessageGenerator(GoogleProtobuf_DescriptorProto this._descriptor, |
| 29 ProtobufContainer this._parent, GenerationContext this._context) { |
| 30 String name = _descriptor.name; |
| 31 _classname = (_parent === null || _parent.classname == "") ? |
| 32 name : |
| 33 "${_parent.classname}_${name}"; |
| 34 |
| 35 _builderClassname = "${_classname}_Builder"; |
| 36 _fqname = (_parent === null || _parent.fqname === null) ? _descriptor.name : |
| 37 ( _parent.fqname == "." ? |
| 38 ".${_descriptor.name}" : |
| 39 "${_parent.fqname}.${_descriptor.name}" ); |
| 40 _optimizeFor = _parent != null ? _parent.optimizeFor : null; |
| 41 _context.register(this); |
| 42 |
| 43 _fieldList = []; |
| 44 |
| 45 _enumGenerators = new List<EnumGenerator>(); |
| 46 for (GoogleProtobuf_EnumDescriptorProto e in _descriptor.enumType) { |
| 47 _enumGenerators.add(new EnumGenerator(e, this, _context)); |
| 48 } |
| 49 |
| 50 _messageGenerators = new List<MessageGenerator>(); |
| 51 for (GoogleProtobuf_DescriptorProto n in _descriptor.nestedType) { |
| 52 _messageGenerators.add(new MessageGenerator(n, this, _context)); |
| 53 } |
| 54 |
| 55 _extensionGenerators = new List<ExtensionGenerator>(); |
| 56 for (GoogleProtobuf_FieldDescriptorProto x in _descriptor.extension) { |
| 57 _extensionGenerators.add(new ExtensionGenerator(x, this, _context)); |
| 58 } |
| 59 } |
| 60 |
| 61 void initializeFields() { |
| 62 _fieldList = []; |
| 63 ProtobufField._resetIndices(); |
| 64 for (GoogleProtobuf_FieldDescriptorProto field in _descriptor.field) { |
| 65 _fieldList.add(new ProtobufField(field, _context)); |
| 66 } |
| 67 for (MessageGenerator m in _messageGenerators) { |
| 68 m.initializeFields(); |
| 69 } |
| 70 } |
| 71 |
| 72 void generate(IndentingWriter out, |
| 73 [List<ExtensionGenerator> allExtensions = null]) { |
| 74 for (EnumGenerator e in _enumGenerators) { |
| 75 e.generate(out); |
| 76 } |
| 77 |
| 78 for (MessageGenerator m in _messageGenerators) { |
| 79 m.generate(out, allExtensions); |
| 80 } |
| 81 |
| 82 out.addBlock("class ${_classname} extends GeneratedMessage${sp}{", "}", (){ |
| 83 for (ExtensionGenerator x in _extensionGenerators) { |
| 84 if (allExtensions != null) { |
| 85 allExtensions.add(x); |
| 86 } |
| 87 x.generate(out); |
| 88 } |
| 89 |
| 90 out.println("static ${_classname} _d;"); |
| 91 out.println("static ${_builderClassname} newBuilder()${sp}=>" |
| 92 "${sp}new ${_builderClassname}();"); |
| 93 out.println("static ${_classname} get defaultInstance()${sp}=>" |
| 94 "${sp}null${sp}==${sp}_d${sp}?${sp}(_d${sp}=${sp}newBuilder()." |
| 95 "buildPartial())${sp}:${sp}_d;"); |
| 96 out.println("static ${_classname} parseFromBuffer(List<int> i," |
| 97 "${sp}[ExtensionRegistry r])" |
| 98 "${sp}=>${sp}GeneratedMessage.parseBuffer(newBuilder()," |
| 99 "${sp}i,${sp}r);"); |
| 100 out.println("static Future<${_classname}> parseFromStream(" |
| 101 "InputStream i,${sp}[ExtensionRegistry r])${sp}=>${sp}" |
| 102 "GeneratedMessage.parseStream(newBuilder(),${sp}i,${sp}r);"); |
| 103 out.println("static ${_classname} parseFromJson(" |
| 104 "String i," |
| 105 "${sp}[ExtensionRegistry r])" |
| 106 "${sp}=>${sp}GeneratedMessage.parseJson(newBuilder(),${sp}i,${sp}r);"); |
| 107 out.println("${_classname}._fromBuilder(${_builderClassname} b)" |
| 108 "${sp}:${sp}super(b);"); |
| 109 out.println("${_builderClassname} toBuilder()" |
| 110 "${sp}=>${sp}newBuilder().mergeFromMessage(this);"); |
| 111 |
| 112 generateFields(out); |
| 113 }); |
| 114 |
| 115 // -------------------------- BUILDER -------------------------- |
| 116 out.blankLine(); |
| 117 out.addBlock("class ${_builderClassname} extends Builder${sp}{", |
| 118 "}", () |
| 119 { |
| 120 out.println("static BuilderInfo _i;"); |
| 121 |
| 122 if (_blankLines) { |
| 123 out.blankLine(); |
| 124 } |
| 125 out.addBlock("void initialize_()${sp}{","}",(){ |
| 126 out.addBlock("if${sp}(null${sp}==${sp}_i)${sp}{", "}", () { |
| 127 out.println("_i${sp}=${sp}new BuilderInfo(this,${sp}" |
| 128 "\"${_classname}\");"); |
| 129 |
| 130 for (ProtobufField field in _fieldList) { |
| 131 String type = field.shortTypeName; |
| 132 |
| 133 String makeDefault = null; |
| 134 if (field.hasInitialization) { |
| 135 makeDefault = "${field.initialization}"; |
| 136 } |
| 137 String subBuilder = null; |
| 138 if (field.message || field.group) { |
| 139 subBuilder = "()${sp}=>${sp}new ${field.baseType}_Builder()"; |
| 140 } |
| 141 String valueOf = null; |
| 142 if (field.enum) { |
| 143 valueOf = "(var v)${sp}=>${sp}${field.baseType}.valueOf(v)"; |
| 144 } |
| 145 if ("PM" == type) { |
| 146 // Repeated message: default is an empty list |
| 147 out.println("_i.m(${field.number},${sp}'" |
| 148 "${field.externalFieldName}',${sp}$subBuilder," |
| 149 "${sp}()${sp}=>${sp}new PbList<${field.baseType}>(this));"); |
| 150 } else if (type[0] == "P" && type != "PG" && type != "PE") { |
| 151 // Repeated, not a message or enum: default is an empty list, |
| 152 // subBuilder is null, valueOf is null |
| 153 out.println("_i.p(${field.number},${sp}" |
| 154 "'${field.externalFieldName}',${sp}Builder.$type);"); |
| 155 } else if (type == "OE" || type == "QE") { |
| 156 out.println("_i.e(${field.number},${sp}" |
| 157 "'${field.externalFieldName}',${sp}Builder.$type," |
| 158 "${sp}$makeDefault,${sp}$valueOf);"); |
| 159 } else { |
| 160 if (makeDefault == null && subBuilder == null && valueOf == null) { |
| 161 out.println("_i.a(${field.number},${sp}" |
| 162 "'${field.externalFieldName}',${sp}Builder.$type);"); |
| 163 } else if (subBuilder == null && valueOf == null) { |
| 164 out.println("_i.a(${field.number},${sp}" |
| 165 "'${field.externalFieldName}',${sp}Builder.$type," |
| 166 "${sp}$makeDefault);"); |
| 167 } else if (valueOf == null) { |
| 168 out.println("_i.a(${field.number},${sp}" |
| 169 "'${field.externalFieldName}',${sp}Builder.$type," |
| 170 "${sp}$makeDefault,${sp}$subBuilder);"); |
| 171 } else { |
| 172 out.println("_i.a(${field.number},${sp}" |
| 173 "'${field.externalFieldName}',${sp}Builder.$type," |
| 174 "${sp}$makeDefault,${sp}$subBuilder,${sp}$valueOf);"); |
| 175 } |
| 176 } |
| 177 } |
| 178 |
| 179 if (_descriptor.extensionRange.length > 0) { |
| 180 out.println("_i.hasExtensions${sp}=${sp}true;"); |
| 181 } |
| 182 if (!_hasRequiredFields(this, new Set())) { |
| 183 out.println("_i.hasRequiredFields${sp}=${sp}false;"); |
| 184 } |
| 185 }); |
| 186 }); |
| 187 |
| 188 if (_blankLines) { |
| 189 out.blankLine(); |
| 190 } |
| 191 out.println("$_classname build()${sp}=>${sp}super.build();"); |
| 192 out.println("$_classname buildPartial()${sp}=>${sp}" |
| 193 "partial(new ${_classname}._fromBuilder(this));"); |
| 194 out.println("BuilderInfo get info_()${sp}=>${sp}_i;"); |
| 195 |
| 196 generateFieldsAccessorsMutators(out); |
| 197 }); |
| 198 out.blankLine(); |
| 199 } |
| 200 |
| 201 void generateFields(IndentingWriter out) { |
| 202 |
| 203 // |
| 204 // Disable 'noSuchMethod' dependency for now until dart2js code generation |
| 205 // improves |
| 206 // |
| 207 |
| 208 // if (optimizeFor == GoogleProtobuf_FileOptions_OptimizeMode.SPEED) { |
| 209 for (ProtobufField field in _fieldList) { |
| 210 if (_blankLines) { |
| 211 out.blankLine(); |
| 212 } |
| 213 |
| 214 String label = field.label.name.substring(6).toLowerCase(); |
| 215 String type = field.type.name.substring(5).toLowerCase(); |
| 216 if ("message" == type || "group" == type) { |
| 217 type = field.typeName.substring(1); |
| 218 } |
| 219 String options = ""; |
| 220 if ((field.options != null) && field.options.packed) { |
| 221 if (options.length == 0) { |
| 222 options = " ["; |
| 223 } |
| 224 options = "${options}packed=true"; |
| 225 } |
| 226 if (options.length > 0) { |
| 227 options = "${options}]"; |
| 228 } |
| 229 if (_fieldComments) { |
| 230 out.println("// $label $type ${field.name}${sp}=${sp}" |
| 231 "${field.number}${options};"); |
| 232 } |
| 233 out.println( |
| 234 "${field.typeString} get ${field.externalFieldName}()${sp}=>" |
| 235 "${sp}g_(${field.number});"); |
| 236 if (field.single) { |
| 237 out.println("bool has${field.titlecaseFieldName}()${sp}=>" |
| 238 "${sp}h_(${field.number});"); |
| 239 } |
| 240 // } |
| 241 } |
| 242 } |
| 243 |
| 244 // Returns true if the message type has any required fields. If it doesn't, |
| 245 // we can optimize out calls to its isInitialized()/_findInvalidFields() |
| 246 // methods. |
| 247 // |
| 248 // already_seen is used to avoid checking the same type multiple times |
| 249 // (and also to protect against unbounded recursion). |
| 250 bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) { |
| 251 if (alreadySeen.contains(type._fqname)) { |
| 252 // The type is already in cache. This means that either: |
| 253 // a. The type has no required fields. |
| 254 // b. We are in the midst of checking if the type has required fields, |
| 255 // somewhere up the stack. In this case, we know that if the type |
| 256 // has any required fields, they'll be found when we return to it, |
| 257 // and the whole call to HasRequiredFields() will return true. |
| 258 // Therefore, we don't have to check if this type has required fields |
| 259 // here. |
| 260 return false; |
| 261 } |
| 262 alreadySeen.add(type._fqname); |
| 263 // If the type has extensions, an extension with message type could contain |
| 264 // required fields, so we have to be conservative and assume such an |
| 265 // extension exists. |
| 266 if (type._descriptor.extensionRange.length > 0) { |
| 267 return true; |
| 268 } |
| 269 |
| 270 for (ProtobufField field in type._fieldList) { |
| 271 if (field.required) { |
| 272 return true; |
| 273 } |
| 274 if (field.message) { |
| 275 ProtobufContainer messageType = _context[field.typeName]; |
| 276 if (messageType != null && messageType is MessageGenerator) { |
| 277 if (_hasRequiredFields(messageType, alreadySeen)) { |
| 278 return true; |
| 279 } |
| 280 } |
| 281 } |
| 282 } |
| 283 return false; |
| 284 } |
| 285 |
| 286 void generateFieldsAccessorsMutators(IndentingWriter out) { |
| 287 for (ProtobufField field in _fieldList) { |
| 288 // |
| 289 // Disable code size optimization for now until dart2js code generation |
| 290 // is better able to handle 'noSuchMethod' |
| 291 // |
| 292 |
| 293 // if (optimizeFor == GoogleProtobuf_FileOptions_OptimizeMode.SPEED) { |
| 294 if (_blankLines) { |
| 295 out.blankLine(); |
| 296 } |
| 297 out.println("${field.typeString} get ${field.externalFieldName}()" |
| 298 "${sp}=>${sp}g_(${field.number});"); |
| 299 if (field.single) { |
| 300 out.println("void set ${field.externalFieldName}" |
| 301 "(${field.typeString} v)${sp}" |
| 302 "{${sp}s_(${field.number},${sp}v);${sp}}"); |
| 303 out.println("bool has${field.titlecaseFieldName}()${sp}=>" |
| 304 "${sp}h_(${field.number});"); |
| 305 out.println("void clear${field.titlecaseFieldName}()${sp}=>" |
| 306 "${sp}c_(${field.number});"); |
| 307 } |
| 308 // } |
| 309 } |
| 310 } |
| 311 } |
OLD | NEW |