// // NJRDateFormatter.m // Pester // // Created by Nicholas Riley on Wed Oct 09 2002. // Copyright (c) 2002 Nicholas Riley. All rights reserved. // #import "NJRDateFormatter.h" // generated by perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c #include #include EXTERN_C void xs_init (pTHX); EXTERN_C void boot_DynaLoader (pTHX_ CV* cv); EXTERN_C void xs_init(pTHX) { char *file = __FILE__; dXSUB_SYS; /* DynaLoader is a special case */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); } // end generated code static PerlInterpreter *my_perl; static NSDateFormatter *dateManipFormatter; static NSDate *parse_natural_language_date(NSString *input) { if (my_perl == NULL) return nil; if ([input rangeOfString: @"|"].length > 0) { NSMutableString *sanitized = [[input mutableCopy] autorelease]; [sanitized replaceOccurrencesOfString: @"|" withString: @"" options: NSLiteralSearch range: NSMakeRange(0, [sanitized length])]; input = sanitized; } NSString *temp = [[NSString alloc] initWithFormat: @"UnixDate(q|%@|, '%%q')", input]; NSLog(@"%@", temp); SV *d = eval_pv([temp UTF8String], TRUE); [temp release]; if (d == NULL) return nil; STRLEN s_len; char *s = SvPV(d, s_len); if (s == NULL || s_len == 0) return nil; NSDate *date = [dateManipFormatter dateFromString: [NSString stringWithUTF8String: s]]; NSLog(@"%@", date); return date; } static void init_perl(void) { const char *argv[] = {"", "-CSD", "-I", "", "-MDate::Manip", "-e", "0"}; argv[3] = [[[NSBundle mainBundle] resourcePath] fileSystemRepresentation]; PERL_SYS_INIT(0, NULL); my_perl = perl_alloc(); if (my_perl == NULL) return; perl_construct(my_perl); if (perl_parse(my_perl, xs_init, 7, (char **)argv, NULL) != 0) goto fail; PL_exit_flags |= PERL_EXIT_DESTRUCT_END; if (perl_run(my_perl) != 0) goto fail; // XXX detect localization changes eval_pv("Date_Init(\"Language=English\", \"DateFormat=non-US\", \"Internal=1\"", TRUE); if (parse_natural_language_date(@"tomorrow") == nil) goto fail; return; fail: perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); my_perl = NULL; } // workaround for bug in Jaguar (and earlier?) NSCalendarDate dateWithNaturalLanguageString: NSString *stringByRemovingSurroundingWhitespace(NSString *string) { static NSCharacterSet *nonWhitespace = nil; NSRange firstValidCharacter, lastValidCharacter; if (!nonWhitespace) { nonWhitespace = [[[NSCharacterSet characterSetWithCharactersInString: @" \t\r\n"] invertedSet] retain]; } firstValidCharacter = [string rangeOfCharacterFromSet:nonWhitespace]; if (firstValidCharacter.length == 0) return @""; lastValidCharacter = [string rangeOfCharacterFromSet:nonWhitespace options: NSBackwardsSearch]; if (firstValidCharacter.location == 0 && lastValidCharacter.location == [string length] - 1) return string; else return [string substringWithRange: NSUnionRange(firstValidCharacter, lastValidCharacter)]; } static const NSDateFormatterStyle formatterStyles[] = { NSDateFormatterShortStyle, NSDateFormatterMediumStyle, NSDateFormatterLongStyle, NSDateFormatterFullStyle, NSDateFormatterNoStyle }; @implementation NJRDateFormatter #pragma mark initialize-release + (void)initialize; { dateManipFormatter = [[NSDateFormatter alloc] init]; [dateManipFormatter setDateFormat: @"yyyyMMddHHmmss"]; // Date::Manip's "%q" init_perl(); } + (NJRDateFormatter *)dateFormatter; { NJRDateFormatter *formatter = [[self alloc] init]; NSMutableArray *tryFormatters = [[NSMutableArray alloc] init]; for (NSDateFormatterStyle *s = formatterStyles ; *s < NSDateFormatterNoStyle ; *s++) { NSDateFormatter *tryFormatter = [[NSDateFormatter alloc] init]; [tryFormatter setLenient: YES]; [tryFormatter setTimeStyle: NSDateFormatterNoStyle]; [tryFormatter setDateStyle: *s]; [tryFormatters addObject: tryFormatter]; [tryFormatter release]; } // XXX do this in init formatter->tryFormatters = tryFormatters; return [formatter autorelease]; } + (NJRDateFormatter *)timeFormatter; { NJRDateFormatter *formatter = [[self alloc] init]; NSMutableArray *tryFormatters = [[NSMutableArray alloc] init]; for (NSDateFormatterStyle *s = formatterStyles ; *s < NSDateFormatterNoStyle ; *s++) { NSDateFormatter *tryFormatter = [[NSDateFormatter alloc] init]; [tryFormatter setLenient: YES]; [tryFormatter setTimeStyle: *s]; [tryFormatter setDateStyle: NSDateFormatterNoStyle]; [tryFormatters addObject: tryFormatter]; [tryFormatter release]; } formatter->tryFormatters = tryFormatters; return [formatter autorelease]; } - (void)dealloc; { [tryFormatters release]; tryFormatters = nil; [super dealloc]; } #pragma mark primitive formatter - (NSString *)stringForObjectValue:(id)obj; { return [super stringForObjectValue: obj]; } - (NSAttributedString *)attributedStringForObjectValue:(id)obj withDefaultAttributes:(NSDictionary *)attrs; { return [super attributedStringForObjectValue: obj withDefaultAttributes: attrs]; } - (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error { if ([super getObjectValue: anObject forString: string errorDescription: error]) return YES; NSDate *date; NSEnumerator *e = [tryFormatters objectEnumerator]; NSDateFormatter *tryFormatter; // XXX untested; does this work? while ( (tryFormatter = [e nextObject]) != nil) { date = [tryFormatter dateFromString: string]; if (date != nil) goto success; } date = parse_natural_language_date(string); if (date != nil) goto success; return NO; success: *anObject = date; if (error != NULL) *error = nil; return YES; } #pragma mark miscellaneous + (BOOL)naturalLanguageParsingAvailable; { return (my_perl != NULL); } @end