PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs

https://code.google.com/p/protobuf-csharp-port/
C# | 671 lines | 508 code | 42 blank | 121 comment | 77 complexity | 26543288fc9256916a78db9ff5c924b0 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, GPL-2.0
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://github.com/jskeet/dotnet-protobufs/
  4. // Original C++/Java/Python code:
  5. // http://code.google.com/p/protobuf/
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are
  9. // met:
  10. //
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Google Inc. nor the names of its
  18. // contributors may be used to endorse or promote products derived from
  19. // this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. using System;
  33. using System.Collections.Generic;
  34. using System.Reflection;
  35. using Google.ProtocolBuffers.Collections;
  36. using Google.ProtocolBuffers.DescriptorProtos;
  37. namespace Google.ProtocolBuffers.Descriptors
  38. {
  39. /// <summary>
  40. /// Descriptor for a field or extension within a message in a .proto file.
  41. /// </summary>
  42. public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>,
  43. IComparable<FieldDescriptor>, IFieldDescriptorLite
  44. {
  45. private readonly MessageDescriptor extensionScope;
  46. private EnumDescriptor enumType;
  47. private MessageDescriptor messageType;
  48. private MessageDescriptor containingType;
  49. private object defaultValue;
  50. private FieldType fieldType;
  51. private MappedType mappedType;
  52. private CSharpFieldOptions csharpFieldOptions;
  53. private readonly object optionsLock = new object();
  54. internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
  55. MessageDescriptor parent, int index, bool isExtension)
  56. : base(proto, file, ComputeFullName(file, parent, proto.Name), index)
  57. {
  58. if (proto.HasType)
  59. {
  60. fieldType = GetFieldTypeFromProtoType(proto.Type);
  61. mappedType = FieldTypeToMappedTypeMap[fieldType];
  62. }
  63. if (FieldNumber <= 0)
  64. {
  65. throw new DescriptorValidationException(this,
  66. "Field numbers must be positive integers.");
  67. }
  68. if (isExtension)
  69. {
  70. if (!proto.HasExtendee)
  71. {
  72. throw new DescriptorValidationException(this,
  73. "FieldDescriptorProto.Extendee not set for extension field.");
  74. }
  75. containingType = null; // Will be filled in when cross-linking
  76. if (parent != null)
  77. {
  78. extensionScope = parent;
  79. }
  80. else
  81. {
  82. extensionScope = null;
  83. }
  84. }
  85. else
  86. {
  87. if (proto.HasExtendee)
  88. {
  89. throw new DescriptorValidationException(this,
  90. "FieldDescriptorProto.Extendee set for non-extension field.");
  91. }
  92. containingType = parent;
  93. extensionScope = null;
  94. }
  95. file.DescriptorPool.AddSymbol(this);
  96. }
  97. private CSharpFieldOptions BuildOrFakeCSharpOptions()
  98. {
  99. // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
  100. if (File.Proto.Name == "google/protobuf/csharp_options.proto")
  101. {
  102. if (Name == "csharp_field_options")
  103. {
  104. return new CSharpFieldOptions.Builder {PropertyName = "CSharpFieldOptions"}.Build();
  105. }
  106. if (Name == "csharp_file_options")
  107. {
  108. return new CSharpFieldOptions.Builder {PropertyName = "CSharpFileOptions"}.Build();
  109. }
  110. }
  111. CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder();
  112. if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions))
  113. {
  114. builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions));
  115. }
  116. if (!builder.HasPropertyName)
  117. {
  118. string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name;
  119. string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName);
  120. if (propertyName == ContainingType.Name)
  121. {
  122. propertyName += "_";
  123. }
  124. builder.PropertyName = propertyName;
  125. }
  126. return builder.Build();
  127. }
  128. /// <summary>
  129. /// Maps a field type as included in the .proto file to a FieldType.
  130. /// </summary>
  131. private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.Types.Type type)
  132. {
  133. switch (type)
  134. {
  135. case FieldDescriptorProto.Types.Type.TYPE_DOUBLE:
  136. return FieldType.Double;
  137. case FieldDescriptorProto.Types.Type.TYPE_FLOAT:
  138. return FieldType.Float;
  139. case FieldDescriptorProto.Types.Type.TYPE_INT64:
  140. return FieldType.Int64;
  141. case FieldDescriptorProto.Types.Type.TYPE_UINT64:
  142. return FieldType.UInt64;
  143. case FieldDescriptorProto.Types.Type.TYPE_INT32:
  144. return FieldType.Int32;
  145. case FieldDescriptorProto.Types.Type.TYPE_FIXED64:
  146. return FieldType.Fixed64;
  147. case FieldDescriptorProto.Types.Type.TYPE_FIXED32:
  148. return FieldType.Fixed32;
  149. case FieldDescriptorProto.Types.Type.TYPE_BOOL:
  150. return FieldType.Bool;
  151. case FieldDescriptorProto.Types.Type.TYPE_STRING:
  152. return FieldType.String;
  153. case FieldDescriptorProto.Types.Type.TYPE_GROUP:
  154. return FieldType.Group;
  155. case FieldDescriptorProto.Types.Type.TYPE_MESSAGE:
  156. return FieldType.Message;
  157. case FieldDescriptorProto.Types.Type.TYPE_BYTES:
  158. return FieldType.Bytes;
  159. case FieldDescriptorProto.Types.Type.TYPE_UINT32:
  160. return FieldType.UInt32;
  161. case FieldDescriptorProto.Types.Type.TYPE_ENUM:
  162. return FieldType.Enum;
  163. case FieldDescriptorProto.Types.Type.TYPE_SFIXED32:
  164. return FieldType.SFixed32;
  165. case FieldDescriptorProto.Types.Type.TYPE_SFIXED64:
  166. return FieldType.SFixed64;
  167. case FieldDescriptorProto.Types.Type.TYPE_SINT32:
  168. return FieldType.SInt32;
  169. case FieldDescriptorProto.Types.Type.TYPE_SINT64:
  170. return FieldType.SInt64;
  171. default:
  172. throw new ArgumentException("Invalid type specified");
  173. }
  174. }
  175. /// <summary>
  176. /// Returns the default value for a mapped type.
  177. /// </summary>
  178. private static object GetDefaultValueForMappedType(MappedType type)
  179. {
  180. switch (type)
  181. {
  182. case MappedType.Int32:
  183. return 0;
  184. case MappedType.Int64:
  185. return (long) 0;
  186. case MappedType.UInt32:
  187. return (uint) 0;
  188. case MappedType.UInt64:
  189. return (ulong) 0;
  190. case MappedType.Single:
  191. return (float) 0;
  192. case MappedType.Double:
  193. return (double) 0;
  194. case MappedType.Boolean:
  195. return false;
  196. case MappedType.String:
  197. return "";
  198. case MappedType.ByteString:
  199. return ByteString.Empty;
  200. case MappedType.Message:
  201. return null;
  202. case MappedType.Enum:
  203. return null;
  204. default:
  205. throw new ArgumentException("Invalid type specified");
  206. }
  207. }
  208. public bool IsRequired
  209. {
  210. get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REQUIRED; }
  211. }
  212. public bool IsOptional
  213. {
  214. get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_OPTIONAL; }
  215. }
  216. public bool IsRepeated
  217. {
  218. get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
  219. }
  220. public bool IsPacked
  221. {
  222. get { return Proto.Options.Packed; }
  223. }
  224. /// <valule>
  225. /// Indicates whether or not the field had an explicitly-defined default value.
  226. /// </value>
  227. public bool HasDefaultValue
  228. {
  229. get { return Proto.HasDefaultValue; }
  230. }
  231. /// <value>
  232. /// The field's default value. Valid for all types except messages
  233. /// and groups. For all other types, the object returned is of the
  234. /// same class that would be returned by IMessage[this].
  235. /// For repeated fields this will always be an empty immutable list compatible with IList[object].
  236. /// For message fields it will always be null. For singular values, it will depend on the descriptor.
  237. /// </value>
  238. public object DefaultValue
  239. {
  240. get
  241. {
  242. if (MappedType == MappedType.Message)
  243. {
  244. throw new InvalidOperationException(
  245. "FieldDescriptor.DefaultValue called on an embedded message field.");
  246. }
  247. return defaultValue;
  248. }
  249. }
  250. /// <value>
  251. /// Indicates whether or not this field is an extension.
  252. /// </value>
  253. public bool IsExtension
  254. {
  255. get { return Proto.HasExtendee; }
  256. }
  257. /*
  258. * Get the field's containing type. For extensions, this is the type being
  259. * extended, not the location where the extension was defined. See
  260. * {@link #getExtensionScope()}.
  261. */
  262. /// <summary>
  263. /// Get the field's containing type. For extensions, this is the type being
  264. /// extended, not the location where the extension was defined. See
  265. /// <see cref="ExtensionScope" />.
  266. /// </summary>
  267. public MessageDescriptor ContainingType
  268. {
  269. get { return containingType; }
  270. }
  271. /// <summary>
  272. /// Returns the C#-specific options for this field descriptor. This will always be
  273. /// completely filled in.
  274. /// </summary>
  275. public CSharpFieldOptions CSharpOptions
  276. {
  277. get
  278. {
  279. lock (optionsLock)
  280. {
  281. if (csharpFieldOptions == null)
  282. {
  283. csharpFieldOptions = BuildOrFakeCSharpOptions();
  284. }
  285. }
  286. return csharpFieldOptions;
  287. }
  288. }
  289. /// <summary>
  290. /// For extensions defined nested within message types, gets
  291. /// the outer type. Not valid for non-extension fields.
  292. /// </summary>
  293. /// <example>
  294. /// <code>
  295. /// message Foo {
  296. /// extensions 1000 to max;
  297. /// }
  298. /// extend Foo {
  299. /// optional int32 baz = 1234;
  300. /// }
  301. /// message Bar {
  302. /// extend Foo {
  303. /// optional int32 qux = 4321;
  304. /// }
  305. /// }
  306. /// </code>
  307. /// The containing type for both <c>baz</c> and <c>qux</c> is <c>Foo</c>.
  308. /// However, the extension scope for <c>baz</c> is <c>null</c> while
  309. /// the extension scope for <c>qux</c> is <c>Bar</c>.
  310. /// </example>
  311. public MessageDescriptor ExtensionScope
  312. {
  313. get
  314. {
  315. if (!IsExtension)
  316. {
  317. throw new InvalidOperationException("This field is not an extension.");
  318. }
  319. return extensionScope;
  320. }
  321. }
  322. public MappedType MappedType
  323. {
  324. get { return mappedType; }
  325. }
  326. public FieldType FieldType
  327. {
  328. get { return fieldType; }
  329. }
  330. public bool IsCLSCompliant
  331. {
  332. get
  333. {
  334. return mappedType != MappedType.UInt32 &&
  335. mappedType != MappedType.UInt64 &&
  336. !NameHelpers.UnderscoresToPascalCase(Name).StartsWith("_");
  337. }
  338. }
  339. public int FieldNumber
  340. {
  341. get { return Proto.Number; }
  342. }
  343. /// <summary>
  344. /// Compares this descriptor with another one, ordering in "canonical" order
  345. /// which simply means ascending order by field number. <paramref name="other"/>
  346. /// must be a field of the same type, i.e. the <see cref="ContainingType"/> of
  347. /// both fields must be the same.
  348. /// </summary>
  349. public int CompareTo(FieldDescriptor other)
  350. {
  351. if (other.containingType != containingType)
  352. {
  353. throw new ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " +
  354. "for fields of the same message type.");
  355. }
  356. return FieldNumber - other.FieldNumber;
  357. }
  358. /// <summary>
  359. /// Compares this descriptor with another one, ordering in "canonical" order
  360. /// which simply means ascending order by field number. <paramref name="other"/>
  361. /// must be a field of the same type, i.e. the <see cref="ContainingType"/> of
  362. /// both fields must be the same.
  363. /// </summary>
  364. public int CompareTo(IFieldDescriptorLite other)
  365. {
  366. return FieldNumber - other.FieldNumber;
  367. }
  368. IEnumLiteMap IFieldDescriptorLite.EnumType
  369. {
  370. get { return EnumType; }
  371. }
  372. bool IFieldDescriptorLite.MessageSetWireFormat
  373. {
  374. get { return ContainingType.Options.MessageSetWireFormat; }
  375. }
  376. /// <summary>
  377. /// For enum fields, returns the field's type.
  378. /// </summary>
  379. public EnumDescriptor EnumType
  380. {
  381. get
  382. {
  383. if (MappedType != MappedType.Enum)
  384. {
  385. throw new InvalidOperationException("EnumType is only valid for enum fields.");
  386. }
  387. return enumType;
  388. }
  389. }
  390. /// <summary>
  391. /// For embedded message and group fields, returns the field's type.
  392. /// </summary>
  393. public MessageDescriptor MessageType
  394. {
  395. get
  396. {
  397. if (MappedType != MappedType.Message)
  398. {
  399. throw new InvalidOperationException("MessageType is only valid for enum fields.");
  400. }
  401. return messageType;
  402. }
  403. }
  404. /// <summary>
  405. /// Immutable mapping from field type to mapped type. Built using the attributes on
  406. /// FieldType values.
  407. /// </summary>
  408. public static readonly IDictionary<FieldType, MappedType> FieldTypeToMappedTypeMap = MapFieldTypes();
  409. private static IDictionary<FieldType, MappedType> MapFieldTypes()
  410. {
  411. var map = new Dictionary<FieldType, MappedType>();
  412. foreach (FieldInfo field in typeof (FieldType).GetFields(BindingFlags.Static | BindingFlags.Public))
  413. {
  414. FieldType fieldType = (FieldType) field.GetValue(null);
  415. FieldMappingAttribute mapping =
  416. (FieldMappingAttribute) field.GetCustomAttributes(typeof (FieldMappingAttribute), false)[0];
  417. map[fieldType] = mapping.MappedType;
  418. }
  419. return Dictionaries.AsReadOnly(map);
  420. }
  421. /// <summary>
  422. /// Look up and cross-link all field types etc.
  423. /// </summary>
  424. internal void CrossLink()
  425. {
  426. if (Proto.HasExtendee)
  427. {
  428. IDescriptor extendee = File.DescriptorPool.LookupSymbol(Proto.Extendee, this);
  429. if (!(extendee is MessageDescriptor))
  430. {
  431. throw new DescriptorValidationException(this, "\"" + Proto.Extendee + "\" is not a message type.");
  432. }
  433. containingType = (MessageDescriptor) extendee;
  434. if (!containingType.IsExtensionNumber(FieldNumber))
  435. {
  436. throw new DescriptorValidationException(this,
  437. "\"" + containingType.FullName + "\" does not declare " +
  438. FieldNumber + " as an extension number.");
  439. }
  440. }
  441. if (Proto.HasTypeName)
  442. {
  443. IDescriptor typeDescriptor =
  444. File.DescriptorPool.LookupSymbol(Proto.TypeName, this);
  445. if (!Proto.HasType)
  446. {
  447. // Choose field type based on symbol.
  448. if (typeDescriptor is MessageDescriptor)
  449. {
  450. fieldType = FieldType.Message;
  451. mappedType = MappedType.Message;
  452. }
  453. else if (typeDescriptor is EnumDescriptor)
  454. {
  455. fieldType = FieldType.Enum;
  456. mappedType = MappedType.Enum;
  457. }
  458. else
  459. {
  460. throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not a type.");
  461. }
  462. }
  463. if (MappedType == MappedType.Message)
  464. {
  465. if (!(typeDescriptor is MessageDescriptor))
  466. {
  467. throw new DescriptorValidationException(this,
  468. "\"" + Proto.TypeName + "\" is not a message type.");
  469. }
  470. messageType = (MessageDescriptor) typeDescriptor;
  471. if (Proto.HasDefaultValue)
  472. {
  473. throw new DescriptorValidationException(this, "Messages can't have default values.");
  474. }
  475. }
  476. else if (MappedType == Descriptors.MappedType.Enum)
  477. {
  478. if (!(typeDescriptor is EnumDescriptor))
  479. {
  480. throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not an enum type.");
  481. }
  482. enumType = (EnumDescriptor) typeDescriptor;
  483. }
  484. else
  485. {
  486. throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
  487. }
  488. }
  489. else
  490. {
  491. if (MappedType == MappedType.Message || MappedType == MappedType.Enum)
  492. {
  493. throw new DescriptorValidationException(this, "Field with message or enum type missing type_name.");
  494. }
  495. }
  496. // We don't attempt to parse the default value until here because for
  497. // enums we need the enum type's descriptor.
  498. if (Proto.HasDefaultValue)
  499. {
  500. if (IsRepeated)
  501. {
  502. throw new DescriptorValidationException(this, "Repeated fields cannot have default values.");
  503. }
  504. try
  505. {
  506. switch (FieldType)
  507. {
  508. case FieldType.Int32:
  509. case FieldType.SInt32:
  510. case FieldType.SFixed32:
  511. defaultValue = TextFormat.ParseInt32(Proto.DefaultValue);
  512. break;
  513. case FieldType.UInt32:
  514. case FieldType.Fixed32:
  515. defaultValue = TextFormat.ParseUInt32(Proto.DefaultValue);
  516. break;
  517. case FieldType.Int64:
  518. case FieldType.SInt64:
  519. case FieldType.SFixed64:
  520. defaultValue = TextFormat.ParseInt64(Proto.DefaultValue);
  521. break;
  522. case FieldType.UInt64:
  523. case FieldType.Fixed64:
  524. defaultValue = TextFormat.ParseUInt64(Proto.DefaultValue);
  525. break;
  526. case FieldType.Float:
  527. defaultValue = TextFormat.ParseFloat(Proto.DefaultValue);
  528. break;
  529. case FieldType.Double:
  530. defaultValue = TextFormat.ParseDouble(Proto.DefaultValue);
  531. break;
  532. case FieldType.Bool:
  533. if (Proto.DefaultValue == "true")
  534. {
  535. defaultValue = true;
  536. }
  537. else if (Proto.DefaultValue == "false")
  538. {
  539. defaultValue = false;
  540. }
  541. else
  542. {
  543. throw new FormatException("Boolean values must be \"true\" or \"false\"");
  544. }
  545. break;
  546. case FieldType.String:
  547. defaultValue = Proto.DefaultValue;
  548. break;
  549. case FieldType.Bytes:
  550. try
  551. {
  552. defaultValue = TextFormat.UnescapeBytes(Proto.DefaultValue);
  553. }
  554. catch (FormatException e)
  555. {
  556. throw new DescriptorValidationException(this,
  557. "Couldn't parse default value: " + e.Message);
  558. }
  559. break;
  560. case FieldType.Enum:
  561. defaultValue = enumType.FindValueByName(Proto.DefaultValue);
  562. if (defaultValue == null)
  563. {
  564. throw new DescriptorValidationException(this,
  565. "Unknown enum default value: \"" +
  566. Proto.DefaultValue + "\"");
  567. }
  568. break;
  569. case FieldType.Message:
  570. case FieldType.Group:
  571. throw new DescriptorValidationException(this, "Message type had default value.");
  572. }
  573. }
  574. catch (FormatException e)
  575. {
  576. DescriptorValidationException validationException =
  577. new DescriptorValidationException(this,
  578. "Could not parse default value: \"" + Proto.DefaultValue +
  579. "\"", e);
  580. throw validationException;
  581. }
  582. }
  583. else
  584. {
  585. // Determine the default default for this field.
  586. if (IsRepeated)
  587. {
  588. defaultValue = Lists<object>.Empty;
  589. }
  590. else
  591. {
  592. switch (MappedType)
  593. {
  594. case MappedType.Enum:
  595. // We guarantee elsewhere that an enum type always has at least
  596. // one possible value.
  597. defaultValue = enumType.Values[0];
  598. break;
  599. case MappedType.Message:
  600. defaultValue = null;
  601. break;
  602. default:
  603. defaultValue = GetDefaultValueForMappedType(MappedType);
  604. break;
  605. }
  606. }
  607. }
  608. if (!IsExtension)
  609. {
  610. File.DescriptorPool.AddFieldByNumber(this);
  611. }
  612. if (containingType != null && containingType.Options.MessageSetWireFormat)
  613. {
  614. if (IsExtension)
  615. {
  616. if (!IsOptional || FieldType != FieldType.Message)
  617. {
  618. throw new DescriptorValidationException(this,
  619. "Extensions of MessageSets must be optional messages.");
  620. }
  621. }
  622. else
  623. {
  624. throw new DescriptorValidationException(this, "MessageSets cannot have fields, only extensions.");
  625. }
  626. }
  627. }
  628. }
  629. }