| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of protoc; | 5 part of protoc; |
| 6 | 6 |
| 7 const String SP = ' '; | 7 const String SP = ' '; |
| 8 | 8 |
| 9 class MessageGenerator extends ProtobufContainer { | 9 class MessageGenerator extends ProtobufContainer { |
| 10 // List of Dart language reserved words in names which cannot be used in a | 10 // List of Dart language reserved words in names which cannot be used in a |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 'writeToBuffer', 'writeToCodedBufferWriter', | 24 'writeToBuffer', 'writeToCodedBufferWriter', |
| 25 'mergeFromCodedBufferReader', 'mergeFromBuffer', | 25 'mergeFromCodedBufferReader', 'mergeFromBuffer', |
| 26 'writeToJson', 'mergeFromJson', | 26 'writeToJson', 'mergeFromJson', |
| 27 'writeToJsonMap', 'mergeFromJsonMap', | 27 'writeToJsonMap', 'mergeFromJsonMap', |
| 28 'addExtension', 'getExtension', 'setExtension', | 28 'addExtension', 'getExtension', 'setExtension', |
| 29 'hasExtension', 'clearExtension', | 29 'hasExtension', 'clearExtension', |
| 30 'getField', 'setField', 'hasField', 'clearField', | 30 'getField', 'setField', 'hasField', 'clearField', |
| 31 'extensionsAreInitialized', 'mergeFromMessage', 'mergeUnknownFields', | 31 'extensionsAreInitialized', 'mergeFromMessage', 'mergeUnknownFields', |
| 32 '==', 'info_', 'GeneratedMessage', 'Object']; | 32 '==', 'info_', 'GeneratedMessage', 'Object']; |
| 33 | 33 |
| 34 // List of names that can't be used in a subclass that implements Map. | 34 // Returns the mixin for this message, or null if none. |
| 35 static final List<String> reservedNamesForMap = | 35 static PbMixin _getMixin(DescriptorProto desc, PbMixin defaultValue) { |
| 36 ['addAll', 'containsKey', 'containsValue', 'forEach', 'putIfAbsent', | 36 if (!desc.hasOptions()) return defaultValue; |
| 37 'remove', 'isEmpty', 'isNotEmpty', 'keys', 'length', 'values']; | 37 if (!desc.options.hasExtension(Dart_options.mixin)) return defaultValue; |
| 38 | 38 |
| 39 // This should match the extension in dart_options.proto. | 39 String name = desc.options.getExtension(Dart_options.mixin); |
| 40 static const int implementMapOption = 95333044; | 40 if (name.isEmpty) return null; // don't use a mixin (override any default) |
| 41 | 41 var mixin = findMixin(name); |
| 42 // Returns true if the implement_map option is turned on for the message. | 42 if (mixin == null) { |
| 43 static bool _shouldImplementMap(DescriptorProto desc, bool defaultValue) { | 43 throw("unknown mixin class: ${name}"); |
| 44 if (!desc.hasOptions()) return defaultValue; | 44 } |
| 45 | 45 return mixin; |
| 46 var val = desc.options.unknownFields.getField(implementMapOption); | |
| 47 if (val == null || val.length != 1) return defaultValue; | |
| 48 | |
| 49 return val.values[0] == 1; | |
| 50 } | 46 } |
| 51 | 47 |
| 52 final String classname; | 48 final String classname; |
| 53 final String fqname; | 49 final String fqname; |
| 54 final bool implementsMap; | 50 final PbMixin mixin; |
| 55 | 51 |
| 56 final ProtobufContainer _parent; | 52 final ProtobufContainer _parent; |
| 57 final GenerationContext _context; | 53 final GenerationContext _context; |
| 58 final DescriptorProto _descriptor; | 54 final DescriptorProto _descriptor; |
| 59 final List<EnumGenerator> _enumGenerators = <EnumGenerator>[]; | 55 final List<EnumGenerator> _enumGenerators = <EnumGenerator>[]; |
| 60 final List<ProtobufField> _fieldList = <ProtobufField>[]; | 56 final List<ProtobufField> _fieldList = <ProtobufField>[]; |
| 61 final List<MessageGenerator> _messageGenerators = <MessageGenerator>[]; | 57 final List<MessageGenerator> _messageGenerators = <MessageGenerator>[]; |
| 62 final List<ExtensionGenerator> _extensionGenerators = <ExtensionGenerator>[]; | 58 final List<ExtensionGenerator> _extensionGenerators = <ExtensionGenerator>[]; |
| 63 final Set<String> _methodNames = new Set<String>(); | 59 final Set<String> _methodNames = new Set<String>(); |
| 64 | 60 |
| 65 MessageGenerator( | 61 MessageGenerator( |
| 66 DescriptorProto descriptor, ProtobufContainer parent, this._context, | 62 DescriptorProto descriptor, ProtobufContainer parent, this._context, |
| 67 bool implementMapByDefault) | 63 PbMixin defaultMixin) |
| 68 : _descriptor = descriptor, | 64 : _descriptor = descriptor, |
| 69 _parent = parent, | 65 _parent = parent, |
| 70 classname = (parent.classname == '') ? | 66 classname = (parent.classname == '') ? |
| 71 descriptor.name : '${parent.classname}_${descriptor.name}', | 67 descriptor.name : '${parent.classname}_${descriptor.name}', |
| 72 fqname = (parent == null || parent.fqname == null) ? descriptor.name : | 68 fqname = (parent == null || parent.fqname == null) ? descriptor.name : |
| 73 (parent.fqname == '.' ? | 69 (parent.fqname == '.' ? |
| 74 '.${descriptor.name}' : '${parent.fqname}.${descriptor.name}'), | 70 '.${descriptor.name}' : '${parent.fqname}.${descriptor.name}'), |
| 75 implementsMap = _shouldImplementMap(descriptor, implementMapByDefault) { | 71 mixin = _getMixin(descriptor, defaultMixin) { |
| 76 _context.register(this); | 72 _context.register(this); |
| 77 | 73 |
| 78 for (EnumDescriptorProto e in _descriptor.enumType) { | 74 for (EnumDescriptorProto e in _descriptor.enumType) { |
| 79 _enumGenerators.add(new EnumGenerator(e, this, _context)); | 75 _enumGenerators.add(new EnumGenerator(e, this, _context)); |
| 80 } | 76 } |
| 81 | 77 |
| 82 for (DescriptorProto n in _descriptor.nestedType) { | 78 for (DescriptorProto n in _descriptor.nestedType) { |
| 83 _messageGenerators.add( | 79 _messageGenerators.add( |
| 84 new MessageGenerator(n, this, _context, implementMapByDefault)); | 80 new MessageGenerator(n, this, _context, defaultMixin)); |
| 85 } | 81 } |
| 86 | 82 |
| 87 for (FieldDescriptorProto x in _descriptor.extension) { | 83 for (FieldDescriptorProto x in _descriptor.extension) { |
| 88 _extensionGenerators.add(new ExtensionGenerator(x, this, _context)); | 84 _extensionGenerators.add(new ExtensionGenerator(x, this, _context)); |
| 89 } | 85 } |
| 90 } | 86 } |
| 91 | 87 |
| 92 String get package => _parent.package; | 88 String get package => _parent.package; |
| 93 | 89 |
| 94 bool get needsMapMixinImport { | 90 /// Adds all mixins used in this message and any submessages. |
| 95 if (implementsMap) return true; | 91 void addMixinsTo(Set<PbMixin> output) { |
| 96 | 92 if (mixin != null) { |
| 93 output.addAll(mixin.findMixinsToApply()); |
| 94 } |
| 97 for (var m in _messageGenerators) { | 95 for (var m in _messageGenerators) { |
| 98 if (m.implementsMap) return true; | 96 m.addMixinsTo(output); |
| 99 } | 97 } |
| 100 | |
| 101 return false; | |
| 102 } | 98 } |
| 103 | 99 |
| 104 void initializeFields() { | 100 void initializeFields() { |
| 105 _fieldList.clear(); | 101 _fieldList.clear(); |
| 106 for (FieldDescriptorProto field in _descriptor.field) { | 102 for (FieldDescriptorProto field in _descriptor.field) { |
| 107 _fieldList.add(new ProtobufField(field, this, _context)); | 103 _fieldList.add(new ProtobufField(field, this, _context)); |
| 108 } | 104 } |
| 109 for (MessageGenerator m in _messageGenerators) { | 105 for (MessageGenerator m in _messageGenerators) { |
| 110 m.initializeFields(); | 106 m.initializeFields(); |
| 111 } | 107 } |
| 112 } | 108 } |
| 113 | 109 |
| 114 void generate(IndentingWriter out) { | 110 void generate(IndentingWriter out) { |
| 115 _methodNames.clear(); | 111 _methodNames.clear(); |
| 116 _methodNames.addAll(reservedWords); | 112 _methodNames.addAll(reservedWords); |
| 117 _methodNames.addAll(reservedNames); | 113 _methodNames.addAll(reservedNames); |
| 118 | 114 |
| 119 if (implementsMap) { | 115 if (mixin != null) { |
| 120 _methodNames.addAll(reservedNamesForMap); | 116 _methodNames.addAll(mixin.findReservedNames()); |
| 121 } | 117 } |
| 122 | 118 |
| 123 for (EnumGenerator e in _enumGenerators) { | 119 for (EnumGenerator e in _enumGenerators) { |
| 124 e.generate(out); | 120 e.generate(out); |
| 125 } | 121 } |
| 126 | 122 |
| 127 for (MessageGenerator m in _messageGenerators) { | 123 for (MessageGenerator m in _messageGenerators) { |
| 128 m.generate(out); | 124 m.generate(out); |
| 129 } | 125 } |
| 130 | 126 |
| 131 var implClause = ""; | 127 var mixinClause = ''; |
| 132 if (implementsMap) { | 128 if (mixin != null) { |
| 133 implClause = " with MapMixin"; | 129 var mixinNames = mixin.findMixinsToApply().map((m) => m.name); |
| 130 mixinClause = ' with ${mixinNames.join(", ")}'; |
| 134 } | 131 } |
| 135 | 132 |
| 136 out.addBlock('class ${classname} extends GeneratedMessage${implClause} {', | 133 out.addBlock('class ${classname} extends GeneratedMessage${mixinClause} {', |
| 137 '}', () | 134 '}', () |
| 138 { | 135 { |
| 139 out.addBlock( | 136 out.addBlock( |
| 140 'static final BuilderInfo _i = new BuilderInfo(\'${classname}\')', | 137 'static final BuilderInfo _i = new BuilderInfo(\'${classname}\')', |
| 141 ';', () { | 138 ';', () { |
| 142 for (ProtobufField field in _fieldList) { | 139 for (ProtobufField field in _fieldList) { |
| 143 String type = field.shortTypeName; | 140 String type = field.shortTypeName; |
| 144 String fieldType = field.baseTypeForPackage(package); | 141 String fieldType = field.baseTypeForPackage(package); |
| 145 | 142 |
| 146 String makeDefault = null; | 143 String makeDefault = null; |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 '${SP}new ${classname}()..mergeFromMessage(this);'); | 213 '${SP}new ${classname}()..mergeFromMessage(this);'); |
| 217 | 214 |
| 218 out.println('BuilderInfo get info_${SP}=>${SP}_i;'); | 215 out.println('BuilderInfo get info_${SP}=>${SP}_i;'); |
| 219 | 216 |
| 220 // Factory functions which can be used as default value closures. | 217 // Factory functions which can be used as default value closures. |
| 221 out.println('static ${classname}${SP}create()${SP}=>' | 218 out.println('static ${classname}${SP}create()${SP}=>' |
| 222 '${SP}new ${classname}();'); | 219 '${SP}new ${classname}();'); |
| 223 out.println('static PbList<${classname}>${SP}createRepeated()${SP}=>' | 220 out.println('static PbList<${classname}>${SP}createRepeated()${SP}=>' |
| 224 '${SP}new PbList<${classname}>();'); | 221 '${SP}new PbList<${classname}>();'); |
| 225 | 222 |
| 226 | |
| 227 if (implementsMap) { | |
| 228 // clear() is inherited from GeneratedMessage. | |
| 229 // Other map operations are implemented by MapMixin. | |
| 230 out.println(''' | |
| 231 @override | |
| 232 operator [] (key) { | |
| 233 if (key is !String) return null; | |
| 234 if (!key.contains(".")) { | |
| 235 var tag = getTagNumber(key); | |
| 236 if (tag == null) return null; | |
| 237 return getField(tag); | |
| 238 } | |
| 239 | |
| 240 var keys = key.split('.'); | |
| 241 var item = this; | |
| 242 for (var key in keys) { | |
| 243 if (item is !GeneratedMessage) return null; | |
| 244 var tag = item.getTagNumber(key); | |
| 245 if (tag == null) return null; | |
| 246 item = item.getField(tag); | |
| 247 } | |
| 248 | |
| 249 return item; | |
| 250 } | |
| 251 | |
| 252 @override | |
| 253 operator []= (String key, val) { | |
| 254 if (!key.contains(".")) { | |
| 255 var tag = _mustGetTagNumber(this, key); | |
| 256 setField(tag, val); | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 var keys = key.split('.'); | |
| 261 var lastKey = keys.removeLast(); | |
| 262 var item = this; | |
| 263 for (var key in keys) { | |
| 264 var tag = _mustGetTagNumber(item, key); | |
| 265 item = item.getField(tag); | |
| 266 if (item is !GeneratedMessage) { | |
| 267 throw new ArgumentError( | |
| 268 "field '\${key}' in \${info._messageName} isn't a GeneratedMessage:"); | |
| 269 } | |
| 270 } | |
| 271 var tag = _mustGetTagNumber(item, lastKey); | |
| 272 item.setField(tag, val); | |
| 273 } | |
| 274 | |
| 275 _mustGetTagNumber(GeneratedMessage msg, String key) { | |
| 276 var tag = msg.getTagNumber(key); | |
| 277 if (tag == null) { | |
| 278 throw new ArgumentError( | |
| 279 "field '\${key}' not found in \${msg.info_.messageName}"); | |
| 280 } | |
| 281 return tag; | |
| 282 } | |
| 283 | |
| 284 @override | |
| 285 get keys => info_.byName.keys; | |
| 286 | |
| 287 @override | |
| 288 get length => info_.byName.length; | |
| 289 | |
| 290 remove(key) { | |
| 291 throw new UnsupportedError("remove() not supported by \${info_.messageName}"); | |
| 292 } | |
| 293 '''); | |
| 294 } | |
| 295 | |
| 296 generateFieldsAccessorsMutators(out); | 223 generateFieldsAccessorsMutators(out); |
| 297 }); | 224 }); |
| 298 out.println(); | 225 out.println(); |
| 299 } | 226 } |
| 300 | 227 |
| 301 // Returns true if the message type has any required fields. If it doesn't, | 228 // Returns true if the message type has any required fields. If it doesn't, |
| 302 // we can optimize out calls to its isInitialized()/_findInvalidFields() | 229 // we can optimize out calls to its isInitialized()/_findInvalidFields() |
| 303 // methods. | 230 // methods. |
| 304 // | 231 // |
| 305 // already_seen is used to avoid checking the same type multiple times | 232 // already_seen is used to avoid checking the same type multiple times |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 '(${fieldTypeString} v)${SP}' | 298 '(${fieldTypeString} v)${SP}' |
| 372 '{${SP}setField(${field.number},${SP}v);${SP}}'); | 299 '{${SP}setField(${field.number},${SP}v);${SP}}'); |
| 373 out.println('bool $hasIdentifier()${SP}=>' | 300 out.println('bool $hasIdentifier()${SP}=>' |
| 374 '${SP}hasField(${field.number});'); | 301 '${SP}hasField(${field.number});'); |
| 375 out.println('void $clearIdentifier()${SP}=>' | 302 out.println('void $clearIdentifier()${SP}=>' |
| 376 '${SP}clearField(${field.number});'); | 303 '${SP}clearField(${field.number});'); |
| 377 } | 304 } |
| 378 } | 305 } |
| 379 } | 306 } |
| 380 } | 307 } |
| OLD | NEW |