blob: 6730d532e337f4c34989dd36821073618f973341 [file] [log] [blame]
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#import "GPBDescriptor_PackagePrivate.h"
32
33#import <objc/runtime.h>
34
35#import "GPBUtilities_PackagePrivate.h"
36#import "GPBWireFormat.h"
37#import "GPBMessage_PackagePrivate.h"
38#import "google/protobuf/Descriptor.pbobjc.h"
39
40// The address of this variable is used as a key for obj_getAssociatedObject.
41static const char kTextFormatExtraValueKey = 0;
42
43// Utility function to generate selectors on the fly.
44static SEL SelFromStrings(const char *prefix, const char *middle,
45 const char *suffix, BOOL takesArg) {
46 if (prefix == NULL && suffix == NULL && !takesArg) {
47 return sel_getUid(middle);
48 }
49 const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
50 const size_t middleLen = strlen(middle);
51 const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
52 size_t totalLen =
53 prefixLen + middleLen + suffixLen + 1; // include space for null on end.
54 if (takesArg) {
55 totalLen += 1;
56 }
57 char buffer[totalLen];
58 if (prefix != NULL) {
59 memcpy(buffer, prefix, prefixLen);
60 memcpy(buffer + prefixLen, middle, middleLen);
61 buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
62 } else {
63 memcpy(buffer, middle, middleLen);
64 }
65 if (suffix != NULL) {
66 memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
67 }
68 if (takesArg) {
69 buffer[totalLen - 2] = ':';
70 }
71 // Always null terminate it.
72 buffer[totalLen - 1] = 0;
73
74 SEL result = sel_getUid(buffer);
75 return result;
76}
77
78static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
79 NSArray *allMessageFields)
80 __attribute__((ns_returns_retained));
81
82static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
83 NSArray *allMessageFields) {
84 NSMutableArray *result = [[NSMutableArray alloc] init];
85 for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
86 if (fieldDesc->description_->hasIndex == hasIndex) {
87 [result addObject:fieldDesc];
88 }
89 }
90 return result;
91}
92
93@implementation GPBDescriptor {
94 Class messageClass_;
95 NSArray *enums_;
96 NSArray *extensions_;
97 GPBFileDescriptor *file_;
98 BOOL wireFormat_;
99}
100
101@synthesize messageClass = messageClass_;
102@synthesize fields = fields_;
103@synthesize oneofs = oneofs_;
104@synthesize enums = enums_;
105@synthesize extensions = extensions_;
106@synthesize extensionRanges = extensionRanges_;
107@synthesize extensionRangesCount = extensionRangesCount_;
108@synthesize file = file_;
109@synthesize wireFormat = wireFormat_;
110
111+ (instancetype)
112 allocDescriptorForClass:(Class)messageClass
113 rootClass:(Class)rootClass
114 file:(GPBFileDescriptor *)file
115 fields:(GPBMessageFieldDescription *)fieldDescriptions
116 fieldCount:(NSUInteger)fieldCount
117 oneofs:(GPBMessageOneofDescription *)oneofDescriptions
118 oneofCount:(NSUInteger)oneofCount
119 enums:(GPBMessageEnumDescription *)enumDescriptions
120 enumCount:(NSUInteger)enumCount
121 ranges:(const GPBExtensionRange *)ranges
122 rangeCount:(NSUInteger)rangeCount
123 storageSize:(size_t)storageSize
124 wireFormat:(BOOL)wireFormat {
125 NSMutableArray *fields = nil;
126 NSMutableArray *oneofs = nil;
127 NSMutableArray *enums = nil;
128 NSMutableArray *extensionRanges = nil;
129 GPBFileSyntax syntax = file.syntax;
130 for (NSUInteger i = 0; i < fieldCount; ++i) {
131 if (fields == nil) {
132 fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
133 }
134 GPBFieldDescriptor *fieldDescriptor = [[GPBFieldDescriptor alloc]
135 initWithFieldDescription:&fieldDescriptions[i]
136 rootClass:rootClass
137 syntax:syntax];
138 [fields addObject:fieldDescriptor];
139 [fieldDescriptor release];
140 }
141 for (NSUInteger i = 0; i < oneofCount; ++i) {
142 if (oneofs == nil) {
143 oneofs = [[NSMutableArray alloc] initWithCapacity:oneofCount];
144 }
145 GPBMessageOneofDescription *oneofDescription = &oneofDescriptions[i];
146 NSArray *fieldsForOneof =
147 NewFieldsArrayForHasIndex(oneofDescription->index, fields);
148 GPBOneofDescriptor *oneofDescriptor =
149 [[GPBOneofDescriptor alloc] initWithOneofDescription:oneofDescription
150 fields:fieldsForOneof];
151 [oneofs addObject:oneofDescriptor];
152 [oneofDescriptor release];
153 [fieldsForOneof release];
154 }
155 for (NSUInteger i = 0; i < enumCount; ++i) {
156 if (enums == nil) {
157 enums = [[NSMutableArray alloc] initWithCapacity:enumCount];
158 }
159 GPBEnumDescriptor *enumDescriptor =
160 enumDescriptions[i].enumDescriptorFunc();
161 [enums addObject:enumDescriptor];
162 }
163
164 // TODO(dmaclach): Add support for extensions
165 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
166 file:file
167 fields:fields
168 oneofs:oneofs
169 enums:enums
170 extensions:nil
171 extensionRanges:ranges
172 extensionRangesCount:rangeCount
173 storageSize:storageSize
174 wireFormat:wireFormat];
175 [fields release];
176 [oneofs release];
177 [enums release];
178 [extensionRanges release];
179 return descriptor;
180}
181
182+ (instancetype)
183 allocDescriptorForClass:(Class)messageClass
184 rootClass:(Class)rootClass
185 file:(GPBFileDescriptor *)file
186 fields:(GPBMessageFieldDescription *)fieldDescriptions
187 fieldCount:(NSUInteger)fieldCount
188 oneofs:(GPBMessageOneofDescription *)oneofDescriptions
189 oneofCount:(NSUInteger)oneofCount
190 enums:(GPBMessageEnumDescription *)enumDescriptions
191 enumCount:(NSUInteger)enumCount
192 ranges:(const GPBExtensionRange *)ranges
193 rangeCount:(NSUInteger)rangeCount
194 storageSize:(size_t)storageSize
195 wireFormat:(BOOL)wireFormat
196 extraTextFormatInfo:(const char *)extraTextFormatInfo {
197 GPBDescriptor *descriptor = [self allocDescriptorForClass:messageClass
198 rootClass:rootClass
199 file:file
200 fields:fieldDescriptions
201 fieldCount:fieldCount
202 oneofs:oneofDescriptions
203 oneofCount:oneofCount
204 enums:enumDescriptions
205 enumCount:enumCount
206 ranges:ranges
207 rangeCount:rangeCount
208 storageSize:storageSize
209 wireFormat:wireFormat];
210 // Extra info is a compile time option, so skip the work if not needed.
211 if (extraTextFormatInfo) {
212 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
213 for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) {
214 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
215 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
216 extraInfoValue,
217 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
218 }
219 }
220 }
221 return descriptor;
222}
223
224- (instancetype)initWithClass:(Class)messageClass
225 file:(GPBFileDescriptor *)file
226 fields:(NSArray *)fields
227 oneofs:(NSArray *)oneofs
228 enums:(NSArray *)enums
229 extensions:(NSArray *)extensions
230 extensionRanges:(const GPBExtensionRange *)extensionRanges
231 extensionRangesCount:(NSUInteger)extensionRangesCount
232 storageSize:(size_t)storageSize
233 wireFormat:(BOOL)wireFormat {
234 if ((self = [super init])) {
235 messageClass_ = messageClass;
236 file_ = file;
237 fields_ = [fields retain];
238 oneofs_ = [oneofs retain];
239 enums_ = [enums retain];
240 extensions_ = [extensions retain];
241 extensionRanges_ = extensionRanges;
242 extensionRangesCount_ = extensionRangesCount;
243 storageSize_ = storageSize;
244 wireFormat_ = wireFormat;
245 }
246 return self;
247}
248
249- (void)dealloc {
250 [fields_ release];
251 [oneofs_ release];
252 [enums_ release];
253 [extensions_ release];
254 [super dealloc];
255}
256
257- (NSString *)name {
258 return NSStringFromClass(messageClass_);
259}
260
261- (id)copyWithZone:(NSZone *)zone {
262#pragma unused(zone)
263 return [self retain];
264}
265
266- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
267 for (GPBFieldDescriptor *descriptor in fields_) {
268 if (GPBFieldNumber(descriptor) == fieldNumber) {
269 return descriptor;
270 }
271 }
272 return nil;
273}
274
275- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
276 for (GPBFieldDescriptor *descriptor in fields_) {
277 if ([descriptor.name isEqual:name]) {
278 return descriptor;
279 }
280 }
281 return nil;
282}
283
284- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
285 for (GPBOneofDescriptor *descriptor in oneofs_) {
286 if ([descriptor.name isEqual:name]) {
287 return descriptor;
288 }
289 }
290 return nil;
291}
292
293- (GPBEnumDescriptor *)enumWithName:(NSString *)name {
294 for (GPBEnumDescriptor *descriptor in enums_) {
295 if ([descriptor.name isEqual:name]) {
296 return descriptor;
297 }
298 }
299 return nil;
300}
301
302- (GPBFieldDescriptor *)extensionWithNumber:(uint32_t)fieldNumber {
303 for (GPBFieldDescriptor *descriptor in extensions_) {
304 if (GPBFieldNumber(descriptor) == fieldNumber) {
305 return descriptor;
306 }
307 }
308 return nil;
309}
310
311- (GPBFieldDescriptor *)extensionWithName:(NSString *)name {
312 for (GPBFieldDescriptor *descriptor in extensions_) {
313 if ([descriptor.name isEqual:name]) {
314 return descriptor;
315 }
316 }
317 return nil;
318}
319
320@end
321
322@implementation GPBFileDescriptor {
323 NSString *package_;
324 GPBFileSyntax syntax_;
325}
326
327@synthesize package = package_;
328@synthesize syntax = syntax_;
329
330- (instancetype)initWithPackage:(NSString *)package
331 syntax:(GPBFileSyntax)syntax {
332 self = [super init];
333 if (self) {
334 package_ = [package copy];
335 syntax_ = syntax;
336 }
337 return self;
338}
339
340@end
341
342@implementation GPBOneofDescriptor
343
344@synthesize fields = fields_;
345
346- (instancetype)initWithOneofDescription:
347 (GPBMessageOneofDescription *)oneofDescription
348 fields:(NSArray *)fields {
349 self = [super init];
350 if (self) {
351 NSAssert(oneofDescription->index < 0, @"Should always be <0");
352 oneofDescription_ = oneofDescription;
353 fields_ = [fields retain];
354 for (GPBFieldDescriptor *fieldDesc in fields) {
355 fieldDesc->containingOneof_ = self;
356 }
357
358 caseSel_ = SelFromStrings(NULL, oneofDescription->name, "OneOfCase", NO);
359 }
360 return self;
361}
362
363- (void)dealloc {
364 [fields_ release];
365 [super dealloc];
366}
367
368- (NSString *)name {
369 return [NSString stringWithUTF8String:oneofDescription_->name];
370}
371
372- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
373 for (GPBFieldDescriptor *descriptor in fields_) {
374 if (GPBFieldNumber(descriptor) == fieldNumber) {
375 return descriptor;
376 }
377 }
378 return nil;
379}
380
381- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
382 for (GPBFieldDescriptor *descriptor in fields_) {
383 if ([descriptor.name isEqual:name]) {
384 return descriptor;
385 }
386 }
387 return nil;
388}
389
390@end
391
392uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
393 GPBMessageFieldDescription *description = self->description_;
394 GPBWireFormat format;
395 if ((description->flags & GPBFieldMapKeyMask) != 0) {
396 // Maps are repeated messages on the wire.
397 format = GPBWireFormatForType(GPBTypeMessage, NO);
398 } else {
399 format = GPBWireFormatForType(description->type,
400 description->flags & GPBFieldPacked);
401 }
402 return GPBWireFormatMakeTag(description->number, format);
403}
404
405@implementation GPBFieldDescriptor {
406 GPBValue defaultValue_;
407 GPBFieldOptions *fieldOptions_;
408
409 // Message ivars
410 Class msgClass_;
411
412 // Enum ivars.
413 // If protos are generated with GenerateEnumDescriptors on then it will
414 // be a enumDescriptor, otherwise it will be a enumVerifier.
415 union {
416 GPBEnumDescriptor *enumDescriptor_;
417 GPBEnumValidationFunc enumVerifier_;
418 } enumHandling_;
419}
420
421@synthesize fieldOptions = fieldOptions_;
422@synthesize msgClass = msgClass_;
423@synthesize containingOneof = containingOneof_;
424
425- (instancetype)init {
426 // Throw an exception if people attempt to not use the designated initializer.
427 self = [super init];
428 if (self != nil) {
429 [self doesNotRecognizeSelector:_cmd];
430 self = nil;
431 }
432 return self;
433}
434
435- (instancetype)initWithFieldDescription:
436 (GPBMessageFieldDescription *)description
437 rootClass:(Class)rootClass
438 syntax:(GPBFileSyntax)syntax {
439 if ((self = [super init])) {
440 description_ = description;
441 getSel_ = sel_getUid(description->name);
442 setSel_ = SelFromStrings("set", description->name, NULL, YES);
443
444 if (description->fieldOptions) {
445 // FieldOptions stored as a length prefixed c-escaped string in descriptor
446 // records.
447 uint8_t *optionsBytes = (uint8_t *)description->fieldOptions;
448 uint32_t optionsLength = *((uint32_t *)optionsBytes);
449 // The length is stored in network byte order.
450 optionsLength = ntohl(optionsLength);
451 if (optionsLength > 0) {
452 optionsBytes += sizeof(optionsLength);
453 NSData *optionsData = [NSData dataWithBytesNoCopy:optionsBytes
454 length:optionsLength
455 freeWhenDone:NO];
456 GPBExtensionRegistry *registry = [rootClass extensionRegistry];
457 fieldOptions_ = [[GPBFieldOptions parseFromData:optionsData
458 extensionRegistry:registry] retain];
459 }
460 }
461
462 GPBType type = description->type;
463 BOOL isMessage = GPBTypeIsMessage(type);
464 if (isMessage) {
465 // No has* for repeated/map or something in a oneof (we can't check
466 // containingOneof_ because it isn't set until after initialization).
467 if ((description->hasIndex >= 0) &&
468 (description->hasIndex != GPBNoHasBit)) {
469 hasSel_ = SelFromStrings("has", description->name, NULL, NO);
470 setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
471 }
472 const char *className = description->typeSpecific.className;
473 msgClass_ = objc_getClass(className);
474 NSAssert1(msgClass_, @"Class %s not defined", className);
475 // The defaultValue_ is fetched directly in -defaultValue to avoid
476 // initialization order issues.
477 } else {
478 if (!GPBFieldIsMapOrArray(self)) {
479 defaultValue_ = description->defaultValue;
480 if (type == GPBTypeData) {
481 // Data stored as a length prefixed c-string in descriptor records.
482 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
483 if (bytes) {
484 uint32_t length = *((uint32_t *)bytes);
485 // The length is stored in network byte order.
486 length = ntohl(length);
487 bytes += sizeof(length);
488 defaultValue_.valueData =
489 [[NSData alloc] initWithBytes:bytes length:length];
490 }
491 }
492 // No has* methods for proto3 or if our hasIndex is < 0 because it
493 // means the field is in a oneof (we can't check containingOneof_
494 // because it isn't set until after initialization).
495 if ((syntax != GPBFileSyntaxProto3) && (description->hasIndex >= 0) &&
496 (description->hasIndex != GPBNoHasBit)) {
497 hasSel_ = SelFromStrings("has", description->name, NULL, NO);
498 setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
499 }
500 }
501 if (GPBTypeIsEnum(type)) {
502 if (description_->flags & GPBFieldHasEnumDescriptor) {
503 enumHandling_.enumDescriptor_ =
504 description->typeSpecific.enumDescFunc();
505 } else {
506 enumHandling_.enumVerifier_ = description->typeSpecific.enumVerifier;
507 }
508 }
509 }
510 }
511 return self;
512}
513
514- (void)dealloc {
515 if (description_->type == GPBTypeData &&
516 !(description_->flags & GPBFieldRepeated)) {
517 [defaultValue_.valueData release];
518 }
519 [super dealloc];
520}
521
522- (GPBType)type {
523 return description_->type;
524}
525
526- (BOOL)hasDefaultValue {
527 return (description_->flags & GPBFieldHasDefaultValue) != 0;
528}
529
530- (uint32_t)number {
531 return description_->number;
532}
533
534- (NSString *)name {
535 return [NSString stringWithUTF8String:description_->name];
536}
537
538- (BOOL)isRequired {
539 return (description_->flags & GPBFieldRequired) != 0;
540}
541
542- (BOOL)isOptional {
543 return (description_->flags & GPBFieldOptional) != 0;
544}
545
546- (GPBFieldType)fieldType {
547 GPBFieldFlags flags = description_->flags;
548 if ((flags & GPBFieldRepeated) != 0) {
549 return GPBFieldTypeRepeated;
550 } else if ((flags & GPBFieldMapKeyMask) != 0) {
551 return GPBFieldTypeMap;
552 } else {
553 return GPBFieldTypeSingle;
554 }
555}
556
557- (GPBType)mapKeyType {
558 switch (description_->flags & GPBFieldMapKeyMask) {
559 case GPBFieldMapKeyInt32:
560 return GPBTypeInt32;
561 case GPBFieldMapKeyInt64:
562 return GPBTypeInt64;
563 case GPBFieldMapKeyUInt32:
564 return GPBTypeUInt32;
565 case GPBFieldMapKeyUInt64:
566 return GPBTypeUInt64;
567 case GPBFieldMapKeySInt32:
568 return GPBTypeSInt32;
569 case GPBFieldMapKeySInt64:
570 return GPBTypeSInt64;
571 case GPBFieldMapKeyFixed32:
572 return GPBTypeFixed32;
573 case GPBFieldMapKeyFixed64:
574 return GPBTypeFixed64;
575 case GPBFieldMapKeySFixed32:
576 return GPBTypeSFixed32;
577 case GPBFieldMapKeySFixed64:
578 return GPBTypeSFixed64;
579 case GPBFieldMapKeyBool:
580 return GPBTypeBool;
581 case GPBFieldMapKeyString:
582 return GPBTypeString;
583
584 default:
585 NSAssert(0, @"Not a map type");
586 return GPBTypeInt32; // For lack of anything better.
587 }
588}
589
590- (BOOL)isPackable {
591 return (description_->flags & GPBFieldPacked) != 0;
592}
593
594- (BOOL)isValidEnumValue:(int32_t)value {
595 NSAssert(description_->type == GPBTypeEnum,
596 @"Field Must be of type GPBTypeEnum");
597 if (description_->flags & GPBFieldHasEnumDescriptor) {
598 return enumHandling_.enumDescriptor_.enumVerifier(value);
599 } else {
600 return enumHandling_.enumVerifier_(value);
601 }
602}
603
604- (GPBEnumDescriptor *)enumDescriptor {
605 if (description_->flags & GPBFieldHasEnumDescriptor) {
606 return enumHandling_.enumDescriptor_;
607 } else {
608 return nil;
609 }
610}
611
612- (GPBValue)defaultValue {
613 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
614 // to an actual defaultValue in our initializer.
615 GPBValue value = defaultValue_;
616
617 if (!(description_->flags & GPBFieldRepeated)) {
618 // We special handle data and strings. If they are nil, we replace them
619 // with empty string/empty data.
620 GPBType type = description_->type;
621 if (type == GPBTypeData && value.valueData == nil) {
622 value.valueData = GPBEmptyNSData();
623 } else if (type == GPBTypeString && value.valueString == nil) {
624 value.valueString = @"";
625 }
626 }
627 return value;
628}
629
630- (NSString *)textFormatName {
631 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
632 NSValue *extraInfoValue =
633 objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
634 // Support can be left out at generation time.
635 if (!extraInfoValue) {
636 return nil;
637 }
638 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
639 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
640 self.name);
641 }
642
643 // The logic here has to match SetCommonFieldVariables() from
644 // objectivec_field.cc in the proto compiler.
645 NSString *name = self.name;
646 NSUInteger len = [name length];
647
648 // Remove the "_p" added to reserved names.
649 if ([name hasSuffix:@"_p"]) {
650 name = [name substringToIndex:(len - 2)];
651 len = [name length];
652 }
653
654 // Remove "Array" from the end for repeated fields.
655 if (((description_->flags & GPBFieldRepeated) != 0) &&
656 [name hasSuffix:@"Array"]) {
657 name = [name substringToIndex:(len - 5)];
658 len = [name length];
659 }
660
661 // Groups vs. other fields.
662 if (description_->type == GPBTypeGroup) {
663 // Just capitalize the first letter.
664 unichar firstChar = [name characterAtIndex:0];
665 if (firstChar >= 'a' && firstChar <= 'z') {
666 NSString *firstCharString =
667 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
668 NSString *result =
669 [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
670 withString:firstCharString];
671 return result;
672 }
673 return name;
674
675 } else {
676 // Undo the CamelCase.
677 NSMutableString *result = [NSMutableString stringWithCapacity:len];
678 for (NSUInteger i = 0; i < len; i++) {
679 unichar c = [name characterAtIndex:i];
680 if (c >= 'A' && c <= 'Z') {
681 if (i > 0) {
682 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
683 } else {
684 [result appendFormat:@"%C", c];
685 }
686 } else {
687 [result appendFormat:@"%C", c];
688 }
689 }
690 return result;
691 }
692}
693
694@end
695
696@implementation GPBEnumDescriptor {
697 NSString *name_;
698 GPBMessageEnumValueDescription *valueDescriptions_;
699 NSUInteger valueDescriptionsCount_;
700 GPBEnumValidationFunc enumVerifier_;
701 const uint8_t *extraTextFormatInfo_;
702}
703
704@synthesize name = name_;
705@synthesize enumVerifier = enumVerifier_;
706
707+ (instancetype)
708 allocDescriptorForName:(NSString *)name
709 values:(GPBMessageEnumValueDescription *)valueDescriptions
710 valueCount:(NSUInteger)valueCount
711 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
712 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
713 values:valueDescriptions
714 valueCount:valueCount
715 enumVerifier:enumVerifier];
716 return descriptor;
717}
718
719+ (instancetype)
720 allocDescriptorForName:(NSString *)name
721 values:(GPBMessageEnumValueDescription *)valueDescriptions
722 valueCount:(NSUInteger)valueCount
723 enumVerifier:(GPBEnumValidationFunc)enumVerifier
724 extraTextFormatInfo:(const char *)extraTextFormatInfo {
725 // Call the common case.
726 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
727 values:valueDescriptions
728 valueCount:valueCount
729 enumVerifier:enumVerifier];
730 // Set the extra info.
731 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
732 return descriptor;
733}
734
735- (instancetype)initWithName:(NSString *)name
736 values:(GPBMessageEnumValueDescription *)valueDescriptions
737 valueCount:(NSUInteger)valueCount
738 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
739 if ((self = [super init])) {
740 name_ = [name copy];
741 valueDescriptions_ = valueDescriptions;
742 valueDescriptionsCount_ = valueCount;
743 enumVerifier_ = enumVerifier;
744 }
745 return self;
746}
747
748- (NSString *)enumNameForValue:(int32_t)number {
749 for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
750 GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
751 if ((scan->number == number) && (scan->name != NULL)) {
752 NSString *fullName =
753 [NSString stringWithFormat:@"%@_%s", name_, scan->name];
754 return fullName;
755 }
756 }
757 return nil;
758}
759
760- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
761 // Must have the prefix.
762 NSUInteger prefixLen = name_.length + 1;
763 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
764 ([name characterAtIndex:prefixLen - 1] != '_')) {
765 return NO;
766 }
767
768 // Skip over the prefix.
769 const char *nameAsCStr = [name UTF8String];
770 nameAsCStr += prefixLen;
771
772 // Find it.
773 for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
774 GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
775 if ((scan->name != NULL) && (strcmp(nameAsCStr, scan->name) == 0)) {
776 if (outValue) {
777 *outValue = scan->number;
778 }
779 return YES;
780 }
781 }
782 return NO;
783}
784
785- (void)dealloc {
786 [name_ release];
787 [super dealloc];
788}
789
790- (NSString *)textFormatNameForValue:(int32_t)number {
791 // Find the EnumValue descriptor and its index.
792 GPBMessageEnumValueDescription *valueDescriptor = NULL;
793 NSUInteger valueDescriptorIndex;
794 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueDescriptionsCount_;
795 ++valueDescriptorIndex) {
796 GPBMessageEnumValueDescription *scan =
797 &valueDescriptions_[valueDescriptorIndex];
798 if (scan->number == number) {
799 valueDescriptor = scan;
800 break;
801 }
802 }
803
804 // If we didn't find it, or names were disable at proto compile time, nothing
805 // we can do.
806 if (!valueDescriptor || !valueDescriptor->name) {
807 return nil;
808 }
809
810 NSString *result = nil;
811 // Naming adds an underscore between enum name and value name, skip that also.
812 NSString *shortName = [NSString stringWithUTF8String:valueDescriptor->name];
813
814 // See if it is in the map of special format handling.
815 if (extraTextFormatInfo_) {
816 result = GPBDecodeTextFormatName(extraTextFormatInfo_,
817 (int32_t)valueDescriptorIndex, shortName);
818 }
819 // Logic here needs to match what objectivec_enum.cc does in the proto
820 // compiler.
821 if (result == nil) {
822 NSUInteger len = [shortName length];
823 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
824 for (NSUInteger i = 0; i < len; i++) {
825 unichar c = [shortName characterAtIndex:i];
826 if (i > 0 && c >= 'A' && c <= 'Z') {
827 [worker appendString:@"_"];
828 }
829 [worker appendFormat:@"%c", toupper((char)c)];
830 }
831 result = worker;
832 }
833 return result;
834}
835
836@end
837
838@implementation GPBExtensionDescriptor
839
840- (instancetype)initWithExtensionDescription:
841 (GPBExtensionDescription *)description {
842 if ((self = [super init])) {
843 description_ = description;
844 }
845 return self;
846}
847
848- (NSString *)singletonName {
849 return [NSString stringWithUTF8String:description_->singletonName];
850}
851
852- (const char *)singletonNameC {
853 return description_->singletonName;
854}
855
856- (uint32_t)fieldNumber {
857 return description_->fieldNumber;
858}
859
860- (GPBType)type {
861 return description_->type;
862}
863
864- (BOOL)isRepeated {
865 return (description_->options & GPBExtensionRepeated) != 0;
866}
867
868- (BOOL)isMap {
869 return (description_->options & GPBFieldMapKeyMask) != 0;
870}
871
872- (BOOL)isPackable {
873 return (description_->options & GPBExtensionPacked) != 0;
874}
875
876- (Class)msgClass {
877 return objc_getClass(description_->messageOrGroupClassName);
878}
879
880- (GPBEnumDescriptor *)enumDescriptor {
881 if (GPBTypeIsEnum(description_->type)) {
882 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
883 return enumDescriptor;
884 }
885 return nil;
886}
887
888@end