OLD | NEW |
| (Empty) |
1 #region Copyright notice and license | |
2 // Protocol Buffers - Google's data interchange format | |
3 // Copyright 2008 Google Inc. All rights reserved. | |
4 // https://developers.google.com/protocol-buffers/ | |
5 // | |
6 // Redistribution and use in source and binary forms, with or without | |
7 // modification, are permitted provided that the following conditions are | |
8 // met: | |
9 // | |
10 // * Redistributions of source code must retain the above copyright | |
11 // notice, this list of conditions and the following disclaimer. | |
12 // * Redistributions in binary form must reproduce the above | |
13 // copyright notice, this list of conditions and the following disclaimer | |
14 // in the documentation and/or other materials provided with the | |
15 // distribution. | |
16 // * Neither the name of Google Inc. nor the names of its | |
17 // contributors may be used to endorse or promote products derived from | |
18 // this software without specific prior written permission. | |
19 // | |
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 #endregion | |
32 | |
33 using System; | |
34 using System.Linq; | |
35 using Google.Protobuf.Compatibility; | |
36 | |
37 namespace Google.Protobuf.Reflection | |
38 { | |
39 /// <summary> | |
40 /// Descriptor for a field or extension within a message in a .proto file. | |
41 /// </summary> | |
42 public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescr
iptor> | |
43 { | |
44 private readonly FieldDescriptorProto proto; | |
45 private EnumDescriptor enumType; | |
46 private MessageDescriptor messageType; | |
47 private readonly MessageDescriptor containingType; | |
48 private readonly OneofDescriptor containingOneof; | |
49 private FieldType fieldType; | |
50 private readonly string propertyName; // Annoyingly, needed in Crosslink
. | |
51 private IFieldAccessor accessor; | |
52 | |
53 internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file
, | |
54 MessageDescriptor parent, int index, string pro
pertyName) | |
55 : base(file, file.ComputeFullName(parent, proto.Name), index) | |
56 { | |
57 this.proto = proto; | |
58 if (proto.Type != 0) | |
59 { | |
60 fieldType = GetFieldTypeFromProtoType(proto.Type); | |
61 } | |
62 | |
63 if (FieldNumber <= 0) | |
64 { | |
65 throw new DescriptorValidationException(this, | |
66 "Field numbers must be p
ositive integers."); | |
67 } | |
68 containingType = parent; | |
69 // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnCo
nstruction. | |
70 if (proto.OneofIndex != -1) | |
71 { | |
72 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.One
ofDecl.Count) | |
73 { | |
74 throw new DescriptorValidationException(this, | |
75 "FieldDescriptorProto.oneof_index is out of range for ty
pe " + parent.Name); | |
76 } | |
77 containingOneof = parent.Oneofs[proto.OneofIndex]; | |
78 } | |
79 | |
80 file.DescriptorPool.AddSymbol(this); | |
81 // We can't create the accessor until we've cross-linked, unfortunat
ely, as we | |
82 // may not know whether the type of the field is a map or not. Remem
ber the property name | |
83 // for later. | |
84 // We could trust the generated code and check whether the type of t
he property is | |
85 // a MapField, but that feels a tad nasty. | |
86 this.propertyName = propertyName; | |
87 } | |
88 | |
89 /// <summary> | |
90 /// The brief name of the descriptor's target. | |
91 /// </summary> | |
92 public override string Name { get { return proto.Name; } } | |
93 | |
94 internal FieldDescriptorProto Proto { get { return proto; } } | |
95 | |
96 /// <summary> | |
97 /// Returns the accessor for this field, or <c>null</c> if this descript
or does | |
98 /// not support reflective access. | |
99 /// </summary> | |
100 /// <remarks> | |
101 /// While a <see cref="FieldDescriptor"/> describes the field, it does n
ot provide | |
102 /// any way of obtaining or changing the value of the field within a spe
cific message; | |
103 /// that is the responsibility of the accessor. | |
104 /// </remarks> | |
105 public IFieldAccessor Accessor { get { return accessor; } } | |
106 | |
107 /// <summary> | |
108 /// Maps a field type as included in the .proto file to a FieldType. | |
109 /// </summary> | |
110 private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.
Types.Type type) | |
111 { | |
112 switch (type) | |
113 { | |
114 case FieldDescriptorProto.Types.Type.TYPE_DOUBLE: | |
115 return FieldType.Double; | |
116 case FieldDescriptorProto.Types.Type.TYPE_FLOAT: | |
117 return FieldType.Float; | |
118 case FieldDescriptorProto.Types.Type.TYPE_INT64: | |
119 return FieldType.Int64; | |
120 case FieldDescriptorProto.Types.Type.TYPE_UINT64: | |
121 return FieldType.UInt64; | |
122 case FieldDescriptorProto.Types.Type.TYPE_INT32: | |
123 return FieldType.Int32; | |
124 case FieldDescriptorProto.Types.Type.TYPE_FIXED64: | |
125 return FieldType.Fixed64; | |
126 case FieldDescriptorProto.Types.Type.TYPE_FIXED32: | |
127 return FieldType.Fixed32; | |
128 case FieldDescriptorProto.Types.Type.TYPE_BOOL: | |
129 return FieldType.Bool; | |
130 case FieldDescriptorProto.Types.Type.TYPE_STRING: | |
131 return FieldType.String; | |
132 case FieldDescriptorProto.Types.Type.TYPE_GROUP: | |
133 return FieldType.Group; | |
134 case FieldDescriptorProto.Types.Type.TYPE_MESSAGE: | |
135 return FieldType.Message; | |
136 case FieldDescriptorProto.Types.Type.TYPE_BYTES: | |
137 return FieldType.Bytes; | |
138 case FieldDescriptorProto.Types.Type.TYPE_UINT32: | |
139 return FieldType.UInt32; | |
140 case FieldDescriptorProto.Types.Type.TYPE_ENUM: | |
141 return FieldType.Enum; | |
142 case FieldDescriptorProto.Types.Type.TYPE_SFIXED32: | |
143 return FieldType.SFixed32; | |
144 case FieldDescriptorProto.Types.Type.TYPE_SFIXED64: | |
145 return FieldType.SFixed64; | |
146 case FieldDescriptorProto.Types.Type.TYPE_SINT32: | |
147 return FieldType.SInt32; | |
148 case FieldDescriptorProto.Types.Type.TYPE_SINT64: | |
149 return FieldType.SInt64; | |
150 default: | |
151 throw new ArgumentException("Invalid type specified"); | |
152 } | |
153 } | |
154 | |
155 /// <summary> | |
156 /// Returns <c>true</c> if this field is a repeated field; <c>false</c>
otherwise. | |
157 /// </summary> | |
158 public bool IsRepeated | |
159 { | |
160 get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_R
EPEATED; } | |
161 } | |
162 | |
163 /// <summary> | |
164 /// Returns <c>true</c> if this field is a map field; <c>false</c> other
wise. | |
165 /// </summary> | |
166 public bool IsMap | |
167 { | |
168 get { return fieldType == FieldType.Message && messageType.Proto.Opt
ions != null && messageType.Proto.Options.MapEntry; } | |
169 } | |
170 | |
171 /// <summary> | |
172 /// Returns <c>true</c> if this field is a packed, repeated field; <c>fa
lse</c> otherwise. | |
173 /// </summary> | |
174 public bool IsPacked | |
175 { | |
176 // Note the || rather than && here - we're effectively defaulting to
packed, because that *is* | |
177 // the default in proto3, which is all we support. We may give the w
rong result for the protos | |
178 // within descriptor.proto, but that's okay, as they're never expose
d and we don't use IsPacked | |
179 // within the runtime. | |
180 get { return Proto.Options == null || Proto.Options.Packed; } | |
181 } | |
182 | |
183 /// <summary> | |
184 /// Get the field's containing message type. | |
185 /// </summary> | |
186 public MessageDescriptor ContainingType | |
187 { | |
188 get { return containingType; } | |
189 } | |
190 | |
191 /// <summary> | |
192 /// Returns the oneof containing this field, or <c>null</c> if it is not
part of a oneof. | |
193 /// </summary> | |
194 public OneofDescriptor ContainingOneof | |
195 { | |
196 get { return containingOneof; } | |
197 } | |
198 | |
199 /// <summary> | |
200 /// Returns the type of the field. | |
201 /// </summary> | |
202 public FieldType FieldType | |
203 { | |
204 get { return fieldType; } | |
205 } | |
206 | |
207 /// <summary> | |
208 /// Returns the field number declared in the proto file. | |
209 /// </summary> | |
210 public int FieldNumber | |
211 { | |
212 get { return Proto.Number; } | |
213 } | |
214 | |
215 /// <summary> | |
216 /// Compares this descriptor with another one, ordering in "canonical" o
rder | |
217 /// which simply means ascending order by field number. <paramref name="
other"/> | |
218 /// must be a field of the same type, i.e. the <see cref="ContainingType
"/> of | |
219 /// both fields must be the same. | |
220 /// </summary> | |
221 public int CompareTo(FieldDescriptor other) | |
222 { | |
223 if (other.containingType != containingType) | |
224 { | |
225 throw new ArgumentException("FieldDescriptors can only be compar
ed to other FieldDescriptors " + | |
226 "for fields of the same message type
."); | |
227 } | |
228 return FieldNumber - other.FieldNumber; | |
229 } | |
230 | |
231 /// <summary> | |
232 /// For enum fields, returns the field's type. | |
233 /// </summary> | |
234 public EnumDescriptor EnumType | |
235 { | |
236 get | |
237 { | |
238 if (fieldType != FieldType.Enum) | |
239 { | |
240 throw new InvalidOperationException("EnumType is only valid
for enum fields."); | |
241 } | |
242 return enumType; | |
243 } | |
244 } | |
245 | |
246 /// <summary> | |
247 /// For embedded message and group fields, returns the field's type. | |
248 /// </summary> | |
249 public MessageDescriptor MessageType | |
250 { | |
251 get | |
252 { | |
253 if (fieldType != FieldType.Message) | |
254 { | |
255 throw new InvalidOperationException("MessageType is only val
id for enum fields."); | |
256 } | |
257 return messageType; | |
258 } | |
259 } | |
260 | |
261 /// <summary> | |
262 /// Look up and cross-link all field types etc. | |
263 /// </summary> | |
264 internal void CrossLink() | |
265 { | |
266 if (Proto.TypeName != "") | |
267 { | |
268 IDescriptor typeDescriptor = | |
269 File.DescriptorPool.LookupSymbol(Proto.TypeName, this); | |
270 | |
271 if (Proto.Type != 0) | |
272 { | |
273 // Choose field type based on symbol. | |
274 if (typeDescriptor is MessageDescriptor) | |
275 { | |
276 fieldType = FieldType.Message; | |
277 } | |
278 else if (typeDescriptor is EnumDescriptor) | |
279 { | |
280 fieldType = FieldType.Enum; | |
281 } | |
282 else | |
283 { | |
284 throw new DescriptorValidationException(this, "\"" + Pro
to.TypeName + "\" is not a type."); | |
285 } | |
286 } | |
287 | |
288 if (fieldType == FieldType.Message) | |
289 { | |
290 if (!(typeDescriptor is MessageDescriptor)) | |
291 { | |
292 throw new DescriptorValidationException(this, | |
293 "\"" + Proto.Typ
eName + "\" is not a message type."); | |
294 } | |
295 messageType = (MessageDescriptor) typeDescriptor; | |
296 | |
297 if (Proto.DefaultValue != "") | |
298 { | |
299 throw new DescriptorValidationException(this, "Messages
can't have default values."); | |
300 } | |
301 } | |
302 else if (fieldType == FieldType.Enum) | |
303 { | |
304 if (!(typeDescriptor is EnumDescriptor)) | |
305 { | |
306 throw new DescriptorValidationException(this, "\"" + Pro
to.TypeName + "\" is not an enum type."); | |
307 } | |
308 enumType = (EnumDescriptor) typeDescriptor; | |
309 } | |
310 else | |
311 { | |
312 throw new DescriptorValidationException(this, "Field with pr
imitive type has type_name."); | |
313 } | |
314 } | |
315 else | |
316 { | |
317 if (fieldType == FieldType.Message || fieldType == FieldType.Enu
m) | |
318 { | |
319 throw new DescriptorValidationException(this, "Field with me
ssage or enum type missing type_name."); | |
320 } | |
321 } | |
322 | |
323 // Note: no attempt to perform any default value parsing | |
324 | |
325 File.DescriptorPool.AddFieldByNumber(this); | |
326 | |
327 if (containingType != null && containingType.Proto.Options != null &
& containingType.Proto.Options.MessageSetWireFormat) | |
328 { | |
329 throw new DescriptorValidationException(this, "MessageSet format
is not supported."); | |
330 } | |
331 accessor = CreateAccessor(propertyName); | |
332 } | |
333 | |
334 private IFieldAccessor CreateAccessor(string propertyName) | |
335 { | |
336 if (containingType.GeneratedType == null || propertyName == null) | |
337 { | |
338 return null; | |
339 } | |
340 var property = containingType.GeneratedType.GetProperty(propertyName
); | |
341 if (property == null) | |
342 { | |
343 throw new DescriptorValidationException(this, "Property " + prop
ertyName + " not found in " + containingType.GeneratedType); | |
344 } | |
345 return IsMap ? new MapFieldAccessor(property, this) | |
346 : IsRepeated ? new RepeatedFieldAccessor(property, this) | |
347 : (IFieldAccessor) new SingleFieldAccessor(property, this); | |
348 } | |
349 } | |
350 } | |
OLD | NEW |