From 9d52266f823daf5cb83e3eebe1b9f57594bc9d4a Mon Sep 17 00:00:00 2001 From: matt handler Date: Wed, 20 Apr 2011 14:39:20 -0400 Subject: first commit --- Classes/AcidCowFeedburnerParser.m | 207 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 Classes/AcidCowFeedburnerParser.m (limited to 'Classes/AcidCowFeedburnerParser.m') diff --git a/Classes/AcidCowFeedburnerParser.m b/Classes/AcidCowFeedburnerParser.m new file mode 100644 index 0000000..46d7ec5 --- /dev/null +++ b/Classes/AcidCowFeedburnerParser.m @@ -0,0 +1,207 @@ +// +// AcidCowFeedburnerParser.m +// PicCast +// +// Created by Matthew Handler on 4/15/11. +// Copyright 2011 Earl Industries. All rights reserved. +// + +#import "AcidCowFeedburnerParser.h" +#import "Topic.h" + +@implementation AcidCowFeedburnerParser + +@synthesize currentString, currentTopic, parseFormatter, xmlData, rssConnection, downloadAndParsePool; + + +- (void)downloadAndParse:(NSURL *)url { + NSLog(@"downloadAndParse"); + self.downloadAndParsePool = [[NSAutoreleasePool alloc] init]; + done = NO; + self.parseFormatter = [[[NSDateFormatter alloc] init] autorelease]; + [parseFormatter setDateStyle:NSDateFormatterLongStyle]; + [parseFormatter setTimeStyle:NSDateFormatterNoStyle]; + // necessary because iTunes RSS feed is not localized, so if the device region has been set to other than US + // the date formatter must be set to US locale in order to parse the dates + [parseFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"US"] autorelease]]; + self.xmlData = [NSMutableData data]; + //[[NSURLCache sharedURLCache] removeAllCachedResponses]; + NSURLRequest *theRequest = [NSURLRequest requestWithURL:url]; + // create the connection with the request and start loading the data + rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; + [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO]; + if (rssConnection != nil) { + do { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } while (!done); + } + self.rssConnection = nil; + self.parseFormatter = nil; + self.currentTopic = nil; + [downloadAndParsePool release]; + self.downloadAndParsePool = nil; +} + +#pragma mark NSURLConnection Delegate methods + +/* + Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application. + */ +//- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { +// return nil; +//} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + done = YES; + [self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + [xmlData appendData:data]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { + NSLog(@"finishedLoading"); + [self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO]; + [self convertXMLData]; + + NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData]; + parser.delegate = self; + self.currentString = [NSMutableString string]; + //NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; + [parser parse]; + //NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start; + //[self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO]; + [self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO]; + [parser release]; + self.currentString = nil; + self.xmlData = nil; + // Set the condition which ends the run loop. + done = YES; +} + +// needed to convert feedburners xml into utf-8 because the iphone parser breaks on windows-1251 +- (void)convertXMLData { + NSString *string = [[NSString alloc] initWithData:xmlData encoding:NSWindowsCP1251StringEncoding]; + string = [string stringByReplacingOccurrencesOfString:@"encoding=\"windows-1251\"" withString:@""]; + + // [xmlData release]; + // xmlData = nil; + xmlData = [[NSMutableData dataWithData:[string dataUsingEncoding:NSUTF8StringEncoding]] retain]; +} + +#pragma mark Parsing support methods + +static const NSUInteger kAutoreleasePoolPurgeFrequency = 20; + +- (void)finishedCurrentTopic { + [self performSelectorOnMainThread:@selector(parsedTopic:) withObject:currentTopic waitUntilDone:NO]; + // performSelectorOnMainThread: will retain the object until the selector has been performed + // setting the local reference to nil ensures that the local reference will be released + self.currentTopic = nil; + countOfParsedTopics++; + // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the + // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but + // taking this action too frequently would be wasteful and reduce performance. + if (countOfParsedTopics == kAutoreleasePoolPurgeFrequency) { + [downloadAndParsePool release]; + self.downloadAndParsePool = [[NSAutoreleasePool alloc] init]; + countOfParsedTopics = 0; + } +} + +#pragma mark NSXMLParser Parsing Callbacks + +// Constants for the XML element names that will be considered during the parse. +// Declaring these as static constants reduces the number of objects created during the run +// and is less prone to programmer error. +static NSString *kName_Item = @"item"; +static NSString *kName_Title = @"title"; +static NSString *kName_Category = @"category"; +static NSString *kName_Creator = @"dc:creator"; +static NSString *kName_Description = @"description"; +static NSString *kName_ReleaseDate = @"pubDate"; +static NSString *kName_Guid = @"guid"; + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict { + //NSLog(@"%@", elementName); + if ([elementName isEqualToString:kName_Item]) { + self.currentTopic = [[[Topic alloc] init] autorelease]; + } else if ([elementName isEqualToString:kName_Title] + || [elementName isEqualToString:kName_Category] + || [elementName isEqualToString:kName_Guid] + || [elementName isEqualToString:kName_Creator] + || [elementName isEqualToString:kName_Description] + || [elementName isEqualToString:kName_ReleaseDate]) { + [currentString setString:@""]; + storingCharacters = YES; + } +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { + if ([elementName isEqualToString:kName_Item]) { + [self finishedCurrentTopic]; + } else if ([elementName isEqualToString:kName_Title]) { + NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"[^(]+" + options:NSRegularExpressionCaseInsensitive + error:nil]; + NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])]; + if ([results count] > 0) { + NSString *title = [currentString substringWithRange:[[results objectAtIndex:0] range]]; + currentTopic.title = title; + } + + NSRegularExpression* regex2 = [NSRegularExpression regularExpressionWithPattern:@"(?<=\\()\\d+" + options:NSRegularExpressionCaseInsensitive + error:nil]; + NSArray *results2 = [regex2 matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])]; + if ([results2 count] > 0) { + NSString *count = [currentString substringWithRange:[[results2 objectAtIndex:0] range]]; + currentTopic.picCount = [NSNumber numberWithInt:[count intValue]]; + //NSLog(@"%@", count); + } + else + currentTopic.picCount = [NSNumber numberWithInt:1]; + + //currentTopic.title = currentString; + } else if ([elementName isEqualToString:kName_Category]) { + currentTopic.category = currentString; + } else if ([elementName isEqualToString:kName_Guid]) { + currentTopic.guid = currentString; + } else if ([elementName isEqualToString:kName_Creator]) { + currentTopic.creator = currentString; + } else if ([elementName isEqualToString:kName_Description]) { + NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"http\\S+(jpg|png|gif|jpeg)" + options:NSRegularExpressionCaseInsensitive + error:nil]; + NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])]; +// for (NSTextCheckingResult *res in results) { +// NSLog(@"regexed: %@", [currentString substringWithRange:res.range]); +// } + if ([results count] > 0) { + NSString *url = [currentString substringWithRange:[[results objectAtIndex:0] range]]; + //NSLog(@"%@", url); + currentTopic.iconUrl = url; + } + currentTopic.description = currentString; + } else if ([elementName isEqualToString:kName_ReleaseDate]) { + currentTopic.releaseDate = [parseFormatter dateFromString:currentString]; + } + storingCharacters = NO; +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { + //NSLog(@"foundCharacters: %@", string); // storing string is when you want the inner text + if (storingCharacters) [currentString appendString:string]; +} + +/* + A production application should include robust error handling as part of its parsing implementation. + The specifics of how errors are handled depends on the application. + */ +- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError { + NSLog(@"parseError: %@", parseError); + // Handle errors as appropriate for your application. +} + +@end -- cgit v1.2.3