libObjCAttr/Reflection/RFTypeDecoder.m (127 lines of code) (raw):
//
// RFTypeDecoder.m
// libObjCAttr
//
// Copyright (c) 2014 EPAM Systems, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// Redistributions in binary form must reproduce the above copyright notice, this
// list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
// Neither the name of the EPAM Systems, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// See the NOTICE file and the LICENSE file distributed with this work
// for additional information regarding copyright ownership and licensing
#import "RFTypeDecoder.h"
static NSString * const kRFEncodingMapFile = @"RFEncoding";
static NSString * const kRFPlistExtension = @"plist";
static NSString * const kRFPointerFormat = @"%@ *";
static NSString * const kRFArrayFormat = @"%@[]";
static NSString * const kRFBitfieldFormat = @"bitfield(%@)";
static NSString * const kRFUnionFormat = @"union %@";
static NSString * const kRFStructFormat = @"struct %@";
static NSString * const kRFFixedArrayFormat = @"%@[%ld]";
static NSDictionary *kRFMapDictionary;
static NSString * const kRFObjectTypeEncoding = @"@\"";
static NSString * const kRFArrayEncoding = @"[]";
static NSString * const kRFBitFieldEncoding = @"b";
static NSString * const kRFStructEncoding = @"{}";
static NSString * const kRFUnionEncoding = @"()";
static NSString * const kRFAssignmentOperator = @"=";
static NSString * const kRFPointerToTypeEncoding = @"^";
static NSString * const kRFDereferenceOperator = @"*";
static NSString * const kRFClassPrefix = @"@";
@interface RFTypeDecoder ()
+ (NSCharacterSet *)RF_pointerCharacterSet;
+ (NSCharacterSet *)RF_objectTypeEncodingCharacterSet;
+ (NSCharacterSet *)RF_valueTypePointerEncodingCharacterSet;
+ (NSCharacterSet *)RF_structEncodingCharacterSet;
+ (NSCharacterSet *)RF_unionEncodingCharacterSet;
+ (NSCharacterSet *)RF_bitFieldEncodingCharacterSet;
+ (NSCharacterSet *)RF_arrayEncodingCharacterSet;
+ (NSCharacterSet *)RF_fixedArrayEncodingCharacterSet;
+ (BOOL)RF_isPrefix:(NSCharacterSet *)prefixSet inString:(NSString *)string;
@end
@implementation RFTypeDecoder
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString * const path = [[NSBundle bundleForClass:self] pathForResource:kRFEncodingMapFile ofType:kRFPlistExtension];
kRFMapDictionary = [NSDictionary dictionaryWithContentsOfFile:path];
});
}
+ (NSString *)nameFromTypeEncoding:(NSString *)encoding {
NSString *result = nil;
if ([encoding length] == 1) {
result = kRFMapDictionary[encoding];
}
else {
result = [self checkCustomEncoding:encoding];
}
if ([result length] == 0) {
// in case no match is found, a fail-safe solution is to keep the encoding itself
result = [encoding copy];
}
return result;
}
+ (NSString *)checkCustomEncoding:(NSString *)encoding {
id result;
if ([self RF_isPrefix:[self RF_objectTypeEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFPointerFormat, [encoding stringByTrimmingCharactersInSet:[self RF_objectTypeEncodingCharacterSet]]];
}
else if ([self RF_isPrefix:[self RF_valueTypePointerEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFPointerFormat, [self nameFromTypeEncoding:[encoding stringByTrimmingCharactersInSet:[self RF_valueTypePointerEncodingCharacterSet]]]];
}
else if ([self RF_isPrefix:[self RF_arrayEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFArrayFormat, [self nameFromTypeEncoding:[encoding stringByTrimmingCharactersInSet:[self RF_arrayEncodingCharacterSet]]]];
}
else if ([self RF_isPrefix:[self RF_bitFieldEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFBitfieldFormat, [encoding stringByTrimmingCharactersInSet:[self RF_bitFieldEncodingCharacterSet]]];
}
else if ([self RF_isPrefix:[self RF_structEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFStructFormat, [encoding stringByTrimmingCharactersInSet:[self RF_structEncodingCharacterSet]]];
}
else if ([self RF_isPrefix:[self RF_unionEncodingCharacterSet] inString:encoding]) {
result = [NSString stringWithFormat:kRFUnionFormat, [encoding stringByTrimmingCharactersInSet:[self RF_unionEncodingCharacterSet]]];
}
else if ([self RF_isPrefix:[NSCharacterSet decimalDigitCharacterSet] inString:encoding] && [encoding rangeOfCharacterFromSet:[self RF_valueTypePointerEncodingCharacterSet]].location != NSNotFound) {
// in case the encoding is a fixed size c-style array, then the numbers preceding the '^' sign is the length of it
// the long casts are required for the %ld format specifier, which in turn is needed for the mac-compatibility, where NSInteger is long instead of int.
NSInteger arraySize = 0;
[[NSScanner scannerWithString:encoding] scanInteger:&arraySize];
NSString * const typeEncoding = [encoding stringByTrimmingCharactersInSet:[self RF_fixedArrayEncodingCharacterSet]];
NSString * const type = [self nameFromTypeEncoding:typeEncoding];
result = [NSString stringWithFormat:kRFFixedArrayFormat, type, (long)arraySize];
}
return result;
}
static NSMutableCharacterSet * RFPointerCharacterSet = nil;
static NSMutableCharacterSet * RFObjectTypeEncodingCharacterSet = nil;
+ (NSCharacterSet *)RF_pointerCharacterSet {
if (!RFPointerCharacterSet) {
RFPointerCharacterSet = [NSMutableCharacterSet whitespaceAndNewlineCharacterSet];
[RFPointerCharacterSet addCharactersInString:kRFDereferenceOperator];
}
return RFPointerCharacterSet;
}
+ (NSCharacterSet *)RF_objectTypeEncodingCharacterSet {
if (!RFObjectTypeEncodingCharacterSet) {
RFObjectTypeEncodingCharacterSet = [NSMutableCharacterSet whitespaceAndNewlineCharacterSet];
[RFObjectTypeEncodingCharacterSet addCharactersInString:kRFObjectTypeEncoding];
}
return RFObjectTypeEncodingCharacterSet;
}
+ (NSCharacterSet *)RF_valueTypePointerEncodingCharacterSet {
return [NSCharacterSet characterSetWithCharactersInString:kRFPointerToTypeEncoding];
}
+ (NSCharacterSet *)RF_structEncodingCharacterSet {
return [NSCharacterSet characterSetWithCharactersInString:kRFStructEncoding];
}
+ (NSCharacterSet *)RF_unionEncodingCharacterSet {
return [NSCharacterSet characterSetWithCharactersInString:kRFUnionEncoding];
}
+ (NSCharacterSet *)RF_bitFieldEncodingCharacterSet {
return [NSCharacterSet characterSetWithCharactersInString:kRFBitFieldEncoding];
}
+ (NSCharacterSet *)RF_arrayEncodingCharacterSet {
return [NSCharacterSet characterSetWithCharactersInString:kRFArrayEncoding];
}
+ (NSCharacterSet *)RF_fixedArrayEncodingCharacterSet {
NSMutableCharacterSet * const set = [NSMutableCharacterSet decimalDigitCharacterSet];
[set addCharactersInString:kRFPointerToTypeEncoding];
return set;
}
+ (BOOL)RF_isPrefix:(NSCharacterSet *)prefixSet inString:(NSString *)string {
NSAssert([string length] > 0, @"Assertion: string (%@) is not empty and is not nil.", string);
return [string rangeOfCharacterFromSet:prefixSet options:NSLiteralSearch range:NSMakeRange(0, 1)].location != NSNotFound;
}
+ (BOOL)RF_isPrimitiveType:(NSString *)typeEncoding {
return (![typeEncoding hasPrefix:kRFClassPrefix]);
}
+ (NSString *)RF_classNameFromTypeName:(NSString *)typeName {
return [typeName stringByTrimmingCharactersInSet:[self RF_pointerCharacterSet]];
}
@end