<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>iPhoneNonatomic.h</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -37,33 +37,23 @@
  */
 
 #import &lt;Foundation/Foundation.h&gt;
+#import &quot;iPhoneNonatomic.h&quot;
 
 @class _AQXMLParserInternal;
-@protocol AQXMLParserDelegate;
+@protocol AQXMLParserDelegate, AQXMLParserProgressDelegate;
 
 extern NSString * const AQXMLParserParsingRunLoopMode;
 
-// iPhone 3 &amp; OS X 10.6 both define NS_NONATOMIC_IPHONEONLY, but it doesn't work
-//  (it doesn't take commas into account) so I'm redefining it here
-#ifdef NS_NONATOMIC_IPHONEONLY
-# undef NS_NONATOMIC_IPHONEONLY
-#endif
-
-#ifdef TARGET_OS_IPHONE
-# define NS_NONATOMIC_IPHONEONLY nonatomic,
-#else
-# define NS_NONATOMIC_IPHONEONLY
-#endif
-
 // delegates should implement the same functions used by AQXMLParser
 
 @interface AQXMLParser : NSObject
 {
-	void *							_parser;
-	id&lt;AQXMLParserDelegate&gt; __weak	_delegate;
-	NSInputStream *					_stream;
-	_AQXMLParserInternal *			_internal;
-	BOOL							_streamComplete;
+	void *                                  _parser;
+	id&lt;AQXMLParserDelegate&gt; __weak          _delegate;
+    id&lt;AQXMLParserProgressDelegate&gt; __weak  _progressDelegate;
+	NSInputStream *                         _stream;
+	_AQXMLParserInternal *                  _internal;
+	BOOL                                    _streamComplete;
 }
 
 // designated initializer
@@ -72,6 +62,7 @@ extern NSString * const AQXMLParserParsingRunLoopMode;
 - (id) initWithData: (NSData *) data;   // creates a stream from the data
 
 @property (NS_NONATOMIC_IPHONEONLY assign) id&lt;AQXMLParserDelegate&gt; __weak delegate;
+@property (NS_NONATOMIC_IPHONEONLY assign) id&lt;AQXMLParserProgressDelegate&gt; __weak progressDelegate;
 
 @property (NS_NONATOMIC_IPHONEONLY assign) BOOL shouldProcessNamespaces;
 @property (NS_NONATOMIC_IPHONEONLY assign) BOOL shouldReportNamespacePrefixes;
@@ -81,7 +72,17 @@ extern NSString * const AQXMLParserParsingRunLoopMode;
 - (BOOL) parse;
 - (void) abortParsing;
 
-@property (NS_NONATOMIC_IPHONEONLY readonly) NSError * parserError;
+// asynchronous parsing on the given runloop/mode
+// completionSelector should match the following structure:
+// - (void) xmlParser: (AQXMLParser *) parser completedOK: (BOOL) parsedOK context: (void *) context;
+// completionSelector will be called in the context of the supplied runloop
+- (BOOL) parseAsynchronouslyUsingRunLoop: (NSRunLoop *) runloop
+                                    mode: (NSString *) mode
+                       notifyingDelegate: (id) asyncCompletionDelegate
+                                selector: (SEL) completionSelector
+                                 context: (void *) contextPtr;
+
+@property NS_NONATOMIC_IPHONEONLY(readonly) NSError * parserError;
 
 @end
 
@@ -92,6 +93,11 @@ extern NSString * const AQXMLParserParsingRunLoopMode;
 @property (nonatomic, readonly) NSInteger columnNumber;
 @end
 
+// parser reports progress as a value between 0.0 and 1.0
+@protocol AQXMLParserProgressDelegate &lt;NSObject&gt;
+- (void) parser: (AQXMLParser *) parser updateProgress: (float) progress;
+@end
+
 // tweaked versions of the delegate functions, accepting AQXMLParser instead of NSXMLParser (gets rid of compiler warnings)
 
 /*</diff>
      <filename>StreamingXMLParser/AQXMLParser.h</filename>
    </modified>
    <modified>
      <diff>@@ -48,6 +48,12 @@
 #import &lt;libxml/encoding.h&gt;
 #import &lt;libxml/entities.h&gt;
 
+#if TARGET_OS_IPHONE
+# import &lt;CFNetwork/CFNetwork.h&gt;
+#else
+# import &lt;CoreServices/../Frameworks/CFNetwork.framework/Headers/CFNetwork.h&gt;
+#endif
+
 NSString * const AQXMLParserParsingRunLoopMode = @&quot;AQXMLParserParsingRunLoopMode&quot;;
 
 @interface _AQXMLParserInternal : NSObject
@@ -62,6 +68,16 @@ NSString * const AQXMLParserParsingRunLoopMode = @&quot;AQXMLParserParsingRunLoopMode
 	NSError *			error;
 	NSMutableArray *	namespaces;
 	BOOL				delegateAborted;
+    
+    // async parse callback data
+    id                  asyncDelegate;
+    SEL                 asyncSelector;
+    void *              asyncContext;
+    
+    // progress variables
+    float               expectedDataLength;
+    float               currentLength;
+    
 }
 @property (nonatomic, readonly) xmlSAXHandlerPtr xmlSaxHandler;
 @property (nonatomic, readonly) xmlParserCtxtPtr xmlParserContext;
@@ -114,6 +130,8 @@ enum
 - (void) _initializeParserWithBytes: (const void *) buf length: (NSUInteger) length;
 - (void) _pushXMLData: (const void *) bytes length: (NSUInteger) length;
 - (_AQXMLParserInternal *) _info;
+- (void) _setStreamComplete: (BOOL) parsedOK;
+- (void) _setupExpectedLength;
 @end
 
 #pragma mark -
@@ -200,7 +218,7 @@ static void __characters( void * ctx, const xmlChar * ch, int len )
 	AQXMLParser * parser = (AQXMLParser *) ctx;
 	xmlParserCtxtPtr p = [parser _xmlParserContext];
 	
-	if ( (int)(p-&gt;_private) == 1 )
+	if ( (long)(p-&gt;_private) == 1 )
 	{
 		p-&gt;_private = 0;
 		return;
@@ -714,6 +732,7 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 @implementation AQXMLParser
 
 @synthesize delegate=_delegate;
+@synthesize progressDelegate=_progressDelegate;
 
 - (id) initWithStream: (NSInputStream *) stream
 {
@@ -730,6 +749,8 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 	_internal-&gt;error = nil;
 	
 	_stream = [stream retain];
+    if ( _internal-&gt;expectedDataLength != 0.0 )
+        [self _setupExpectedLength];
 	
 	[self _initializeSAX2Callbacks];
 	
@@ -739,6 +760,7 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 - (id) initWithData: (NSData *) data
 {
     NSInputStream * stream = [[NSInputStream alloc] initWithData: data];
+    _internal-&gt;expectedDataLength = (float) [data length];
     id result = [self initWithStream: stream];
     [stream release];
     return ( result );
@@ -858,7 +880,40 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 
 - (BOOL) parse
 {
-	if ( _stream == nil )
+	if ( [self parseAsynchronouslyUsingRunLoop: [NSRunLoop currentRunLoop]
+                                          mode: AQXMLParserParsingRunLoopMode
+                             notifyingDelegate: nil
+                                      selector: NULL
+                                       context: NULL] == NO )
+    {
+        return ( NO );
+    }
+	
+	// run in the common runloop modes while we read the data from the stream
+	do
+	{
+		NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+		[[NSRunLoop currentRunLoop] runMode: AQXMLParserParsingRunLoopMode
+                                 beforeDate: [NSDate distantFuture]];
+		[pool drain];
+		
+	} while ( _streamComplete == NO );
+	
+	[_stream setDelegate: nil];
+	[_stream removeFromRunLoop: [NSRunLoop currentRunLoop]
+                       forMode: AQXMLParserParsingRunLoopMode];
+	[_stream close];
+	
+	return ( _internal-&gt;error == nil );
+}
+
+- (BOOL) parseAsynchronouslyUsingRunLoop: (NSRunLoop *) runloop
+                                    mode: (NSString *) mode
+                       notifyingDelegate: (id) asyncCompletionDelegate
+                                selector: (SEL) completionSelector
+                                 context: (void *) contextPtr
+{
+    if ( _stream == nil )
 		return ( NO );
 	
 	xmlSAXHandlerPtr saxHandler = NULL;
@@ -878,28 +933,20 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 		buflen = [_stream read: buf maxLength: 4];
         [self _initializeParserWithBytes: buf length: buflen];
     }
+    
+    // store async callbacks details
+    _internal-&gt;asyncDelegate = asyncCompletionDelegate;
+    _internal-&gt;asyncSelector = completionSelector;
+    _internal-&gt;asyncContext  = contextPtr;
 	
 	// start the stream processing going
 	[_stream setDelegate: self];
-	[_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: AQXMLParserParsingRunLoopMode];
+	[_stream scheduleInRunLoop: runloop forMode: mode];
 	
 	if ( [_stream streamStatus] == NSStreamStatusNotOpen )
 		[_stream open];
-	
-	// run in the common runloop modes while we read the data from the stream
-	do
-	{
-		NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-		[[NSRunLoop currentRunLoop] runMode: AQXMLParserParsingRunLoopMode beforeDate: [NSDate distantFuture]];
-		[pool drain];
-		
-	} while ( _streamComplete == NO );
-	
-	[_stream setDelegate: nil];
-	[_stream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: AQXMLParserParsingRunLoopMode];
-	[_stream close];
-	
-	return ( _internal-&gt;error == nil );
+    
+    return ( YES );
 }
 
 - (void) stream: (NSStream *) stream handleEvent: (NSStreamEvent) streamEvent
@@ -917,14 +964,14 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 			_internal-&gt;error = [input streamError];
 			if ( [_delegate respondsToSelector: @selector(parser:parseErrorOccurred:)] )
 				[_delegate parser: self parseErrorOccurred: _internal-&gt;error];
-			_streamComplete = YES;
+			[self _setStreamComplete: NO];
 			break;
 		}
 			
 		case NSStreamEventEndEncountered:
 		{
 			xmlParseChunk( _internal-&gt;parserContext, NULL, 0, 1 );
-			_streamComplete = YES;
+			[self _setStreamComplete: YES];
 			break;
 		}
 			
@@ -1126,14 +1173,21 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
         if ( self.HTMLMode )
             err = htmlParseChunk( _internal.htmlParserContext, (const char *)bytes, length, 0 );
         else
-            err = xmlParseChunk( _internal-&gt;parserContext, (const char *)bytes, length, 0 );
+            err = xmlParseChunk( _internal.xmlParserContext, (const char *)bytes, length, 0 );
         
         if ( err != XML_ERR_OK )
         {
             [self _setParserError: err];
-            _streamComplete = YES;
+            [self _setStreamComplete: NO];
         }
     }
+    
+    if ( _progressDelegate != nil )
+    {
+        _internal-&gt;currentLength += (float) length;
+        [_progressDelegate parser: self
+                   updateProgress: (_internal-&gt;currentLength / _internal-&gt;expectedDataLength)];
+    }
 }
 
 - (_AQXMLParserInternal *) _info
@@ -1141,5 +1195,59 @@ static void __ignorableWhitespace( void * ctx, const xmlChar * ch, int len )
 	return ( _internal );
 }
 
+- (void) _setStreamComplete: (BOOL) parsedOK
+{
+    _streamComplete = YES;
+    
+    if ( _internal-&gt;asyncDelegate != nil )
+    {
+        @try
+        {
+            NSInvocation * invoc = [NSInvocation invocationWithMethodSignature: [_internal-&gt;asyncDelegate methodSignatureForSelector: _internal-&gt;asyncSelector]];
+            
+            [invoc setTarget: _internal-&gt;asyncDelegate];
+            [invoc setSelector: _internal-&gt;asyncSelector];
+            [invoc setArgument: &amp;self atIndex: 2];
+            [invoc setArgument: &amp;parsedOK atIndex: 3];
+            [invoc setArgument: &amp;_internal-&gt;asyncContext atIndex: 4];
+            
+            [invoc invoke];
+        }
+        @catch (NSException * e)
+        {
+            NSLog( @&quot;Caught %@ while calling AQXMLParser async delegate: %@&quot;, e.name, e.reason );
+            @throw;
+        }
+    }
+}
+
+- (void) _setupExpectedLength
+{
+    CFHTTPMessageRef msg = (CFHTTPMessageRef) [_stream propertyForKey: (NSString *)kCFStreamPropertyHTTPResponseHeader];
+    if ( msg != NULL )
+    {
+        CFStringRef str = CFHTTPMessageCopyHeaderFieldValue( msg, CFSTR(&quot;Content-Length&quot;) );
+        if ( str != NULL )
+        {
+            _internal-&gt;expectedDataLength = [(NSString *)str floatValue];
+            CFRelease( str );
+        }
+        return;
+    }
+    
+    CFNumberRef num = (CFNumberRef) [_stream propertyForKey: (NSString *)kCFStreamPropertyFTPResourceSize];
+    if ( num != NULL )
+    {
+        _internal-&gt;expectedDataLength = [(NSNumber *)num floatValue];
+        CFRelease( num );
+        return;
+    }
+    
+    // for some forthcoming stream classes...
+    NSNumber * guess = [_stream propertyForKey: @&quot;UncompressedDataLength&quot;];
+    if ( guess != nil )
+        _internal-&gt;expectedDataLength = [guess floatValue];
+}
+
 @end
 </diff>
      <filename>StreamingXMLParser/AQXMLParser.m</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>cede6b840b811397605a62365445037b9a9320cf</id>
    </parent>
  </parents>
  <author>
    <name>Jim Dovey</name>
    <email>jimdovey@mac.com</email>
  </author>
  <url>http://github.com/AlanQuatermain/aqtoolkit/commit/1ae4fdbe0d3f4cc6fddd7d97b306c9224b580216</url>
  <id>1ae4fdbe0d3f4cc6fddd7d97b306c9224b580216</id>
  <committed-date>2009-04-20T19:10:54-07:00</committed-date>
  <authored-date>2009-04-20T19:10:54-07:00</authored-date>
  <message>Added async mode to AQXMLParser, along with the beginnings of progress reporting.
Moved NS_IPHONE_NONATOMIC into its own header file.</message>
  <tree>3dae35ed6f429cfa478b1b42b45e443467ab9cc6</tree>
  <committer>
    <name>Jim Dovey</name>
    <email>jimdovey@mac.com</email>
  </committer>
</commit>
