Skip to content

Instantly share code, notes, and snippets.

@wildthink
Created August 15, 2018 03:44
Show Gist options
  • Save wildthink/540079997e48c06915cdf3078b2b8308 to your computer and use it in GitHub Desktop.
Save wildthink/540079997e48c06915cdf3078b2b8308 to your computer and use it in GitHub Desktop.
Tagged Attribute Langage
//
// NSXMLNode+TAL.h
// WTTAL
//
// Created by Jason Jobe on 10/29/05.
// Copyright (c) 2005, 2013 Jason Jobe. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TALContext;
@interface NSXMLDocument (TALExtensions)
- initWithContentsOfFile:(NSString*)filename;
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context delegate:anObject;
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context;
// Output
- (NSData*)XMLDataWithOptions:(unsigned int)options omitRoot:(BOOL)flag;
- (NSString *)XMLStringWithOptions:(unsigned int)options omitRoot:(BOOL)flag;
@end
@interface NSXMLElement (TALExtensions)
+ (void)setDebugTaggedAttributeProcessing:(BOOL)flag;
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context delegate:anObject;
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context;
- (void)processTaggedAttributesWithTALContext:(TALContext*)context;
@end
//
// NSXMLNode+TAL.m
// WTTAL
//
// Created by Jason Jobe on 10/29/05.
// Copyright (c) 2005, 2013 Jason Jobe. All rights reserved.
//
#import "NSXMLNode+TAL.h"
#import "TALContext.h"
#import "WTIterator.h"
static BOOL g_tal_debug = NO;
@interface NSObject (TALContextDelegate)
-(NSXMLElement*)processTaggedAttribute:(NSXMLNode*)node
forElement:(NSXMLElement*)element inTALContext:(TALContext*)context;
@end
@implementation NSXMLElement (TALExtensions)
+(void)setDebugTaggedAttributeProcessing:(BOOL)flag
{
g_tal_debug = flag;
}
- (NSDictionary*)parseTALAttributes:(NSString*)str
{
NSMutableDictionary *attr = [[NSMutableDictionary alloc] init];
NSScanner *scanner = [[NSScanner alloc] initWithString:str];
NSString *key, *value;
NSCharacterSet *space = [NSCharacterSet whitespaceCharacterSet];
while (! [scanner isAtEnd]) {
[scanner scanUpToCharactersFromSet:space intoString:&key];
[scanner scanUpToString:@";" intoString:&value];
[scanner scanString:@";" intoString:NULL];
if (value && key)
[attr setValue:value forKey:key];
}
return attr;
}
-(void)processTaggedAttributesWithTALContext:(TALContext*)context
forEach:(NSString*)ident inArray:(NSArray*)values
{
// remove tal:repeat to avoid infinite loop
[self removeAttributeForName:context.TAL_repeat];
NSXMLElement *parent = (NSXMLElement*)[self parent]; // get it before we detach
[self detach];
[context initiateLoop:ident withArray:values];
while ([context nextObjectForLoop:ident]) {
id nodeCopy = [self copy];
[parent addChild:nodeCopy];
[nodeCopy processTaggedAttributesWithTALContext:context];
}
}
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context
{
return [self processTaggedAttributesWithPrefix:prefix context:context delegate:nil];
}
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context delegate:anObject;
{
TALContext *tc = [[TALContext alloc] initWithValues:context prefix:prefix];
[tc setDelegate:anObject];
[self processTaggedAttributesWithTALContext:tc];
return self;
}
- (void)processTaggedAttributesWithTALContext:(TALContext*)context;
{
NSString *talCommand;
id value;
if ((talCommand = [[self attributeForName:context.TAL_condition] stringValue])) {
if ([talCommand length]) {
value = [context valueForKeyPath:talCommand];
} else {
value = @"0";
}
if ([value intValue] == 0)
[self detach];
if (![context debug]) [self removeAttributeForName:context.TAL_condition];
}
if ((talCommand = [[self attributeForName:context.TAL_repeat] stringValue])) {
NSArray *array = [talCommand componentsSeparatedByString:@" "];
value = [context valueForKeyPath:[array objectAtIndex:1]];
[self processTaggedAttributesWithTALContext:(TALContext*)context forEach:[array objectAtIndex:0] inArray:value];
return;
}
if ((talCommand = [[self attributeForName:context.TAL_replace] stringValue])) {
if ((value = [context valueForKeyPath:talCommand])) {
NSXMLElement *parent = (NSXMLElement*)[self parent];
NSUInteger myIndex = [self index]; // [[parent children] indexOfObject:self];
NSXMLNode *node = [[NSXMLNode alloc] initWithKind:NSXMLTextKind];
// node = [NSXMLNode textWithStringValue:value];
//[node setStringValue:value];
[node setObjectValue:value]; // treats value as a literal
[parent replaceChildAtIndex:myIndex withNode:node];
}
return;
}
if ((talCommand = [[self attributeForName:context.TAL_content] stringValue])) {
value = [context valueForKeyPath:talCommand];
[self setStringValue:value resolvingEntities:NO];
if (![context debug]) [self removeAttributeForName:context.TAL_content];
}
/*
if (talCommand = [[self attributeForName:context.TAL_cdata] stringValue]) {
value = [context valueForKeyPath:talCommand];
[self set
[self setStringValue:value resolvingEntities:YES];
if (![context debug]) [self removeAttributeForName:context.TAL_content];
}
*/
if ((talCommand = [[self attributeForName:context.TAL_include] stringValue])) {
NSXMLDocument *doc = [context loadContentsOfResource:talCommand];
NSXMLElement *parent = (NSXMLElement*)[self parent];
NSUInteger myIndex = [self index]; // [[parent children] indexOfObject:self];
NSXMLElement *node = [doc rootElement];
[node detach];
[parent replaceChildAtIndex:myIndex withNode:node];
[node processTaggedAttributesWithTALContext:context];
return;
}
if ((talCommand = [[self attributeForName:context.TAL_attributes] stringValue]) && [talCommand length])
{
NSDictionary *attr = [self parseTALAttributes:talCommand];
NSEnumerator *keyCurs = [attr keyEnumerator];
NSString *key;
while (key = [keyCurs nextObject]) {
value = [context valueForKeyPath:[attr valueForKey:key]];
NSXMLNode *attribute = [NSXMLNode attributeWithName:key stringValue:value];
[self removeAttributeForName:key];
[self addAttribute:attribute];
}
if (![context debug]) [self removeAttributeForName:context.TAL_attributes];
}
if ((talCommand = [[self attributeForName:context.TAL_omit] stringValue])) {
if ([talCommand length]) {
value = [context valueForKeyPath:talCommand];
} else {
value = @"1";
}
if ([value intValue] == 1) {
id parent = [self parent];
NSUInteger ndx = [self index]; // [[parent children] indexOfObject:self];
[self detach];
NSEnumerator *curs = [[self children] objectEnumerator];
NSXMLElement *node;
while (node = [curs nextObject]) {
[node detach];
[parent insertChild:node atIndex:ndx];
++ndx;
}
}
if (![context debug]) [self removeAttributeForName:context.TAL_omit];
}
if (context.delegate) {
NSEnumerator *attrCurs = [[self attributes] objectEnumerator];
NSXMLElement *node;
while (node = [attrCurs nextObject]) {
if ([[node name] hasPrefix:context.TAL_prefix])
node = [context.delegate processTaggedAttribute:node forElement:self inTALContext:context];
}
}
NSEnumerator *childCurs = [[self children] objectEnumerator];
NSXMLElement *node;
while (node = [childCurs nextObject]) {
if ([node respondsToSelector:@selector(processTaggedAttributesWithTALContext:)])
[node processTaggedAttributesWithTALContext:context];
}
}
@end
@implementation NSXMLDocument (TALExtensions)
- initWithContentsOfFile:(NSString*)filename
{
NSURL *url = [NSURL fileURLWithPath:filename];
NSError *error;
self = [[[self class] alloc]
initWithContentsOfURL:url
options:(NSXMLDocumentTidyXML|NSXMLNodePrettyPrint)
error:&error];
if (error) {
NSLog (@"ERROR: [NSXMLDocument initWithContentsOfFile:%@] => %@", filename, error);
}
return self;
}
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context
{
return [self processTaggedAttributesWithPrefix:prefix context:context delegate:nil];
}
- processTaggedAttributesWithPrefix:(NSString*)prefix context:context delegate:anObject;
{
[[self rootElement] processTaggedAttributesWithPrefix:prefix context:context delegate:anObject];
return self;
}
// Output
- (NSData*)XMLDataWithOptions:(unsigned int)options omitRoot:(BOOL)omitRoot;
{
if (omitRoot) {
NSString *xmlStr = [self XMLStringWithOptions:options omitRoot:omitRoot];
NSData *data = [xmlStr dataUsingEncoding:NSASCIIStringEncoding];
return data;
}
else {
return [self XMLDataWithOptions:options];
}
}
- (NSString *)XMLStringWithOptions:(unsigned int)options omitRoot:(BOOL)omitRoot;
{
if (omitRoot) {
NSMutableString *xmlStr = [NSMutableString string];
NSEnumerator *curs = [[[self rootElement] children] objectEnumerator];
NSXMLNode *node;
while (node = [curs nextObject]) {
[xmlStr appendString:[node XMLStringWithOptions:options]];
[xmlStr appendString:@"\n"];
}
return xmlStr;
}
else {
return [[self rootElement] XMLStringWithOptions:options];
}
}
@end
//
// TALContext.h
// WTTAL
//
// Created by Jason Jobe on 1/23/07.
// Copyright (c) 2007, 2013 Jason Jobe. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TALContext : NSObject
{
}
@property (strong, nonatomic) NSString *TAL_prefix;
@property (strong, nonatomic) NSString *TAL_condition;
@property (strong, nonatomic) NSString *TAL_content;
@property (strong, nonatomic) NSString *TAL_replace;
@property (strong, nonatomic) NSString *TAL_attributes;
@property (strong, nonatomic) NSString *TAL_omit;
@property (strong, nonatomic) NSString *TAL_include;
@property (strong, nonatomic) NSString *TAL_repeat;
@property (weak, nonatomic) id delegate;
@property (strong, nonatomic) NSMutableDictionary *defines;
@property (strong, nonatomic) NSMutableDictionary *loops;
@property (strong, nonatomic) NSMutableDictionary *values;
@property (assign, nonatomic) BOOL debug;
- initWithValues:(NSDictionary*)vals;
- initWithValues:(NSDictionary*)vals prefix:(NSString*)prefix;
- (void)initiateLoop:(NSString*)name withArray:(NSArray*)anArray;
- nextObjectForLoop:(NSString*)name;
- (NSString*)resourcePathForKey:(NSString*)key;
- (NSXMLDocument*)loadContentsOfResource:(NSString*)key;
-(void)setDelegate:anObject;
- delegate;
-(BOOL)debug;
// Template processing
-(NSXMLDocument*)documentUsingTemplate:(NSXMLDocument*)templateDoc;
-(NSXMLDocument*)documentUsingTemplateURL:(NSURL*)templateURL;
-(NSXMLDocument*)documentUsingTemplateURL:(NSURL*)url withValue:value forKey:(NSString*)key;
@end
//
// TALContext.m
// WTTAL
//
// Created by Jason Jobe on 1/23/07.
// Copyright (c) 2007, 2013 Jason Jobe. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TALContext.h"
#import "NSXMLNode+TAL.h"
#import "WTIterator.h"
//static NSString *TemplateMark = @"$";
static NSString *TemplateRepeatKey = @"for";
@implementation TALContext
- initWithValues:(NSDictionary*)vals;
{
return [self initWithValues:vals prefix:@"tal"];
}
- initWithValues:(NSDictionary*)vals prefix:(NSString*)prefix;
{
if (prefix == nil)
prefix = @"tal";
self.TAL_prefix = [[NSString alloc] initWithFormat:@"%@:", prefix];
self.TAL_condition = [[NSString alloc] initWithFormat:@"%@:if", prefix];
self.TAL_content = [[NSString alloc] initWithFormat:@"%@:content", prefix];
self.TAL_replace = [[NSString alloc] initWithFormat:@"%@:replace", prefix];
self.TAL_attributes = [[NSString alloc] initWithFormat:@"%@:attributes", prefix];
self.TAL_omit = [[NSString alloc] initWithFormat:@"%@:omit-tag", prefix];
self.TAL_include = [[NSString alloc] initWithFormat:@"%@:include", prefix];
self.TAL_repeat = [[NSString alloc] initWithFormat:@"%@:%@", prefix, TemplateRepeatKey];
_loops = [[NSMutableDictionary alloc] init];
_defines = [[NSMutableDictionary alloc] init];
_values = [vals mutableCopy];
return self;
}
- (void)initiateLoop:(NSString*)name withArray:(NSArray*)anArray
{
if ([_loops valueForKey:name]) {
NSLog (@"TAL Loop ERROR: loop %@ already in use", name);
return;
}
WTIterator *looper = [[WTIterator alloc] initWithArray:anArray];
[_loops setValue:looper forKey:name];
}
- nextObjectForLoop:(NSString*)name
{
WTIterator *looper = [_loops valueForKey:name];
id value = [looper nextObject];
if (value) {
[_values setValue:value forKey:name];
} else {
// clean up because we're DONE
[_loops removeObjectForKey:name];
[_values removeObjectForKey:name];
}
return value;
}
- valueForKeyPath:(NSString*)keypath
{
// // having the mark indicates we have a template
// NSRange range = [keypath rangeOfString:TemplateMark];
// if (range.location != NSNotFound)
// return [keypath stringWithTemplateValues:values];
// else
return [super valueForKeyPath:keypath];
}
- valueForKey:(NSString*)key;
{
WTIterator *looper;
id value;
if ([key isEqualToString:TemplateRepeatKey]) {
return _loops;
} else if ((value = [_defines valueForKey:key])) {
return value;
} else if ((looper = [_loops valueForKey:key])) {
return [looper currentObject];
} else {
// // having a mark indicates we have a template
// NSRange range = [key rangeOfString:TemplateMark];
// if (range.location != NSNotFound)
// return [key stringWithTemplateValues:values];
// // else
return [_values valueForKey:key];
}
}
-(NSString*)resourcePathForKey:(NSString*)key
{
// // having the mark indicates we have a template
// NSRange range = [key rangeOfString:TemplateMark];
// if (range.location != NSNotFound)
// return [key stringWithTemplateValues:values];
// else
return key;
}
- (NSXMLDocument*)loadContentsOfResource:(NSString*)key
{
NSString *path = [self resourcePathForKey:key];
NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfFile:path];
// NSString *prefix = [[TAL_prefix componentsSeparatedByString:@":"] objectAtIndex:0];
// [doc processTaggedAttributesWithPrefix:prefix context:self];
return doc;
}
// Template processing
-(NSXMLDocument*)documentUsingTemplate:(NSXMLDocument*)templateDoc
{
// [templateDoc processTaggedAttributesWithPrefix:@"tal" context:self delegate:delegate];
[[templateDoc rootElement] processTaggedAttributesWithTALContext:self];
return templateDoc;
}
-(NSXMLDocument*)documentUsingTemplateURL:(NSURL*)url;
{
NSXMLDocument *templateDoc;
NSError *error;
templateDoc = [[NSXMLDocument alloc]
initWithContentsOfURL:url
options:(NSXMLDocumentTidyXML|NSXMLNodePrettyPrint) error:&error];
return [self documentUsingTemplate:templateDoc];
}
-(NSXMLDocument*)documentUsingTemplateURL:(NSURL*)url withValue:value forKey:(NSString*)key
{
NSXMLDocument *doc;
[_defines setValue:value forKey:key];
doc = [self documentUsingTemplateURL:url];
return doc;
}
@end
//
// WTIterator.h
// WTTAL
//
// Created by Jason Jobe on 10/30/05.
// Copyright (c) 2005, 2013 Jason Jobe. All rights reserved.
//
#import <Foundation/Foundation.h>
/*
The following information is available from a Loop Iterator:
index - repetition number, starting from zero.
number - repetition number, starting from one.
even - true for even-indexed repetitions (0, 2, 4, ...).
odd - true for odd-indexed repetitions (1, 3, 5, ...).
start - true for the starting repetition (index 0).
end - true for the ending, or final, repetition.
length - length of the sequence, which will be the total number of repetitions.
letter - count reps with lower-case letters: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and so forth.
Letter - upper-case version of letter.
*/
@interface WTIterator : NSObject
@property (strong, nonatomic) NSArray *objects;
@property (assign, nonatomic) NSUInteger nextIndex;
@property (assign, nonatomic) NSUInteger count;
+ iteratorWithArray:(NSArray*)nobs;
- initWithArray:(NSArray*)nobs;
-(BOOL)start;
-(BOOL)end;
-(BOOL)odd;
-(BOOL)even;
-(NSUInteger)index;
-(NSUInteger)number;
-(NSUInteger)length;
- currentObject;
- nextObject;
// -(NSString*)letter;
// -(NSString*)Letter;
// -(NSString*)romanLetter;
@end
@interface NSArray (WTIterator)
-(WTIterator*)iterator;
@end
//
// WTIterator.m
// WTTAL
//
// Created by Jason Jobe on 10/30/05.
// Copyright (c) 2005, 2013 Jason Jobe. All rights reserved.
//
#import "WTIterator.h"
@implementation WTIterator
+ iteratorWithArray:(NSArray*)nobs {
return [[[self class] alloc] initWithArray:nobs];
}
- initWithArray:(NSArray*)nobs
{
self.objects = nobs;
self.count = [nobs count];
return self;
}
-(BOOL)start { return (self.nextIndex == 1); }
-(BOOL)end { return (self.nextIndex == self.count); }
-(BOOL)odd { return (self.nextIndex % 2 ? YES : NO); }
-(BOOL)even { return (self.nextIndex % 2 ? NO : YES); }
-(NSUInteger)index { return self.nextIndex - 1; }
-(NSUInteger)number { return self.nextIndex; }
-(NSUInteger)length { return self.count; }
- nextObject
{
if (self.nextIndex < self.count)
return [self.objects objectAtIndex:(self.nextIndex++)];
else
return nil;
}
- currentObject
{
NSInteger ndx;
if (self.nextIndex == 0) {
ndx = 0;
} else if (self.nextIndex -1 < self.count) {
ndx = self.nextIndex - 1;
} else {
ndx = -1;
}
return (ndx < 0 ? nil : [self.objects objectAtIndex:(self.nextIndex - 1)]);
}
@end
@implementation NSArray (WTIterator)
-(WTIterator*)iterator {
return [WTIterator iteratorWithArray:self];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment