/// <summary> /// Look up and cross-link all field types etc. /// </summary> internal void CrossLink() { if (Proto.HasExtendee) { IDescriptor extendee = File.DescriptorPool.LookupSymbol(Proto.Extendee, this); if (!(extendee is MessageDescriptor)) { throw new DescriptorValidationException(this, "\"" + Proto.Extendee + "\" is not a message type."); } containingType = (MessageDescriptor)extendee; if (!containingType.IsExtensionNumber(FieldNumber)) { throw new DescriptorValidationException(this, "\"" + containingType.FullName + "\" does not declare " + FieldNumber + " as an extension number."); } } if (Proto.HasTypeName) { IDescriptor typeDescriptor = File.DescriptorPool.LookupSymbol(Proto.TypeName, this); if (!Proto.HasType) { // Choose field type based on symbol. if (typeDescriptor is MessageDescriptor) { fieldType = FieldType.Message; mappedType = MappedType.Message; } else if (typeDescriptor is EnumDescriptor) { fieldType = FieldType.Enum; mappedType = MappedType.Enum; } else { throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not a type."); } } if (MappedType == MappedType.Message) { if (!(typeDescriptor is MessageDescriptor)) { throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not a message type."); } messageType = (MessageDescriptor)typeDescriptor; if (Proto.HasDefaultValue) { throw new DescriptorValidationException(this, "Messages can't have default values."); } } else if (MappedType == Descriptors.MappedType.Enum) { if (!(typeDescriptor is EnumDescriptor)) { throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not an enum type."); } enumType = (EnumDescriptor)typeDescriptor; } else { throw new DescriptorValidationException(this, "Field with primitive type has type_name."); } } else { if (MappedType == MappedType.Message || MappedType == MappedType.Enum) { throw new DescriptorValidationException(this, "Field with message or enum type missing type_name."); } } // We don't attempt to parse the default value until here because for // enums we need the enum type's descriptor. if (Proto.HasDefaultValue) { if (IsRepeated) { throw new DescriptorValidationException(this, "Repeated fields cannot have default values."); } try { switch (FieldType) { case FieldType.Int32: case FieldType.SInt32: case FieldType.SFixed32: defaultValue = TextFormat.ParseInt32(Proto.DefaultValue); break; case FieldType.UInt32: case FieldType.Fixed32: defaultValue = TextFormat.ParseUInt32(Proto.DefaultValue); break; case FieldType.Int64: case FieldType.SInt64: case FieldType.SFixed64: defaultValue = TextFormat.ParseInt64(Proto.DefaultValue); break; case FieldType.UInt64: case FieldType.Fixed64: defaultValue = TextFormat.ParseUInt64(Proto.DefaultValue); break; case FieldType.Float: defaultValue = TextFormat.ParseFloat(Proto.DefaultValue); break; case FieldType.Double: defaultValue = TextFormat.ParseDouble(Proto.DefaultValue); break; case FieldType.Bool: if (Proto.DefaultValue == "true") { defaultValue = true; } else if (Proto.DefaultValue == "false") { defaultValue = false; } else { throw new FormatException("Boolean values must be \"true\" or \"false\""); } break; case FieldType.String: defaultValue = Proto.DefaultValue; break; case FieldType.Bytes: try { defaultValue = TextFormat.UnescapeBytes(Proto.DefaultValue); } catch (FormatException e) { throw new DescriptorValidationException(this, "Couldn't parse default value: " + e.Message); } break; case FieldType.Enum: defaultValue = enumType.FindValueByName(Proto.DefaultValue); if (defaultValue == null) { throw new DescriptorValidationException(this, "Unknown enum default value: \"" + Proto.DefaultValue + "\""); } break; case FieldType.Message: case FieldType.Group: throw new DescriptorValidationException(this, "Message type had default value."); } } catch (FormatException e) { DescriptorValidationException validationException = new DescriptorValidationException(this, "Could not parse default value: \"" + Proto.DefaultValue + "\"", e); throw validationException; } } else { // Determine the default default for this field. if (IsRepeated) { defaultValue = Lists <object> .Empty; } else { switch (MappedType) { case MappedType.Enum: // We guarantee elsewhere that an enum type always has at least // one possible value. defaultValue = enumType.Values[0]; break; case MappedType.Message: defaultValue = null; break; default: defaultValue = GetDefaultValueForMappedType(MappedType); break; } } } if (!IsExtension) { File.DescriptorPool.AddFieldByNumber(this); } if (containingType != null && containingType.Options.MessageSetWireFormat) { if (IsExtension) { if (!IsOptional || FieldType != FieldType.Message) { throw new DescriptorValidationException(this, "Extensions of MessageSets must be optional messages."); } } else { throw new DescriptorValidationException(this, "MessageSets cannot have fields, only extensions."); } } }