| // 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 ProtobufField { |
| static final RegExp HEX_LITERAL_REGEX = |
| new RegExp(r'^0x[0-9a-f]+$', multiLine: false, caseSensitive: false); |
| static final RegExp INTEGER_LITERAL_REGEX = new RegExp(r'^[+-]?[0-9]+$'); |
| static final RegExp DECIMAL_LITERAL_REGEX_A = |
| new RegExp(r'^[+-]?([0-9]*)\.[0-9]+(e[+-]?[0-9]+)?$', |
| multiLine: false, caseSensitive: false); |
| static final RegExp DECIMAL_LITERAL_REGEX_B = |
| new RegExp(r'^[+-]?[0-9]+e[+-]?[0-9]+$', multiLine: false, |
| caseSensitive: false); |
| |
| final FieldDescriptorProto _field; |
| final ProtobufContainer parent; |
| final GenerationContext context; |
| final String fqname; |
| final String typePackage; |
| final String baseType; |
| final String typeString; |
| final String prefixedBaseType; |
| final String prefixedTypeString; |
| final String codedStreamType; |
| final bool repeats; |
| final String initialization; |
| final String prefixedInitialization; |
| final bool required; |
| // True if the field is to be encoded with [packed=true] encoding. |
| final bool packed; |
| // True if the fields's type can handle [packed=true] encoding. |
| final bool packable; |
| |
| bool get single => !repeats; |
| |
| bool get group => type == FieldDescriptorProto_Type.TYPE_GROUP; |
| bool get message => type == FieldDescriptorProto_Type.TYPE_MESSAGE; |
| bool get enm => type == FieldDescriptorProto_Type.TYPE_ENUM; |
| bool get primitive => !group && !message; |
| |
| bool get hasInitialization => initialization != null; |
| |
| bool get optional => !required; // includes repeated |
| |
| // Delegate methods. |
| String get name => _field.name; |
| int get number => _field.number; |
| FieldDescriptorProto_Label get label => _field.label; |
| FieldDescriptorProto_Type get type => _field.type; |
| FieldOptions get options => _field.options; |
| String get typeName => _field.typeName; |
| |
| String baseTypeForPackage(String package) => |
| package == typePackage ? baseType : prefixedBaseType; |
| String typeStringForPackage(String package) => |
| package == typePackage ? typeString : prefixedTypeString; |
| String initializationForPackage(String package) => |
| package == typePackage ? initialization : prefixedInitialization; |
| |
| String get shortTypeName { |
| String prefix; |
| if (required) { |
| prefix = 'Q'; |
| } else if (packed) { |
| prefix = 'K'; |
| } else if (repeats) { |
| prefix = 'P'; |
| } else if (optional) { |
| prefix = 'O'; |
| } else { |
| throw '$this'; |
| } |
| switch (codedStreamType.toUpperCase()) { |
| case 'BOOL': return '${prefix}B'; |
| case 'BYTES': return '${prefix}Y'; |
| case 'STRING': return '${prefix}S'; |
| case 'FLOAT': return '${prefix}F'; |
| case 'DOUBLE': return '${prefix}D'; |
| case 'ENUM': return '${prefix}E'; |
| case 'GROUP': return '${prefix}G'; |
| case 'INT32': return '${prefix}3'; |
| case 'INT64': return '${prefix}6'; |
| case 'UINT32': return '${prefix}U3'; |
| case 'UINT64': return '${prefix}U6'; |
| case 'SINT32': return '${prefix}S3'; |
| case 'SINT64': return '${prefix}S6'; |
| case 'FIXED32': return '${prefix}F3'; |
| case 'FIXED64': return '${prefix}F6'; |
| case 'SFIXED32': return '${prefix}SF3'; |
| case 'SFIXED64': return '${prefix}SF6'; |
| case 'MESSAGE': return '${prefix}M'; |
| } |
| throw 'Unknown type'; |
| } |
| |
| ProtobufField._( |
| field, parent, this.context, this.typePackage, this.baseType, |
| this.typeString, this.prefixedBaseType, this.prefixedTypeString, |
| this.codedStreamType, this.repeats, |
| this.initialization, this.prefixedInitialization, this.required, |
| this.packed, this.packable) : |
| this._field = field, |
| this.parent = parent, |
| fqname = '${parent.fqname}.${field.name}'; |
| |
| |
| factory ProtobufField(FieldDescriptorProto field, |
| ProtobufContainer parent, |
| GenerationContext context) { |
| bool required = field.label == FieldDescriptorProto_Label.LABEL_REQUIRED; |
| bool repeats = field.label == FieldDescriptorProto_Label.LABEL_REPEATED; |
| bool packed = false; |
| |
| var write; |
| if (repeats) { |
| packed = field.options == null ? false : field.options.packed; |
| write = (String typeString) => 'List<$typeString>'; |
| } else { |
| write = (String typeString) => typeString; |
| } |
| |
| String typePackage = ''; |
| String baseType; |
| String typeString; |
| String prefixedBaseType; |
| String prefixedTypeString; |
| bool packable = false; |
| String codedStreamType; |
| String initialization; |
| String prefixedInitialization; |
| switch (field.type) { |
| case FieldDescriptorProto_Type.TYPE_BOOL: |
| baseType = 'bool'; |
| typeString = write('bool'); |
| packable = true; |
| codedStreamType = 'Bool'; |
| if (!repeats) { |
| if (field.hasDefaultValue() && 'false' != field.defaultValue) { |
| initialization = '()${SP}=>${SP}${field.defaultValue}'; |
| } |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_FLOAT: |
| case FieldDescriptorProto_Type.TYPE_DOUBLE: |
| baseType = 'double'; |
| typeString = write('double'); |
| packable = true; |
| codedStreamType = |
| (field.type == FieldDescriptorProto_Type.TYPE_FLOAT) ? |
| 'Float' : 'Double'; |
| if (!repeats) { |
| if (field.hasDefaultValue() && |
| ('0.0' != field.defaultValue || '0' != field.defaultValue)) { |
| if (field.defaultValue == 'inf') { |
| initialization = '()${SP}=>${SP}double.INFINITY'; |
| } else if (field.defaultValue == '-inf') { |
| initialization = '()${SP}=>${SP}double.NEGATIVE_INFINITY'; |
| } else if (field.defaultValue == 'nan') { |
| initialization = '()${SP}=>${SP}double.NAN'; |
| } else if (HEX_LITERAL_REGEX.hasMatch(field.defaultValue)) { |
| initialization = '()${SP}=>${SP}(${field.defaultValue})' |
| '.toDouble()'; |
| } else if (INTEGER_LITERAL_REGEX.hasMatch(field.defaultValue)) { |
| initialization = '()${SP}=>${SP}${field.defaultValue}.0'; |
| } else if (DECIMAL_LITERAL_REGEX_A.hasMatch(field.defaultValue) |
| || DECIMAL_LITERAL_REGEX_B.hasMatch(field.defaultValue)) { |
| initialization = '()${SP}=>${SP}${field.defaultValue}'; |
| } else { |
| throw new InvalidDefaultValue.double( |
| field.name, field.defaultValue); |
| } |
| } |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_INT32: |
| case FieldDescriptorProto_Type.TYPE_UINT32: |
| case FieldDescriptorProto_Type.TYPE_SINT32: |
| case FieldDescriptorProto_Type.TYPE_FIXED32: |
| case FieldDescriptorProto_Type.TYPE_SFIXED32: |
| baseType = 'int'; |
| typeString = write('int'); |
| packable = true; |
| switch (field.type) { |
| case FieldDescriptorProto_Type.TYPE_INT32: |
| codedStreamType = 'Int32'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_UINT32: |
| codedStreamType = 'Uint32'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_SINT32: |
| codedStreamType = 'Sint32'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_FIXED32: |
| codedStreamType = 'Fixed32'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_SFIXED32: |
| codedStreamType = 'Sfixed32'; |
| break; |
| } |
| if (!repeats) { |
| if (field.hasDefaultValue() && '0' != field.defaultValue) { |
| initialization = '()${SP}=>${SP}${field.defaultValue}'; |
| } |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_INT64: |
| case FieldDescriptorProto_Type.TYPE_UINT64: |
| case FieldDescriptorProto_Type.TYPE_SINT64: |
| case FieldDescriptorProto_Type.TYPE_FIXED64: |
| case FieldDescriptorProto_Type.TYPE_SFIXED64: |
| baseType = 'Int64'; |
| typeString = write('Int64'); |
| packable = true; |
| switch (field.type) { |
| case FieldDescriptorProto_Type.TYPE_INT64: |
| codedStreamType = 'Int64'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_UINT64: |
| codedStreamType = 'Uint64'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_SINT64: |
| codedStreamType = 'Sint64'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_FIXED64: |
| codedStreamType = 'Fixed64'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_SFIXED64: |
| codedStreamType = 'Sfixed64'; |
| break; |
| } |
| if (!repeats) { |
| final defaultValue = field.hasDefaultValue() ? |
| field.defaultValue : '0'; |
| initialization = '()${SP}=>${SP}makeLongInt($defaultValue)'; |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_STRING: |
| baseType = 'String'; |
| typeString = write('String'); |
| codedStreamType = 'String'; |
| if (!repeats) { |
| if (field.hasDefaultValue() && !field.defaultValue.isEmpty) { |
| String defaultValue = field.defaultValue.replaceAll(r'$', r'\$'); |
| initialization = '()${SP}=>${SP}\'$defaultValue\''; |
| } |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_BYTES: |
| baseType = 'List<int>'; |
| typeString = write('List<int>'); |
| codedStreamType = 'Bytes'; |
| if (!repeats) { |
| if (field.hasDefaultValue() && !field.defaultValue.isEmpty) { |
| String byteList = field.defaultValue.codeUnits |
| .map((b) => '0x${b.toRadixString(16)}') |
| .join(','); |
| initialization = '()${SP}=>${SP}<int>[$byteList]'; |
| } |
| } |
| break; |
| case FieldDescriptorProto_Type.TYPE_GROUP: |
| ProtobufContainer groupType = context[field.typeName]; |
| if (groupType != null) { |
| typePackage = groupType.package; |
| baseType = groupType.classname; |
| typeString = write(groupType.classname); |
| if (groupType.packageImportPrefix.isNotEmpty) { |
| prefixedBaseType = groupType.packageImportPrefix + '.' + baseType; |
| } else { |
| prefixedBaseType = baseType; |
| } |
| prefixedTypeString = write(prefixedBaseType); |
| codedStreamType = 'Group'; |
| } else { |
| throw 'FAILURE: Unknown group type reference ${field.typeName}'; |
| } |
| initialization = '()${SP}=>${SP}new ${baseType}()'; |
| prefixedInitialization = '()${SP}=>${SP}new ${prefixedBaseType}()'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_MESSAGE: |
| ProtobufContainer messageType = context[field.typeName]; |
| if (messageType != null) { |
| typePackage = messageType.package; |
| baseType = messageType.classname; |
| typeString = write(baseType); |
| if (messageType.packageImportPrefix.isNotEmpty) { |
| prefixedBaseType = messageType.packageImportPrefix + '.' + baseType; |
| } else { |
| prefixedBaseType = baseType; |
| } |
| prefixedTypeString = write(prefixedBaseType); |
| codedStreamType = 'Message'; |
| } else { |
| throw 'FAILURE: Unknown message type reference ${field.typeName}'; |
| } |
| initialization = '()${SP}=>${SP}new ${baseType}()'; |
| prefixedInitialization = '()${SP}=>${SP}new ${prefixedBaseType}()'; |
| break; |
| case FieldDescriptorProto_Type.TYPE_ENUM: |
| EnumGenerator enumType = context[field.typeName]; |
| if (enumType != null) { |
| typePackage = enumType.package; |
| baseType = enumType.classname; |
| typeString = write(enumType.classname); |
| codedStreamType = 'Enum'; |
| if (enumType.packageImportPrefix.isNotEmpty) { |
| prefixedBaseType = enumType.packageImportPrefix + '.' + baseType; |
| } else { |
| prefixedBaseType = baseType; |
| } |
| prefixedTypeString = write(prefixedBaseType); |
| packable = true; |
| if (!repeats) { |
| if (field.hasDefaultValue() && !field.defaultValue.isEmpty) { |
| initialization = |
| '()${SP}=>${SP}${baseType}.${field.defaultValue}'; |
| prefixedInitialization = |
| '()${SP}=>${SP}${prefixedBaseType}.${field.defaultValue}'; |
| } else if (!enumType._canonicalValues.isEmpty) { |
| initialization = |
| '()${SP}=>${SP}${baseType}.' |
| '${enumType._canonicalValues[0].name}'; |
| prefixedInitialization = |
| '()${SP}=>${SP}${prefixedBaseType}.' |
| '${enumType._canonicalValues[0].name}'; |
| } |
| } |
| } else { |
| throw 'FAILURE: Unknown enum type reference ${field.typeName}'; |
| } |
| break; |
| default: |
| throw 'Unknown type ${field.type.name}'; |
| // No default -- should be an error. |
| } |
| |
| if (repeats) { |
| initialization = '()${SP}=>${SP}new PbList()'; |
| } |
| |
| if (prefixedBaseType == null) prefixedBaseType = baseType; |
| if (prefixedTypeString == null) prefixedTypeString = typeString; |
| if (prefixedInitialization == null) prefixedInitialization = initialization; |
| return new ProtobufField._( |
| field, parent, context, typePackage, baseType, typeString, |
| prefixedBaseType, prefixedTypeString, codedStreamType, repeats, |
| initialization, prefixedInitialization, required, packed, packable); |
| } |
| |
| // camelCase field name. |
| String get externalFieldName { |
| String name = titlecaseFieldName; |
| return '${name[0].toLowerCase()}${name.substring(1)}'; |
| } |
| |
| // TitleCase field name. |
| String get titlecaseFieldName { |
| String underscoresToCamelCase(String s) { |
| cap(s) => s.isEmpty ? s : '${s[0].toUpperCase()}${s.substring(1)}'; |
| return s.split('_').map(cap).join(''); |
| } |
| |
| // For groups, use capitalization of 'typeName' rather than 'name'. |
| if (codedStreamType == 'Group') { |
| String name = _field.typeName; |
| int index = name.lastIndexOf('.'); |
| if (index != -1) { |
| name = name.substring(index + 1); |
| } |
| return underscoresToCamelCase(name); |
| } |
| var name = context.options.fieldNameOverrides[fqname]; |
| return name != null ? name : underscoresToCamelCase(_field.name); |
| } |
| |
| int get wireType { |
| switch (_field.type) { |
| case FieldDescriptorProto_Type.TYPE_INT32: |
| case FieldDescriptorProto_Type.TYPE_INT64: |
| case FieldDescriptorProto_Type.TYPE_UINT32: |
| case FieldDescriptorProto_Type.TYPE_UINT64: |
| case FieldDescriptorProto_Type.TYPE_SINT32: |
| case FieldDescriptorProto_Type.TYPE_SINT64: |
| case FieldDescriptorProto_Type.TYPE_BOOL: |
| case FieldDescriptorProto_Type.TYPE_ENUM: |
| return 0; // Varint |
| case FieldDescriptorProto_Type.TYPE_DOUBLE: |
| case FieldDescriptorProto_Type.TYPE_FIXED64: |
| case FieldDescriptorProto_Type.TYPE_SFIXED64: |
| return 1; // 64-bit |
| case FieldDescriptorProto_Type.TYPE_STRING: |
| case FieldDescriptorProto_Type.TYPE_BYTES: |
| case FieldDescriptorProto_Type.TYPE_MESSAGE: |
| return 2; // Length-delimited |
| case FieldDescriptorProto_Type.TYPE_GROUP: |
| return 3; // Start group |
| case FieldDescriptorProto_Type.TYPE_FLOAT: |
| case FieldDescriptorProto_Type.TYPE_FIXED32: |
| case FieldDescriptorProto_Type.TYPE_SFIXED32: |
| return 5; // 32-bit |
| } |
| } |
| } |