Permalink
Browse files

First pass at adding JSON support with YAJL.

  • Loading branch information...
1 parent ed7b478 commit fbfc444d2c027ac1471f15099299545bf2c3ca45 Craig Hockenberry committed Jan 17, 2009
View
166 MGTwitterEngine.m
@@ -13,16 +13,27 @@
#define USE_LIBXML 0
-#if USE_LIBXML
- #import "MGTwitterStatusesLibXMLParser.h"
- #import "MGTwitterMessagesLibXMLParser.h"
- #import "MGTwitterUsersLibXMLParser.h"
- #import "MGTwitterMiscLibXMLParser.h"
+#if YAJL_AVAILABLE
+ #define API_FORMAT @"json"
+
+ #import "MGTwitterStatusesYAJLParser.h"
+ #import "MGTwitterMessagesYAJLParser.h"
+ #import "MGTwitterUsersYAJLParser.h"
+ #import "MGTwitterMiscYAJLParser.h"
#else
- #import "MGTwitterStatusesParser.h"
- #import "MGTwitterUsersParser.h"
- #import "MGTwitterMessagesParser.h"
- #import "MGTwitterMiscParser.h"
+ #define API_FORMAT @"xml"
+
+ #if USE_LIBXML
+ #import "MGTwitterStatusesLibXMLParser.h"
+ #import "MGTwitterMessagesLibXMLParser.h"
+ #import "MGTwitterUsersLibXMLParser.h"
+ #import "MGTwitterMiscLibXMLParser.h"
+ #else
+ #import "MGTwitterStatusesParser.h"
+ #import "MGTwitterUsersParser.h"
+ #import "MGTwitterMessagesParser.h"
+ #import "MGTwitterMiscParser.h"
+ #endif
#endif
#define TWITTER_DOMAIN @"twitter.com"
@@ -57,7 +68,7 @@ - (NSString *)_sendRequestWithMethod:(NSString *)method
responseType:(MGTwitterResponseType)responseType;
// Parsing methods
-- (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection;
+- (void)_parseDataForConnection:(MGTwitterHTTPURLConnection *)connection;
// Delegate methods
- (BOOL) _isValidDelegateForSelector:(SEL)selector;
@@ -479,8 +490,48 @@ - (NSString *)_sendRequestWithMethod:(NSString *)method
#pragma mark Parsing methods
+#if YAJL_AVAILABLE
+- (void)_parseDataForConnection:(MGTwitterHTTPURLConnection *)connection
+{
+ NSString *identifier = [[[connection identifier] copy] autorelease];
+ NSData *jsonData = [[[connection data] copy] autorelease];
+ MGTwitterRequestType requestType = [connection requestType];
+ MGTwitterResponseType responseType = [connection responseType];
+
+ NSURL *URL = [connection URL];
+
+// NSLog(@"jsonData = %@ from %@", [[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] autorelease], URL);
-- (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection
+ switch (responseType) {
+ case MGTwitterStatuses:
+ case MGTwitterStatus:
+ [MGTwitterStatusesYAJLParser parserWithJSON:jsonData delegate:self
+ connectionIdentifier:identifier requestType:requestType
+ responseType:responseType URL:URL];
+ break;
+ case MGTwitterUsers:
+ case MGTwitterUser:
+ [MGTwitterUsersYAJLParser parserWithJSON:jsonData delegate:self
+ connectionIdentifier:identifier requestType:requestType
+ responseType:responseType URL:URL];
+ break;
+ case MGTwitterDirectMessages:
+ case MGTwitterDirectMessage:
+ [MGTwitterMessagesYAJLParser parserWithJSON:jsonData delegate:self
+ connectionIdentifier:identifier requestType:requestType
+ responseType:responseType URL:URL];
+ break;
+ case MGTwitterMiscellaneous:
+ [MGTwitterMiscYAJLParser parserWithJSON:jsonData delegate:self
+ connectionIdentifier:identifier requestType:requestType
+ responseType:responseType URL:URL];
+ break;
+ default:
+ break;
+ }
+}
+#else
+- (void)_parseDataForConnection:(MGTwitterHTTPURLConnection *)connection
{
NSString *identifier = [[[connection identifier] copy] autorelease];
NSData *xmlData = [[[connection data] copy] autorelease];
@@ -548,6 +599,7 @@ - (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection
}
#endif
}
+#endif
#pragma mark Delegate methods
@@ -695,7 +747,7 @@ - (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
if (NO) {
// Dump XML to file for debugging.
NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
- [dataString writeToFile:[@"~/Desktop/twitter_messages.xml" stringByExpandingTildeInPath]
+ [dataString writeToFile:[[NSString stringWithFormat:@"~/Desktop/twitter_messages.%@", API_FORMAT] stringByExpandingTildeInPath]
atomically:NO encoding:NSUnicodeStringEncoding error:NULL];
}
@@ -711,8 +763,8 @@ - (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
if ([self _isValidDelegateForSelector:@selector(imageReceived:forRequest:)])
[_delegate imageReceived:image forRequest:[connection identifier]];
} else {
- // Parse XML appropriately.
- [self _parseXMLForConnection:connection];
+ // Parse data from the connection (either XML or JSON.)
+ [self _parseDataForConnection:connection];
}
}
@@ -731,7 +783,7 @@ - (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
- (NSString *)checkUserCredentials
{
- NSString *path = @"account/verify_credentials.xml";
+ NSString *path = [NSString stringWithFormat:@"account/verify_credentials.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -755,7 +807,7 @@ - (NSString *)enableUpdatesFor:(NSString *)username
if (!username) {
return nil;
}
- NSString *path = [NSString stringWithFormat:@"friendships/create/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"friendships/create/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -769,7 +821,7 @@ - (NSString *)disableUpdatesFor:(NSString *)username
if (!username) {
return nil;
}
- NSString *path = [NSString stringWithFormat:@"friendships/destroy/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"friendships/destroy/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -786,7 +838,7 @@ - (NSString *)isUser:(NSString *)username1 receivingUpdatesFor:(NSString *)usern
[params setObject:username1 forKey:@"user_a"];
[params setObject:username2 forKey:@"user_b"];
- NSString *path = @"friendships/exists.xml";
+ NSString *path = [NSString stringWithFormat:@"friendships/exists.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
requestType:MGTwitterAccountRequest
@@ -799,7 +851,7 @@ - (NSString *)enableNotificationsFor:(NSString *)username
if (!username) {
return nil;
}
- NSString *path = [NSString stringWithFormat:@"notifications/follow/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"notifications/follow/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -812,7 +864,7 @@ - (NSString *)disableNotificationsFor:(NSString *)username
if (!username) {
return nil;
}
- NSString *path = [NSString stringWithFormat:@"notifications/leave/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"notifications/leave/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -822,7 +874,7 @@ - (NSString *)disableNotificationsFor:(NSString *)username
- (NSString *)getRateLimitStatus
{
- NSString *path = @"account/rate_limit_status.xml";
+ NSString *path = [NSString stringWithFormat:@"account/rate_limit_status.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -836,7 +888,7 @@ - (NSString *)setLocation:(NSString *)location
return nil;
}
- NSString *path = @"account/update_location.xml";
+ NSString *path = [NSString stringWithFormat:@"account/update_location.%@", API_FORMAT];
NSString *trimmedText = location;
if ([trimmedText length] > MAX_LOCATION_LENGTH) {
@@ -861,7 +913,7 @@ - (NSString *)setNotificationsDeliveryMethod:(NSString *)method
deliveryMethod = @"none";
}
- NSString *path = @"account/update_delivery_device.xml";
+ NSString *path = [NSString stringWithFormat:@"account/update_delivery_device.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (deliveryMethod) {
@@ -880,7 +932,7 @@ - (NSString *)block:(NSString *)username
return nil;
}
- NSString *path = [NSString stringWithFormat:@"blocks/create/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"blocks/create/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -894,7 +946,7 @@ - (NSString *)unblock:(NSString *)username
return nil;
}
- NSString *path = [NSString stringWithFormat:@"blocks/destroy/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"blocks/destroy/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -904,7 +956,7 @@ - (NSString *)unblock:(NSString *)username
- (NSString *)testService
{
- NSString *path = @"help/test.xml";
+ NSString *path = [NSString stringWithFormat:@"help/test.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -914,7 +966,7 @@ - (NSString *)testService
- (NSString *)getDowntimeSchedule
{
- NSString *path = @"help/downtime_schedule.xml";
+ NSString *path = [NSString stringWithFormat:@"help/downtime_schedule.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -934,7 +986,7 @@ - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date s
- (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)count
{
- NSString *path = @"statuses/friends_timeline.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/friends_timeline.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (date) {
@@ -944,7 +996,7 @@ - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date s
[params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
}
if (username) {
- path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.xml", username];
+ path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.%@", username, API_FORMAT];
}
int tweetCount = DEFAULT_TWEET_COUNT;
if (count > 0) {
@@ -960,7 +1012,7 @@ - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date s
- (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(int)updateID startingAtPage:(int)pageNum count:(int)count
{
- NSString *path = @"statuses/friends_timeline.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/friends_timeline.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (updateID > 0) {
@@ -970,7 +1022,7 @@ - (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(int)updateID
[params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
}
if (username) {
- path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.xml", username];
+ path = [NSString stringWithFormat:@"statuses/friends_timeline/%@.%@", username, API_FORMAT];
}
int tweetCount = DEFAULT_TWEET_COUNT;
if (count > 0) {
@@ -993,7 +1045,7 @@ - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date count
- (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)numUpdates
{
- NSString *path = @"statuses/user_timeline.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/user_timeline.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (date) {
@@ -1006,7 +1058,7 @@ - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date start
[params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
}
if (username) {
- path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
+ path = [NSString stringWithFormat:@"statuses/user_timeline/%@.%@", username, API_FORMAT];
}
return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
@@ -1017,7 +1069,7 @@ - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date start
- (NSString *)getUserTimelineFor:(NSString *)username sinceID:(int)updateID startingAtPage:(int)pageNum count:(int)numUpdates
{
- NSString *path = @"statuses/user_timeline.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/user_timeline.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (updateID > 0) {
@@ -1030,7 +1082,7 @@ - (NSString *)getUserTimelineFor:(NSString *)username sinceID:(int)updateID star
[params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
}
if (username) {
- path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
+ path = [NSString stringWithFormat:@"statuses/user_timeline/%@.%@", username, API_FORMAT];
}
return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
@@ -1041,7 +1093,7 @@ - (NSString *)getUserTimelineFor:(NSString *)username sinceID:(int)updateID star
- (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum
{
- NSString *path = @"account/archive.xml";
+ NSString *path = [NSString stringWithFormat:@"account/archive.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (pageNum > 0) {
@@ -1056,7 +1108,7 @@ - (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum
- (NSString *)getPublicTimelineSinceID:(int)updateID
{
- NSString *path = @"statuses/public_timeline.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/public_timeline.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (updateID > 0) {
@@ -1071,7 +1123,7 @@ - (NSString *)getPublicTimelineSinceID:(int)updateID
- (NSString *)getRepliesStartingAtPage:(int)pageNum
{
- NSString *path = @"statuses/replies.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/replies.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (pageNum > 0) {
@@ -1086,14 +1138,14 @@ - (NSString *)getRepliesStartingAtPage:(int)pageNum
- (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pageNum
{
- NSString *path = @"favorites.xml";
+ NSString *path = [NSString stringWithFormat:@"favorites.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (pageNum > 0) {
[params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
}
if (username) {
- path = [NSString stringWithFormat:@"favorites/%@.xml", username];
+ path = [NSString stringWithFormat:@"favorites/%@.%@", username, API_FORMAT];
}
return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
@@ -1104,7 +1156,7 @@ - (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pag
- (NSString *)getUpdate:(int)updateID
{
- NSString *path = [NSString stringWithFormat:@"statuses/show/%d.xml", updateID];
+ NSString *path = [NSString stringWithFormat:@"statuses/show/%d.%@", updateID, API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterStatusesRequest
@@ -1117,7 +1169,7 @@ - (NSString *)getUpdate:(int)updateID
- (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
{
- NSString *path = @"direct_messages.xml";
+ NSString *path = [NSString stringWithFormat:@"direct_messages.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (date) {
@@ -1135,7 +1187,7 @@ - (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
- (NSString *)getDirectMessagesSinceID:(int)updateID startingAtPage:(int)pageNum
{
- NSString *path = @"direct_messages.xml";
+ NSString *path = [NSString stringWithFormat:@"direct_messages.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (updateID > 0) {
@@ -1153,7 +1205,7 @@ - (NSString *)getDirectMessagesSinceID:(int)updateID startingAtPage:(int)pageNum
- (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
{
- NSString *path = @"direct_messages/sent.xml";
+ NSString *path = [NSString stringWithFormat:@"direct_messages/sent.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (date) {
@@ -1171,7 +1223,7 @@ - (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)page
- (NSString *)getSentDirectMessagesSinceID:(int)updateID startingAtPage:(int)pageNum
{
- NSString *path = @"direct_messages/sent.xml";
+ NSString *path = [NSString stringWithFormat:@"direct_messages/sent.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (updateID > 0) {
@@ -1195,7 +1247,7 @@ - (NSString *)getUserInformationFor:(NSString *)username
if (!username) {
return nil;
}
- NSString *path = [NSString stringWithFormat:@"users/show/%@.xml", username];
+ NSString *path = [NSString stringWithFormat:@"users/show/%@.%@", username, API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterUserInfoRequest
@@ -1205,7 +1257,7 @@ - (NSString *)getUserInformationFor:(NSString *)username
- (NSString *)getUserInformationForEmail:(NSString *)email
{
- NSString *path = @"users/show.xml";
+ NSString *path = [NSString stringWithFormat:@"users/show.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (email) {
[params setObject:email forKey:@"email"];
@@ -1221,11 +1273,11 @@ - (NSString *)getUserInformationForEmail:(NSString *)email
- (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(int)pageNum
{
- NSString *path = @"statuses/friends.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/friends.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (username) {
- path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
+ path = [NSString stringWithFormat:@"statuses/friends/%@.%@", username, API_FORMAT];
}
if (pageNum > 0) {
[params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
@@ -1239,7 +1291,7 @@ - (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(
- (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag
{
- NSString *path = @"statuses/followers.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/followers.%@", API_FORMAT];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
if (!flag) {
@@ -1254,7 +1306,7 @@ - (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag
- (NSString *)getFeaturedUsers
{
- NSString *path = @"statuses/featured.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/featured.%@", API_FORMAT];
return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
requestType:MGTwitterUserInfoRequest
@@ -1277,7 +1329,7 @@ - (NSString *)sendUpdate:(NSString *)status inReplyTo:(int)updateID
return nil;
}
- NSString *path = @"statuses/update.xml";
+ NSString *path = [NSString stringWithFormat:@"statuses/update.%@", API_FORMAT];
NSString *trimmedText = status;
if ([trimmedText length] > MAX_MESSAGE_LENGTH) {
@@ -1300,7 +1352,7 @@ - (NSString *)sendUpdate:(NSString *)status inReplyTo:(int)updateID
- (NSString *)deleteUpdate:(int)updateID
{
- NSString *path = [NSString stringWithFormat:@"statuses/destroy/%d.xml", updateID];
+ NSString *path = [NSString stringWithFormat:@"statuses/destroy/%d.%@", updateID, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -1310,9 +1362,9 @@ - (NSString *)deleteUpdate:(int)updateID
- (NSString *)markUpdate:(int)updateID asFavorite:(BOOL)flag
{
- NSString *path = [NSString stringWithFormat:@"favorites/%@/%d.xml",
+ NSString *path = [NSString stringWithFormat:@"favorites/%@/%d.%@",
(flag) ? @"create" : @"destroy" ,
- updateID];
+ updateID, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
@@ -1329,7 +1381,7 @@ - (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username
return nil;
}
- NSString *path = @"direct_messages/new.xml";
+ NSString *path = [NSString stringWithFormat:@"direct_messages/new.%@", API_FORMAT];
NSString *trimmedText = message;
if ([trimmedText length] > MAX_MESSAGE_LENGTH) {
@@ -1350,7 +1402,7 @@ - (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username
- (NSString *)deleteDirectMessage:(int)updateID
{
- NSString *path = [NSString stringWithFormat:@"direct_messages/destroy/%d.xml", updateID];
+ NSString *path = [NSString stringWithFormat:@"direct_messages/destroy/%d.%@", updateID, API_FORMAT];
return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
requestType:MGTwitterAccountRequest
View
53 MGTwitterEngine.xcodeproj/project.pbxproj
@@ -7,6 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
+ 44AE00810F214F26004F5AE3 /* libyajl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 44AE00800F214F26004F5AE3 /* libyajl.dylib */; };
+ 44AEFFF10F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEFFE80F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.m */; };
+ 44AEFFF20F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEFFEA0F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.m */; };
+ 44AEFFF30F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEFFEC0F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.m */; };
+ 44AEFFF40F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEFFEE0F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.m */; };
+ 44AEFFF50F2141AB004F5AE3 /* MGTwitterYAJLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEFFF00F2141AB004F5AE3 /* MGTwitterYAJLParser.m */; };
44F7517C0E47DBD600858A1B /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 44F7517B0E47DBD600858A1B /* libxml2.dylib */; };
44F7518F0E47DDD500858A1B /* MGTwitterUsersLibXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44F751890E47DDD500858A1B /* MGTwitterUsersLibXMLParser.m */; };
44F751900E47DDD500858A1B /* MGTwitterMiscLibXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 44F7518A0E47DDD500858A1B /* MGTwitterMiscLibXMLParser.m */; };
@@ -37,6 +43,17 @@
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32CA4F630368D1EE00C91783 /* MGTwitterEngine_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterEngine_Prefix.pch; sourceTree = "<group>"; };
+ 44AE00800F214F26004F5AE3 /* libyajl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libyajl.dylib; path = "yajl/build/yajl-0.4.0/lib/libyajl.dylib"; sourceTree = "<group>"; };
+ 44AEFFE70F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterMessagesYAJLParser.h; sourceTree = "<group>"; };
+ 44AEFFE80F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterMessagesYAJLParser.m; sourceTree = "<group>"; };
+ 44AEFFE90F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterMiscYAJLParser.h; sourceTree = "<group>"; };
+ 44AEFFEA0F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterMiscYAJLParser.m; sourceTree = "<group>"; };
+ 44AEFFEB0F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterStatusesYAJLParser.h; sourceTree = "<group>"; };
+ 44AEFFEC0F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterStatusesYAJLParser.m; sourceTree = "<group>"; };
+ 44AEFFED0F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterUsersYAJLParser.h; sourceTree = "<group>"; };
+ 44AEFFEE0F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterUsersYAJLParser.m; sourceTree = "<group>"; };
+ 44AEFFEF0F2141AB004F5AE3 /* MGTwitterYAJLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterYAJLParser.h; sourceTree = "<group>"; };
+ 44AEFFF00F2141AB004F5AE3 /* MGTwitterYAJLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterYAJLParser.m; sourceTree = "<group>"; };
44F7517B0E47DBD600858A1B /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = "<absolute>"; };
44F751880E47DDD500858A1B /* MGTwitterMiscLibXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterMiscLibXMLParser.h; sourceTree = "<group>"; };
44F751890E47DDD500858A1B /* MGTwitterUsersLibXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterUsersLibXMLParser.m; sourceTree = "<group>"; };
@@ -86,6 +103,7 @@
files = (
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
44F7517C0E47DBD600858A1B /* libxml2.dylib in Frameworks */,
+ 44AE00810F214F26004F5AE3 /* libyajl.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -112,6 +130,7 @@
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
+ 44AE00800F214F26004F5AE3 /* libyajl.dylib */,
44F7517B0E47DBD600858A1B /* libxml2.dylib */,
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
@@ -170,6 +189,23 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 44AEFFD00F213814004F5AE3 /* Twitter YAJL Parsers */ = {
+ isa = PBXGroup;
+ children = (
+ 44AEFFEF0F2141AB004F5AE3 /* MGTwitterYAJLParser.h */,
+ 44AEFFF00F2141AB004F5AE3 /* MGTwitterYAJLParser.m */,
+ 44AEFFEB0F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.h */,
+ 44AEFFEC0F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.m */,
+ 44AEFFED0F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.h */,
+ 44AEFFEE0F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.m */,
+ 44AEFFE70F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.h */,
+ 44AEFFE80F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.m */,
+ 44AEFFE90F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.h */,
+ 44AEFFEA0F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.m */,
+ );
+ name = "Twitter YAJL Parsers";
+ sourceTree = "<group>";
+ };
44F7517D0E47DBF600858A1B /* Twitter LibXML Parsers */ = {
isa = PBXGroup;
children = (
@@ -237,6 +273,7 @@
C9E497610D686B3500C83995 /* MGTwitterRequestTypes.h */,
44F7517D0E47DBF600858A1B /* Twitter LibXML Parsers */,
C90F74CB0D6A3B5300F602A5 /* Twitter NSXML Parsers */,
+ 44AEFFD00F213814004F5AE3 /* Twitter YAJL Parsers */,
C90F748C0D69FDE000F602A5 /* Categories */,
);
name = MATwitterEngine;
@@ -313,6 +350,11 @@
44F751920E47DDD500858A1B /* MGTwitterLibXMLParser.m in Sources */,
44F751930E47DDD500858A1B /* MGTwitterStatusesLibXMLParser.m in Sources */,
44F751A70E47DEC400858A1B /* NSData+Base64.m in Sources */,
+ 44AEFFF10F2141AB004F5AE3 /* MGTwitterMessagesYAJLParser.m in Sources */,
+ 44AEFFF20F2141AB004F5AE3 /* MGTwitterMiscYAJLParser.m in Sources */,
+ 44AEFFF30F2141AB004F5AE3 /* MGTwitterStatusesYAJLParser.m in Sources */,
+ 44AEFFF40F2141AB004F5AE3 /* MGTwitterUsersYAJLParser.m in Sources */,
+ 44AEFFF50F2141AB004F5AE3 /* MGTwitterYAJLParser.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -350,6 +392,10 @@
GCC_PREFIX_HEADER = MGTwitterEngine_Prefix.pch;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/yajl/build/yajl-0.4.0/lib\"",
+ );
PRODUCT_NAME = MGTwitterEngine;
WRAPPER_EXTENSION = app;
ZERO_LINK = YES;
@@ -365,6 +411,10 @@
GCC_PREFIX_HEADER = MGTwitterEngine_Prefix.pch;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/yajl/build/yajl-0.4.0/lib\"",
+ );
PRODUCT_NAME = MGTwitterEngine;
WRAPPER_EXTENSION = app;
};
@@ -379,8 +429,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/usr/include/libxml2,
- "<Multiple",
- "values>",
+ "\"$(SRCROOT)/yajl/build/yajl-0.4.0/include\"",
);
PREBINDING = NO;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
View
13 MGTwitterEngineGlobalHeader.h
@@ -16,3 +16,16 @@
#else
#import <Cocoa/Cocoa.h>
#endif
+
+/*
+ Set this if the YAJL JSON parser is available. More information about this parser here:
+
+ http://lloydforge.org/projects/yajl/
+
+ There are some speed advantages to using JSON instead of XML. Also, the Twitter Search API
+ uses JSON, so adding this library to your project makes additional methods available to your
+ application.
+*/
+
+#define YAJL_AVAILABLE 0
+
View
17 MGTwitterMessagesYAJLParser.h
@@ -0,0 +1,17 @@
+//
+// MGTwitterMessagesYAJLParser.h
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterEngineGlobalHeader.h"
+
+#import "MGTwitterYAJLParser.h"
+
+@interface MGTwitterMessagesYAJLParser : MGTwitterYAJLParser {
+
+}
+
+@end
View
117 MGTwitterMessagesYAJLParser.m
@@ -0,0 +1,117 @@
+//
+// MGTwitterMessagesYAJLParser.m
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterMessagesYAJLParser.h"
+
+
+@implementation MGTwitterMessagesYAJLParser
+
+/*
+- (NSDictionary *)_directMessageDictionaryForNodeWithName:(const xmlChar *)parentNodeName {
+ if (xmlTextReaderIsEmptyElement(_reader))
+ return nil;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return nil;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "sender") || xmlStrEqual(name, BAD_CAST "recipient"))
+ {
+ // "user" is the name of a sub-dictionary in each <status> item
+ [dictionary setObject:[self _userDictionaryForNodeWithName:name] forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ else if (xmlStrEqual(name, BAD_CAST "id") || xmlStrEqual(name, BAD_CAST "sender_id") || xmlStrEqual(name, BAD_CAST "recipient_id"))
+ {
+ // process element as an integer
+ NSNumber *number = [self _nodeValueAsInt];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else if (xmlStrEqual(name, BAD_CAST "created_at"))
+ {
+ // process element as a date
+ NSDate *date = [self _nodeValueAsDate];
+ if (date)
+ {
+ [dictionary setObject:date forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else if (xmlStrEqual(name, BAD_CAST "protected"))
+ {
+ // process element as a boolean
+ NSNumber *number = [self _nodeValueAsBool];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else
+ {
+ // process element as a string
+ NSString *string = [self _nodeValueAsString];
+ if (string)
+ {
+ [dictionary setObject:string forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ break;
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+
+ // save the request type in the tweet
+ [dictionary setObject:[NSNumber numberWithInt:requestType] forKey:TWITTER_SOURCE_REQUEST_TYPE];
+
+ return dictionary;
+}
+*/
+
+- (void)parse
+{
+/*
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(BAD_CAST "direct-messages", name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "direct_message"))
+ {
+ [parsedObjects addObject:[self _directMessageDictionaryForNodeWithName:BAD_CAST "direct_message"]];
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ {
+ break;
+ }
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+*/
+}
+
+@end
View
17 MGTwitterMiscYAJLParser.h
@@ -0,0 +1,17 @@
+//
+// MGTwitterMiscYAJLParser.h
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterEngineGlobalHeader.h"
+
+#import "MGTwitterYAJLParser.h"
+
+@interface MGTwitterMiscYAJLParser : MGTwitterYAJLParser {
+
+}
+
+@end
View
56 MGTwitterMiscYAJLParser.m
@@ -0,0 +1,56 @@
+//
+// MGTwitterMiscYAJLParser.m
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterMiscYAJLParser.h"
+
+
+@implementation MGTwitterMiscYAJLParser
+
+- (void)parse
+{
+/*
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return;
+
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "hash"))
+ {
+ [parsedObjects addObject:[self _hashDictionaryForNodeWithName:name]];
+ }
+ else
+ {
+ // process element as a string -- API calls like friendships/exists.xml just return <friends>false</friends> or <friends>true</friends>
+ NSString *string = [self _nodeValueAsString];
+ if (string)
+ {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+ [dictionary setObject:string forKey:[NSString stringWithUTF8String:(const char *)name]];
+ [parsedObjects addObject:dictionary];
+ }
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ {
+ break;
+ }
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+*/
+}
+
+@end
View
19 MGTwitterStatusesYAJLParser.h
@@ -0,0 +1,19 @@
+//
+// MGTwitterStatusesYAJLParser.h
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterEngineGlobalHeader.h"
+
+#import "MGTwitterYAJLParser.h"
+
+@interface MGTwitterStatusesYAJLParser : MGTwitterYAJLParser {
+
+ NSMutableDictionary *_status;
+ NSMutableDictionary *_user;
+}
+
+@end
View
45 MGTwitterStatusesYAJLParser.m
@@ -0,0 +1,45 @@
+//
+// MGTwitterStatusesYAJLParser.m
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterStatusesYAJLParser.h"
+
+
+@implementation MGTwitterStatusesYAJLParser
+
+
+- (void)parse
+{
+/*
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(BAD_CAST "statuses", name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "status"))
+ {
+ [parsedObjects addObject:[self _statusDictionaryForNodeWithName:name]];
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ {
+ break;
+ }
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+*/
+}
+
+@end
View
17 MGTwitterUsersYAJLParser.h
@@ -0,0 +1,17 @@
+//
+// MGTwitterUsersYAJLParser.h
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterEngineGlobalHeader.h"
+
+#import "MGTwitterYAJLParser.h"
+
+@interface MGTwitterUsersYAJLParser : MGTwitterYAJLParser {
+
+}
+
+@end
View
44 MGTwitterUsersYAJLParser.m
@@ -0,0 +1,44 @@
+//
+// MGTwitterUsersYAJLParser.m
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 11/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterUsersYAJLParser.h"
+
+
+@implementation MGTwitterUsersYAJLParser
+
+- (void)parse
+{
+/*
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(BAD_CAST "users", name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "user"))
+ {
+ [parsedObjects addObject:[self _userDictionaryForNodeWithName:name]];
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ {
+ break;
+ }
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+*/
+}
+
+@end
View
54 MGTwitterYAJLParser.h
@@ -0,0 +1,54 @@
+//
+// MGTwitterYAJLParser.h
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 18/02/2008.
+// Copyright 2008 Instinctive Code.
+//
+
+#import "MGTwitterEngineGlobalHeader.h"
+//#include <libxml/xmlreader.h>
+#include <yajl/yajl_parse.h>
+
+#import "MGTwitterParserDelegate.h"
+
+@interface MGTwitterYAJLParser : NSObject {
+ __weak NSObject <MGTwitterParserDelegate> *delegate; // weak ref
+ NSString *identifier;
+ MGTwitterRequestType requestType;
+ MGTwitterResponseType responseType;
+ NSURL *URL;
+ NSData *json;
+ NSMutableArray *parsedObjects;
+
+// xmlTextReaderPtr _reader;
+ yajl_handle _handle;
+}
+
++ (id)parserWithJSON:(NSData *)theJSON delegate:(NSObject *)theDelegate
+connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
+ responseType:(MGTwitterResponseType)respType URL:(NSURL *)URL;
+- (id)initWithJSON:(NSData *)theJSON delegate:(NSObject *)theDelegate
+connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
+ responseType:(MGTwitterResponseType)respType URL:(NSURL *)URL;
+
+- (void)parse;
+
+// subclass utilities
+/*
+- (xmlChar *)_nodeValue;
+- (NSString *)_nodeValueAsString;
+- (NSDate *)_nodeValueAsDate;
+- (NSNumber *)_nodeValueAsInt;
+- (NSNumber *)_nodeValueAsBool;
+- (NSDictionary *)_statusDictionaryForNodeWithName:(const xmlChar *)parentNodeName;
+- (NSDictionary *)_userDictionaryForNodeWithName:(const xmlChar *)parentNodeName;
+- (NSDictionary *)_hashDictionaryForNodeWithName:(const xmlChar *)parentNodeName;
+*/
+
+// delegate callbacks
+- (void)_parsingDidEnd;
+- (void)_parsingErrorOccurred:(NSError *)parseError;
+
+
+@end
View
526 MGTwitterYAJLParser.m
@@ -0,0 +1,526 @@
+//
+// MGTwitterYAJLParser.m
+// MGTwitterEngine
+//
+// Created by Matt Gemmell on 18/02/2008.
+// Copyright 2008 Instinctive Code.
+
+#import "MGTwitterYAJLParser.h"
+
+
+@implementation MGTwitterYAJLParser
+
+#pragma mark Callbacks
+
+int process_yajl_null(void *ctx)
+{
+ id self = ctx;
+
+ NSLog(@"%@: null", self);
+/*
+ if (dict && key)
+ {
+ [dict setValue:[NSNull null] forKey:key];
+ }
+*/
+ return 1;
+}
+
+int process_yajl_boolean(void * ctx, int boolVal)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: bool: %s", theSelf, boolVal ? "true" : "false");
+/*
+ if (dict && key)
+ {
+ [dict setValue:[NSNumber numberWithBool:boolVal] forKey:key];
+ }
+*/
+ return 1;
+}
+
+int process_yajl_integer(void *ctx, long integerVal)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: integer: %ld", theSelf, integerVal);
+/*
+ if (dict && key)
+ {
+ [dict setValue:[NSNumber numberWithLong:integerVal] forKey:key];
+ }
+*/
+ return 1;
+}
+
+int process_yajl_double(void *ctx, double doubleVal)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: double: %lf", theSelf, doubleVal);
+/*
+ if (dict && key)
+ {
+ [dict setValue:[NSNumber numberWithDouble:doubleVal] forKey:key];
+ }
+*/
+ return 1;
+}
+
+int process_yajl_string(void *ctx, const unsigned char * stringVal, unsigned int stringLen)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: string: %@", theSelf, [[[NSString alloc] initWithBytes:stringVal length:stringLen encoding:NSUTF8StringEncoding] autorelease]);
+/*
+ if (dict && key)
+ {
+ NSString *value = [[[NSString alloc] initWithBytes:stringVal length:stringLen encoding:NSUTF8StringEncoding] autorelease];
+ [dict setValue:value forKey:key];
+ }
+*/
+ return 1;
+}
+
+int process_yajl_map_key(void *ctx, const unsigned char * stringVal, unsigned int stringLen)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: key: %@", theSelf, [[[NSString alloc] initWithBytes:stringVal length:stringLen encoding:NSUTF8StringEncoding] autorelease]);
+
+/*
+ if (key)
+ {
+ [key release];
+ key = nil;
+ }
+
+ key = [[NSString alloc] initWithBytes:stringVal length:stringLen encoding:NSUTF8StringEncoding];
+*/
+ return 1;
+}
+
+int process_yajl_start_map(void *ctx)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: map open '{'", theSelf);
+/*
+ dict = [[NSMutableDictionary alloc] initWithCapacity:0];
+*/
+ return 1;
+}
+
+
+int process_yajl_end_map(void *ctx)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: map close '}'", theSelf);
+/*
+ [dict release];
+ dict = nil;
+*/
+ return 1;
+}
+
+int process_yajl_start_array(void *ctx)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: array open '['", theSelf);
+
+ return 1;
+}
+
+int process_yajl_end_array(void *ctx)
+{
+ id theSelf = ctx;
+
+ NSLog(@"%@: array close ']'", theSelf);
+
+ return 1;
+}
+
+static yajl_callbacks callbacks = {
+ process_yajl_null,
+ process_yajl_boolean,
+ process_yajl_integer,
+ process_yajl_double,
+ NULL,
+ process_yajl_string,
+ process_yajl_start_map,
+ process_yajl_map_key,
+ process_yajl_end_map,
+ process_yajl_start_array,
+ process_yajl_end_array
+};
+
+#pragma mark Creation and Destruction
+
+
++ (id)parserWithJSON:(NSData *)theJSON delegate:(NSObject *)theDelegate
+connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
+ responseType:(MGTwitterResponseType)respType URL:(NSURL *)URL
+{
+ id parser = [[self alloc] initWithJSON:theJSON
+ delegate:theDelegate
+ connectionIdentifier:identifier
+ requestType:reqType
+ responseType:respType
+ URL:URL];
+
+ return [parser autorelease];
+}
+
+
+- (id)initWithJSON:(NSData *)theJSON delegate:(NSObject *)theDelegate
+connectionIdentifier:(NSString *)theIdentifier requestType:(MGTwitterRequestType)reqType
+ responseType:(MGTwitterResponseType)respType URL:(NSURL *)theURL
+{
+ if (self = [super init])
+ {
+ json = [theJSON retain];
+ identifier = [theIdentifier retain];
+ requestType = reqType;
+ responseType = respType;
+ URL = [theURL retain];
+ delegate = theDelegate;
+ parsedObjects = [[NSMutableArray alloc] initWithCapacity:0];
+
+ // setup the yajl parser
+ yajl_parser_config cfg = {
+ 0, // allowComments: if nonzero, javascript style comments will be allowed in the input (both /* */ and //)
+ 1 // checkUTF8: if nonzero, invalid UTF8 strings will cause a parse error
+ };
+ _handle = yajl_alloc(&callbacks, &cfg, self);
+ if (! _handle)
+ {
+ return nil;
+ }
+
+ // run the parser and create parsedObjects
+ [self parse];
+
+ yajl_status status = yajl_parse(_handle, [json bytes], [json length]);
+ if (status != yajl_status_insufficient_data && status != yajl_status_ok)
+ {
+ unsigned char *errorMessage = yajl_get_error(_handle, 0, [json bytes], [json length]);
+ NSLog(@"YAJL error = %s", errorMessage);
+ yajl_free_error(errorMessage);
+ }
+
+ // free the yajl parser
+ yajl_free(_handle);
+
+ // notify the delegate that parsing completed
+ [self _parsingDidEnd];
+ }
+
+ return self;
+}
+
+
+- (void)dealloc
+{
+ [parsedObjects release];
+ [json release];
+ [identifier release];
+ [URL release];
+
+ delegate = nil;
+ [super dealloc];
+}
+
+- (void)parse
+{
+ // empty implementation -- override in subclasses
+}
+
+#pragma mark Subclass utilities
+
+/*
+// get the value from the current node
+- (xmlChar *)_nodeValue
+{
+ if (xmlTextReaderIsEmptyElement(_reader))
+ {
+ return nil;
+ }
+
+ xmlChar *result = nil;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ while (nodeType != XML_READER_TYPE_END_ELEMENT)
+ {
+ if (nodeType == XML_READER_TYPE_TEXT)
+ {
+ result = xmlTextReaderValue(_reader);
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ {
+ break;
+ }
+ nodeType = xmlTextReaderNodeType(_reader);
+ }
+
+ //NSLog(@"node: %25s = %s", xmlTextReaderConstName(_reader), result);
+
+ return result;
+}
+
+- (NSString *)_nodeValueAsString
+{
+ xmlChar *nodeValue = [self _nodeValue];
+ if (! nodeValue)
+ {
+ return nil;
+ }
+
+ NSMutableString *value = [NSMutableString stringWithUTF8String:(const char *)nodeValue];
+ xmlFree(nodeValue);
+
+ // convert HTML entities back into UTF-8
+ [value replaceOccurrencesOfString:@"&gt;" withString:@">" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [value length])];
+ [value replaceOccurrencesOfString:@"&lt;" withString:@"<" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [value length])];
+
+ NSString *result = [NSString stringWithString:value];
+ return result;
+}
+
+- (NSDate *)_nodeValueAsDate
+{
+ xmlChar *nodeValue = [self _nodeValue];
+ if (! nodeValue)
+ {
+ return nil;
+ }
+
+ struct tm theTime;
+ strptime((char *)nodeValue, "%a %b %d %H:%M:%S +0000 %Y", &theTime);
+ xmlFree(nodeValue);
+ time_t epochTime = timegm(&theTime);
+ return [NSDate dateWithTimeIntervalSince1970:epochTime];
+}
+
+- (NSNumber *)_nodeValueAsInt
+{
+ xmlChar *nodeValue = [self _nodeValue];
+ if (! nodeValue)
+ {
+ return nil;
+ }
+
+ NSString *intString = [NSString stringWithUTF8String:(const char *)nodeValue];
+ xmlFree(nodeValue);
+ return [NSNumber numberWithInt:[intString intValue]];
+}
+
+- (NSNumber *)_nodeValueAsBool
+{
+ xmlChar *nodeValue = [self _nodeValue];
+ if (! nodeValue)
+ {
+ return nil;
+ }
+
+ NSString *boolString = [NSString stringWithUTF8String:(const char *)nodeValue];
+ xmlFree(nodeValue);
+ return [NSNumber numberWithBool:[boolString isEqualToString:@"true"]];
+}
+
+- (NSDictionary *)_statusDictionaryForNodeWithName:(const xmlChar *)parentNodeName
+{
+ if (xmlTextReaderIsEmptyElement(_reader))
+ return nil;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return nil;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "user"))
+ {
+ // "user" is the name of a sub-dictionary in each <status> item
+ [dictionary setObject:[self _userDictionaryForNodeWithName:name] forKey:@"user"];
+ }
+ else if (xmlStrEqual(name, BAD_CAST "id") || xmlStrEqual(name, BAD_CAST "in_reply_to_user_id") || xmlStrEqual(name, BAD_CAST "in_reply_to_status_id"))
+ {
+ // process element as an integer
+ NSNumber *number = [self _nodeValueAsInt];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else if (xmlStrEqual(name, BAD_CAST "created_at"))
+ {
+ // process element as a date
+ NSDate *date = [self _nodeValueAsDate];
+ if (date)
+ {
+ [dictionary setObject:date forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else if (xmlStrEqual(name, BAD_CAST "truncated") || xmlStrEqual(name, BAD_CAST "favorited"))
+ {
+ // process element as a boolean
+ NSNumber *number = [self _nodeValueAsBool];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else
+ {
+ // process element as a string
+ NSString *string = [self _nodeValueAsString];
+ if (string)
+ {
+ [dictionary setObject:string forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ break;
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+
+ // save the request type in the tweet
+ [dictionary setObject:[NSNumber numberWithInt:requestType] forKey:TWITTER_SOURCE_REQUEST_TYPE];
+
+ return dictionary;
+}
+
+- (NSDictionary *)_userDictionaryForNodeWithName:(const xmlChar *)parentNodeName
+{
+ if (xmlTextReaderIsEmptyElement(_reader))
+ return nil;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return nil;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "id") || xmlStrEqual(name, BAD_CAST "followers_count")
+ || xmlStrEqual(name, BAD_CAST "friends_count") || xmlStrEqual(name, BAD_CAST "favourites_count")
+ || xmlStrEqual(name, BAD_CAST "statuses_count"))
+ {
+ // process element as an integer
+ NSNumber *number = [self _nodeValueAsInt];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else if (xmlStrEqual(name, BAD_CAST "protected"))
+ {
+ // process element as a boolean
+ NSNumber *number = [self _nodeValueAsBool];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else
+ {
+ // process element as a string
+ NSString *s = [self _nodeValueAsString];
+ if (s)
+ {
+ [dictionary setObject:s forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ break;
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+
+ return dictionary;
+}
+
+- (NSDictionary *)_hashDictionaryForNodeWithName:(const xmlChar *)parentNodeName
+{
+ if (xmlTextReaderIsEmptyElement(_reader))
+ return nil;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ return nil;
+ int nodeType = xmlTextReaderNodeType(_reader);
+ const xmlChar *name = xmlTextReaderConstName(_reader);
+ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name)))
+ {
+ if (nodeType == XML_READER_TYPE_ELEMENT)
+ {
+ if (xmlStrEqual(name, BAD_CAST "hourly-limit") || xmlStrEqual(name, BAD_CAST "remaining-hits")
+ || xmlStrEqual(name, BAD_CAST "reset-time-in-seconds"))
+ {
+ // process element as an integer
+ NSNumber *number = [self _nodeValueAsInt];
+ if (number)
+ {
+ [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ else
+ {
+ // process element as a string
+ NSString *s = [self _nodeValueAsString];
+ if (s)
+ {
+ [dictionary setObject:s forKey:[NSString stringWithUTF8String:(const char *)name]];
+ }
+ }
+ }
+
+ // advance reader
+ int readerResult = xmlTextReaderRead(_reader);
+ if (readerResult != 1)
+ break;
+ nodeType = xmlTextReaderNodeType(_reader);
+ name = xmlTextReaderConstName(_reader);
+ }
+
+ return dictionary;
+}
+*/
+
+#pragma mark Delegate callbacks
+
+- (void)_parsingDidEnd
+{
+ //NSLog(@"Parsing complete: %@", parsedObjects);
+ [delegate parsingSucceededForRequest:identifier ofResponseType:responseType withParsedObjects:parsedObjects];
+}
+
+- (void)_parsingErrorOccurred:(NSError *)parseError
+{
+ //NSLog(@"Parsing error occurred: %@", parseError);
+ [delegate parsingFailedForRequest:identifier ofResponseType:responseType withError:parseError];
+}
+
+@end

0 comments on commit fbfc444

Please sign in to comment.