diff --git a/CoreParse/CoreParse.xcodeproj/project.pbxproj b/CoreParse/CoreParse.xcodeproj/project.pbxproj index f4cf1f1..438f0e3 100755 --- a/CoreParse/CoreParse.xcodeproj/project.pbxproj +++ b/CoreParse/CoreParse.xcodeproj/project.pbxproj @@ -298,6 +298,9 @@ 1FC18286139BA47D0027F597 /* CoreParse-template.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CoreParse-template.md"; sourceTree = ""; }; 1FE77D551375AE6F00879A41 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1FE77D821375EA8F00879A41 /* CoreParse Documentation */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "CoreParse Documentation"; sourceTree = BUILT_PRODUCTS_DIR; }; + + 549B67AB187F187100EF4A6E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; + 54197207188F7D49004240B4 /* CPRegexpRecogniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPRegexpRecogniser.h; sourceTree = ""; }; 54197208188F7D49004240B4 /* CPRegexpRecogniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPRegexpRecogniser.m; sourceTree = ""; }; 5419721C188F9A54004240B4 /* CPRegexpRecogniserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPRegexpRecogniserTest.m; sourceTree = ""; }; @@ -305,6 +308,7 @@ DA2B1DF61566B704002FDBD7 /* CPSTAssertionsTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPSTAssertionsTests.h; sourceTree = ""; }; DA2B1DF71566B704002FDBD7 /* CPSTAssertionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPSTAssertionsTests.m; sourceTree = ""; }; DC3E9BEE191DBAC800F6023C /* CPWillFinishDelegateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CPWillFinishDelegateTest.m; sourceTree = ""; }; + /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -732,6 +736,25 @@ productReference = 1F92817F145C11050033BC34 /* libCoreParse.a */; productType = "com.apple.product-type.library.static"; }; + 1F92818B145C11050033BC34 /* iOSCoreParseTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1F9281A3145C11050033BC34 /* Build configuration list for PBXNativeTarget "iOSCoreParseTests" */; + buildPhases = ( + 1F928187145C11050033BC34 /* Sources */, + 1F928188145C11050033BC34 /* Frameworks */, + 1F928189145C11050033BC34 /* Resources */, + 1F92818A145C11050033BC34 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 1F928193145C11050033BC34 /* PBXTargetDependency */, + ); + name = iOSCoreParseTests; + productName = iOSCoreParseTests; + productReference = 1F92818C145C11050033BC34 /* CoreParseTests.octest */; + productType = "com.apple.product-type.bundle.ocunit-test"; + }; 1FE77D811375EA8F00879A41 /* CoreParse Documentation */ = { isa = PBXNativeTarget; buildConfigurationList = 1FE77D8B1375EA8F00879A41 /* Build configuration list for PBXNativeTarget "CoreParse Documentation" */; @@ -753,7 +776,7 @@ 1F0E88EC130462F300537D04 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0640; ORGANIZATIONNAME = "In The Beginning..."; }; buildConfigurationList = 1F0E88EF130462F300537D04 /* Build configuration list for PBXProject "CoreParse" */; @@ -762,6 +785,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, + de, ); mainGroup = 1F0E88EA130462F300537D04; productRefGroup = 1F0E88F7130462F300537D04 /* Products */; @@ -961,6 +985,7 @@ isa = PBXVariantGroup; children = ( 1F0E8903130462F300537D04 /* en */, + 549B67AB187F187100EF4A6E /* de */, ); name = InfoPlist.strings; sourceTree = ""; @@ -969,6 +994,7 @@ isa = PBXVariantGroup; children = ( 1F0E8914130462F300537D04 /* en */, + 549B67AE187F187100EF4A6E /* de */, ); name = InfoPlist.strings; sourceTree = ""; @@ -979,8 +1005,14 @@ 1F0E891B130462F300537D04 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + ARCHS = ( + "$(ARCHS_STANDARD)", + armv7s, + ); + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1010,8 +1042,9 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = ""; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx; + SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; VALID_ARCHS = "i386 x86_64 armv7 armv7s"; }; @@ -1020,8 +1053,14 @@ 1F0E891C130462F300537D04 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + ARCHS = ( + "$(ARCHS_STANDARD)", + armv7s, + ); + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1085,10 +1124,13 @@ GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = "CoreParse/CoreParse-Info.plist"; - INSTALL_PATH = "@rpath"; + ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; TEST_AFTER_BUILD = YES; + VALID_ARCHS = "armv7 armv7s arm64"; WRAPPER_EXTENSION = framework; }; name = Debug; @@ -1122,10 +1164,14 @@ GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = "CoreParse/CoreParse-Info.plist"; + ONLY_ACTIVE_ARCH = NO; INSTALL_PATH = "@rpath"; OTHER_CFLAGS = ""; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; TEST_AFTER_BUILD = YES; + VALID_ARCHS = "armv7 armv7s arm64"; WRAPPER_EXTENSION = framework; }; name = Release; @@ -1175,8 +1221,8 @@ 1F92819E145C11050033BC34 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - ARCHS = "$(ARCHS_STANDARD)"; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = NO; COPY_PHASE_STRIP = NO; DSTROOT = /tmp/iOSCoreParse.dst; GCC_DYNAMIC_NO_PIC = NO; @@ -1194,15 +1240,15 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - VALID_ARCHS = "armv7 armv7s x86_x64"; + VALID_ARCHS = "armv6 armv7 armv7s arm64"; }; name = Debug; }; 1F92819F145C11050033BC34 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - ARCHS = "$(ARCHS_STANDARD)"; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = NO; COPY_PHASE_STRIP = YES; DSTROOT = /tmp/iOSCoreParse.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1215,7 +1261,64 @@ SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; VALIDATE_PRODUCT = YES; - VALID_ARCHS = "armv7 armv7s x86_x64"; + VALID_ARCHS = "armv6 armv7 armv7s arm64"; + }; + name = Release; + }; + 1F9281A0145C11050033BC34 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(DEVELOPER_LIBRARY_DIR)/Frameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CoreParse/CoreParse-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_THUMB_SUPPORT = NO; + INFOPLIST_FILE = "CoreParseTests/CoreParseTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-all_load", + ); + PRODUCT_NAME = CoreParseTests; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + 1F9281A1145C11050033BC34 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(DEVELOPER_LIBRARY_DIR)/Frameworks", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CoreParse/CoreParse-Prefix.pch"; + GCC_THUMB_SUPPORT = NO; + INFOPLIST_FILE = "CoreParseTests/CoreParseTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-all_load", + ); + PRODUCT_NAME = CoreParseTests; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = octest; }; name = Release; }; @@ -1223,7 +1326,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -1238,7 +1340,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; diff --git a/CoreParse/CoreParse/Grammar/CPGrammarInternal.m b/CoreParse/CoreParse/Grammar/CPGrammarInternal.m index fcd8ea4..a364c3a 100755 --- a/CoreParse/CoreParse/Grammar/CPGrammarInternal.m +++ b/CoreParse/CoreParse/Grammar/CPGrammarInternal.m @@ -41,7 +41,7 @@ - (NSString *)description NSUInteger idx = 0; for (CPRule *r in ordered) { - [s appendFormat:@"%3ld %@\n", (long)idx, r]; + [s appendFormat:@"%3lu %@\n", (unsigned long)idx, r]; idx++; } @@ -337,7 +337,7 @@ - (NSDictionary *)nameNewRules:(NSSet *)rhsElements withRules:(NSArray *)oldRule NSMutableDictionary *namedRules = [NSMutableDictionary dictionaryWithCapacity:[rhsElements count]]; for (CPRHSItem *item in rhsElements) { - [namedRules setObject:[self symbolNameNotInSet:symbolNames basedOnName:[NSString stringWithFormat:@"RHS%ld", (long)name]] forKey:item]; + [namedRules setObject:[self symbolNameNotInSet:symbolNames basedOnName:[NSString stringWithFormat:@"RHS%lu", (unsigned long)name]] forKey:item]; name++; } return namedRules; diff --git a/CoreParse/CoreParse/Parsers/CPShiftReduceParser.m b/CoreParse/CoreParse/Parsers/CPShiftReduceParser.m index 0804af3..8e01faf 100755 --- a/CoreParse/CoreParse/Parsers/CPShiftReduceParser.m +++ b/CoreParse/CoreParse/Parsers/CPShiftReduceParser.m @@ -253,8 +253,8 @@ - (CPRecoveryAction *)error:(CPTokenStream *)tokenStream expecting:(NSSet *)acce } else { - CPToken *t = [tokenStream peekToken]; - NSLog(@"%ld:%ld: parse error. Expected %@, found %@", (long)[t lineNumber] + 1, (long)[t columnNumber] + 1, acceptableTokens, t); + //CPToken *t = [tokenStream peekToken]; + //NSLog(@"%lu:%d: parse error. Expected %@, found %@", (unsigned long)[t lineNumber], [t columnNumber], acceptableTokens, t); return nil; } } diff --git a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceAction.m b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceAction.m index f03e1d2..28ecfef 100755 --- a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceAction.m +++ b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceAction.m @@ -17,32 +17,34 @@ kActionTypeAccept } ActionType; -typedef union -{ - NSUInteger shift; - CPRule *reductionRule; -} -ActionDetails; +//typedef union +//{ +// NSUInteger shift; +// CPRule *reductionRule; +//} +//ActionDetails; @implementation CPShiftReduceAction { ActionType type; - ActionDetails details; + NSUInteger shift; + CPRule *reductionRule; +// ActionDetails details; } + (id)shiftAction:(NSUInteger)shiftLocation { - return [[[self alloc] initWithShift:shiftLocation] autorelease]; + return [[self alloc] initWithShift:shiftLocation]; } + (id)reduceAction:(CPRule *)reduction { - return [[[self alloc] initWithReductionRule:reduction] autorelease]; + return [[self alloc] initWithReductionRule:reduction]; } + (id)acceptAction { - return [[[self alloc] init] autorelease]; + return [[self alloc] init]; } - (id)initWithShift:(NSUInteger)shiftLocation @@ -52,7 +54,7 @@ - (id)initWithShift:(NSUInteger)shiftLocation if (nil != self) { type = kActionTypeShift; - details.shift = shiftLocation; + shift = shiftLocation; } return self; @@ -65,7 +67,7 @@ - (id)initWithReductionRule:(CPRule *)reduction if (nil != self) { type = kActionTypeReduce; - details.reductionRule = [reduction retain]; + reductionRule = reduction; } return self; @@ -97,10 +99,10 @@ - (id)initWithCoder:(NSCoder *)aDecoder switch (type) { case kActionTypeShift: - details.shift = [aDecoder decodeIntegerForKey:CPShiftReduceActionShiftKey]; + shift = [aDecoder decodeIntegerForKey:CPShiftReduceActionShiftKey]; break; case kActionTypeReduce: - details.reductionRule = [[aDecoder decodeObjectForKey:CPShiftReduceActionRuleKey] retain]; + reductionRule = [aDecoder decodeObjectForKey:CPShiftReduceActionRuleKey]; case kActionTypeAccept: default: break; @@ -116,10 +118,10 @@ - (void)encodeWithCoder:(NSCoder *)aCoder switch (type) { case kActionTypeShift: - [aCoder encodeInteger:details.shift forKey:CPShiftReduceActionShiftKey]; + [aCoder encodeInteger:shift forKey:CPShiftReduceActionShiftKey]; break; case kActionTypeReduce: - [aCoder encodeObject:details.reductionRule forKey:CPShiftReduceActionRuleKey]; + [aCoder encodeObject:reductionRule forKey:CPShiftReduceActionRuleKey]; case kActionTypeAccept: default: break; @@ -130,9 +132,8 @@ - (void)dealloc { if (kActionTypeReduce == type) { - [details.reductionRule release]; + reductionRule = nil; } - [super dealloc]; } @@ -153,12 +154,12 @@ - (BOOL)isAccept - (NSUInteger)newState { - return details.shift; + return shift; } - (CPRule *)reductionRule { - return details.reductionRule; + return reductionRule; } - (NSUInteger)hash @@ -179,9 +180,9 @@ - (BOOL)isEqual:(id)object switch (type) { case kActionTypeShift: - return [other newState] == details.shift; + return [other newState] == shift; case kActionTypeReduce: - return [other reductionRule] == details.reductionRule; + return [other reductionRule] == reductionRule; case kActionTypeAccept: return YES; } @@ -213,9 +214,9 @@ - (NSString *)description switch (type) { case kActionTypeShift: - return [NSString stringWithFormat:@"s%ld", (long)details.shift]; + return [NSString stringWithFormat:@"s%lu", (unsigned long)shift]; case kActionTypeReduce: - return [NSString stringWithFormat:@"r%@", [details.reductionRule name]]; + return [NSString stringWithFormat:@"r%@", [reductionRule name]]; case kActionTypeAccept: return @"acc"; } @@ -226,9 +227,9 @@ - (NSString *)descriptionWithGrammar:(CPGrammar *)g switch (type) { case kActionTypeShift: - return [NSString stringWithFormat:@"s%ld", (long)details.shift]; + return [NSString stringWithFormat:@"s%lu", (unsigned long)shift]; case kActionTypeReduce: - return [NSString stringWithFormat:@"r%ld", (long)[g indexOfRule:details.reductionRule]]; + return [NSString stringWithFormat:@"r%lu", (unsigned long)[g indexOfRule:reductionRule]]; case kActionTypeAccept: return @"acc"; } diff --git a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.h b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.h index 4ae9005..a1fc9a4 100755 --- a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.h +++ b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.h @@ -14,9 +14,15 @@ #import "CPGrammar.h" @interface CPShiftReduceActionTable : NSObject -{} +{ +@private + + NSMutableDictionary * table; + int64_t capacity; -- (id)initWithCapacity:(NSUInteger)capacity; +} + +- (id)initWithCapacity:(int64_t)capacity; - (BOOL)setAction:(CPShiftReduceAction *)action forState:(NSUInteger)state name:(NSString *)token; diff --git a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.m b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.m index cef7cc6..2fb23a8 100755 --- a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.m +++ b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceActionTable.m @@ -6,32 +6,30 @@ // Copyright 2011 In The Beginning... All rights reserved. // +#import "CPShiftReduceAction.h" #import "CPShiftReduceActionTable.h" - #import "CPItem.h" #import "CPGrammarSymbol.h" #import "CPShiftReduceAction.h" @implementation CPShiftReduceActionTable -{ - NSMutableDictionary **table; - NSUInteger capacity; -} -- (id)initWithCapacity:(NSUInteger)initCapacity +- (id)initWithCapacity:(int64_t)initCapacity { self = [super init]; if (nil != self) { capacity = initCapacity; - table = malloc(capacity * sizeof(NSMutableDictionary *)); - for (NSUInteger buildingState = 0; buildingState < capacity; buildingState++) + //table = malloc(capacity * sizeof(NSMutableDictionary *)); + table = [[NSMutableDictionary alloc] init]; + + for (int buildingState = 0; buildingState < capacity; buildingState++) { - table[buildingState] = [[NSMutableDictionary alloc] init]; + NSNumber * tbs = [[NSNumber alloc] initWithInt:buildingState]; + [table setObject:[[NSMutableDictionary alloc] init] forKey:tbs]; } } - return self; } @@ -41,15 +39,16 @@ - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; + capacity = 0; + if (nil != self) { NSArray *rows = [aDecoder decodeObjectForKey:CPShiftReduceActionTableTableKey]; - capacity = [rows count]; - table = malloc(capacity * sizeof(NSMutableDictionary *)); - [rows getObjects:table range:NSMakeRange(0, capacity)]; - for (NSUInteger i = 0; i < capacity; i++) + capacity = rows.count; + table = [[NSMutableDictionary alloc] init]; + for (int i = 0; i < capacity; i++) { - [table[i] retain]; + [table setObject:rows[i] forKey:[[NSNumber alloc ] initWithInt:i]]; } } @@ -58,24 +57,14 @@ - (id)initWithCoder:(NSCoder *)aDecoder - (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:[NSArray arrayWithObjects:table count:capacity] forKey:CPShiftReduceActionTableTableKey]; + [aCoder encodeObject:[NSArray arrayWithObjects:&table count:(int32_t)capacity] forKey:CPShiftReduceActionTableTableKey]; } -- (void)dealloc -{ - for (NSUInteger state = 0; state < capacity; state++) - { - [table[state] release]; - } - free(table); - - [super dealloc]; -} - (BOOL)setAction:(CPShiftReduceAction *)action forState:(NSUInteger)state name:(NSString *)token { - NSMutableDictionary *row = table[state]; - if (nil != [row objectForKey:token] && ![[row objectForKey:token] isEqualToShiftReduceAction:action]) + NSMutableDictionary *row = [table objectForKey:[[NSNumber alloc] initWithLong:state]]; + if (nil != [row objectForKey:token] && ![[row objectForKey:token] isEqual:action]) { return NO; } @@ -85,20 +74,23 @@ - (BOOL)setAction:(CPShiftReduceAction *)action forState:(NSUInteger)state name: - (CPShiftReduceAction *)actionForState:(NSUInteger)state token:(CPToken *)token { - return [table[state] objectForKey:token.name]; + NSMutableDictionary * tmp = [table objectForKey:[[NSNumber alloc] initWithLong:state]]; + return [tmp objectForKey:token.name]; } - (NSSet *)acceptableTokenNamesForState:(NSUInteger)state { NSMutableSet *toks = [NSMutableSet set]; - for (NSString *tok in table[state]) + for (NSString *tok in [table objectForKey:[[NSNumber alloc] initWithLong:state]]) { - if (nil != [table[state] objectForKey:tok]) + NSMutableDictionary * tmp = [table objectForKey:[[NSNumber alloc] initWithLong:state]]; + if (nil != [tmp objectForKey:tok]) { [toks addObject:tok]; } } - return [[toks copy] autorelease]; + return [toks copy]; + return nil; } - (NSString *)description @@ -108,9 +100,9 @@ - (NSString *)description NSMutableString *s = [NSMutableString string]; NSMutableSet *keys = [NSMutableSet set]; NSUInteger width = 3; - for (NSUInteger state = 0; state < capacity; state++) + for (int state = 0; state < capacity; state++) { - [keys addObjectsFromArray:[table[state] allKeys]]; + [keys addObjectsFromArray:[[table objectForKey:[[NSNumber alloc] initWithInt:state]] allKeys]]; } for (NSString *key in keys) { @@ -130,10 +122,10 @@ - (NSString *)description [s appendString:@"\n"]; NSUInteger idx = 0; - for (NSUInteger state = 0; state < capacity; state++) + for (int state = 0; state < capacity; state++) { - NSDictionary *row = table[state]; - [s appendFormat:@"%5ld | ", (long)idx]; + NSDictionary *row = [table objectForKey:[[NSNumber alloc] initWithInt:state]]; + [s appendFormat:@"%5lu | ", (unsigned long)idx]; for (NSString *key in orderedKeys) { CPShiftReduceAction *action = [row objectForKey:key]; @@ -169,9 +161,9 @@ - (NSString *)descriptionWithGrammar:(CPGrammar *)g NSMutableString *s = [NSMutableString string]; NSMutableSet *keys = [NSMutableSet set]; NSUInteger width = 3; - for (NSUInteger state = 0; state < capacity; state++) + for (int state = 0; state < capacity; state++) { - [keys addObjectsFromArray:[table[state] allKeys]]; + [keys addObjectsFromArray:[[table objectForKey:[[NSNumber alloc] initWithInt:state]] allKeys]]; } for (NSString *key in keys) { @@ -191,10 +183,10 @@ - (NSString *)descriptionWithGrammar:(CPGrammar *)g [s appendString:@"\n"]; NSUInteger idx = 0; - for (NSUInteger state = 0; state < capacity; state++) + for (int state = 0; state < capacity; state++) { - NSDictionary *row = table[state]; - [s appendFormat:@"%5ld | ", (long)idx]; + NSDictionary *row = [table objectForKey:[[NSNumber alloc] initWithInt:state]]; + [s appendFormat:@"%5lu | ", (unsigned long)idx]; for (NSString *key in orderedKeys) { CPShiftReduceAction *action = [row objectForKey:key]; diff --git a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceState.m b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceState.m index 7281881..ba3ee4a 100755 --- a/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceState.m +++ b/CoreParse/CoreParse/Parsers/CPShiftReduceParsers/CPShiftReduceState.m @@ -22,7 +22,7 @@ @implementation CPShiftReduceState + (id)shiftReduceStateWithObject:(NSObject *)object state:(NSUInteger)state { - return [[[self alloc] initWithObject:object state:state] autorelease]; + return [[self alloc] initWithObject:object state:state]; } - (id)initWithObject:(NSObject *)initObject state:(NSUInteger)initState @@ -38,12 +38,6 @@ - (id)initWithObject:(NSObject *)initObject state:(NSUInteger)initState return self; } -- (void)dealloc -{ - [object release]; - - [super dealloc]; -} - (NSString *)description { diff --git a/CoreParse/CoreParse/Parsers/Error Recovery/CPRecoveryAction.m b/CoreParse/CoreParse/Parsers/Error Recovery/CPRecoveryAction.m index d5af294..bab10c3 100755 --- a/CoreParse/CoreParse/Parsers/Error Recovery/CPRecoveryAction.m +++ b/CoreParse/CoreParse/Parsers/Error Recovery/CPRecoveryAction.m @@ -15,17 +15,17 @@ @implementation CPRecoveryAction + (id)recoveryActionWithAdditionalToken:(CPToken *)token { - return [[[self alloc] initWithAdditionalToken:token] autorelease]; + return [[self alloc] initWithAdditionalToken:token]; } + (id)recoveryActionDeletingCurrentToken { - return [[[self alloc] initWithDeleteAction] autorelease]; + return [[self alloc] initWithDeleteAction]; } + (id)recoveryActionStop { - return [[[self alloc] initWithStopAction] autorelease]; + return [[self alloc] initWithStopAction]; } - (id)initWithAdditionalToken:(CPToken *)token diff --git a/CoreParse/CoreParse/de.lproj/InfoPlist.strings b/CoreParse/CoreParse/de.lproj/InfoPlist.strings new file mode 100755 index 0000000..477b28f --- /dev/null +++ b/CoreParse/CoreParse/de.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/CoreParse/CoreParseTests/de.lproj/InfoPlist.strings b/CoreParse/CoreParseTests/de.lproj/InfoPlist.strings new file mode 100755 index 0000000..477b28f --- /dev/null +++ b/CoreParse/CoreParseTests/de.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MapView/Info.plist b/MapView/Info.plist index 3dfcc3c..56f3b08 100755 --- a/MapView/Info.plist +++ b/MapView/Info.plist @@ -24,6 +24,8 @@ 1.0 LSRequiresIPhoneOS + NSLocationWhenInUseUsageDescription + $YOURDESCRIPTION NSMainNibFile MainWindow diff --git a/MapView/Map/FMDB/FMDatabase.h b/MapView/Map/FMDB/FMDatabase.h deleted file mode 100755 index 398381b..0000000 --- a/MapView/Map/FMDB/FMDatabase.h +++ /dev/null @@ -1,116 +0,0 @@ -#import -#import "sqlite3.h" -#import "FMResultSet.h" - -@interface FMDatabase : NSObject -{ - sqlite3* db; - NSString* databasePath; - BOOL logsErrors; - BOOL crashOnErrors; - BOOL inUse; - BOOL inTransaction; - BOOL traceExecution; - BOOL checkedOut; - int busyRetryTimeout; - BOOL shouldCacheStatements; - NSMutableDictionary *cachedStatements; -} - - -+ (id)databaseWithPath:(NSString*)inPath; -- (id)initWithPath:(NSString*)inPath; - -- (BOOL) open; -#if SQLITE_VERSION_NUMBER >= 3005000 -- (BOOL) openWithFlags:(int)flags; -#endif -- (BOOL) close; -- (BOOL) goodConnection; -- (void) clearCachedStatements; - -// encryption methods. You need to have purchased the sqlite encryption extensions for these to work. -- (BOOL) setKey:(NSString*)key; -- (BOOL) rekey:(NSString*)key; - - -- (NSString *) databasePath; - -- (NSString*) lastErrorMessage; - -- (int) lastErrorCode; -- (BOOL) hadError; -- (sqlite_int64) lastInsertRowId; - -- (sqlite3*) sqliteHandle; - -- (BOOL) executeUpdate:(NSString*)sql, ...; -- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; -- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead. - -- (id) executeQuery:(NSString*)sql, ...; -- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; -- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead. - -- (BOOL) rollback; -- (BOOL) commit; -- (BOOL) beginTransaction; -- (BOOL) beginDeferredTransaction; - -- (BOOL)logsErrors; -- (void)setLogsErrors:(BOOL)flag; - -- (BOOL)crashOnErrors; -- (void)setCrashOnErrors:(BOOL)flag; - -- (BOOL)inUse; -- (void)setInUse:(BOOL)value; - -- (BOOL)inTransaction; -- (void)setInTransaction:(BOOL)flag; - -- (BOOL)traceExecution; -- (void)setTraceExecution:(BOOL)flag; - -- (BOOL)checkedOut; -- (void)setCheckedOut:(BOOL)flag; - -- (int)busyRetryTimeout; -- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout; - -- (BOOL)shouldCacheStatements; -- (void)setShouldCacheStatements:(BOOL)value; - -- (NSMutableDictionary *)cachedStatements; -- (void)setCachedStatements:(NSMutableDictionary *)value; - - -+ (NSString*) sqliteLibVersion; - - -- (int)changes; - -@end - -@interface FMStatement : NSObject { - sqlite3_stmt *statement; - NSString *query; - long useCount; -} - - -- (void) close; -- (void) reset; - -- (sqlite3_stmt *)statement; -- (void)setStatement:(sqlite3_stmt *)value; - -- (NSString *)query; -- (void)setQuery:(NSString *)value; - -- (long)useCount; -- (void)setUseCount:(long)value; - - -@end - diff --git a/MapView/Map/FMDB/FMDatabase.m b/MapView/Map/FMDB/FMDatabase.m deleted file mode 100755 index ee4295c..0000000 --- a/MapView/Map/FMDB/FMDatabase.m +++ /dev/null @@ -1,753 +0,0 @@ -#import "FMDatabase.h" -#import "unistd.h" - -@implementation FMDatabase - -+ (id)databaseWithPath:(NSString*)aPath { - return [[[self alloc] initWithPath:aPath] autorelease]; -} - -- (id)initWithPath:(NSString*)aPath { - self = [super init]; - - if (self) { - databasePath = [aPath copy]; - db = 0x00; - logsErrors = 0x00; - crashOnErrors = 0x00; - busyRetryTimeout = 0x00; - } - - return self; -} - -- (void)dealloc { - [self close]; - - [cachedStatements release]; - [databasePath release]; - - [super dealloc]; -} - -+ (NSString*) sqliteLibVersion { - return [NSString stringWithFormat:@"%s", sqlite3_libversion()]; -} - -- (NSString *) databasePath { - return databasePath; -} - -- (sqlite3*) sqliteHandle { - return db; -} - -- (BOOL) open { - int err = sqlite3_open([databasePath fileSystemRepresentation], &db ); - if(err != SQLITE_OK) { - NSLog(@"error opening!: %d", err); - return NO; - } - - return YES; -} - -#if SQLITE_VERSION_NUMBER >= 3005000 -- (BOOL) openWithFlags:(int)flags { - int err = sqlite3_open_v2([databasePath fileSystemRepresentation], &db, flags, NULL /* Name of VFS module to use */); - if(err != SQLITE_OK) { - NSLog(@"error opening!: %d", err); - return NO; - } - return YES; -} -#endif - - -- (BOOL) close { - - [self clearCachedStatements]; - - if (!db) { - return YES; - } - - int rc; - BOOL retry; - int numberOfRetries = 0; - do { - retry = NO; - rc = sqlite3_close(db); - if (SQLITE_BUSY == rc) { - retry = YES; - usleep(20); - if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { - NSLog(@"%s:%d", __FUNCTION__, __LINE__); - NSLog(@"Database busy, unable to close"); - return NO; - } - } - else if (SQLITE_OK != rc) { - NSLog(@"error closing!: %d", rc); - } - } - while (retry); - - db = nil; - return YES; -} - -- (void) clearCachedStatements { - - NSEnumerator *e = [cachedStatements objectEnumerator]; - FMStatement *cachedStmt; - - while ((cachedStmt = [e nextObject])) { - [cachedStmt close]; - } - - [cachedStatements removeAllObjects]; -} - -- (FMStatement*) cachedStatementForQuery:(NSString*)query { - return [cachedStatements objectForKey:query]; -} - -- (void) setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query { - //NSLog(@"setting query: %@", query); - query = [query copy]; // in case we got handed in a mutable string... - [statement setQuery:query]; - [cachedStatements setObject:statement forKey:query]; - [query release]; -} - - -- (BOOL) rekey:(NSString*)key { -#ifdef SQLITE_HAS_CODEC - if (!key) { - return NO; - } - - int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String])); - - if (rc != SQLITE_OK) { - NSLog(@"error on rekey: %d", rc); - NSLog(@"%@", [self lastErrorMessage]); - } - - return (rc == SQLITE_OK); -#else - return NO; -#endif -} - -- (BOOL) setKey:(NSString*)key { -#ifdef SQLITE_HAS_CODEC - if (!key) { - return NO; - } - - int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String])); - - return (rc == SQLITE_OK); -#else - return NO; -#endif -} - -- (BOOL) goodConnection { - - if (!db) { - return NO; - } - - FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"]; - - if (rs) { - [rs close]; - return YES; - } - - return NO; -} - -- (void) compainAboutInUse { - NSLog(@"The FMDatabase %@ is currently in use.", self); - - if (crashOnErrors) { - NSAssert1(false, @"The FMDatabase %@ is currently in use.", self); - } -} - -- (NSString*) lastErrorMessage { - return [NSString stringWithUTF8String:sqlite3_errmsg(db)]; -} - -- (BOOL) hadError { - int lastErrCode = [self lastErrorCode]; - - return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW); -} - -- (int) lastErrorCode { - return sqlite3_errcode(db); -} - -- (sqlite_int64) lastInsertRowId { - - if (inUse) { - [self compainAboutInUse]; - return NO; - } - [self setInUse:YES]; - - sqlite_int64 ret = sqlite3_last_insert_rowid(db); - - [self setInUse:NO]; - - return ret; -} - -- (void) bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; { - - if ((!obj) || ((NSNull *)obj == [NSNull null])) { - sqlite3_bind_null(pStmt, idx); - } - - // FIXME - someday check the return codes on these binds. - else if ([obj isKindOfClass:[NSData class]]) { - sqlite3_bind_blob(pStmt, idx, [obj bytes], (int)[obj length], SQLITE_STATIC); - } - else if ([obj isKindOfClass:[NSDate class]]) { - sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); - } - else if ([obj isKindOfClass:[NSNumber class]]) { - - if (strcmp([obj objCType], @encode(BOOL)) == 0) { - sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); - } - else if (strcmp([obj objCType], @encode(int)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longValue]); - } - else if (strcmp([obj objCType], @encode(long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longValue]); - } - else if (strcmp([obj objCType], @encode(long long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); - } - else if (strcmp([obj objCType], @encode(float)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj floatValue]); - } - else if (strcmp([obj objCType], @encode(double)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj doubleValue]); - } - else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } - } - else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } -} - -- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args { - - if (inUse) { - [self compainAboutInUse]; - return nil; - } - - [self setInUse:YES]; - - FMResultSet *rs = nil; - - int rc = 0x00;; - sqlite3_stmt *pStmt = 0x00;; - FMStatement *statement = 0x00; - - if (traceExecution && sql) { - NSLog(@"%@ executeQuery: %@", self, sql); - } - - if (shouldCacheStatements) { - statement = [self cachedStatementForQuery:sql]; - pStmt = statement ? [statement statement] : 0x00; - } - - int numberOfRetries = 0; - BOOL retry = NO; - - if (!pStmt) { - do { - retry = NO; - rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0); - - if (SQLITE_BUSY == rc) { - retry = YES; - usleep(20); - - if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); - NSLog(@"Database busy"); - sqlite3_finalize(pStmt); - [self setInUse:NO]; - return nil; - } - } - else if (SQLITE_OK != rc) { - - - if (logsErrors) { - NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - NSLog(@"DB Query: %@", sql); - if (crashOnErrors) { -//#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR -// asm{ trap }; -//#endif - NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - } - } - - sqlite3_finalize(pStmt); - - [self setInUse:NO]; - return nil; - } - } - while (retry); - } - - id obj; - int idx = 0; - int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!) - - while (idx < queryCount) { - - if (arrayArgs) { - obj = [arrayArgs objectAtIndex:idx]; - } - else { - obj = va_arg(args, id); - } - - if (traceExecution) { - NSLog(@"obj: %@", obj); - } - - idx++; - - [self bindObject:obj toColumn:idx inStatement:pStmt]; - } - - if (idx != queryCount) { - NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)"); - sqlite3_finalize(pStmt); - [self setInUse:NO]; - return nil; - } - - [statement retain]; // to balance the release below - - if (!statement) { - statement = [[FMStatement alloc] init]; - [statement setStatement:pStmt]; - - if (shouldCacheStatements) { - [self setCachedStatement:statement forQuery:sql]; - } - } - - // the statement gets close in rs's dealloc or [rs close]; - rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self]; - [rs setQuery:sql]; - - statement.useCount = statement.useCount + 1; - - [statement release]; - - [self setInUse:NO]; - - return rs; -} - -- (id) executeQuery:(NSString*)sql, ... { - va_list args; - va_start(args, sql); - - id result = [self executeQuery:sql withArgumentsInArray:nil orVAList:args]; - - va_end(args); - return result; -} - -- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments { - return [self executeQuery:sql withArgumentsInArray:arguments orVAList:nil]; -} - -- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args { - - if (inUse) { - [self compainAboutInUse]; - return NO; - } - - [self setInUse:YES]; - - int rc = 0x00; - sqlite3_stmt *pStmt = 0x00; - FMStatement *cachedStmt = 0x00; - - if (traceExecution && sql) { - NSLog(@"%@ executeUpdate: %@", self, sql); - } - - if (shouldCacheStatements) { - cachedStmt = [self cachedStatementForQuery:sql]; - pStmt = cachedStmt ? [cachedStmt statement] : 0x00; - } - - int numberOfRetries = 0; - BOOL retry = NO; - - if (!pStmt) { - - do { - retry = NO; - rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0); - if (SQLITE_BUSY == rc) { - retry = YES; - usleep(20); - - if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); - NSLog(@"Database busy"); - sqlite3_finalize(pStmt); - [self setInUse:NO]; - return NO; - } - } - else if (SQLITE_OK != rc) { - - - if (logsErrors) { - NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - NSLog(@"DB Query: %@", sql); - if (crashOnErrors) { -//#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR -// asm{ trap }; -//#endif - NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - } - } - - sqlite3_finalize(pStmt); - [self setInUse:NO]; - - return NO; - } - } - while (retry); - } - - - id obj; - int idx = 0; - int queryCount = sqlite3_bind_parameter_count(pStmt); - - while (idx < queryCount) { - - if (arrayArgs) { - obj = [arrayArgs objectAtIndex:idx]; - } - else { - obj = va_arg(args, id); - } - - - if (traceExecution) { - NSLog(@"obj: %@", obj); - } - - idx++; - - [self bindObject:obj toColumn:idx inStatement:pStmt]; - } - - if (idx != queryCount) { - NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql); - sqlite3_finalize(pStmt); - [self setInUse:NO]; - return NO; - } - - /* Call sqlite3_step() to run the virtual machine. Since the SQL being - ** executed is not a SELECT statement, we assume no data will be returned. - */ - numberOfRetries = 0; - do { - rc = sqlite3_step(pStmt); - retry = NO; - - if (SQLITE_BUSY == rc) { - // this will happen if the db is locked, like if we are doing an update or insert. - // in that case, retry the step... and maybe wait just 10 milliseconds. - retry = YES; - usleep(20); - - if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); - NSLog(@"Database busy"); - retry = NO; - } - } - else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { - // all is well, let's return. - } - else if (SQLITE_ERROR == rc) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(db)); - NSLog(@"DB Query: %@", sql); - } - else if (SQLITE_MISUSE == rc) { - // uh oh. - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(db)); - NSLog(@"DB Query: %@", sql); - } - else { - // wtf? - NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db)); - NSLog(@"DB Query: %@", sql); - } - - } while (retry); - - assert( rc!=SQLITE_ROW ); - - - if (shouldCacheStatements && !cachedStmt) { - cachedStmt = [[FMStatement alloc] init]; - - [cachedStmt setStatement:pStmt]; - - [self setCachedStatement:cachedStmt forQuery:sql]; - - [cachedStmt release]; - } - - if (cachedStmt) { - cachedStmt.useCount = cachedStmt.useCount + 1; - rc = sqlite3_reset(pStmt); - } - else { - /* Finalize the virtual machine. This releases all memory and other - ** resources allocated by the sqlite3_prepare() call above. - */ - rc = sqlite3_finalize(pStmt); - } - - [self setInUse:NO]; - - return (rc == SQLITE_OK); -} - - -- (BOOL) executeUpdate:(NSString*)sql, ... { - va_list args; - va_start(args, sql); - - BOOL result = [self executeUpdate:sql withArgumentsInArray:nil orVAList:args]; - - va_end(args); - return result; -} - - - -- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments { - return [self executeUpdate:sql withArgumentsInArray:arguments orVAList:nil]; -} - -/* -- (id) executeUpdate:(NSString *)sql arguments:(va_list)args { - -} -*/ - -- (BOOL) rollback { - BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"]; - if (b) { - inTransaction = NO; - } - return b; -} - -- (BOOL) commit { - BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"]; - if (b) { - inTransaction = NO; - } - return b; -} - -- (BOOL) beginDeferredTransaction { - BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"]; - if (b) { - inTransaction = YES; - } - return b; -} - -- (BOOL) beginTransaction { - BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"]; - if (b) { - inTransaction = YES; - } - return b; -} - -- (BOOL)logsErrors { - return logsErrors; -} -- (void)setLogsErrors:(BOOL)flag { - logsErrors = flag; -} - -- (BOOL)crashOnErrors { - return crashOnErrors; -} -- (void)setCrashOnErrors:(BOOL)flag { - crashOnErrors = flag; -} - -- (BOOL)inUse { - return inUse || inTransaction; -} - -- (void) setInUse:(BOOL)b { - inUse = b; -} - -- (BOOL)inTransaction { - return inTransaction; -} -- (void)setInTransaction:(BOOL)flag { - inTransaction = flag; -} - -- (BOOL)traceExecution { - return traceExecution; -} -- (void)setTraceExecution:(BOOL)flag { - traceExecution = flag; -} - -- (BOOL)checkedOut { - return checkedOut; -} -- (void)setCheckedOut:(BOOL)flag { - checkedOut = flag; -} - - -- (int)busyRetryTimeout { - return busyRetryTimeout; -} -- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout { - busyRetryTimeout = newBusyRetryTimeout; -} - - -- (BOOL)shouldCacheStatements { - return shouldCacheStatements; -} - -- (void)setShouldCacheStatements:(BOOL)value { - - shouldCacheStatements = value; - - if (shouldCacheStatements && !cachedStatements) { - [self setCachedStatements:[NSMutableDictionary dictionary]]; - } - - if (!shouldCacheStatements) { - [self setCachedStatements:nil]; - } -} - -- (NSMutableDictionary *) cachedStatements { - return cachedStatements; -} - -- (void)setCachedStatements:(NSMutableDictionary *)value { - if (cachedStatements != value) { - [cachedStatements release]; - cachedStatements = [value retain]; - } -} - - -- (int)changes { - return(sqlite3_changes(db)); -} - -@end - - - -@implementation FMStatement - -- (void)dealloc { - [self close]; - [query release]; - [super dealloc]; -} - - -- (void) close { - if (statement) { - sqlite3_finalize(statement); - statement = 0x00; - } -} - -- (void) reset { - if (statement) { - sqlite3_reset(statement); - } -} - -- (sqlite3_stmt *) statement { - return statement; -} - -- (void)setStatement:(sqlite3_stmt *)value { - statement = value; -} - -- (NSString *) query { - return query; -} - -- (void)setQuery:(NSString *)value { - if (query != value) { - [query release]; - query = [value retain]; - } -} - -- (long)useCount { - return useCount; -} - -- (void)setUseCount:(long)value { - if (useCount != value) { - useCount = value; - } -} - -- (NSString*) description { - return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], useCount, query]; -} - - -@end - diff --git a/MapView/Map/FMDB/FMDatabaseAdditions.h b/MapView/Map/FMDB/FMDatabaseAdditions.h deleted file mode 100755 index 3846268..0000000 --- a/MapView/Map/FMDB/FMDatabaseAdditions.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// FMDatabaseAdditions.h -// fmkit -// -// Created by August Mueller on 10/30/05. -// Copyright 2005 Flying Meat Inc.. All rights reserved. -// - -#import -@interface FMDatabase (FMDatabaseAdditions) - - -- (int)intForQuery:(NSString*)objs, ...; -- (long)longForQuery:(NSString*)objs, ...; -- (BOOL)boolForQuery:(NSString*)objs, ...; -- (double)doubleForQuery:(NSString*)objs, ...; -- (NSString*)stringForQuery:(NSString*)objs, ...; -- (NSData*)dataForQuery:(NSString*)objs, ...; -- (NSDate*)dateForQuery:(NSString*)objs, ...; - -// Notice that there's no dataNoCopyForQuery:. -// That would be a bad idea, because we close out the result set, and then what -// happens to the data that we just didn't copy? Who knows, not I. - - -- (BOOL)tableExists:(NSString*)tableName; -- (FMResultSet*)getSchema; -- (FMResultSet*)getTableSchema:(NSString*)tableName; -- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName; - -@end diff --git a/MapView/Map/FMDB/FMDatabaseAdditions.m b/MapView/Map/FMDB/FMDatabaseAdditions.m deleted file mode 100755 index 2e1287a..0000000 --- a/MapView/Map/FMDB/FMDatabaseAdditions.m +++ /dev/null @@ -1,114 +0,0 @@ -// -// FMDatabaseAdditions.m -// fmkit -// -// Created by August Mueller on 10/30/05. -// Copyright 2005 Flying Meat Inc.. All rights reserved. -// - -#import "FMDatabase.h" -#import "FMDatabaseAdditions.h" - -@implementation FMDatabase (FMDatabaseAdditions) - -#define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ -va_list args; \ -va_start(args, query); \ -FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orVAList:args]; \ -va_end(args); \ -if (![resultSet next]) { return (type)0; } \ -type ret = [resultSet sel:0]; \ -[resultSet close]; \ -[resultSet setParentDB:nil]; \ -return ret; - - -- (NSString*)stringForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); -} - -- (int)intForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); -} - -- (long)longForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); -} - -- (BOOL)boolForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); -} - -- (double)doubleForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); -} - -- (NSData*)dataForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); -} - -- (NSDate*)dateForQuery:(NSString*)query, ...; { - RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); -} - - -//check if table exist in database (patch from OZLB) -- (BOOL)tableExists:(NSString*)tableName { - - BOOL returnBool; - //lower case table name - tableName = [tableName lowercaseString]; - //search in sqlite_master table if table exists - FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; - //if at least one next exists, table exists - returnBool = [rs next]; - //close and free object - [rs close]; - - return returnBool; -} - -//get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] -//check if table exist in database (patch from OZLB) -- (FMResultSet*)getSchema { - - //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] - FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; - - return rs; -} - -//get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] -- (FMResultSet*)getTableSchema:(NSString*)tableName { - - //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] - FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"PRAGMA table_info(%@)", tableName]]; - - return rs; -} - - -//check if column exist in table -- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName { - - BOOL returnBool = NO; - //lower case table name - tableName = [tableName lowercaseString]; - //lower case column name - columnName = [columnName lowercaseString]; - //get table schema - FMResultSet *rs = [self getTableSchema: tableName]; - //check if column is present in table schema - while ([rs next]) { - if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString: columnName]) { - returnBool = YES; - break; - } - } - //close and free object - [rs close]; - - return returnBool; -} - -@end diff --git a/MapView/Map/FMDB/FMResultSet.h b/MapView/Map/FMDB/FMResultSet.h deleted file mode 100755 index 257ef78..0000000 --- a/MapView/Map/FMDB/FMResultSet.h +++ /dev/null @@ -1,75 +0,0 @@ -#import -#import "sqlite3.h" - -@class FMDatabase; -@class FMStatement; - -@interface FMResultSet : NSObject { - FMDatabase *parentDB; - FMStatement *statement; - - NSString *query; - NSMutableDictionary *columnNameToIndexMap; - BOOL columnNamesSetup; -} - - -+ (id) resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; - -- (void) close; - -- (NSString *)query; -- (void)setQuery:(NSString *)value; - -- (FMStatement *)statement; -- (void)setStatement:(FMStatement *)value; - -- (void)setParentDB:(FMDatabase *)newDb; - -- (BOOL) next; -- (BOOL) hasAnotherRow; - -- (int) columnIndexForName:(NSString*)columnName; -- (NSString*) columnNameForIndex:(int)columnIdx; - -- (int) intForColumn:(NSString*)columnName; -- (int) intForColumnIndex:(int)columnIdx; - -- (long) longForColumn:(NSString*)columnName; -- (long) longForColumnIndex:(int)columnIdx; - -- (long long int) longLongIntForColumn:(NSString*)columnName; -- (long long int) longLongIntForColumnIndex:(int)columnIdx; - -- (BOOL) boolForColumn:(NSString*)columnName; -- (BOOL) boolForColumnIndex:(int)columnIdx; - -- (double) doubleForColumn:(NSString*)columnName; -- (double) doubleForColumnIndex:(int)columnIdx; - -- (NSString*) stringForColumn:(NSString*)columnName; -- (NSString*) stringForColumnIndex:(int)columnIdx; - -- (NSDate*) dateForColumn:(NSString*)columnName; -- (NSDate*) dateForColumnIndex:(int)columnIdx; - -- (NSData*) dataForColumn:(NSString*)columnName; -- (NSData*) dataForColumnIndex:(int)columnIdx; - -- (const unsigned char *) UTF8StringForColumnIndex:(int)columnIdx; -- (const unsigned char *) UTF8StringForColumnName:(NSString*)columnName; - -/* -If you are going to use this data after you iterate over the next row, or after you close the -result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:) -If you don't, you're going to be in a world of hurt when you try and use the data. -*/ -- (NSData*) dataNoCopyForColumn:(NSString*)columnName; -- (NSData*) dataNoCopyForColumnIndex:(int)columnIdx; - -- (BOOL) columnIndexIsNull:(int)columnIdx; -- (BOOL) columnIsNull:(NSString*)columnName; - -- (void) kvcMagic:(id)object; - -@end diff --git a/MapView/Map/FMDB/FMResultSet.m b/MapView/Map/FMDB/FMResultSet.m deleted file mode 100755 index a210dbc..0000000 --- a/MapView/Map/FMDB/FMResultSet.m +++ /dev/null @@ -1,332 +0,0 @@ -#import "FMResultSet.h" -#import "FMDatabase.h" -#import "unistd.h" - -@interface FMResultSet (Private) -- (NSMutableDictionary *)columnNameToIndexMap; -- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value; -@end - -@implementation FMResultSet - -+ (id) resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { - - FMResultSet *rs = [[FMResultSet alloc] init]; - - [rs setStatement:statement]; - [rs setParentDB:aDB]; - - return [rs autorelease]; -} - -- (void)dealloc { - [self close]; - - [query release]; - query = nil; - - [columnNameToIndexMap release]; - columnNameToIndexMap = nil; - - [super dealloc]; -} - -- (void) close { - - [statement reset]; - [statement release]; - statement = nil; - - // we don't need this anymore... (i think) - //[parentDB setInUse:NO]; - parentDB = nil; -} - -- (void) setupColumnNames { - - if (!columnNameToIndexMap) { - [self setColumnNameToIndexMap:[NSMutableDictionary dictionary]]; - } - - int columnCount = sqlite3_column_count(statement.statement); - - int columnIdx = 0; - for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { - [columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] - forKey:[[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)] lowercaseString]]; - } - columnNamesSetup = YES; -} - -- (void) kvcMagic:(id)object { - - int columnCount = sqlite3_column_count(statement.statement); - - int columnIdx = 0; - for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { - - const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx); - - // check for a null row - if (c) { - NSString *s = [NSString stringWithUTF8String:c]; - - [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)]]; - } - } -} - -- (BOOL) next { - - int rc; - BOOL retry; - int numberOfRetries = 0; - do { - retry = NO; - - rc = sqlite3_step(statement.statement); - - if (SQLITE_BUSY == rc) { - // this will happen if the db is locked, like if we are doing an update or insert. - // in that case, retry the step... and maybe wait just 10 milliseconds. - retry = YES; - usleep(20); - - if ([parentDB busyRetryTimeout] && (numberOfRetries++ > [parentDB busyRetryTimeout])) { - - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [parentDB databasePath]); - NSLog(@"Database busy"); - break; - } - } - else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { - // all is well, let's return. - } - else if (SQLITE_ERROR == rc) { - NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); - break; - } - else if (SQLITE_MISUSE == rc) { - // uh oh. - NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); - break; - } - else { - // wtf? - NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); - break; - } - - } while (retry); - - - if (rc != SQLITE_ROW) { - [self close]; - } - - return (rc == SQLITE_ROW); -} - -- (BOOL) hasAnotherRow { - return sqlite3_errcode([parentDB sqliteHandle]) == SQLITE_ROW; -} - -- (int) columnIndexForName:(NSString*)columnName { - - if (!columnNamesSetup) { - [self setupColumnNames]; - } - - columnName = [columnName lowercaseString]; - - NSNumber *n = [columnNameToIndexMap objectForKey:columnName]; - - if (n) { - return [n intValue]; - } - - NSLog(@"Warning: I could not find the column named '%@'.", columnName); - - return -1; -} - - - -- (int) intForColumn:(NSString*)columnName { - return [self intForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (int) intForColumnIndex:(int)columnIdx { - return sqlite3_column_int(statement.statement, columnIdx); -} - -- (long) longForColumn:(NSString*)columnName { - return [self longForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (long) longForColumnIndex:(int)columnIdx { - return (long)sqlite3_column_int64(statement.statement, columnIdx); -} - -- (long long int) longLongIntForColumn:(NSString*)columnName { - return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (long long int) longLongIntForColumnIndex:(int)columnIdx { - return sqlite3_column_int64(statement.statement, columnIdx); -} - -- (BOOL) boolForColumn:(NSString*)columnName { - return [self boolForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (BOOL) boolForColumnIndex:(int)columnIdx { - return ([self intForColumnIndex:columnIdx] != 0); -} - -- (double) doubleForColumn:(NSString*)columnName { - return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (double) doubleForColumnIndex:(int)columnIdx { - return sqlite3_column_double(statement.statement, columnIdx); -} - -- (NSString*) stringForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx); - - if (!c) { - // null row. - return nil; - } - - return [NSString stringWithUTF8String:c]; -} - -- (NSString*) stringForColumn:(NSString*)columnName { - return [self stringForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSDate*) dateForColumn:(NSString*)columnName { - return [self dateForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSDate*) dateForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; -} - - -- (NSData*) dataForColumn:(NSString*)columnName { - return [self dataForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSData*) dataForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - int dataSize = sqlite3_column_bytes(statement.statement, columnIdx); - - NSMutableData *data = [NSMutableData dataWithLength:dataSize]; - - memcpy([data mutableBytes], sqlite3_column_blob(statement.statement, columnIdx), dataSize); - - return data; -} - - -- (NSData*) dataNoCopyForColumn:(NSString*)columnName { - return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; -} - -- (NSData*) dataNoCopyForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - int dataSize = sqlite3_column_bytes(statement.statement, columnIdx); - - NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob(statement.statement, columnIdx) length:dataSize freeWhenDone:NO]; - - return data; -} - - -- (BOOL) columnIndexIsNull:(int)columnIdx { - return sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL; -} - -- (BOOL) columnIsNull:(NSString*)columnName { - return [self columnIndexIsNull:[self columnIndexForName:columnName]]; -} - -- (const unsigned char *) UTF8StringForColumnIndex:(int)columnIdx { - - if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { - return nil; - } - - return sqlite3_column_text(statement.statement, columnIdx); -} - -- (const unsigned char *) UTF8StringForColumnName:(NSString*)columnName { - return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; -} - - -// returns autoreleased NSString containing the name of the column in the result set -- (NSString*) columnNameForIndex:(int)columnIdx { - return [NSString stringWithUTF8String: sqlite3_column_name(statement.statement, columnIdx)]; -} - -- (void)setParentDB:(FMDatabase *)newDb { - parentDB = newDb; -} - - -- (NSString *)query { - return query; -} - -- (void)setQuery:(NSString *)value { - [value retain]; - [query release]; - query = value; -} - -- (NSMutableDictionary *)columnNameToIndexMap { - return columnNameToIndexMap; -} - -- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value { - [value retain]; - [columnNameToIndexMap release]; - columnNameToIndexMap = value; -} - -- (FMStatement *) statement { - return statement; -} - -- (void)setStatement:(FMStatement *)value { - if (statement != value) { - [statement release]; - statement = [value retain]; - } -} - - - -@end diff --git a/MapView/Map/FMDB/README.txt b/MapView/Map/FMDB/README.txt deleted file mode 100755 index 3a9b6fc..0000000 --- a/MapView/Map/FMDB/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is FMDB; an objective-c wrapper around SQLITE by August Mueller from here: -http://gusmueller.com/blog/archives/2008/03/fmdb_for_iphone.html \ No newline at end of file diff --git a/MapView/Map/FMDB/fmdb.m b/MapView/Map/FMDB/fmdb.m deleted file mode 100755 index fb7afec..0000000 --- a/MapView/Map/FMDB/fmdb.m +++ /dev/null @@ -1,496 +0,0 @@ -#import -#import "FMDatabase.h" -#import "FMDatabaseAdditions.h" - -#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); return 123; } } - -int main (int argc, const char * argv[]) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - // delete the old db. - NSFileManager *fileManager = [NSFileManager defaultManager]; - [fileManager removeFileAtPath:@"/tmp/tmp.db" handler:nil]; - - FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"]; - if (![db open]) { - NSLog(@"Could not open db."); - [pool release]; - return 0; - } - - // kind of experimentalish. - [db setShouldCacheStatements:YES]; - - // create a bad statement, just to test the error code. - [db executeUpdate:@"blah blah blah"]; - - FMDBQuickCheck([db hadError]); - - if ([db hadError]) { - NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]); - } - - // but of course, I don't bother checking the error codes below. - // Bad programmer, no cookie. - - [db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"]; - - - [db beginTransaction]; - int i = 0; - while (i++ < 20) { - [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" , - @"hi'", // look! I put in a ', and I'm not escaping it! - [NSString stringWithFormat:@"number %d", i], - [NSNumber numberWithInt:i], - [NSDate date], - [NSNumber numberWithFloat:2.2f]]; - } - [db commit]; - - - - // do it again, just because - [db beginTransaction]; - i = 0; - while (i++ < 20) { - [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" , - @"hi again'", // look! I put in a ', and I'm not escaping it! - [NSString stringWithFormat:@"number %d", i], - [NSNumber numberWithInt:i], - [NSDate date], - [NSNumber numberWithFloat:2.2f]]; - } - [db commit]; - - - - - - FMResultSet *rs = [db executeQuery:@"select rowid,* from test where a = ?", @"hi'"]; - while ([rs next]) { - // just print out what we've got in a number of formats. - NSLog(@"%d %@ %@ %@ %@ %f %f", - [rs intForColumn:@"c"], - [rs stringForColumn:@"b"], - [rs stringForColumn:@"a"], - [rs stringForColumn:@"rowid"], - [rs dateForColumn:@"d"], - [rs doubleForColumn:@"d"], - [rs doubleForColumn:@"e"]); - - - if (!([[rs columnNameForIndex:0] isEqualToString:@"rowid"] && - [[rs columnNameForIndex:1] isEqualToString:@"a"]) - ) { - NSLog(@"WHOA THERE BUDDY, columnNameForIndex ISN'T WORKING!"); - return 7; - } - } - // close the result set. - // it'll also close when it's dealloc'd, but we're closing the database before - // the autorelease pool closes, so sqlite will complain about it. - [rs close]; - - // ---------------------------------------------------------------------------------------- - // blob support. - [db executeUpdate:@"create table blobTable (a text, b blob)"]; - - // let's read in an image from safari's app bundle. - NSData *safariCompass = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"]; - if (safariCompass) { - [db executeUpdate:@"insert into blobTable (a, b) values (?,?)", @"safari's compass", safariCompass]; - - rs = [db executeQuery:@"select b from blobTable where a = ?", @"safari's compass"]; - if ([rs next]) { - safariCompass = [rs dataForColumn:@"b"]; - [safariCompass writeToFile:@"/tmp/compass.icns" atomically:NO]; - - // let's look at our fancy image that we just wrote out.. - system("/usr/bin/open /tmp/compass.icns"); - - // ye shall read the header for this function, or suffer the consequences. - safariCompass = [rs dataNoCopyForColumn:@"b"]; - [safariCompass writeToFile:@"/tmp/compass_data_no_copy.icns" atomically:NO]; - system("/usr/bin/open /tmp/compass_data_no_copy.icns"); - } - else { - NSLog(@"Could not select image."); - } - - [rs close]; - - } - else { - NSLog(@"Can't find compass image.."); - } - - - // test out the convenience methods in +Additions - [db executeUpdate:@"create table t1 (a integer)"]; - [db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]]; - int a = [db intForQuery:@"select a from t1 where a = ?", [NSNumber numberWithInt:5]]; - if (a != 5) { - NSLog(@"intForQuery didn't work (a != 5)"); - } - - // test the busy rety timeout schtuff. - - [db setBusyRetryTimeout:50000]; - - FMDatabase *newDb = [FMDatabase databaseWithPath:@"/tmp/tmp.db"]; - [newDb open]; - - rs = [newDb executeQuery:@"select rowid,* from test where a = ?", @"hi'"]; - [rs next]; // just grab one... which will keep the db locked. - - NSLog(@"Testing the busy timeout"); - - BOOL success = [db executeUpdate:@"insert into t1 values (5)"]; - - if (success) { - NSLog(@"Whoa- the database didn't stay locked!"); - return 7; - } - else { - NSLog(@"Hurray, our timeout worked"); - } - - [rs close]; - [newDb close]; - - success = [db executeUpdate:@"insert into t1 values (5)"]; - if (!success) { - NSLog(@"Whoa- the database shouldn't be locked!"); - return 8; - } - else { - NSLog(@"Hurray, we can insert again!"); - } - - - - // test some nullness. - [db executeUpdate:@"create table t2 (a integer, b integer)"]; - - if (![db executeUpdate:@"insert into t2 values (?, ?)", nil, [NSNumber numberWithInt:5]]) { - NSLog(@"UH OH, can't insert a nil value for some reason..."); - } - - - - - rs = [db executeQuery:@"select * from t2"]; - while ([rs next]) { - NSString *a = [rs stringForColumnIndex:0]; - NSString *b = [rs stringForColumnIndex:1]; - - if (a != nil) { - NSLog(@"%s:%d", __FUNCTION__, __LINE__); - NSLog(@"OH OH, PROBLEMO!"); - return 10; - } - else { - NSLog(@"YAY, NULL VALUES"); - } - - if (![b isEqualToString:@"5"]) { - NSLog(@"%s:%d", __FUNCTION__, __LINE__); - NSLog(@"OH OH, PROBLEMO!"); - return 10; - } - } - - - - - - - - - - - // test some inner loop funkness. - [db executeUpdate:@"create table t3 (a somevalue)"]; - - - // do it again, just because - [db beginTransaction]; - i = 0; - while (i++ < 20) { - [db executeUpdate:@"insert into t3 (a) values (?)" , [NSNumber numberWithInt:i]]; - } - [db commit]; - - - - - rs = [db executeQuery:@"select * from t3"]; - while ([rs next]) { - int foo = [rs intForColumnIndex:0]; - - int newVal = foo + 100; - - [db executeUpdate:@"update t3 set a = ? where a = ?" , [NSNumber numberWithInt:newVal], [NSNumber numberWithInt:foo]]; - - - FMResultSet *rs2 = [db executeQuery:@"select a from t3 where a = ?", [NSNumber numberWithInt:newVal]]; - [rs2 next]; - - if ([rs2 intForColumnIndex:0] != newVal) { - NSLog(@"Oh crap, our update didn't work out!"); - return 9; - } - - [rs2 close]; - } - - - // NSNull tests - [db executeUpdate:@"create table nulltest (a text, b text)"]; - - [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , [NSNull null], @"a"]; - [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , nil, @"b"]; - - rs = [db executeQuery:@"select * from nulltest"]; - - while ([rs next]) { - - NSString *a = [rs stringForColumnIndex:0]; - NSString *b = [rs stringForColumnIndex:1]; - - if (!b) { - NSLog(@"Oh crap, the nil / null inserts didn't work!"); - return 10; - } - - if (a) { - NSLog(@"Oh crap, the nil / null inserts didn't work (son of error message)!"); - return 11; - } - else { - NSLog(@"HURRAH FOR NSNULL (and nil)!"); - } - } - - - - - - - // null dates - - NSDate *date = [NSDate date]; - [db executeUpdate:@"create table datetest (a double, b double, c double)"]; - [db executeUpdate:@"insert into datetest (a, b, c) values (?, ?, 0)" , [NSNull null], date]; - - rs = [db executeQuery:@"select * from datetest"]; - - while ([rs next]) { - - NSDate *a = [rs dateForColumnIndex:0]; - NSDate *b = [rs dateForColumnIndex:1]; - NSDate *c = [rs dateForColumnIndex:2]; - - if (a) { - NSLog(@"Oh crap, the null date insert didn't work!"); - return 12; - } - - if (!c) { - NSLog(@"Oh crap, the 0 date insert didn't work!"); - return 12; - } - - NSTimeInterval dti = fabs([b timeIntervalSinceDate:date]); - - if (floor(dti) > 0.0) { - NSLog(@"Date matches didn't really happen... time difference of %f", dti); - return 13; - } - - - dti = fabs([c timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:0]]); - - if (floor(dti) > 0.0) { - NSLog(@"Date matches didn't really happen... time difference of %f", dti); - return 13; - } - } - - NSDate *foo = [db dateForQuery:@"select b from datetest where c = 0"]; - assert(foo); - NSTimeInterval dti = fabs([foo timeIntervalSinceDate:date]); - if (floor(dti) > 0.0) { - NSLog(@"Date matches didn't really happen... time difference of %f", dti); - return 14; - } - - [db executeUpdate:@"create table nulltest2 (s text, d data, i integer, f double, b integer)"]; - - [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , @"Hi", safariCompass, [NSNumber numberWithInt:12], [NSNumber numberWithFloat:4.4], [NSNumber numberWithBool:YES]]; - [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , nil, nil, nil, nil, [NSNull null]]; - - rs = [db executeQuery:@"select * from nulltest2"]; - - while ([rs next]) { - - int i = [rs intForColumnIndex:2]; - - if (i == 12) { - // it's the first row we inserted. - FMDBQuickCheck(![rs columnIndexIsNull:0]); - FMDBQuickCheck(![rs columnIndexIsNull:1]); - FMDBQuickCheck(![rs columnIndexIsNull:2]); - FMDBQuickCheck(![rs columnIndexIsNull:3]); - FMDBQuickCheck(![rs columnIndexIsNull:4]); - FMDBQuickCheck( [rs columnIndexIsNull:5]); - - FMDBQuickCheck([[rs dataForColumn:@"d"] length] == [safariCompass length]); - FMDBQuickCheck(![rs dataForColumn:@"notthere"]); - FMDBQuickCheck(![rs stringForColumnIndex:-2]); - FMDBQuickCheck([rs boolForColumnIndex:4]); - FMDBQuickCheck([rs boolForColumn:@"b"]); - - FMDBQuickCheck(fabs(4.4 - [rs doubleForColumn:@"f"]) < 0.0000001); - - FMDBQuickCheck(12 == [rs intForColumn:@"i"]); - FMDBQuickCheck(12 == [rs intForColumnIndex:2]); - - FMDBQuickCheck(0 == [rs intForColumnIndex:12]); // there is no 12 - FMDBQuickCheck(0 == [rs intForColumn:@"notthere"]); - - FMDBQuickCheck(12 == [rs longForColumn:@"i"]); - FMDBQuickCheck(12 == [rs longLongIntForColumn:@"i"]); - } - else { - // let's test various null things. - - FMDBQuickCheck([rs columnIndexIsNull:0]); - FMDBQuickCheck([rs columnIndexIsNull:1]); - FMDBQuickCheck([rs columnIndexIsNull:2]); - FMDBQuickCheck([rs columnIndexIsNull:3]); - FMDBQuickCheck([rs columnIndexIsNull:4]); - FMDBQuickCheck([rs columnIndexIsNull:5]); - - - FMDBQuickCheck(![rs dataForColumn:@"d"]); - - } - } - - - - { - [db executeUpdate:@"create table testOneHundredTwelvePointTwo (a text, b integer)"]; - [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:2], nil]]; - [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:3], nil]]; - - - rs = [db executeQuery:@"select * from testOneHundredTwelvePointTwo where b > ?" withArgumentsInArray:[NSArray arrayWithObject:[NSNumber numberWithInteger:1]]]; - - FMDBQuickCheck([rs next]); - - FMDBQuickCheck([rs hasAnotherRow]); - FMDBQuickCheck(![db hadError]); - - FMDBQuickCheck([[rs stringForColumnIndex:0] isEqualToString:@"one"]); - FMDBQuickCheck([rs intForColumnIndex:1] == 2); - - FMDBQuickCheck([rs next]); - - FMDBQuickCheck([rs intForColumnIndex:1] == 3); - - FMDBQuickCheck(![rs next]); - FMDBQuickCheck(![rs hasAnotherRow]); - - } - - { - - FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)"]); - FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)", @"one", @"two"])); - - rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;"]; - - FMDBQuickCheck((rs != nil)); - - [rs next]; - - FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]); - FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]); - - FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0); - - [rs close]; - - // let's try these again, with the withArgumentsInArray: variation - FMDBQuickCheck([db executeUpdate:@"drop table t4;" withArgumentsInArray:[NSArray array]]); - FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)" withArgumentsInArray:[NSArray array]]); - FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", @"two", nil]])); - - rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;" withArgumentsInArray:[NSArray array]]; - - FMDBQuickCheck((rs != nil)); - - [rs next]; - - FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]); - FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]); - - FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0); - - [rs close]; - - - - - - - } - - - - - { - FMDBQuickCheck([db tableExists:@"t4"]); - FMDBQuickCheck(![db tableExists:@"thisdoesntexist"]); - - rs = [db getSchema]; - while ([rs next]) { - FMDBQuickCheck([[rs stringForColumn:@"type"] isEqualToString:@"table"]); - } - - - } - - - - - - // just for fun. - rs = [db executeQuery:@"PRAGMA database_list"]; - while ([rs next]) { - NSString *file = [rs stringForColumn:@"file"]; - NSLog(@"database_list: %@", file); - } - - - // print out some stats if we are using cached statements. - if ([db shouldCacheStatements]) { - - NSEnumerator *e = [[db cachedStatements] objectEnumerator];; - FMStatement *statement; - - while ((statement = [e nextObject])) { - NSLog(@"%@", statement); - } - } - NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]); - - - [db close]; - - [pool release]; - return 0; -} diff --git a/MapView/Map/GRMustache/AppledocSettings.plist b/MapView/Map/GRMustache/AppledocSettings.plist new file mode 100644 index 0000000..aff730f --- /dev/null +++ b/MapView/Map/GRMustache/AppledocSettings.plist @@ -0,0 +1,30 @@ + + + + + --project-name + GRMustache 7.3 + --project-version + 7.3 + --project-company + Gwendal Roué + --create-html + + --create-docset + + --clean-output + + --keep-intermediate-files + + --keep-undocumented-objects + + --keep-undocumented-members + + --search-undocumented-doc + + --repeat-first-par + + --print-information-block-titles + + + diff --git a/MapView/Map/GRMustache/Articles/TheNatureOfLogicLessTemplates.md b/MapView/Map/GRMustache/Articles/TheNatureOfLogicLessTemplates.md new file mode 100644 index 0000000..21bfd7b --- /dev/null +++ b/MapView/Map/GRMustache/Articles/TheNatureOfLogicLessTemplates.md @@ -0,0 +1,77 @@ +# The nature of logicless templates + +[@pvande](https://github.com/pvande) [wonders](https://github.com/mustache/spec/wiki/%5BDiscussion%5D-Logic-Free-vs.-Non-Evaled) what is the difference between the "Logic Free" templates such as [Mustache](http://mustache.github.io) and the "Non-Evaled" templates like [Liquid](http://liquidmarkup.org). + +He enumerates different properties of both kinds of templates, and feels perplexed when wondering what are the fundamental properties he should be the guardian of, as the maintainer of the [Mustache Specification](http://github.com/mustache/spec). + +My opinion on the subject is that he has been misled by an artificial distinction created by names such as "Logic-Free" and "Non-Evaled", which are actual synonyms for "codeless". + +## "Get the code out of the view!" + +We have seen MVC emerging as a powerful pattern to code desktop, mobile and web applications. It became quickly clear that template engines were the weak link in this nice building. Most of them used to allow the coder to embed raw code right into his views. and raw code means any code, including code that should not lie in a view component. And while embedding code has more and more been considered as a quick and dirty practice, nothing would prevent the coder to do so, because the template engines were explicitely allowing it. + +For some people, allowing bad practices is the same as advocating it. The need for strict and clean template engine that totally forbid the coder to embed code in his view was now imperious. + +So came Mustache, Liquid, and others. All have this single common property: *they explicitely disallow embedding raw code*. Plus, add that those template engines are fundamentally language-agnostic (Mustache has achieved a [tremendous success](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations) here), and you know why those new template language have such a momentum these days. + +## Logic and evaluation? They're right under the carpet + +So, names. "Logic-Free". "Non-Evaled". + +Is the logic totally banned? Of course not: template engines still provide a syntax for controlling the rendering of templates. But the control is a consequence of the values that are computed, and provided by the template user. The actual controlling code is in *userland*. + +Is the evaluation totally banned? Of course not: template engines provide syntax for rendering values. But not all values can be rendered: only values that are available to the template, chosen by the template user. Those values come, again, from *userland*. + +There we are now: in codeless languages, the code (there is always code) has been sent out to userland. + + +## There is no other important property + +@pvande [enumerates](https://github.com/mustache/spec/wiki/%5BDiscussion%5D-Logic-Free-vs.-Non-Evaled) a few other properties for Liquid and Mustache. Let's see if they wouldn't be plain consequence of the fundamental "codeless" motto: + +- promotes "safe" templating (Liquid + Mustache) + +The idea is that a template can't crash the runtime it is rendered in. Since the library user can not run arbitrary code right from the template, this property looks like it is a direct consequence from the codelessness. + +Actually, a template engine that would define its own Turing-complete language and provide a robust virtual machine could be very safe as well. Think PHP, for instance. Unfortunately, this is very difficult, and the "safe templating" argument of codeless languages could be rewritten as "easily-implemented safety". Anyway, as long as code from userland is executed, I don't know which kind of safety we're discussing here: eventually "safe templating" means "safety is not my problem". The Liquid team is rather honest here, claiming safety from *template editors*, and not claiming anything about the code written by *developpers* that gets executed by the templates. + +- disallows execution of any code accessible from the data (Liquid) + +Yet Liquid allows execution of filters. Filters whose code lies in userland. Check. + +- permits execution of code accessible from the data stack (Mustache) + +Yes, Mustache "lambda sections" contain code. In userland. Check. + +- keeps executable code in a separate context (Liquid) + +Check. + +- allows basic literal types in templates as values (Liquid) +- encourages "procedural" templates and internal template state (e.g. via assign variables) (Liquid) +- discourages internal template state (Mustache) +has (should have?) no explicit order-dependency -- "declarative" templates (Mustache) + +It looks like the Liquid designers, generally, needed some expressivity. Yet these points are irrelevant to the "Non-Evaled" claim of Liquid and "Logic-Less" claim of Mustache: I can't see any relationship between those interesting properties and these nice expressions. + +So as the dedicated reader has noticed, "Logic Less" and "Non Evaled" are really just plain synonyms for "GTFCO", as Get The Filthy Code Out. + + +## The last @pvande's questions + +> Open questions: +> +> - Since Mustache has basic conditionals, what is the logic we're trying to avoid in templates? +> - Database access? +> - Data construction? +> - Data manipulation? +> - Arbitrary data manipulation? +> - Predefined data manipulation? +> - Do filters fit in that worldview? +> - Do parameterized filters fit in that worldview? +> - Do data literals fit in that worldview? +> - Are there other significant differences between Logic-Free and Non-Evaling templates? + +Keep relaxed. You're not trying to avoid anything. All the job has already been done when the code has been removed from the template. + +Now it's time to empower your users, and to give them the tools and the expressivity they need. diff --git a/MapView/Map/GRMustache/Articles/WhyMustacheFilters.md b/MapView/Map/GRMustache/Articles/WhyMustacheFilters.md new file mode 100644 index 0000000..001cb80 --- /dev/null +++ b/MapView/Map/GRMustache/Articles/WhyMustacheFilters.md @@ -0,0 +1,205 @@ +# Mustache support for "Filters" + +Here are a few arguments for the introduction of "filters" in Mustache, and a description of what they should be, as a contribution to the [open discussion](http://github.com/mustache/spec/issues/41) on the mustache/spec repository. + +GRMustache provides an implementation of [filters](../Guides/filters.md) that fully cover all the points described here. + +1. Why filters are good for Mustache +2. Why Mustache tags should contain expressions, not statements +3. Parsing GRMustache expressions +4. The details + +## 1. Why filters are good for Mustache + +### History of user-provided code: lambdas + +Mustache users today have a single way to have their own code executed while rendering a template: "Mustache lambdas". + +Lambdas operate at the *template canvas* level: they can alter raw portions of a template, insert and process raw text, add and remove mustache tags, and their output is then processed by the Mustache engine which renders it. + +One can for instance write a lambda that turns `{{#link}}{{name}}{{/link}}` into `{{name}}`, which is later rendered as `blah`. + +However, lambdas do not have access to the *view model* level. They can not, for instance, render the uppercase version of a value. + +> Precisely: should a lambda evaluate the inner rendering of a section, turn it into uppercase, and provide the result to the Mustache engine, there is the possibility that the view model data would contain mustache tags that would be then processed by the Mustache engine. An application user could "attack" the rendering engine by setting his name to `{{pwned}}`, for instance. + +### The consequences of a drastic interpretation of "logiclessness" + +The inability for library user's to provide code that operates on the view model level has until now be considered positive and "pure", because of the "logiclessness" of Mustache. Yes, there is no logic code in the template itself, no "if", no "while", no operators, etc. Actually, there is no code at all in a Mustache template. + +However, the interpretation of "logiclessness" becomes uselessly drastic, and painful to the library user when the view model is made 100% responsible for the rendering of value tags and the control of section tags. The problem arises at the the *view model preparation phase*, when the library user has to prepare all the values that will be interpreted by the Mustache engine. The preparation phase becomes a chore when the user has to process many values in the same way. + +For instance, a model may hold a dozen named numerical values, that should be rendered in a formatted way. It thus has to be turned into a view model holding a dozen named formatted values, with the necessity of duplicated code. I, as a Mustache implementor, have received many feature requests on this topic. There is more evidence that this is a recurrent issue with Mustache at: [mustache/spec/issues/41](https://github.com/mustache/spec/issues/41) and [bobthecow/mustache.php/pull/102](https://github.com/bobthecow/mustache.php/pull/102). + +Another common chore is preparing the input in order to test if a collection is empty or not. See [mustache/spec/issues/23](https://github.com/mustache/spec/pull/23), and [defunkt/mustache/issues/4](https://github.com/defunkt/mustache/issues/4). + +Another chore is processing model arrays so that the view model contains arrays whose items know about their index in the array. Again, if many model arrays should be processed this way, we again have a duplicated code problem. Evidence can be found at [janl/mustache.js/pull/205](https://github.com/janl/mustache.js/pull/205), [groue/GRMustache/issues/14](https://github.com/groue/GRMustache/issues/14), [groue/GRMustache/issues/18](https://github.com/groue/GRMustache/issues/18), and the language extension implemented by [samskivert/jmustache](https://github.com/samskivert/jmustache) and [christophercotton/GRMustache](https://github.com/christophercotton/GRMustache). + +Some would say: "use your language features, and dynamically add the needed properties to your objects". This argument is invalid for many reasons, and primarily because Mustache is a language-agnostic template language, and some host languages do not sport any dynamic features. + +Some readers might be interested by a [more general rebuttal of the drastic interpretation of Mustache "logiclessness"](TheNatureOfLogicLessTemplates.md). + +### Filters empower the library user, and Mustache itself + +This is why Mustache should provide a way to let the library user provide code that processes the view model values before they enter the rendering engine, and express directly in the template how the view model values should be processed. + +These code chunks would be called *filters*, because they are functions that take a mustache-interpretable value as an input, and return an other mustache-interpretable value. In the template itself, tags would contain *filtered expressions* that would tell the rendering engine which filters should be applied to the raw view model values. + +Since the role of filters is to relieve view models from providing "final" values, filters do not conceptually belong to them. They instead belong the template: for instance, a template would provide a filter for rendering uppercase values. Now all the view models are relieved from the burden of computing those. Another template would provide a filter for rendering array indexes. View models would then provide raw arrays, and the template would be able to render item indexes. (For real examples, check [number formatting](../Guides/sample_code/number_formatting.md) and [indexes](../Guides/sample_code/indexes.md) sample code). + +Since filters belong to the templates, Mustache can provide a *standard library* of filters, that would be pre-baked into all Mustache templates. + +Since filters are not tied to the view model, they are *reusable*. + + +## 2. Why Mustache tags should contain expressions, not statements + +### Composition + +There are major differences between *expressions* and *statements*. Statements chain, one after the other, independently, and can not provide any value. Statements *perform* and return nothing. Expressions are a different kind of beast: by essence, they provide *values*, and can be *composed* from other expressions. + +Obviously, Mustache needs values: variable tags need a value that they can render, section tags need a value that they can test, loop, or make enter the context stack. Since only expressions provide with values, they are what Mustache need. + +Mustache already has two kinds of expressions: keys and key paths. `name` is a key. `person.name` is a key path. Both expressions evaluate in a different manner. The key expression looks in the context stack for an object that would provide the "name" key. The key path expression looks in the context stack for an object that would provide the "person" key, and then extract the "name" key right from this person. The latter behavior is called a "scoped lookup". + +Let filters enter, and turn them into expressions: + +Library users should be able to build filter expressions with other expressions. One should be able to filter `person.name` with the filter `uppercase`. + +Composition goes further: library users should be able to perform a "scoped" lookup out of a filtered expression. + +The latter point is important: there is no good reason to prevent the library user to perform a scoped lookup out of a filtered expression. + +### A syntax that fulfills those properties + +GRMustache implements filters with a good old function call syntax: `f(x)`. + +Just like `x`, `f(x)` is an expression that has a value. The GRMustache expression syntax let the user write `f(*)` and `*(x)` anywhere he can write `*`: + +- One can render `{{ f(x) }}` instead of `{{ x }}`. +- One can render `{{ f(x.y) }}` instead of `{{ x.y }}`. +- One can render `{{ f(g(x)) }}` instead of `{{ g(x) }}`. +- One can render `{{ f(x)(y) }}` instead of `{{ f(x) }}` (`f` is a meta-filter: a filter that returns a filter). + +This fits pretty well with the "scoped" Mustache expression: the regular Mustache syntax lets the user write `*.y` anywhere he can write `*`: + +- One can render `{{ x.y }}` instead of `{{ x }}`. +- One can render `{{ f(x).y }}` instead of `{{ f(x) }}`. +- One can render `{{ f.g(x) }}` instead of `{{ f(x) }}`. + +A contrieved user could write `{{a.b(c.d(e.f).g.h).i.j(k.l)}}`. Whether this is sane or not is not the business of a library that embraces userland code. + +Last point: white space is irrelevant. `f(x)` is the same as `f ( x )`. + +You'll find below a grammar and a state machine that implement the parsing of those expressions. + +### A syntax that does not fullfill those properties + +The only other syntax that I'm aware of is the one of bobthecow's [mustache.php](https://github.com/bobthecow/mustache.php/pull/102), which is not yet merged in the released branch of his library. + + {{ created_at | date.iso8601 }} + +Pipes have great ascendants (unix shell, Liquid filters), and this syntax sports a genuine relevance for its purpose. Pipable unix commands such as sort, uniq, etc. have a great deal in common with template filters. + +However, it fails on the composition part, since pipes build *statements*, not expressions. + +For example, how would pipes handle cases like `f(x).y` without the introduction of parenthesis in a fashion that is not common to pipes? + + {{ (x | f).y }} vs. {{ f(x).y }} + {{ (x | f).y | g }} vs. {{ g(f(x).y) }} + +More, how would pipes handle meta-filters like `f(x)(y)` ? + + {{ y | (x | f) }} vs. {{ f(x)(y) }} + +The `f(x)` notation has here an advantage, which is its pervasiveness if many widely adopted languages that also use the dot as a property accessor. + + +### Filters can't load from the "implicit iterator" + +We've said above that filters should not come from the view model provided by the user, but instead be tied to a template. This allows a template to provide filters as services, including a standard library of filters. + +As a consequence, the `.(x)` syntax is forbidden. In Mustache, `.` aka the "implicit iterator", represents the currently rendered object from the view model. It thus can not provide any filter. Identically, the `.a(x)` syntax is invalid as well (it would mean "perform a scoped lookup for `a` in the view model, and apply the result as a filter"). + + +## 3. Parsing GRMustache expressions + +Here is a state machine that describes GRMustache expressions. It reads one character +after the other, until it reaches the *VALID*, *EMPTY*, or *INVALID* state: + + # ID stands for "identifier character" + # WS stands for "white space character" + # EOF stands for "end of input" + # All non explicited transitions end up in the INVALID state. + -> parenthesisLevel=0, INITIAL + INITIAL -> WS -> INITIAL + INITIAL -> ID -> scopable=YES, IDENTIFIER + INITIAL -> '.' -> scopable=NO, LEADING_DOT + INITIAL && parenthesisLevel==0 -> EOF -> EMPTY + LEADING_DOT -> WS -> IDENTIFIER_DONE + LEADING_DOT -> ID -> IDENTIFIER + LEADING_DOT && parenthesisLevel>0 -> ')' -> --parenthesisLevel, FILTER_DONE + LEADING_DOT && parenthesisLevel==0 -> EOF -> VALID + IDENTIFIER -> WS -> IDENTIFIER_DONE + IDENTIFIER -> ID -> IDENTIFIER + IDENTIFIER -> '.' -> WAITING_FOR_IDENTIFIER + IDENTIFIER && scopable -> '(' -> ++parenthesisLevel, INITIAL + IDENTIFIER && parenthesisLevel>0 -> ')' -> --parenthesisLevel, FILTER_DONE + IDENTIFIER && parenthesisLevel==0 -> EOF -> VALID + WAITING_FOR_IDENTIFIER -> ID -> IDENTIFIER + IDENTIFIER_DONE -> WS -> IDENTIFIER_DONE + IDENTIFIER_DONE && scopable -> '(' -> ++parenthesisLevel, INITIAL + IDENTIFIER_DONE && parenthesisLevel==0 -> EOF -> VALID + FILTER_DONE -> WS -> FILTER_DONE + FILTER_DONE -> '.' -> WAITING_FOR_IDENTIFIER + FILTER_DONE -> '(' -> ++parenthesisLevel, INITIAL + FILTER_DONE && parenthesisLevel>0 -> ')' -> --parenthesisLevel, FILTER_DONE + FILTER_DONE && parenthesisLevel==0 -> EOF -> VALID + + +## 4. The details + +### Filtered variables, filtered sections + +Expressions as a way for the library user to build values that would be rendered by Mustache. Now those values are actually rendered by variable tags, or section tags. + +The only argument so far I've read against filtered sections is: "I see no compelling use case that need this feature". + +This argument fails for two reasons. First it only shows the lack of imagination of the one expressing it. Second, it artificially limits the empowerment of the library user, who deserves more respect. If Mustache allows the library user to inject code, there is no point nannying him and preventing him from injecting his code where he thinks it is relevant. This only makes Mustache painful to use, without any benefit for anybody. + +Here is a nice section filter, for the unimaginative ones: + +```js +with_index = function(array) { + for (i=0; i 'MIT', :file => 'LICENSE' } + s.summary = 'Flexible and production-ready Mustache templates for MacOS Cocoa and iOS.' + s.homepage = 'https://github.com/groue/GRMustache' + s.author = { 'Gwendal Roué' => 'gr@pierlis.com' } + s.source = { :git => 'https://github.com/groue/GRMustache.git', :tag => 'v7.3.0' } + s.source_files = 'src/classes/**/*.{h,m}' + s.private_header_files = 'src/classes/**/*_private.h' + s.ios.deployment_target = '4.3' + s.osx.deployment_target = '10.6' + s.requires_arc = false + s.framework = 'Foundation' + s.dependency 'JRSwizzle', '~> 1.0' +end diff --git a/MapView/Map/GRMustache/Guides/NSFormatter.md b/MapView/Map/GRMustache/Guides/NSFormatter.md new file mode 100644 index 0000000..e549743 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/NSFormatter.md @@ -0,0 +1,108 @@ +[up](../../../../GRMustache#documentation), [next](filters.md) + +NSFormatter +=========== + +GRMustache provides built-in support for NSFormatter and its subclasses such as NSNumberFormatter and NSDateFormatter. + +- [Formatting a value](#formatting-a-value) +- [Formatting all values in a section](#formatting-all-values-in-a-section) + + +Formatting a value +------------------ + +Just add your formatters to the data you render: they get ready to be used as filters: + +`Document.mustache`: + + {{ percent(x) }} + +Rendering code: + +```objc +NSNumberFormatter *percentFormatter = [NSNumberFormatter new]; +percentFormatter.numberStyle = NSNumberFormatterPercentStyle; + +id data = @{ + @"x": @(0.5), + @"percent": percentFormatter, +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Rendering: + + 50% + + +Formatting all values in a section +---------------------------------- + +NSFormatters are able to *format all variable tags* inside the section: + +`Document.mustache`: + + {{# percent }} + hourly: {{ hourly }} + daily: {{ daily }} + weekly: {{ weekly }} + {{/ percent }} + +Rendering code: + +```objc +NSNumberFormatter *percentFormatter = [NSNumberFormatter new]; +percentFormatter.numberStyle = NSNumberFormatterPercentStyle; + +id data = @{ + @"hourly": @(0.1), + @"daily": @(1.5), + @"weekly": @(4), + @"percent": percentFormatter, +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Rendering: + + hourly: 10% + daily: 150% + weekly: 400% + +Variable tags buried inside inner sections are escaped as well, so that you can render loop and conditional sections. However, values that can't be formatted are left untouched: + +`Document.mustache`: + + {{# percent }} + {{# ingredients }} + - {{ name }}: {{ proportion }} {{! name is intact, proportion is formatted. }} + {{/ ingredients }} + {{/ percent }} + +Would render: + + - bread: 50% + - ham: 22% + - butter: 43% + +Precisely speaking, "values that can't be formatted" are the ones that have the `stringForObjectValue:` method return nil, as stated by [apple](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSFormatter_Class/Reference/Reference.html#//apple_ref/occ/instm/NSFormatter/stringForObjectValue:). + +Typically, NSNumberFormatter only formats numbers, and NSDateFormatter, dates: you can safely mix various data types in a section controlled by those well-behaved formatters. + + + +Get inspired +------------ + +NSFormatter has been turned into a citizen of GRMustache using public APIs: check [the code](../src/classes/Services/NSFormatter%2BGRMustache.m) for inspiration. + +[up](../../../../GRMustache#documentation), [next](filters.md) diff --git a/MapView/Map/GRMustache/Guides/README.md b/MapView/Map/GRMustache/Guides/README.md new file mode 100644 index 0000000..1c3e899 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/README.md @@ -0,0 +1,43 @@ +[up](../../../../GRMustache#documentation) + +GRMustache Guides +================= + + +## Introduction + +- [Introduction](introduction.md): a tour of the library features, and most common use cases. + +## Basics + +The core Mustache: + +- [Templates](templates.md): how to load templates. +- [Runtime](runtime.md): how your data is rendered. +- [ViewModel](view_model.md): an overview of various techniques to feed templates. +- [Partials](partials.md): decompose your templates into components named "partials". + +## Mustache, and beyond + +GRMustache offers services on top of the minimalistic genuine Mustache: + +- [Template Inheritance](template_inheritance.md): have templates inherit other ones, and reuse common layouts, for example. +- [Standard Library](standard_library.md): built-in candy, for your convenience. +- [HTML vs. Text templates](html_vs_text.md) +- [NSFormatter](NSFormatter.md), NSNumberFormatter, NSDateFormatter, etc. Use them. +- [Filters](filters.md): `{{ uppercase(name) }}` et al. +- [Rendering Objects](rendering_objects.md): "Mustache lambdas", and more. +- [Tag Delegates](delegate.md): observe and alter template rendering. +- [Compatibility](compatibility.md): compatibility with other Mustache implementations, in details. + +## Application tools + +- [Security](security.md): an important matter. +- [Templates Repositories](template_repositories.md): manage groups of templates. +- [Configuration](configuration.md) + +## Forking + +- [Forking Guide](forking.md): general information about the library. + +[up](../../../../GRMustache#documentation) diff --git a/MapView/Map/GRMustache/Guides/compatibility.md b/MapView/Map/GRMustache/Guides/compatibility.md new file mode 100644 index 0000000..842559f --- /dev/null +++ b/MapView/Map/GRMustache/Guides/compatibility.md @@ -0,0 +1,159 @@ +[up](../../../../GRMustache#documentation), [next](security.md) + +Compatibility With Other Mustache Engines +========================================= + +There are many [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations) out there. + +GRMustache makes sure you can render templates in a [specification](https://github.com/mustache/spec)-compliant way. **What the specification says is possible, is possible with GRMustache.** + +There is a caveat, though: GRMustache does not honor the white-space rules of the spec, line suppression, indentation and other niceties. Your templates are rendered *raw*. Contributions are welcome ([Forking Guide](GRMustache/blob/master/Guides/forking.md)). + +That being said, you may use GRMustache to its full extent, and build templates that can not be rendered by other Mustache implementations. + +This guide is here to tell you where the border line is, topic by topic: + +- [Syntax extensions](#syntax-extensions) +- [Boolean interpretation](#boolean-interpretation) +- [Standard Library](#standard-library) +- [Text templates](#text-templates) +- [File system hierarchy of template and partials](#file-system-hierarchy-of-template-and-partials) +- [Dynamic partials](#dynamic-partials) +- [Template inheritance](#template-inheritance) +- [Priority keys](#priority-keys) +- [Custom rendering objects](#custom-rendering-objects) +- [Filters](#filters) +- [Tag delegates](#tag-delegates) + + +Syntax extensions +----------------- + +GRMustache introduces syntax that is not defined by the Mustache specification. Some other implementations may already provide support for these features (you should check their documentation): + +- **Empty closing tags**, as in `{{#name}}...{{/}}` + + You don't have to repeat the opening expression in the closing tag. + +- **"Else"**, as in `{{#name}}...{{^name}}...{{/name}}` + + You don't have to close a regular section if it is immediately followed by its inverted form. + + The short form `{{#name}}...{{^}}...{{/}}` is accepted, as well as the "unless" form `{{^name}}...{{#}}...{{/}}`. + +- **"Anchored key paths"**, as `{{ .name }}` which enforces lookup of the `name` key in the immediate context instead of going through the context stack built by Mustache sections. + + If you are not familiar with the "context stack" and the Mustache key lookup mechanism, check the [Runtime Guide](runtime.md#the-context-stack). + +- **Loops in variable tags**: a simple variable tag `{{items}}` renders a concatenation of the rendering of each individual item. You may think of Ruby on Rails' `<%= render @items %>`: check the [Rendering Objects Guide](rendering_objects.md). + + +Boolean interpretation +---------------------- + +The Mustache specification does not enforce the list of *false* values, the values that trigger or prevent the rendering of sections and inverted sections: + +There is *no guarantee* that `{{# value }}...{{/ value }}` and `{{^ value }}...{{/ value }}` will render the same, provided with the exact same input, in all Mustache implementations. + +That's unfortunate. Anyway, for the record, here is a reminder of all false values in GRMustache: + +- `nil` and missing keys +- `[NSNull null]` +- `NSNumber` instances whose `boolValue` method returns `NO` +- empty strings `@""` +- empty enumerables. + + +Standard Library +---------------- + +The Mustache specification does not provide any service like the [GRMustache standard library](standard_library.md). + + +Text templates +-------------- + +In GRMustache, [text templates](html_vs_text.md) render text, do not HTML-escape their input, and can be safely embedded in HTML templates (they get HTML-escaped). + +This topic is ignored by the Mustache specification, which only provides you with the triple-mustache `{{{ name }}}` tags (that do not HTML-escape). + +Some other implementations allow you to disable HTML-escaping. However they may not allow mixing HTML and text templates, and `{{% CONTENT_TYPE:TEXT }}` pragma tags are, as far as I know, a specificity of GRMustache. + +Writing cross-language templates require you to use {{{ triple }}} mustache tags, and to avoid mixing text with HTML. + + +File system hierarchy of template and partials +---------------------------------------------- + +You may want to store your templates and partials in a hierarchy of directories. + +GRMustache allows to embed partials with relative or absolute paths: `{{> header }}`, `{{> ../header }}`, `{{> shared/header }}`, `{{> /shared/header }}`. See the [Partials Guide](partials.md). + +This is a GRMustache nicety that is unheard of the Mustache specification. + +Writing cross-language templates require you to use a flat storage of templates and partials. + + +Dynamic partials +---------------- + +GRMustache lets you embed partial templates that are chosen at runtime (see the [Rendering Objects Guide](rendering_objects.md)). + +The Mustache specification does not cover this use case, and provides with lambda-based workarounds that eventually lead to unwanted HTML-escaping issues. + +[Jamie Hill](https://github.com/thelucid) has a [Ruby](https://github.com/thelucid/tache) engine that support dynamic partials. + +Generally speaking, writing cross-language templates requires you to avoid this feature. + + +Template inheritance +-------------------- + +This [GRMustache feature](partials.md) is directly inspired by [hogan.js](http://twitter.github.com/hogan.js/) and [spullara/mustache.java](https://github.com/spullara/mustache.java). + +GRMustache passes all template inheritance tests from hogan.js & mustache.java, without exact white-space conformance: GRMustache doesn't honor line suppression, indentation and other white-space niceties. + +The reciprocal is not sure: hogan.js & mustache.jave may, or not, pass all template inheritance tests of GRMustache. + +When looking for compatibility with other implementations, use this feature with great care. + + +Priority keys +------------- + +GRMustache lets you give priority to some keys so that they are always evaluated to the same value, regardless of other data that you feed your templates with. See the [Security Guide](security.md#priority-keys) for more information. + +This feature is usually implemented by other implementations with functions or methods whose name start with `register`. + +Check their documentation. This feature is, anyway, missing from the Mustache specification. + + +Custom rendering objects +------------------------ + +[Rendering objects](rendering_objects.md) let you inject your own rendering code. + +They allow you to implement "Mustache lambdas", as described by the Mustache specification. + +Rendering objects are more versatile, though. As such, they are an ambiguous tool. You will have to know when you cross the line. + + +Filters +------- + +Now it's easy: [filters](filters.md), as in `{{ uppercase(name) }}`, are an extension that is simply not in the Mustache specification. + +Don't use them if you want to write cross-language templates. Check the [Tag Delegates Guide](delegate.md): you'll find a way to implement filtering in a spec-compliant way. + + +Tag delegates +------------- + +GRMustache's [tag delegates](delegate.md), unknown to the Mustache specification, let you observe, and possibly alter the rendering of the Mustache tags. + +Tag delegates may be used for formatting values in a spec-compliant way (see sample code in [Tag Delegates Guide](delegate.md)). They may also at the core of many items of the [standard library](standard_library.md). + +They are an ambiguous tool. You will have to know when you cross the line. + + +[up](../../../../GRMustache#documentation), [next](security.md) diff --git a/MapView/Map/GRMustache/Guides/configuration.md b/MapView/Map/GRMustache/Guides/configuration.md new file mode 100644 index 0000000..04a7953 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/configuration.md @@ -0,0 +1,165 @@ +[up](../../../../GRMustache#documentation), [next](forking.md) + +Configuration +============= + +GRMustache has options: they are properties of a GRMustacheConfiguration instance. + +- [Three levels of tuning](#three-levels-of-tuning) +- [Factory configuration](#factory-configuration) +- [Configuration properties](#configuration-properties) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + + +Three levels of tuning +---------------------- + +You can tune GRMustache at three different levels: + +- Globally +- For all templates of a [template repository](template_repositories.md), +- For a single template. + +The global default configuration is `[GRMustacheConfiguration defaultConfiguration]`: + +```objc +// Have GRMustache templates render text by default, +// and do not HTML-escape their input. +[GRMustacheConfiguration defaultConfiguration].contentType = GRMustacheContentTypeText; + +// Load the text template `profile.mustache` from the main bundle: +GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"profile" bundle:nil error:NULL]; +``` + +Each template repository has its custom configuration, initialized with the default one: + +```objc +GRMustacheTemplateRepository *repo = [GRMustacheTemplateRepository templateRepositoryWithDirectory:@"/path/to/templates"]; + +// Have all templates in /path/to/templates render HTML +repo.configuration.contentType = GRMustacheContentTypeHTML; + +// Load the HTML template `profile.mustache`: +GRMustacheTemplate *template = [repo templateNamed:@"profile" error:NULL]; +``` + +Options can be configured at the template level also: this is described below. + + +Factory configuration +--------------------- + +The global default configuration is there to suit your needs: tweak it. + +Whenever you need a raw pristine configuration, use the `[GRMustacheConfiguration configuration]` class method. It returns a configuration initialized with factory defaults. + +```objc +GRMustacheTemplateRepository *repo = [GRMustacheTemplateRepository templateRepositoryWithDirectory:@"/path/to/templates"]; + +// Have all templates in /path/to/templates render with factory configuration: +repo.configuration = [GRMustacheConfiguration configuration]; +``` + +Configuration properties +------------------------ + +- [baseContext](#basecontext) +- [contentType](#contenttype) +- [tagStartDelimiter](#tagstartdelimiter-and-tagenddelimiter) +- [tagEndDelimiter](#tagstartdelimiter-and-tagenddelimiter) + +### baseContext + +Mustache rendering is all about looking for values in a *context stack*. That context stack is initialized with the *base context*, gets extended with the objects you provide to templates, and grows as Mustache sections get rendered each on its turn. See the [Runtime Guide](runtime.md#the-context-stack) for more information. + +The default configuration contains the default base context, pre-filled with the GRMustache [standard library](standard_library.md). + +The standard library pre-defines a few keys, such as `localize` and `uppercase`. For instance, the following template: + + {{# localize }}Hello {{ uppercase(name) }}!{{/ localize }} + +Would render: + + Bonjour ARTHUR ! + +Provided with a name and a localization for "Hello %@!" string in the Localizable.strings file of the main bundle. + +You can extend the base context: + +```objc +// Globally for all templates: +[[GRMustache defaultConfiguration] extendBaseContextWithObject:myCustomLibrary] + +// For templates of a template repository: +GRMustacheTemplateRepository *repo = [GRMustacheTemplateRepository templateRepositoryWith...]; +[repo.configuration extendBaseContextWithObject:myCustomLibrary] +``` + +You can also reset it to a blank slate, getting rid of the whole standard library: + +```objc +[GRMustache defaultConfiguration].baseContext = [GRMustacheContext context]; +repo.configuration.baseContext = [GRMustacheContext context]; +``` + +You may also be interested in [priority keys](security.md#priority-keys). They guarantee that a particular identifier will always evaluate to the same value. + +```objc +// Guarantee that {{my_important_value}} will always render the same and cannot +// be overriden by custom data: +id library = @{ @"my_important_value": ... }; +[repo.configuration extendBaseContextWithProtectedObject:library]; +``` + +See the [GRMustacheContext Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheContext.html) for a full documentation of the GRMustacheContext class. + +#### At the template level + +The base context can also be defined right at the template level: + +```objc +GRMustacheTemplate *template = [GRMustacheTemplate templateFrom...]; +[template extendBaseContextWith...]; // base context extension +template.baseContext = ...; // base context replacement +``` + +### contentType + +The default configuration has the `GRMustacheContentTypeHTML` contentType, meaning that all templates render HTML by default, and escape their input. + +GRMustache also supports *text templates*, that render text and do not escape anything. + +This subject is fully covered in the [HTML vs. Text Templates Guide](html_vs_text.md). + + +### tagStartDelimiter and tagEndDelimiter + +Mustache takes its name from its tag delimiters: `{{` and `}}`. + +You can configure them through GRMustacheConfiguration: + +```objc +// Have all templates use <% and %> as tag delimiters: +[GRMustacheConfiguration defaultConfiguration].tagStartDelimiter = @"<%"; +[GRMustacheConfiguration defaultConfiguration].tagEndDelimiter = @"%>"; + +// Only for templates of a template repository: +GRMustacheTemplateRepository *repo = [GRMustacheTemplateRepository templateRepositoryWith...]; +repo.configuration.tagStartDelimiter = @"[["; +repo.configuration.tagEndDelimiter = @"]]"; +``` + +#### At the template level + +The tag delimiters can be overriden at the template level using a "Set Delimiters Tag" such as `{{=<% %>=}}`: now tag would look like `<% name %>`. + + +Compatibility with other Mustache implementations +------------------------------------------------- + +The [Mustache specification](https://github.com/mustache/spec) does not talk about any of the options above. + +**If your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), check their documentation.** + + +[up](../../../../GRMustache#documentation), [next](forking.md) diff --git a/MapView/Map/GRMustache/Guides/delegate.md b/MapView/Map/GRMustache/Guides/delegate.md new file mode 100644 index 0000000..0413b85 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/delegate.md @@ -0,0 +1,237 @@ +[up](../../../../GRMustache#documentation), [next](compatibility.md) + +Tag Delegates +============= + +- [The GRMustacheTagDelegate protocol](#the-grmustachetagdelegate-protocol) +- [Providing Tag Delegates](#providing-tag-delegates) +- [Use Cases for Tag Delegates](#use-cases-for-tag-delegates) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + + +The GRMustacheTagDelegate protocol +---------------------------------- + +This protocol lets you observe, and possibly alter the rendering of the Mustache tags that consume your data. + +It provides you with a pair of classic "will.../did..." methods that are invoked just before, and just after a tag gets rendered: + +```objc +@protocol GRMustacheTagDelegate +@optional +- (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object; +- (void)mustacheTag:(GRMustacheTag *)tag didRenderObject:(id)object as:(NSString *)rendering; +- (void)mustacheTag:(GRMustacheTag *)tag didFailRenderingObject:(id)object withError:(NSError *)error; +@end +``` + +- The _object_ argument is the rendered value: a string, a number, an array, depending on the data you provided. +- The _tag_ argument represents the rendering tag: `{{ name }}`, `{{# name }}...{{/}}`, etc. + +See the [GRMustacheTag Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTag.html) for a full documentation of the GRMustacheTag class. + +- `mustacheTag:willRenderObject:` returns the value that should be rendered by the tag. It can return its `object` argument, leaving this value untouched, or it can return another value. + +- `mustacheTag:didRenderObject:as:` can let you clean up anything that has been prepared in `mustacheTag:willRenderObject:`. Besides, it is provided with the actual rendering of the tag. + +- `mustacheTag:didFailRenderingObject:withError:` has no other purpose but to let you perform any necessary clean up. There is no error recovery, and the error would eventually come out of the initial rendering method. + +Note that a tag like `{{ person.name }}` is rendered once. Thus the delegate will be called once. If the person has been found, the rendered object will be the name of the person. If the person could not be found, the rendered object will be `nil`. + +Also: when a section tag `{{# pets }}...{{/ pets }}` is provided with an array, its content is rendered several times. However the delegate will be called once, with the array passed in the _object_ argument. + + +Providing Tag Delegates +----------------------- + +Tag delegates are an uncommon kind of delegate. There is no object exposing a `delegate` property that you would set to your custom delegate, as one could expect. + +Actually, *many* tag delegates can enter the game, observe, and alter the rendering of a template. For example, NSDateFormatter and NSNumberFormatter are tag delegates, and this is how they can format all dates and numbers inside the section they are attached to (see the [NSFormatter Guide](NSFormatter.md)). + +Delegates are scoped. They can observe all tags in a template, or all inner tags of a template section, such as `{{# xxx }}...{{/ xxx }}`. + +You have to ways to inject tag delegates. The simplest is to have them enter the [context stack](runtime.md#the-context-stack). However, sometimes, your delegate should not "pollute" the template rendering with its own keys, and you will need to explicitely derive new contexts. Please follow us: + +### By Entering the Context Stack + +The [context stack](runtime.md#the-context-stack) contains all objects that can provide values to templates through their methods and properties. It is initialized by the object you render, and it extends by objects attached to sections. + +*An object conforming to the GRMustacheTagDelegate protocol gets "active" as soon as it enters the context stack.* + +For example, consider the following template and rendering code: + +`Document.mustache` + + {{# currentDate }} + {{# user }} + {{ name }} ({{ age }}) + {{/ user }} + +```objc +// Load Document.mustache +GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"Document" bundle:nil error:NULL]; + +// Initialize Document object +Document *document = [[Document alloc] init]; +document.user = self.user; + +// Render +NSString *rendering = [template renderObject:document error:NULL]; +``` + +The first object entering the context stack is the document. As long as it conforms to the GRMustacheTagDelegate protocol, it will get notified of the rendering of *all* tags (`{{# currentDate }}`, `{{# user }}...{{/ user }}`, `{{ name }}`, and `{{ age }}`). + +As soon as the `{{# user }}...{{/ user }}` section renders, the user enters the context stack. It will get notified of the rendering of the inner tags of the section (explicitly: `{{ name }}`, and `{{ age }}`). + + +### By Entering the Base Context of a Template + +As soon as an object enters the [context stack](runtime.md#the-context-stack), values returned by the `objectForKeyedSubscript:` and `valueForKey:` methods are available for the templates (see the [Runtime Guide](runtime.md)). + +This may be undesirable. You may want an object to be a tag delegate while not providing any value to the templates. + +In this case, you can observe all tags in a template by deriving its *base context*. The base context contains values and tag delegates that are always available for the template rendering. It contains all the ready for use tools of the [standard library](standard_library.md), for example. + +Let's see how `self` can become a tag delegate for the whole template: + +```objc +// Load Document.mustache +GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"Document" bundle:nil error:NULL]; + +// Add self as a tag delegate +[template extendBaseContextWithTagDelegate:self]; + +// Initialize Document object +Document *document = [[Document alloc] init]; +document.user = self.user; + +// Render +NSString *rendering = [template renderObject:document error:NULL]; +``` + +See the [GRMustacheTemplate Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTemplate.html) for a full discussion of `extendBaseContextWithTagDelegate:`. + + + +### By Deriving a Deep Context + +To illustrate this last use case, let's write an object that renders the uppercase version of all inner tags of the section it is attached to. + +We do not want it to "pollute" the [context stack](runtime.md#the-context-stack), because we want it to be able to transform *all* tags, including `{{ description }}`. Should our object be in the context stack, its own description (inherited from NSObject) would render, and we would have a bug. + +`Document.mustache` + + {{ firstName }} {{ lastName }}: {{ description }} + {{# uppercase }} + {{ firstName }} {{ lastName }}: {{ description }} + {{/ uppercase }} + +We expect the rendering to be: + + John Locke: English philosopher and physician + JOHN LOCKE: ENGLISH PHILOSOPHER AND PHYSICIAN + +Here is the implementation of our UppercaseTagDelegate class. + +It conforms to GRMustacheTagDelegate, obviously, but also to the [GRMustacheRendering protocol](rendering_objects.md). This protocol allows it to avoid the default rendering, that would send it right into the the context stack. + +```objc +@interface UppercaseTagDelegate : NSObject +@end + +@implementation UppercaseTagDelegate + +// GRMustacheRendering +- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error +{ + // Render the Mustache tag with an extended context + context = [context contextByAddingTagDelegate:self]; + return [tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]; +} + +// GRMustacheTagDelegate +- (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object +{ + return [[object description] uppercaseString]; +} + +@end +``` + +See the [GRMustacheContext Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheContext.html) for a full discussion of `contextByAddingTagDelegate:`. + +See also the [GRMustacheRendering Protocol Reference](http://groue.github.io/GRMustache/Reference/Protocols/GRMustacheRendering.html) and [GRMustacheTag Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTag.html) for a full documentation of GRMustacheRendering and GRMustacheTag. + + +Use Cases for Tag Delegates +--------------------------- + +### Default Values + +```objc +- (id)mustacheTag:(GRMustacheTag *)tag willRenderObject:(id)object +{ + if (object == nil) { + NSLog(@"Missing value for %@", tag); + return @"DEFAULT"; + } + return object; +} +``` + +Your application log will contain lines like: + + Missing value for + Missing value for + + +### Cross-Platform Filters + +Tag delegates can alter the rendering of all tags inside the section they are attached to. + +Let's consider the behavior of NSFormatter in GRMustache. They are able to format all variable tags inside a section (check the [NSFormatter Guide](NSFormatter.md)). + +For example, `{{#percent}}x = {{x}}{{/percent}}` renders as `x = 50 %` when `percent` is attached to an NSNumberFormatter. That is because formatters are tag delegates, just as our UppercaseTagDelegate class above. + +We could also use [filters](filters.md) in order to format numbers: `x = {{ percent(x) }}` would render just as well. + +However, `{{#percent}}x = {{x}}{{/percent}}` has one advantage over `x = {{ percent(x) }}`: it uses plain Mustache syntax, and is compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations). + +With such a common template, it's now a matter of providing different data, depending on the platform: + + // common template + {{#percent}}x = {{x}}{{/percent}} + + // data for GRMustache + { + "x": 0.5, + "percent": (some well-configured NSNumberFormatter) + } + + // data for other Mustache implementations + { + "percent": { + "x": "50 %" (computed during the "ViewModel preparation phase") + } + } + +See? When you, GRMustache user, can provide your raw model data and have tag delegates do the formatting, users of the other implementations can still *prepare* their data and build a "ViewModel" that contains the values that should be rendered. Eventually both renderings are identical. + + +### Funny Hooks + +Many objects of the [standard library](standard_library.md) are tag delegates. They are all built on top of public APIs, APIs that you can use, so check them out. For example: + +- GRMustacheHTMLEscapeFilter is quite simple: [GRMustacheHTMLLibrary.m](../src/classes/Services/StandardLibrary/GRMustacheHTMLLibrary.m) +- GRMustacheLocalizer is less simple: [GRMustacheLocalizer.m](../src/classes/Services/StandardLibrary/GRMustacheLocalizer.m) + + +Compatibility with other Mustache implementations +------------------------------------------------- + +The [Mustache specification](https://github.com/mustache/spec) does not have the concept of "tag delegates". + +**As a consequence, if your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), use `GRMustacheTagDelegate` with great care.** + + +[up](../../../../GRMustache#documentation), [next](compatibility.md) diff --git a/MapView/Map/GRMustache/Guides/faq.md b/MapView/Map/GRMustache/Guides/faq.md new file mode 100644 index 0000000..dd8d1e3 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/faq.md @@ -0,0 +1,56 @@ +GRMustache FAQ +============== + +- **Is GRMustache thread-safe?** + + Thread-safety of non-mutating methods is guaranteed. Thread-safety of mutating methods is not guaranteed. + +- **Is it possible to iterate over key/value pairs of a dictionary?** + + [Yes](standard_library.md#each). + +- **Is it possible to render array indexes? Customize first and last elements? Distinguish odd and even items?** + + [Yes, yes, and yes](standard_library.md#each). + +- **Is it possible to format numbers and dates?** + + Yes. Use [NSNumberFormatter and NSDateFormatter](NSFormatter.md). + +- **Is it possible to pluralize/singularize strings?** + + Yes. You have some [sample code](https://github.com/groue/GRMustache/issues/50#issuecomment-16197912) in issue #50. You may check [@mattt's InflectorKit](https://github.com/mattt/InflectorKit) for actual inflection methods. + +- **Is it possible to write Handlebars-like helpers?** + + [Yes](rendering_objects.md#example-a-handlebarsjs-helper) + +- **Is it possible to localize templates?** + + [Yes](standard_library.md#localize) + +- **Is it possible to embed partial templates whose name is only known at runtime?** + + [Yes](rendering_objects.md) + +- **Does GRMustache provide any layout or template inheritance facility?** + + [Yes](template_inheritance.md) + +- **Is it possible to render a default value for missing keys?** + + [Yes](view_model.md#default-values) + +- **Is it possible to disable HTML escaping?** + + [Yes](html_vs_text.md) + +- **What are those NSUndefinedKeyException?** + + When GRMustache has to try several objects until it finds the one that provides a `{{key}}`, several NSUndefinedKeyException may be raised and caught. Those exceptions are part of the normal template rendering. You can be prevent them, though: see the [Runtime Guide](runtime.md#detailed-description-of-grmustache-handling-of-valueforkey). + +- **Why does GRMustache need JRSwizzle?** + + GRMustache does not need it, and does not swizzle anything unless you explicitly ask for it. `[GRMustache preventNSUndefinedKeyExceptionAttack]` swizzles NSObject's `valueForUndefinedKey:` in order to prevent NSUndefinedKeyException during template rendering. See the [Runtime Guide](runtime.md#detailed-description-of-grmustache-handling-of-valueforkey) for a detailed discussion. + + diff --git a/MapView/Map/GRMustache/Guides/filters.md b/MapView/Map/GRMustache/Guides/filters.md new file mode 100644 index 0000000..4e26e0f --- /dev/null +++ b/MapView/Map/GRMustache/Guides/filters.md @@ -0,0 +1,209 @@ +[up](../../../../GRMustache#documentation), [next](rendering_objects.md) + +Filters +======= + +- [Overview](#overview) +- [Defining your own filters](#defining-your-own-filters) +- [Variadic filters](#variadic-filters) +- [Filters that return rendering objects](#filters-that-return-rendering-objects) +- [Filters namespaces](#filters-namespaces) +- [Filters errors](#filters-errors) +- [Sample code](#sample-code) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + +Overview +-------- + +You apply a filter just like calling a function, with parentheses: + +- `My name is {{ uppercase(name) }}` would render `My name is ARTHUR`, provided with "Arthur" as a name. + +- Filters can chain: `{{ uppercase(reversed(name)) }}` would render `RUHTRA`. + +- Filters can apply to compound key paths: `{{ uppercase(person.name) }}`. + +- You can extract values from filtered values: `{{ last(people).name }}`. + +- You can filter sections as well : `{{^ isEmpty(people) }}...{{/ isEmpty(people) }}` renders if the people collection is not empty. + + For brevity's sake, closing section tags can be empty: `{{^ isEmpty(people) }}...{{/}}` is valid. + +- Filters can take several arguments: `{{ localize(date, format) }}`. + +- Filters can return filters: `{{ f(x)(y) }}`. + + +Defining your own filters +------------------------- + +You can implement your own filters with objects that conform to the `GRMustacheFilter` protocol. + +This protocol defines a single required method: + +```objc +@protocol GRMustacheFilter +@required +- (id)transformedValue:(id)object; +@end +``` + +You can for instance declare a filter that outputs numbers as percentages: + +```objc +@interface PercentFilter : NSObject +@end + +@implementation PercentFilter +- (id)transformedValue:(id)object +{ + NSNumberFormatter *percentNumberFormatter = [[NSNumberFormatter alloc] init]; + percentNumberFormatter.numberStyle = kCFNumberFormatterPercentStyle; + return [numberFormatter stringFromNumber:object]; +} +@end + +id percentFilters = [[PercentFilter alloc] init]; +``` + +The protocol comes with a `GRMustacheFilter` class, which provides a convenient method for building a filter without implementing a full class that conforms to the protocol: + +```objc +id data = @{ + @"gain": @0.5, + @"percent": [GRMustacheFilter filterWithBlock:^id(id object) { + NSNumberFormatter *numberFormatter = [NSNumberFormatter new]; + numberFormatter.numberStyle = kCFNumberFormatterPercentStyle; + return [numberFormatter stringFromNumber:object]; + }], +}; + +// Enjoy your 50% productivity bump! +NSString *templateString = @"Enjoy your {{ percent(gain) }} productivity bump!"; +NSString *rendering = [GRMustacheTemplate renderObject:data + fromString:templateString + error:NULL]; +``` + +Variadic filters +---------------- + +A *variadic filter* is a filter that accepts a variable number of arguments. + +You create a variadic filter with the `variadicFilterWithBlock:` method: + +`Document.mustache`: + + {{#object1}} + {{ dateFormat(date, format) }} + {{/object1}} + {{#object2}} + {{ dateFormat(date, format) }} + {{/object2}} + +`Render.m`: + +```objc +id data = @{ + @"object1": @{ + @"format": @"yyyy-MM-dd 'at' HH:mm", + @"date": [NSDate date] + }, + @"object2": @{ + @"format": @"yyyy-MM-dd", + @"date": [NSDate date] + }, + @"dateFormat": [GRMustacheFilter variadicFilterWithBlock:^id(NSArray *arguments) { + // first argument is date + NSDate *date = [arguments objectAtIndex:0]; + + // second argument is format + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = [arguments objectAtIndex:1]; + + // compute the result + return [dateFormatter stringFromDate:date]; + }] +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + 2012-10-28 at 17:10 + 2012-10-28 + + +Filters that return rendering objects +------------------------------------- + +"Rendering objects" are objects that perform a custom rendering. They are described in detail in the [Rendering Objects Guide](rendering_objects.md). + +A fundamental technique of advanced GRMustache rendering is filters that return rendering objects. For example: + + I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}. + +would render, depending on the number of cats: + + I have 1 cat. + I have 5 cats. + +The `pluralize` filter returns an object that is able to pluralize the inner content of the section it is attached to. Go check the [Rendering Objects Guide](rendering_objects.md) for more details. + + +Filters namespaces +------------------ + +Just as you can provide an object hierarchy for rendered values, and extract `person.pet.name` from it, you can provide filters as an object hierarchy, and "namespace" your filters. For instance, let's declare the `math.abs` filter, and render `{{ math.abs(x) }}`: + +```objc +NSString *templateString = @"{{ math.abs(x) }}"; +GRMustacheTemplate *template = [GRMustacheTemplate templateFromString:templateString error:NULL]; + +id data = @{ + @"x": @(-1), + @"math": @{ + @"abs": [GRMustacheFilter filterWithBlock:^id(id object) { + return @(abs([object intValue])); + }], + }, +}; + +// 1 +NSString *rendering = [template renderObject:data error:NULL]; +``` + + +Filters errors +-------------- + +Should a filter be missing, or should the matching object not conform to the `GRMustacheFilter` protocol, GRMustache will return an error of domain `GRMustacheErrorDomain` and code `GRMustacheErrorCodeRenderingError`. + +The message describes the exact place where the error occur has occurred: + + Missing filter for key `f` in tag `{{ f(foo) }}` at line 13 of /path/to/template. + + Object for key `f` in tag `{{ f(foo) }}` at line 13 of /path/to/template does not conform to GRMustacheFilter protocol: "blah" + + +Sample code +----------- + +Custom filters are used in many items of the [standard library](standard_library.md). [NSFormatter](NSFormatter.md) are ready-made filters in GRMustache. Go check inspiration there. + + +Compatibility with other Mustache implementations +------------------------------------------------- + +The [Mustache specification](https://github.com/mustache/spec) does not have any concept of "filters". + +**If your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), do NOT use filters.** + +Instead, have a look at tag delegates, especially the [Cross-Platform Filters](delegate.md#cross-platform-filters) section of the Tag Delegates Guide. + + +[up](../../../../GRMustache#documentation), [next](rendering_objects.md) diff --git a/MapView/Map/GRMustache/Guides/forking.md b/MapView/Map/GRMustache/Guides/forking.md new file mode 100644 index 0000000..7bc46d4 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/forking.md @@ -0,0 +1,169 @@ +[up](../../../../GRMustache#documentation) + +# Forking GRMustache + +After you have forked groue/GRMustache, you might want to change stuff, test, and then build the library. + +You'll find below some useful information on each of those topics. + +## Change GRMustache + +### Classes at a glance + +The library features are described in the [guides](introduction.md). This section describes the classes that implement those features. They are organized in a few big domains: + +- **Parsing** + - `GRMustacheTemplateRepository` + - `` (protocol) + + *Template repositories* are objects that load template strings from various sources. + + GRMustache ships with various template repositories that are able to load templates from the file system, and from a dictionary of template strings. The library user can also provide a *data source* to a template repository, in order to load template strings from unimagined locations. + + - `GRMustacheParser` + - `GRMustacheToken` + + The *parser* is able to produce a [parse tree](http://en.wikipedia.org/wiki/Parse_tree) of *tokens* out of a template string. + + For instance, a parser generates three tokens from `Hello {{name}}!`: two text tokens and a variable token. + + - `GRMustacheExpression` + - `GRMustacheFilteredExpression` + - `GRMustacheIdentifierExpression` + - `GRMustacheImplicitIteratorExpression` + - `GRMustacheScopedExpression` + + Some tokens contain an *expression*. Expressions will go live during the rendering of a template (see below), being able to compute rendered values: + + - `{{ name }}` contains an *identifier expression*. + - `{{ . }}` contains an *implicit iterator expression*. + - `{{ person.name }}` contains a *scoped expression*. + - `{{ uppercase(name) }}` contains a *filtered expression*. + +- **Compiling** + - `GRMustacheConfiguration` + - `GRMustacheCompiler` + - `GRMustacheAST` + - `` (protocol) + + The *compiler* consumes a parse tree of tokens and outputs an *AST* ([abstract syntax tree](http://en.wikipedia.org/wiki/Abstract_syntax_tree)) of *template components*. The *configuration* tells the compiler whether the AST should represent a HTML or a text template. + + Template components are actually able to provide the rendering expected by the library user: + + - `GRMustacheInheritableSection` + - `GRMustacheInheritablePartial` + - `GRMustacheTemplate` + - `GRMustacheTextComponent` + - `GRMustacheTag` + + *Templates* render full templates and partials, *tags* render user data, *text elements* render raw text, *inheritable templates* render inheritable partial tags, and *inheritable sections* render sections starting with a dollar sign, such as `{{$ content }}...{{/ }}`. + + For instance, from the tokens parsed from `Hello {{name}}!`, a compiler outputs an AST made of two text elements and a tag element. + + There are two subclasses of GRMustacheTag: + + - `GRMustacheSectionTag` + - `GRMustacheVariableTag` + + *Section tags* and *Variable tags* represent their "physical" counterpart `{{#^$ name}}...{{/name}}` and `{{name}}` respectively. + +- **Runtime** + - `GRMustacheContext` + + A *rendering context* implements a state of four different stacks: + + - a *context stack*. + - a *priority context stack*. + - a *tag delegate stack*. + - a *inheritable partial stack*, that grows when a inheritable partial renders. + + A rendering context is able to provide the value for an identifier such as `name` found in a `{{name}}` tag. However, runtime is not directly responsible for providing values that should be rendered. Expressions built at the parsing phase are. They query the context in order to compute their values. + + - `` (protocol) + + Tags iterate all *tag delegates* in a rendering context and let them observe or alter their rendering. + + - `` (protocol) + + The library user can implement his own *rendering objects* in order to perform custom rendering. + + - `` (protocol) + - `GRMustacheFilter` (class) + + The library user can implement her own *filters*, that will add to the built-in ones. + + + +### Project organisation + +Objective-C files that make GRMustache are stored in the `src/classes` folder. They are added to both `GRMustache7-MacOS` and `GRMustache7-iOS` targets of the `src/GRMustache.xcodeproj` project. + +Headers are splitted in two categories: + +- public headers +- private headers + +#### Public headers + +Public headers must contain only declarations for APIs that are exposed to the GRMustache users. They must not import or include any private header. + +Methods and functions declared in public headers must be decorated with the macros defined in `Classes/GRMustacheAvailabilityMacros.h`. Check existing public headers for inspiration. + +`src/classes/Shared/GRMustacheAvailabilityMacros.h` is generated by `src/bin/buildGRMustacheAvailabilityMacros`. + +#### Private headers + +Private headers have names ending in `_private.h`. They must not import or include any public header. The set of public APIs must be duplicated in both public and private headers. + + +## Test GRMustache + +Before running the tests, make sure git submodules are downloaded: + + $ git submodule update --init + +There are two kinds of tests, all stored in the `src/tests` folder. + +- tests of private APIs +- tests of public APIs + +When a file is added or removed from the `src/tests` folder, both `GRMustache7-MacOSTests` and `GRMustache7-iOSTests` targets of the `src/GRMustache.xcodeproj` project are updated. + +### Tests of private APIs + +Tests of private internals are stored in the `src/tests/Private` folder, and are all subclasses of `GRMustachePrivateAPITest`. + +The implementation files of those tests must not include any public header. + +### Tests of public APIS + +Tests of public GRMustache API are versionned: the `src/tests/Public/v7.0` folder contains tests for features introduced in the version 7.0 of the library. `src/tests/Public/v7.2` contains tests for the version 7.2, etc. + +Those tests are all subclasses of `GRMustachePublicAPITest`. Their implementation files must not include any private header. + +You will use the macros defined in `Classes/GRMustacheAvailabilityMacros.h`. They help the tests acheiving three goals: + +- use only APIs that are available in the GRMustache version they test against, +- emit deprecation warning when they use deprecated GRMustache APIs, +- help GRMustache achieve full backward compatibility. + +For instance, all header files for public API tests in `src/tests/Public/v7.2` would begin with: + + #define GRMUSTACHE_VERSION_MAX_ALLOWED GRMUSTACHE_VERSION_7_2 + #import "GRMustachePublicAPITest.h" + +When you add a test for a public API, make sure you place it in the folder that introduced the API (check the release notes), and NOT in the version that will include the new code. For instance, if version 7.6 introduces a fix for an API that was introduced in version 7.2, the version 7.6 will then ship with new tests in the src/tests/Public/v7.2 folder. + +## Building + +Building GRMustache is building the `/lib` and `/include` folders, which contain public headers and static libraries for iOS and MacOS. + +In order to build them: make sure git submodules are downloaded first: + + $ git submodule update --init + +Then, issue the following command: + + $ make clean && make + +[up](../../../../GRMustache#documentation) diff --git a/MapView/Map/GRMustache/Guides/helpers.md b/MapView/Map/GRMustache/Guides/helpers.md new file mode 100644 index 0000000..c8fff6a --- /dev/null +++ b/MapView/Map/GRMustache/Guides/helpers.md @@ -0,0 +1 @@ +This document has been [superseded](rendering_objects.md). diff --git a/MapView/Map/GRMustache/Guides/html_vs_text.md b/MapView/Map/GRMustache/Guides/html_vs_text.md new file mode 100644 index 0000000..4e40a60 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/html_vs_text.md @@ -0,0 +1,165 @@ +[up](../../../../GRMustache#documentation), [next](NSFormatter.md) + +HTML vs. Text Templates +======================= + +The Mustache language has a big focus on HTML: it provides HTML-escaping of values by default. + +However, GRMustache supports both *HTML templates*, and *text templates*. + +HTML templates return HTML: their `{{ name }}` variable tags escape their input. Their `{{{ name }}}` triple mustache variable tags assume HTML input, and do not perform HTML-escape. + +Text templates return text: their `{{ name }}` and `{{{ name }}}` tags do not escape their input: they have identical rendering. + +- [Global configuration](#global-configuration) +- [Template Repository Configuration](#template-repository-configuration) +- [Content Type Of Individual Templates](#content-type-of-individual-templates) +- [Mixing HTML And Text Templates](#mixing-html-and-text-templates) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + + +Global configuration +-------------------- + +The default [configuration](configuration.md) `[GRMustacheConfiguration defaultConfiguration]` +applies to all GRMustache rendering unless specified otherwise: + +```objc +// Have GRMustache templates render text by default, +// and do not HTML-escape their input. +[GRMustacheConfiguration defaultConfiguration].contentType = GRMustacheContentTypeText; +``` + +The `contentType` property of the default configuration can take a value among: + +```objc +typedef enum { + GRMustacheContentTypeHTML, + GRMustacheContentTypeText, +} GRMustacheContentType; +``` + +GRMustache is a Mustache engine: templates are HTML by default, and you do not have to explicitly require it. + + +Template Repository Configuration +--------------------------------- + +[Template repositories](template_repositories.md) can be given a specific configuration, that will only apply to the templates built by this repository. + +```objc +// All templates loaded from the bash_script_templates directory will be +// rendered as text, and will not HTML-escape their input. +NSString *path = @"/path/to/bash_script_templates"; +GRMustacheTemplateRepository *repository = [GRMustacheTemplateRepository templateRepositoryWithDirectory:path]; +repository.configuration.contentType = GRMustacheContentTypeText; + +// Render +GRMustacheTemplate *template = [repository templateNamed:...]; +NSString *rendering = [template renderObject:...]; +```` + +Template repository configuration has higher priority than the default configuration. + + +Content Type Of Individual Templates +------------------------------------ + +Templates can also be given a specific content type: + +Insert those pragma tags right in the content of your templates: + +- `{{% CONTENT_TYPE:TEXT }}` turns a template into a text template. +- `{{% CONTENT_TYPE:HTML }}` turns a template into a HTML template. + +For example: + + {{! This template renders a bash script. }} + {{% CONTENT_TYPE:TEXT }} + export LANG={{ENV.LANG}} + ... + +Pragma tags have higher priority than repository and default configurations. + + +Mixing HTML And Text Templates +------------------------------ + +Text templates return text. They get HTML-escaped when they get embedded in HTML templates: + +### Embedding via a partial tag + +`Document.mustache`: + +
+    {{> BashScript }}
+    
+ +`BashScript.mustache`: + + {{% CONTENT_TYPE:TEXT }} + cd {{path}} && {{command}} + +Rendering code: + + id data = @{ + @"path": @"/path/", + @"command": @"echo \"yeah\"" , + }; + + // the script, alone + NSString *script = [GRMustacheTemplate renderObject:data fromResource:@"BashScript" bundle:nil error:NULL]; + + // the document + NSString *document = [GRMustacheTemplate renderObject:data fromResource:@"Document" bundle:nil error:NULL]; + +script: + + cd /path/ && echo "yeah" + +document: + +
+    cd /path/ && echo "yeah"
+    
+ +### Embedding a dynamic partial + +`Document.mustache`: + +
+    {{ bash_script }}
+    
+ +`BashScript.mustache`: + + {{% CONTENT_TYPE:TEXT }} + cd {{path}} && {{command}} + +Rendering code: + + id data = @{ + @"path": @"/path/", + @"command": @"echo \"yeah\"" , + @"bash_script": [GRMustacheTemplate templateFromResource:@"BashScript" bundle:nil error:NULL] + }; + + NSString *document = [GRMustacheTemplate renderObject:data fromResource:@"Document" bundle:nil error:NULL]; + +document: + +
+    cd /path/ && echo "yeah"
+    
+ +See the [Rendering Objects Guide](rendering_objects.md) for more information about inclusion of partials chosen at runtime. + + +Compatibility with other Mustache implementations +------------------------------------------------- + +The [Mustache specification](https://github.com/mustache/spec) does not have any concept of "text template". + +**If your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), use {{{ triple }}} mustache tags, and don't mix text with HTML.** + +[up](../../../../GRMustache#documentation), [next](NSFormatter.md) diff --git a/MapView/Map/GRMustache/Guides/installation.md b/MapView/Map/GRMustache/Guides/installation.md new file mode 100644 index 0000000..6513b68 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/installation.md @@ -0,0 +1,55 @@ +[up](../../../../GRMustache#documentation), [next](introduction.md) + +Installation +============ + +Option 1: CocoaPods +------------------- + +Append `pod 'GRMustache', '~> 7.3.0'` to your [Podfile](https://github.com/CocoaPods/CocoaPods). + + +Option 2: Static Library +------------------------ + +The distribution includes pre-built static libraries: + +1. Clone the repository with the `git clone https://github.com/groue/GRMustache.git` command. + +2. Embed GRMustache in your Xcode project: + - For MacOS development, add `include/GRMustache.h` and `lib/libGRMustache7-MacOS.a` to your project. + - For iOS development, add `include/GRMustache.h` and `lib/libGRMustache7-iOS.a` to your project. + + NB: If you have GRMustache files *copied* in your project, you'll need to copy all header files of the `include` directory, not only `GRMustache.h`. + +3. Edit your target settings, and pass the `-ObjC` option in the "Other Linker Flags" ([how to](http://developer.apple.com/library/mac/#qa/qa1490/_index.html)). + +The armv6 slice is not included. In order to target this architecture, you have to compile GRMustache yourself (see below), or to use CocoaPods (see above). + +### Updating your static library + +When pulling the `master` branch of GRMustache, you'll get the latest stable release. Should a new major version be shipped, you may pull incompatible changes. In order to prevent this, checkout and pull the `GRMustache7` branch: + + $ git clone https://github.com/groue/GRMustache.git + $ cd GRMustache + $ git checkout -b GRMustache7 origin/GRMustache7 + $ git pull # checkout the latest version 7 + +Option 3: Compiling the raw sources +----------------------------------- + +You may also embed the raw GRMustache sources in your project: + + $ git clone https://github.com/groue/GRMustache.git + $ cd GRMustache + $ git checkout v7.3.0 # checkout the latest stable release + $ git submodule update --init src/vendor/groue/jrswizzle + +Add all files of `src/classes` plus `src/vendor/groue/jrswizzle/JRSwizzle.*` to your project. + +If your project uses ARC, flag the source files with the `-fno-objc-arc` compiler flag ([how to](http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project)). + +In your own sources, avoid importing header files whose name ends with `_private.h`: those are private headers that may change, without notice, in future releases. + + +[up](../../../../GRMustache#documentation), [next](introduction.md) diff --git a/MapView/Map/GRMustache/Guides/introduction.md b/MapView/Map/GRMustache/Guides/introduction.md new file mode 100644 index 0000000..2c67fdc --- /dev/null +++ b/MapView/Map/GRMustache/Guides/introduction.md @@ -0,0 +1,112 @@ +[up](../../../../GRMustache), [next](templates.md) + +GRMustache introduction +======================= + +- [The Mustache language](#the-mustache-language) +- [Beyond Mustache](#beyond-mustache) + + +The Mustache language +--------------------- + +Make sure you get familiar with the Mustache syntax and features first: http://mustache.github.io/mustache.5.html. + +- **Variable tags**, as `{{name}}`, `{{{name}}}` and `{{&name}}` (HTML-escaped or not) +- **Section tags** (boolean, loop, lambda, inverted), as `{{#name}}...{{/name}}` and `{{^name}}...{{/name}}` +- **Partial tags**, as `{{> partial}}` +- **Comment tag**, as `{{! comment }}` +- **"Set delimiter tags"**, as `{{=<% %>=}}` + +Features below are not documented in [mustache.5.html](http://mustache.github.io/mustache.5.html), despite their inclusion in the [Mustache specification](https://github.com/mustache/spec): + +- **Key paths**, as `{{ person.name }}`, for direct access to an object's property. +- **"Implicit iterator"**, aka `{{.}}`, directly renders the current object (useful when looping over strings, for instance). +- **"Mustache lambdas"**, allow both `{{name}}` and `{{#name}}...{{/name}}` tags to perform custom rendering. + + +Beyond Mustache +--------------- + +GRMustache adds many services on top of the minimalistic template engine. + +Check the [Compatibility Guide](compatibility.md) whenever you want to keep your templates compatible with other Mustache implementations. + + +### Syntax extensions + +- **Empty closing tags**, as in `{{#name}}...{{/}}` + + You don't have to repeat the opening expression in the closing tag. + +- **"Else"**, as in `{{#name}}...{{^name}}...{{/name}}` + + You don't have to close a regular section if it is immediately followed by its inverted form. + + The short form `{{#name}}...{{^}}...{{/}}` is accepted, as well as the "unless" form `{{^name}}...{{#}}...{{/}}`. + +- **"Anchored key paths"**, as `{{ .name }}` which enforces lookup of the `name` key in the immediate context instead of going through the context stack built by Mustache sections. + + If you are not familiar with the "context stack" and the Mustache key lookup mechanism, check the [Runtime Guide](runtime.md#the-context-stack). + +- **Loops in variable tags**: a simple variable tag `{{items}}` renders a concatenation of the rendering of each individual item. You may think of Ruby on Rails' `<%= render @items %>`: check the [Rendering Objects Guide](rendering_objects.md). + + +### More partials + +- **Support for the file system hierarchy**. + + Use relative `{{> header }}` or absolute paths `{{> /shared/header }}` to your partial templates: see the [Partials Guide](partials.md). + +- **Template inheritance**, inspired by [hogan.js](http://twitter.github.com/hogan.js/) and [spullara/mustache.java](https://github.com/spullara/mustache.java), allow you to define reusable template layouts: + + {{< page }} {{! page.mustache defines the layout. }} + {{$ content }} {{! this template defines the content. }} + ... + {{/ content }} + {{/ page }} + + Template inheritance is documented in the [Template Inheritance Guide](template_inheritance.md). + + +### Text templates + +Mustache focuses on rendering HTML, and safely HTML-escape your data. + +GRMustache also supports text templates, that do not escape anything. Check the [HTML vs. Text Templates Guide](html_vs_text.md). + + +### Filters + +Filters, as `{{ uppercase(name) }}`, are documented in the [Filters Guide](filters.md). + + +### Powerful Lambdas + +Forget everything you know about the stifled genuine Mustache lambdas, and give GRMustache [rendering objects](rendering_objects.md) a try. + + +### Services + +The library ships with a [standard library](standard_library.md) of various filters and tools for rendering your data. + +Our old friend [NSFormatter](NSFormatter.md) is also welcome to the party. + + +### Flexibility + +GRMustache's core engine is extensible. Feel free to hook in: + +- [Filters](filters.md) transform your raw data. +- [Rendering objects](rendering_objects.md) provide custom rendering. +- [Tag delegates](delegate.md) observe and alter tag rendering. + +Those three hooks are lego bricks: from them you can build more complex tools, such as [NSFormatter](NSFormatter.md) and the [localize](standard_library.md#localize) helper. + + +### Security + +GRMustache has built-in features that prevents it from threatening your application whenever you render untrusted templates or data. See the [Security Guide](security.md) for more information. + + +[up](../../../../GRMustache), [next](templates.md) diff --git a/MapView/Map/GRMustache/Guides/partials.md b/MapView/Map/GRMustache/Guides/partials.md new file mode 100644 index 0000000..d1816dd --- /dev/null +++ b/MapView/Map/GRMustache/Guides/partials.md @@ -0,0 +1,85 @@ +[up](../../../../GRMustache#documentation), [next](template_inheritance.md) + +Partial templates +================= + +When a `{{> name }}` Mustache tag occurs in a template, GRMustache renders in place the content of another template, the *partial*, identified by its name. + +You can write recursive partials. Just avoid infinite loops in your context objects. + +- [Sources of partials](#sources-of-partials) +- [Partials in the file system](#partials-in-the-file-system) +- [Dynamic partials](#dynamic-partials) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + + +Sources of partials +------------------- + +Depending on the method which has been used to create the original template, partials will be searched in different places : + +- In the main bundle, with ".mustache" extension: + - `renderObject:fromString:error:` + - `templateFromString:error:` +- In the specified bundle, with ".mustache" extension: + - `renderObject:fromResource:bundle:error:` + - `templateFromResource:bundle:error:` +- Relatively to the URL of the including template, with the same extension: + - `templateFromContentsOfURL:error:` +- Relatively to the path of the including template, with the same extension: + - `templateFromContentsOfFile:error:` + +Check the [Template Repositories Guide](template_repositories.md) for more partial loading strategies. + + +Partials in the file system +--------------------------- + +When you identify a template through a URL, a file path, or a bundle resource name (see the [Templates Guide](templates.md)), you are able to navigate through a hierarchy of directories and partial files. + +The partial tag `{{> name }}` interprets the *name* as a *relative path*, and loads the partial template relatively to the embedding template. For example, given the following hierarchy: + + - templates + - a.mustache + - partials + - b.mustache + +The a.mustache template can embed b.mustache with the `{{> partials/b }}` tag, and b.mustache can embed a.mustache with the `{{> ../a }}` tag. + +*Never use file extensions in your partial tags.* `{{> partials/b.mustache }}` would try to load the `b.mustache.mustache` file which does not exist: you'd get an error of domain `GRMustacheErrorDomain` and code `GRMustacheErrorCodeTemplateNotFound`. + + +### Absolute paths to partials + +When your templates are stored in a hierarchy of directories, you sometimes need to refer to a partial template in an absolute way, that does not depend on the location of the embedding template. + +Compare: + + `{{> partials/header }}` + `{{> /partials/header }}` {{! with a leading slash }} + +The first partial tag provides a *relative path*, and refers to a different template, depending on the path of the including template. + +The latter always references the same partial, with an *absolute path*. + +Absolute partial paths need a root, and the objects that set this root are `GRMustacheTemplateRepository` objects. The rest of the story is documented at [Template Repositories Guide](template_repositories.md). + + +Dynamic partials +---------------- + +Partial templates identified with a partial tag such as `{{> name }}` are *hard-coded*. Such a tag always renders the same partial template. + +You may want to choose the rendered partial at runtime: this use case is covered in the [Rendering Objects Guide](rendering_objects.md). + + +Compatibility with other Mustache implementations +------------------------------------------------- + +The [Mustache specification](https://github.com/mustache/spec) does not have the concepts of relative vs. absolute partial paths, or dynamic partials. + +**As a consequence, if your goal is to design templates that are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), use those features with great care.** + + +[up](../../../../GRMustache#documentation), [next](template_inheritance.md) + diff --git a/MapView/Map/GRMustache/Guides/protected_contexts.md b/MapView/Map/GRMustache/Guides/protected_contexts.md new file mode 100644 index 0000000..46929d2 --- /dev/null +++ b/MapView/Map/GRMustache/Guides/protected_contexts.md @@ -0,0 +1 @@ +This document has been [superseded](security.md#priority-keys). diff --git a/MapView/Map/GRMustache/Guides/proxies.md b/MapView/Map/GRMustache/Guides/proxies.md new file mode 100644 index 0000000..c8fff6a --- /dev/null +++ b/MapView/Map/GRMustache/Guides/proxies.md @@ -0,0 +1 @@ +This document has been [superseded](rendering_objects.md). diff --git a/MapView/Map/GRMustache/Guides/rendering_objects.md b/MapView/Map/GRMustache/Guides/rendering_objects.md new file mode 100644 index 0000000..e40523e --- /dev/null +++ b/MapView/Map/GRMustache/Guides/rendering_objects.md @@ -0,0 +1,581 @@ +[up](../../../../GRMustache#documentation), [next](delegate.md) + +Rendering Objects +================= + +- [Overview](#overview) +- [GRMustacheRendering protocol](#grmustacherendering-protocol) +- [Trivial Example](#trivial-example) +- [Example: Wrapping the content of a section tag](#example-wrapping-the-content-of-a-section-tag) +- [Example: Have a section render an alternate template string](#example-have-a-section-render-an-alternate-template-string) +- [Example: Dynamic partials](#example-dynamic-partials) +- [Example: Objects that render themselves](#example-objects-that-render-themselves) +- [Example: A Handlebars.js Helper](#example-a-handlebarsjs-helper) +- [More Sample Code](#more-sample-code) +- [Compatibility with other Mustache implementations](#compatibility-with-other-mustache-implementations) + + +Overview +-------- + +The [Runtime Guide](runtime.md) describes what happens whenever a tag such as `{{ name }}` or `{{# items }}...{{/ items }}` gets rendered. Strings are HTML-escaped, arrays are iterated, numbers control boolean sections, etc. + +But sometimes you need something more dynamic, you need to inject your own code into the template rendering, and extend the language. Orthodox Mustache provides with "lambda sections". [Handlebars.js](http://handlebarsjs.com), an extended Mustache engine, has introduced "helpers". + +Let us introduce GRMustache "rendering objects". + +### Examples + +Rendering objects are quite versatile. Let's look at a few examples: + + {{# localize }}...{{/ }} + +`localize` is part of the [standard library](standard_library.md#localize). It performs a custom rendering by localizing the inner content of the section it renders. + + {{> partial }} vs. {{ template }} + +The `{{> partial }}` tag renders a hard-coded template, identified by its name. By providing instead a GRMustacheTemplate object to a tag, which performs its own custom rendering, you can render a "dynamic partial". + + {{# dateFormat }}...{{ birthDate }}...{{ joinDate }}...{{/ }} + +[NSDateFormatter](NSFormatter.md) is a rendering object able to format all dates in a section. + + I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}. + +`pluralize` is a filter that returns an object able to pluralize the content of the section (see sample code in [issue #50](https://github.com/groue/GRMustache/issues/50#issuecomment-16197912)). + + {{# each(items) }}{{ @index }}: {{ name }}{{/ }} + +`each` is part of the [standard library](standard_library.md#each). It returns rendering objects that define extra keys such as `@index`. + +---- + +**All examples above are built using public GRMustache APIs.** Even the built-in ones such as `localize`, `each`, or the date formatter. Your own rendering objects are not artificially limited. + +The last two examples involve [filters](filters.md). Filters themselves do not provide custom rendering: they just transform values. However, when they return objects that provide custom rendering, the fun can begin. This two-fold pattern is how GRMustache let you implement [Handlebars-like helpers](http://handlebarsjs.com/block_helpers.html). + +Let's begin the detailed tour. + + +GRMustacheRendering protocol +---------------------------- + +This protocol declares the method that all rendering objects must implement: + +```objc +@protocol GRMustacheRendering + +- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag + context:(GRMustacheContext *)context + HTMLSafe:(BOOL *)HTMLSafe + error:(NSError **)error; + +@end +``` + +- The _tag_ represents the tag you must render for. It may be a variable tag `{{ name }}`, a section tag `{{# name }}...{{/}}`, etc. + +- The _context_ represents the [context stack](runtime.md#the-context-stack), and all information that tags need to render. + +- _HTMLSafe_ is a pointer to a BOOL: upon return, it must be set to YES or NO, depending on the safety of the string you render. If you forget to set it, it is of course assumed to be NO. Returning NO would have GRMustache HTML-escape the returned string before inserting it in the final rendering of HTML templates (see the [HTML vs. Text Templates Guide](html_vs_text.md) for more information). + +- _error_ is... the eventual error. You can return nil without setting any error: in this case, everything happens as if you returned the empty string. + +See the [GRMustacheTag Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheTag.html) and [GRMustacheContext Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheContext.html) for a full documentation of GRMustacheTag and GRMustacheContext. + +The `+[GRMustacheRendering renderingObjectWithBlock:]` method comes in handy for creating a rendering object without declaring any class: + +```objc +id renderingObject = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) +{ + switch(tag.type) { + case GRMustacheTagTypeVariable: + return @"I'm rendering a {{ variable }} tag."; + case GRMustacheTagTypeSection: + return @"I'm rendering a {{# regular }}...{{/ }} section tag."; + } +}]; +``` + + +Trivial Example +--------------- + +`Document.mustache`: + + {{ name }} + {{{ name }}} + +`Render.m`: + +```objc +id nameRenderingObject = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) +{ + return @"Arthur & Cie"; +}]; + +id data = @{ @"name": nameRenderingObject }; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + Arthur & Cie + Arthur & Cie + +### What did we learn here? + +Rendering objects are not difficult to trigger: when you know how to have a tag `{{ name }}` render a regular value, you know how to have it handled by a rendering object. + +The HTML escaping is negociated between the tag and the rendering object: `{{ name }}` escapes HTML, when `{{{ name }}}` does not. Since the rendering object does not explicitly set the _HTMLSafe_ parameter to YES, the first tag escapes the result. + + +Example: Wrapping the content of a section tag +---------------------------------------------- + +Let's write a rendering object which wraps a section in a `` HTML tag: + +`Document.mustache`: + + {{# strong }} + {{ name }} is awesome. + {{/ strong }} + +`Render.m`: + +```objc +id strong = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) +{ + // First perform a raw rendering of the tag, using its + // `renderContentWithContext:HTMLSafe:error` method. + // + // We'll get `Arthur is awesome.` + + NSString *rawRendering = [tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]; + + // Return the raw rendering, wrapped in a HTML tag: + + return [NSString stringWithFormat:@"%@", rawRendering]; +}]; + +id data = @{ + @"name": @"Arthur", + @"strong": strong, +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + Arthur is awesome. + +### What did we learn here? + +Variable tags such as `{{ name }}` don't have much inner content. But section tags do: `{{# name }} inner content {{/ name }}`. + +The `renderContentWithContext:HTMLSafe:error:` method returns the rendering of the inner content, processing all the inner tags with the provided context. + +It also sets the `HTMLSafe` boolean for you, so that you do not have to worry about it. GRMustache templates render HTML by default, so `HTMLSafe` will generally be YES. There are also text templates (see the [HTML vs. Text Templates Guide](html_vs_text.md)): in this case, `HTMLSafe` would be NO. Depending on how reusable you want your rendering object to be, you may have to deal with it. + +Your rendering objects can thus delegate their rendering to the tag they are given. They can render the tag once or many times: + +`Document.mustache`: + + {{# twice }} + Mustache is awesome! + {{/ twice }} + +`Render.m`: + +```objc +id data = @{ + @"twice": [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) { + NSMutableString *buffer = [NSMutableString string]; + [buffer appendString:[tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]]; + [buffer appendString:[tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]]; + return buffer; + }] +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + Mustache is awesome! + Mustache is awesome! + + +Example: Have a section render an alternate template string +----------------------------------------------------------- + +Let's write a rendering object that wraps a section in a HTML link. The URL of the link will be fetched with the `url` key: + +`Document.mustache`: + + {{# link }}{{ firstName }} {{ lastName }}{{/ link }} + +`Render.m`: + +```objc +id link = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) +{ + // Build an alternate template string by wrapping the inner content of + // the section in a `` HTML tag: + // + // We'll get `{{ firstName }} {{ lastName }}` + + NSString *innerTemplateString = tag.innerTemplateString; + NSString *format = @"%@"; + NSString *templateString = [NSString stringWithFormat:format, innerTemplateString]; + + // Build a new template, and return its rendering: + + GRMustacheTemplate *template = [GRMustacheTemplate templateFromString:templateString error:NULL]; + return [template renderContentWithContext:context HTMLSafe:HTMLSafe error:error]; +}]; + +id data = @{ + @"firstName": @"Orson", + @"lastName": @"Welles", + @"url": @"/people/123", + @"link": link, +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + Orson Welles + +### What did we learn here? + +Again, variable tags such as `{{ name }}` don't have much inner content, but section tags do: `{{# name }} inner content {{/ name }}`. + +The `innerTemplateString` property returns the raw content of the section, with Mustache tags left untouched. + +You can derive new template strings from this raw content, even by appending new tags to it (the `{{ url }}` tag, above). + +From those template strings, you create template objects, just as you usually do. Their `renderContentWithContext:HTMLSafe:error:` method render in the given context. + +The template sets the `HTMLSafe` boolean for you, so that you do not have to worry about it. + + +Example: Dynamic partials +------------------------- + +When a `{{> name }}` Mustache tag occurs in a template, GRMustache renders in place the content of another template, the *partial*, identified by its name. + +Such partials are *hard-coded*. + +You can still choose the rendered partial at runtime, with simple variable tags: + +`Document.mustache`: + + {{# items }} + - {{ link }} + {{/ items }} + +`MovieLink.mustache`: + + {{ title }} + +`PersonLink.mustache`: + + {{ firstName }} {{ lastName }} + +`Render.m`: + +```objc +id data = @{ + @"items": @[ + @{ + @"title": @"Citizen Kane", + @"url":@"/movies/321", + @"link": [GRMustacheTemplate templateFromResource:@"MovieLink" bundle:nil error:nil], + }, + @{ + @"firstName": @"Orson", + @"lastName": @"Welles", + @"url":@"/people/123", + @"link": [GRMustacheTemplate templateFromResource:@"PersonLink" bundle:nil error:nil], + }, + ], +}; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + - Citizen Kane + - Orson Welles + +### What did we learn here? + +Let's say a handy technique: we haven't use the `GRMustacheRendering` protocol here, because `GRMustacheTemplate` does it for us. + + +Example: Objects that render themselves +--------------------------------------- + +Let's implement something similar to Ruby on Rails's `<%= render @movie %>`: + +`Document.mustache`: + + {{ movie }} + +`Movie.mustache`: + + {{ title }} by {{ director }} + +`Person.mustache`: + + {{ firstName }} {{ lastName }} + +`Render.m`: + +```objc +Person *director = [Person personWithFirstName:@"Orson" lastName:@"Welles"]; +Movie *movie = [Movie movieWithTitle:@"Citizen Kane" director:director]; +id data = @{ @"movie": movie }; + +NSString *rendering = [GRMustacheTemplate renderObject:data + fromResource:@"Document" + bundle:nil + error:NULL]; +``` + +Final rendering: + + <Movie: 0x1011052a0> + +Oops. GRMustache is written in Objective-C, not Ruby: there is no built-in automagic rendering of an object with a partial, through some conversion from a class name to a partial name. + +We have to explicitely have our Movie and Person classes render with their dedicated Movie.mustache and Person.mustache partials: + +```objc +// Declare categories on our classes so that they conform to the +// GRMustacheRendering protocol: + +@interface Movie(GRMustache) +@end + +@interface Person(GRMustache) +@end + + +// Now implement the protocol: + +@implementation Movie(GRMustache) + +- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error +{ + // Extract the "Movie.mustache" partial: + GRMustacheTemplate *partial = [GRMustacheTemplate templateFromResource:@"Movie" bundle:nil error:NULL]; + + // Add self to the top of the context stack, so that the partial + // can access our keys: + context = [context contextByAddingObject:self]; + + // Return the rendering of the partial: + return [partial renderContentWithContext:context HTMLSafe:HTMLSafe error:error]; +} + +@end + +@implementation Person(GRMustache) + +- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error +{ + // Extract the "Person.mustache" partial: + GRMustacheTemplate *partial = [GRMustacheTemplate templateFromResource:@"Person" bundle:nil error:NULL]; + + // Add self to the top of the context stack, so that the partial + // can access our keys: + context = [context contextByAddingObject:self]; + + // Return the rendering of the partial: + return [template renderContentWithContext:context HTMLSafe:HTMLSafe error:error]; +} + +@end +``` + +Final rendering: + + Citizen Kane by Orson Welles + +### What did we learn here? + +Two useful things: + +1. *`GRMustacheRendering` is a protocol*. + + Surely `+[GRMustacheRendering renderingObjectWithBlock:]` is convenient since it lets us create rendering objects from scratch. Yet the GRMustacheRendering protocol is available for you to use on your custom classes. + + You can even mix it with the [GRMustacheFilter protocol](filters.md). The conformance to both protocols gives you objects with multiple facets. For example, the [NSFormatter](NSFormatter.md) class takes this opportunity to format values, as in `{{ format(value) }}`, and to format all variable tags in a section, when used as in `{{# format }}...{{ value1 }}...{{ value2 }}...{{/ }}`. + +2. *Rendering objects manage the context stack*. + + When GRMustache renders `{{ name }}`, it looks for the `name` key in the [context stack](runtime.md#the-context-stack): for the title and names of our movies and people to render, movies and people must enter the context stack. This is the reason for the derivation of new contexts, using the `contextByAddingObject:` method, before partials are rendered. + +See the [GRMustacheContext Class Reference](http://groue.github.io/GRMustache/Reference/Classes/GRMustacheContext.html) for a full documentation of the GRMustacheContext class. + + +Example: A Handlebars.js Helper +------------------------------- + +From [http://handlebarsjs.com/block_helpers.html](http://handlebarsjs.com/block_helpers.html): + +> For instance, let's create an iterator that creates a `