blob: b955018c4bbe437404b511336ff341317f31dea1 [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_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -040096 GPBFileDescriptor *file_;
97 BOOL wireFormat_;
98}
99
100@synthesize messageClass = messageClass_;
101@synthesize fields = fields_;
102@synthesize oneofs = oneofs_;
103@synthesize enums = enums_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400104@synthesize extensionRanges = extensionRanges_;
105@synthesize extensionRangesCount = extensionRangesCount_;
106@synthesize file = file_;
107@synthesize wireFormat = wireFormat_;
108
109+ (instancetype)
110 allocDescriptorForClass:(Class)messageClass
111 rootClass:(Class)rootClass
112 file:(GPBFileDescriptor *)file
113 fields:(GPBMessageFieldDescription *)fieldDescriptions
114 fieldCount:(NSUInteger)fieldCount
115 oneofs:(GPBMessageOneofDescription *)oneofDescriptions
116 oneofCount:(NSUInteger)oneofCount
117 enums:(GPBMessageEnumDescription *)enumDescriptions
118 enumCount:(NSUInteger)enumCount
119 ranges:(const GPBExtensionRange *)ranges
120 rangeCount:(NSUInteger)rangeCount
121 storageSize:(size_t)storageSize
122 wireFormat:(BOOL)wireFormat {
123 NSMutableArray *fields = nil;
124 NSMutableArray *oneofs = nil;
125 NSMutableArray *enums = nil;
126 NSMutableArray *extensionRanges = nil;
127 GPBFileSyntax syntax = file.syntax;
128 for (NSUInteger i = 0; i < fieldCount; ++i) {
129 if (fields == nil) {
130 fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
131 }
132 GPBFieldDescriptor *fieldDescriptor = [[GPBFieldDescriptor alloc]
133 initWithFieldDescription:&fieldDescriptions[i]
134 rootClass:rootClass
135 syntax:syntax];
136 [fields addObject:fieldDescriptor];
137 [fieldDescriptor release];
138 }
139 for (NSUInteger i = 0; i < oneofCount; ++i) {
140 if (oneofs == nil) {
141 oneofs = [[NSMutableArray alloc] initWithCapacity:oneofCount];
142 }
143 GPBMessageOneofDescription *oneofDescription = &oneofDescriptions[i];
144 NSArray *fieldsForOneof =
145 NewFieldsArrayForHasIndex(oneofDescription->index, fields);
146 GPBOneofDescriptor *oneofDescriptor =
147 [[GPBOneofDescriptor alloc] initWithOneofDescription:oneofDescription
148 fields:fieldsForOneof];
149 [oneofs addObject:oneofDescriptor];
150 [oneofDescriptor release];
151 [fieldsForOneof release];
152 }
153 for (NSUInteger i = 0; i < enumCount; ++i) {
154 if (enums == nil) {
155 enums = [[NSMutableArray alloc] initWithCapacity:enumCount];
156 }
157 GPBEnumDescriptor *enumDescriptor =
158 enumDescriptions[i].enumDescriptorFunc();
159 [enums addObject:enumDescriptor];
160 }
161
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400162 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
163 file:file
164 fields:fields
165 oneofs:oneofs
166 enums:enums
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400167 extensionRanges:ranges
168 extensionRangesCount:rangeCount
169 storageSize:storageSize
170 wireFormat:wireFormat];
171 [fields release];
172 [oneofs release];
173 [enums release];
174 [extensionRanges release];
175 return descriptor;
176}
177
178+ (instancetype)
179 allocDescriptorForClass:(Class)messageClass
180 rootClass:(Class)rootClass
181 file:(GPBFileDescriptor *)file
182 fields:(GPBMessageFieldDescription *)fieldDescriptions
183 fieldCount:(NSUInteger)fieldCount
184 oneofs:(GPBMessageOneofDescription *)oneofDescriptions
185 oneofCount:(NSUInteger)oneofCount
186 enums:(GPBMessageEnumDescription *)enumDescriptions
187 enumCount:(NSUInteger)enumCount
188 ranges:(const GPBExtensionRange *)ranges
189 rangeCount:(NSUInteger)rangeCount
190 storageSize:(size_t)storageSize
191 wireFormat:(BOOL)wireFormat
192 extraTextFormatInfo:(const char *)extraTextFormatInfo {
193 GPBDescriptor *descriptor = [self allocDescriptorForClass:messageClass
194 rootClass:rootClass
195 file:file
196 fields:fieldDescriptions
197 fieldCount:fieldCount
198 oneofs:oneofDescriptions
199 oneofCount:oneofCount
200 enums:enumDescriptions
201 enumCount:enumCount
202 ranges:ranges
203 rangeCount:rangeCount
204 storageSize:storageSize
205 wireFormat:wireFormat];
206 // Extra info is a compile time option, so skip the work if not needed.
207 if (extraTextFormatInfo) {
208 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
209 for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) {
210 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
211 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
212 extraInfoValue,
213 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
214 }
215 }
216 }
217 return descriptor;
218}
219
220- (instancetype)initWithClass:(Class)messageClass
221 file:(GPBFileDescriptor *)file
222 fields:(NSArray *)fields
223 oneofs:(NSArray *)oneofs
224 enums:(NSArray *)enums
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400225 extensionRanges:(const GPBExtensionRange *)extensionRanges
226 extensionRangesCount:(NSUInteger)extensionRangesCount
227 storageSize:(size_t)storageSize
228 wireFormat:(BOOL)wireFormat {
229 if ((self = [super init])) {
230 messageClass_ = messageClass;
231 file_ = file;
232 fields_ = [fields retain];
233 oneofs_ = [oneofs retain];
234 enums_ = [enums retain];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400235 extensionRanges_ = extensionRanges;
236 extensionRangesCount_ = extensionRangesCount;
237 storageSize_ = storageSize;
238 wireFormat_ = wireFormat;
239 }
240 return self;
241}
242
243- (void)dealloc {
244 [fields_ release];
245 [oneofs_ release];
246 [enums_ release];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400247 [super dealloc];
248}
249
250- (NSString *)name {
251 return NSStringFromClass(messageClass_);
252}
253
254- (id)copyWithZone:(NSZone *)zone {
255#pragma unused(zone)
256 return [self retain];
257}
258
259- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
260 for (GPBFieldDescriptor *descriptor in fields_) {
261 if (GPBFieldNumber(descriptor) == fieldNumber) {
262 return descriptor;
263 }
264 }
265 return nil;
266}
267
268- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
269 for (GPBFieldDescriptor *descriptor in fields_) {
270 if ([descriptor.name isEqual:name]) {
271 return descriptor;
272 }
273 }
274 return nil;
275}
276
277- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
278 for (GPBOneofDescriptor *descriptor in oneofs_) {
279 if ([descriptor.name isEqual:name]) {
280 return descriptor;
281 }
282 }
283 return nil;
284}
285
286- (GPBEnumDescriptor *)enumWithName:(NSString *)name {
287 for (GPBEnumDescriptor *descriptor in enums_) {
288 if ([descriptor.name isEqual:name]) {
289 return descriptor;
290 }
291 }
292 return nil;
293}
294
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400295@end
296
297@implementation GPBFileDescriptor {
298 NSString *package_;
299 GPBFileSyntax syntax_;
300}
301
302@synthesize package = package_;
303@synthesize syntax = syntax_;
304
305- (instancetype)initWithPackage:(NSString *)package
306 syntax:(GPBFileSyntax)syntax {
307 self = [super init];
308 if (self) {
309 package_ = [package copy];
310 syntax_ = syntax;
311 }
312 return self;
313}
314
315@end
316
317@implementation GPBOneofDescriptor
318
319@synthesize fields = fields_;
320
321- (instancetype)initWithOneofDescription:
322 (GPBMessageOneofDescription *)oneofDescription
323 fields:(NSArray *)fields {
324 self = [super init];
325 if (self) {
326 NSAssert(oneofDescription->index < 0, @"Should always be <0");
327 oneofDescription_ = oneofDescription;
328 fields_ = [fields retain];
329 for (GPBFieldDescriptor *fieldDesc in fields) {
330 fieldDesc->containingOneof_ = self;
331 }
332
333 caseSel_ = SelFromStrings(NULL, oneofDescription->name, "OneOfCase", NO);
334 }
335 return self;
336}
337
338- (void)dealloc {
339 [fields_ release];
340 [super dealloc];
341}
342
343- (NSString *)name {
Thomas Van Lenten1dcc3292015-05-21 17:14:52 -0400344 return @(oneofDescription_->name);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400345}
346
347- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
348 for (GPBFieldDescriptor *descriptor in fields_) {
349 if (GPBFieldNumber(descriptor) == fieldNumber) {
350 return descriptor;
351 }
352 }
353 return nil;
354}
355
356- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
357 for (GPBFieldDescriptor *descriptor in fields_) {
358 if ([descriptor.name isEqual:name]) {
359 return descriptor;
360 }
361 }
362 return nil;
363}
364
365@end
366
367uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
368 GPBMessageFieldDescription *description = self->description_;
369 GPBWireFormat format;
370 if ((description->flags & GPBFieldMapKeyMask) != 0) {
371 // Maps are repeated messages on the wire.
372 format = GPBWireFormatForType(GPBTypeMessage, NO);
373 } else {
374 format = GPBWireFormatForType(description->type,
375 description->flags & GPBFieldPacked);
376 }
377 return GPBWireFormatMakeTag(description->number, format);
378}
379
380@implementation GPBFieldDescriptor {
381 GPBValue defaultValue_;
382 GPBFieldOptions *fieldOptions_;
383
384 // Message ivars
385 Class msgClass_;
386
387 // Enum ivars.
388 // If protos are generated with GenerateEnumDescriptors on then it will
389 // be a enumDescriptor, otherwise it will be a enumVerifier.
390 union {
391 GPBEnumDescriptor *enumDescriptor_;
392 GPBEnumValidationFunc enumVerifier_;
393 } enumHandling_;
394}
395
396@synthesize fieldOptions = fieldOptions_;
397@synthesize msgClass = msgClass_;
398@synthesize containingOneof = containingOneof_;
399
400- (instancetype)init {
401 // Throw an exception if people attempt to not use the designated initializer.
402 self = [super init];
403 if (self != nil) {
404 [self doesNotRecognizeSelector:_cmd];
405 self = nil;
406 }
407 return self;
408}
409
410- (instancetype)initWithFieldDescription:
411 (GPBMessageFieldDescription *)description
412 rootClass:(Class)rootClass
413 syntax:(GPBFileSyntax)syntax {
414 if ((self = [super init])) {
415 description_ = description;
416 getSel_ = sel_getUid(description->name);
417 setSel_ = SelFromStrings("set", description->name, NULL, YES);
418
419 if (description->fieldOptions) {
420 // FieldOptions stored as a length prefixed c-escaped string in descriptor
421 // records.
422 uint8_t *optionsBytes = (uint8_t *)description->fieldOptions;
423 uint32_t optionsLength = *((uint32_t *)optionsBytes);
424 // The length is stored in network byte order.
425 optionsLength = ntohl(optionsLength);
426 if (optionsLength > 0) {
427 optionsBytes += sizeof(optionsLength);
428 NSData *optionsData = [NSData dataWithBytesNoCopy:optionsBytes
429 length:optionsLength
430 freeWhenDone:NO];
431 GPBExtensionRegistry *registry = [rootClass extensionRegistry];
432 fieldOptions_ = [[GPBFieldOptions parseFromData:optionsData
Thomas Van Lenten1dcc3292015-05-21 17:14:52 -0400433 extensionRegistry:registry
434 error:NULL] retain];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400435 }
436 }
437
438 GPBType type = description->type;
439 BOOL isMessage = GPBTypeIsMessage(type);
440 if (isMessage) {
441 // No has* for repeated/map or something in a oneof (we can't check
442 // containingOneof_ because it isn't set until after initialization).
443 if ((description->hasIndex >= 0) &&
444 (description->hasIndex != GPBNoHasBit)) {
445 hasSel_ = SelFromStrings("has", description->name, NULL, NO);
446 setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
447 }
448 const char *className = description->typeSpecific.className;
449 msgClass_ = objc_getClass(className);
450 NSAssert1(msgClass_, @"Class %s not defined", className);
451 // The defaultValue_ is fetched directly in -defaultValue to avoid
452 // initialization order issues.
453 } else {
454 if (!GPBFieldIsMapOrArray(self)) {
455 defaultValue_ = description->defaultValue;
456 if (type == GPBTypeData) {
457 // Data stored as a length prefixed c-string in descriptor records.
458 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
459 if (bytes) {
460 uint32_t length = *((uint32_t *)bytes);
461 // The length is stored in network byte order.
462 length = ntohl(length);
463 bytes += sizeof(length);
464 defaultValue_.valueData =
465 [[NSData alloc] initWithBytes:bytes length:length];
466 }
467 }
468 // No has* methods for proto3 or if our hasIndex is < 0 because it
469 // means the field is in a oneof (we can't check containingOneof_
470 // because it isn't set until after initialization).
471 if ((syntax != GPBFileSyntaxProto3) && (description->hasIndex >= 0) &&
472 (description->hasIndex != GPBNoHasBit)) {
473 hasSel_ = SelFromStrings("has", description->name, NULL, NO);
474 setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
475 }
476 }
477 if (GPBTypeIsEnum(type)) {
478 if (description_->flags & GPBFieldHasEnumDescriptor) {
479 enumHandling_.enumDescriptor_ =
480 description->typeSpecific.enumDescFunc();
481 } else {
482 enumHandling_.enumVerifier_ = description->typeSpecific.enumVerifier;
483 }
484 }
485 }
486 }
487 return self;
488}
489
490- (void)dealloc {
491 if (description_->type == GPBTypeData &&
492 !(description_->flags & GPBFieldRepeated)) {
493 [defaultValue_.valueData release];
494 }
495 [super dealloc];
496}
497
498- (GPBType)type {
499 return description_->type;
500}
501
502- (BOOL)hasDefaultValue {
503 return (description_->flags & GPBFieldHasDefaultValue) != 0;
504}
505
506- (uint32_t)number {
507 return description_->number;
508}
509
510- (NSString *)name {
Thomas Van Lenten1dcc3292015-05-21 17:14:52 -0400511 return @(description_->name);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400512}
513
514- (BOOL)isRequired {
515 return (description_->flags & GPBFieldRequired) != 0;
516}
517
518- (BOOL)isOptional {
519 return (description_->flags & GPBFieldOptional) != 0;
520}
521
522- (GPBFieldType)fieldType {
523 GPBFieldFlags flags = description_->flags;
524 if ((flags & GPBFieldRepeated) != 0) {
525 return GPBFieldTypeRepeated;
526 } else if ((flags & GPBFieldMapKeyMask) != 0) {
527 return GPBFieldTypeMap;
528 } else {
529 return GPBFieldTypeSingle;
530 }
531}
532
533- (GPBType)mapKeyType {
534 switch (description_->flags & GPBFieldMapKeyMask) {
535 case GPBFieldMapKeyInt32:
536 return GPBTypeInt32;
537 case GPBFieldMapKeyInt64:
538 return GPBTypeInt64;
539 case GPBFieldMapKeyUInt32:
540 return GPBTypeUInt32;
541 case GPBFieldMapKeyUInt64:
542 return GPBTypeUInt64;
543 case GPBFieldMapKeySInt32:
544 return GPBTypeSInt32;
545 case GPBFieldMapKeySInt64:
546 return GPBTypeSInt64;
547 case GPBFieldMapKeyFixed32:
548 return GPBTypeFixed32;
549 case GPBFieldMapKeyFixed64:
550 return GPBTypeFixed64;
551 case GPBFieldMapKeySFixed32:
552 return GPBTypeSFixed32;
553 case GPBFieldMapKeySFixed64:
554 return GPBTypeSFixed64;
555 case GPBFieldMapKeyBool:
556 return GPBTypeBool;
557 case GPBFieldMapKeyString:
558 return GPBTypeString;
559
560 default:
561 NSAssert(0, @"Not a map type");
562 return GPBTypeInt32; // For lack of anything better.
563 }
564}
565
566- (BOOL)isPackable {
567 return (description_->flags & GPBFieldPacked) != 0;
568}
569
570- (BOOL)isValidEnumValue:(int32_t)value {
571 NSAssert(description_->type == GPBTypeEnum,
572 @"Field Must be of type GPBTypeEnum");
573 if (description_->flags & GPBFieldHasEnumDescriptor) {
574 return enumHandling_.enumDescriptor_.enumVerifier(value);
575 } else {
576 return enumHandling_.enumVerifier_(value);
577 }
578}
579
580- (GPBEnumDescriptor *)enumDescriptor {
581 if (description_->flags & GPBFieldHasEnumDescriptor) {
582 return enumHandling_.enumDescriptor_;
583 } else {
584 return nil;
585 }
586}
587
588- (GPBValue)defaultValue {
589 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
590 // to an actual defaultValue in our initializer.
591 GPBValue value = defaultValue_;
592
593 if (!(description_->flags & GPBFieldRepeated)) {
594 // We special handle data and strings. If they are nil, we replace them
595 // with empty string/empty data.
596 GPBType type = description_->type;
597 if (type == GPBTypeData && value.valueData == nil) {
598 value.valueData = GPBEmptyNSData();
599 } else if (type == GPBTypeString && value.valueString == nil) {
600 value.valueString = @"";
601 }
602 }
603 return value;
604}
605
606- (NSString *)textFormatName {
607 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
608 NSValue *extraInfoValue =
609 objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
610 // Support can be left out at generation time.
611 if (!extraInfoValue) {
612 return nil;
613 }
614 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
615 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
616 self.name);
617 }
618
619 // The logic here has to match SetCommonFieldVariables() from
620 // objectivec_field.cc in the proto compiler.
621 NSString *name = self.name;
622 NSUInteger len = [name length];
623
624 // Remove the "_p" added to reserved names.
625 if ([name hasSuffix:@"_p"]) {
626 name = [name substringToIndex:(len - 2)];
627 len = [name length];
628 }
629
630 // Remove "Array" from the end for repeated fields.
631 if (((description_->flags & GPBFieldRepeated) != 0) &&
632 [name hasSuffix:@"Array"]) {
633 name = [name substringToIndex:(len - 5)];
634 len = [name length];
635 }
636
637 // Groups vs. other fields.
638 if (description_->type == GPBTypeGroup) {
639 // Just capitalize the first letter.
640 unichar firstChar = [name characterAtIndex:0];
641 if (firstChar >= 'a' && firstChar <= 'z') {
642 NSString *firstCharString =
643 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
644 NSString *result =
645 [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
646 withString:firstCharString];
647 return result;
648 }
649 return name;
650
651 } else {
652 // Undo the CamelCase.
653 NSMutableString *result = [NSMutableString stringWithCapacity:len];
654 for (NSUInteger i = 0; i < len; i++) {
655 unichar c = [name characterAtIndex:i];
656 if (c >= 'A' && c <= 'Z') {
657 if (i > 0) {
658 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
659 } else {
660 [result appendFormat:@"%C", c];
661 }
662 } else {
663 [result appendFormat:@"%C", c];
664 }
665 }
666 return result;
667 }
668}
669
670@end
671
672@implementation GPBEnumDescriptor {
673 NSString *name_;
674 GPBMessageEnumValueDescription *valueDescriptions_;
675 NSUInteger valueDescriptionsCount_;
676 GPBEnumValidationFunc enumVerifier_;
677 const uint8_t *extraTextFormatInfo_;
678}
679
680@synthesize name = name_;
681@synthesize enumVerifier = enumVerifier_;
682
683+ (instancetype)
684 allocDescriptorForName:(NSString *)name
685 values:(GPBMessageEnumValueDescription *)valueDescriptions
686 valueCount:(NSUInteger)valueCount
687 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
688 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
689 values:valueDescriptions
690 valueCount:valueCount
691 enumVerifier:enumVerifier];
692 return descriptor;
693}
694
695+ (instancetype)
696 allocDescriptorForName:(NSString *)name
697 values:(GPBMessageEnumValueDescription *)valueDescriptions
698 valueCount:(NSUInteger)valueCount
699 enumVerifier:(GPBEnumValidationFunc)enumVerifier
700 extraTextFormatInfo:(const char *)extraTextFormatInfo {
701 // Call the common case.
702 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
703 values:valueDescriptions
704 valueCount:valueCount
705 enumVerifier:enumVerifier];
706 // Set the extra info.
707 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
708 return descriptor;
709}
710
711- (instancetype)initWithName:(NSString *)name
712 values:(GPBMessageEnumValueDescription *)valueDescriptions
713 valueCount:(NSUInteger)valueCount
714 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
715 if ((self = [super init])) {
716 name_ = [name copy];
717 valueDescriptions_ = valueDescriptions;
718 valueDescriptionsCount_ = valueCount;
719 enumVerifier_ = enumVerifier;
720 }
721 return self;
722}
723
724- (NSString *)enumNameForValue:(int32_t)number {
725 for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
726 GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
727 if ((scan->number == number) && (scan->name != NULL)) {
728 NSString *fullName =
729 [NSString stringWithFormat:@"%@_%s", name_, scan->name];
730 return fullName;
731 }
732 }
733 return nil;
734}
735
736- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
737 // Must have the prefix.
738 NSUInteger prefixLen = name_.length + 1;
739 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
740 ([name characterAtIndex:prefixLen - 1] != '_')) {
741 return NO;
742 }
743
744 // Skip over the prefix.
745 const char *nameAsCStr = [name UTF8String];
746 nameAsCStr += prefixLen;
747
748 // Find it.
749 for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
750 GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
751 if ((scan->name != NULL) && (strcmp(nameAsCStr, scan->name) == 0)) {
752 if (outValue) {
753 *outValue = scan->number;
754 }
755 return YES;
756 }
757 }
758 return NO;
759}
760
761- (void)dealloc {
762 [name_ release];
763 [super dealloc];
764}
765
766- (NSString *)textFormatNameForValue:(int32_t)number {
767 // Find the EnumValue descriptor and its index.
768 GPBMessageEnumValueDescription *valueDescriptor = NULL;
769 NSUInteger valueDescriptorIndex;
770 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueDescriptionsCount_;
771 ++valueDescriptorIndex) {
772 GPBMessageEnumValueDescription *scan =
773 &valueDescriptions_[valueDescriptorIndex];
774 if (scan->number == number) {
775 valueDescriptor = scan;
776 break;
777 }
778 }
779
780 // If we didn't find it, or names were disable at proto compile time, nothing
781 // we can do.
782 if (!valueDescriptor || !valueDescriptor->name) {
783 return nil;
784 }
785
786 NSString *result = nil;
787 // Naming adds an underscore between enum name and value name, skip that also.
Thomas Van Lenten1dcc3292015-05-21 17:14:52 -0400788 NSString *shortName = @(valueDescriptor->name);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400789
790 // See if it is in the map of special format handling.
791 if (extraTextFormatInfo_) {
792 result = GPBDecodeTextFormatName(extraTextFormatInfo_,
793 (int32_t)valueDescriptorIndex, shortName);
794 }
795 // Logic here needs to match what objectivec_enum.cc does in the proto
796 // compiler.
797 if (result == nil) {
798 NSUInteger len = [shortName length];
799 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
800 for (NSUInteger i = 0; i < len; i++) {
801 unichar c = [shortName characterAtIndex:i];
802 if (i > 0 && c >= 'A' && c <= 'Z') {
803 [worker appendString:@"_"];
804 }
805 [worker appendFormat:@"%c", toupper((char)c)];
806 }
807 result = worker;
808 }
809 return result;
810}
811
812@end
813
814@implementation GPBExtensionDescriptor
815
816- (instancetype)initWithExtensionDescription:
817 (GPBExtensionDescription *)description {
818 if ((self = [super init])) {
819 description_ = description;
820 }
821 return self;
822}
823
824- (NSString *)singletonName {
Thomas Van Lenten1dcc3292015-05-21 17:14:52 -0400825 return @(description_->singletonName);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400826}
827
828- (const char *)singletonNameC {
829 return description_->singletonName;
830}
831
832- (uint32_t)fieldNumber {
833 return description_->fieldNumber;
834}
835
836- (GPBType)type {
837 return description_->type;
838}
839
840- (BOOL)isRepeated {
841 return (description_->options & GPBExtensionRepeated) != 0;
842}
843
844- (BOOL)isMap {
845 return (description_->options & GPBFieldMapKeyMask) != 0;
846}
847
848- (BOOL)isPackable {
849 return (description_->options & GPBExtensionPacked) != 0;
850}
851
852- (Class)msgClass {
853 return objc_getClass(description_->messageOrGroupClassName);
854}
855
856- (GPBEnumDescriptor *)enumDescriptor {
857 if (GPBTypeIsEnum(description_->type)) {
858 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
859 return enumDescriptor;
860 }
861 return nil;
862}
863
864@end