From a74b119ee8aaca43defa9251466c0316fc410046 Mon Sep 17 00:00:00 2001
From: Robert Sesek <rsesek@bluestatic.org>
Date: Mon, 31 May 2010 22:49:42 -0400
Subject: [PATCH] Re-organize DebuggerConnection to give it some shape and a
 decent public interface.

---
 Source/DebuggerConnection.h |  19 +--
 Source/DebuggerConnection.m | 260 +++++++++++++++++++-----------------
 2 files changed, 144 insertions(+), 135 deletions(-)

diff --git a/Source/DebuggerConnection.h b/Source/DebuggerConnection.h
index c34ea8b..89e8341 100644
--- a/Source/DebuggerConnection.h
+++ b/Source/DebuggerConnection.h
@@ -19,6 +19,10 @@
 @protocol DebuggerConnectionDelegate;
 @class LoggingController;
 
+// This class is the lowest level component to the network. It deals with all
+// the intracies of network and stream programming. Almost all the work this
+// class does is on a background thread, which is created when the connection is
+// asked to connect and shutdown when asked to close.
 @interface DebuggerConnection : NSObject
 {
 	// The port to connect on.
@@ -51,7 +55,7 @@
 	
 	// To prevent blocked writing, we enqueue all writes and then wait for the
 	// write stream to tell us it's ready. We store the pending commands in this
-	// array. We use this as a stack (FIFO), with index 0 being first.
+	// array. We use this as a queue (FIFO), with index 0 being first.
 	NSMutableArray* queuedWrites_;
 	
 	// We send queued writes in multiple places, sometimes off a run loop event.
@@ -77,20 +81,11 @@
 
 - (void)connect;
 - (void)close;
-- (void)socketDidAccept;
-- (void)socketDisconnected;
-- (void)readStreamHasData;
-- (void)send:(NSString*)command;
-- (void)performSend:(NSString*)command;
-- (void)errorEncountered:(NSString*)error;
 
-- (void)handleResponse:(NSXMLDocument*)response;
-- (void)handlePacket:(NSString*)packet;
+- (void)send:(NSString*)command;
 
 - (NSNumber*)sendCommandWithFormat:(NSString*)format, ...;
 
-- (void)sendQueuedWrites;
-
 - (NSString*)escapedURIPath:(NSString*)path;
 - (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response;
 - (NSInteger)transactionIDFromCommand:(NSString*)command;
@@ -104,7 +99,7 @@
 @optional
 
 - (void)connectionDidAccept:(DebuggerConnection*)cx;
-- (void)connectionDidCose:(DebuggerConnection*)cx;
+- (void)connectionDidClose:(DebuggerConnection*)cx;
 
 - (void)handleInitialResponse:(NSXMLDocument*)response;
 
diff --git a/Source/DebuggerConnection.m b/Source/DebuggerConnection.m
index 760ca7e..a9bb2e8 100644
--- a/Source/DebuggerConnection.m
+++ b/Source/DebuggerConnection.m
@@ -36,6 +36,18 @@
 
 - (void)connectInternal;
 
+- (void)socketDidAccept;
+- (void)socketDisconnected;
+- (void)readStreamHasData;
+
+- (void)performSend:(NSString*)command;
+- (void)sendQueuedWrites;
+
+- (void)handleResponse:(NSXMLDocument*)response;
+- (void)handlePacket:(NSString*)packet;
+
+- (void)errorEncountered:(NSString*)error;
+
 @end
 
 // CFNetwork Callbacks /////////////////////////////////////////////////////////
@@ -200,27 +212,6 @@ void SocketAcceptCallback(CFSocketRef socket,
 	[super dealloc];
 }
 
-/**
- * Called by SocketWrapper after the connection is successful. This immediately calls
- * -[SocketWrapper receive] to clear the way for communication, though the information
- * could be useful server information that we don't use right now.
- */
-- (void)socketDidAccept
-{
-	connected_ = YES;
-	transactionID = 1;
-	self.queuedWrites = [NSMutableArray array];
-	writeQueueLock_ = [NSRecursiveLock new];
-}
-
-/**
- * Receives errors from the SocketWrapper and updates the display
- */
-- (void)errorEncountered:(NSString*)error
-{
-	[delegate_ errorEncountered:error];
-}
-
 /**
  * Kicks off the socket on another thread.
  */
@@ -282,12 +273,27 @@ void SocketAcceptCallback(CFSocketRef socket,
 	[runLoop_ run];
 }
 
+/**
+ * Called by SocketWrapper after the connection is successful. This immediately calls
+ * -[SocketWrapper receive] to clear the way for communication, though the information
+ * could be useful server information that we don't use right now.
+ */
+- (void)socketDidAccept
+{
+	connected_ = YES;
+	transactionID = 1;
+	self.queuedWrites = [NSMutableArray array];
+	writeQueueLock_ = [NSRecursiveLock new];
+}
+
 /**
  * Closes a socket and releases the ref.
  */
 - (void)close
 {
-	CFRunLoopStop([runLoop_ getCFRunLoop]);
+	if (runLoop_) {
+		CFRunLoopStop([runLoop_ getCFRunLoop]);
+	}
 
 	// The socket goes down, so do the streams, which clean themselves up.
 	if (socket_) {
@@ -304,7 +310,98 @@ void SocketAcceptCallback(CFSocketRef socket,
 - (void)socketDisconnected
 {
 	[self close];
-	[delegate_ connectionDidCose:self];
+	[delegate_ connectionDidClose:self];
+}
+
+/**
+ * Writes a command into the write stream. If the stream is ready for writing,
+ * we do so immediately. If not, the command is queued and will be written
+ * when the stream is ready.
+ */
+- (void)send:(NSString*)command
+{
+	if (lastReadTransaction_ >= lastWrittenTransaction_ && CFWriteStreamCanAcceptBytes(writeStream_)) {
+		[self performSend:command];
+	} else {
+		[writeQueueLock_ lock];
+		[queuedWrites_ addObject:command];
+		[writeQueueLock_ unlock];
+	}
+	[self sendQueuedWrites];
+}
+
+/**
+ * This will send a command to the debugger engine. It will append the
+ * transaction ID automatically. It accepts a NSString command along with a
+ * a variable number of arguments to substitute into the command, a la
+ * +[NSString stringWithFormat:]. Returns the transaction ID as a NSNumber.
+ */
+- (NSNumber*)sendCommandWithFormat:(NSString*)format, ...
+{
+	// Collect varargs and format command.
+	va_list args;
+	va_start(args, format);
+	NSString* command = [[NSString alloc] initWithFormat:format arguments:args];
+	va_end(args);
+	
+	NSNumber* callbackKey = [NSNumber numberWithInt:transactionID++];
+	[self send:[NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey]];
+	
+	return callbackKey;
+}
+
+/**
+ * Given a file path, this returns a file:// URI and escapes any spaces for the
+ * debugger engine.
+ */
+- (NSString*)escapedURIPath:(NSString*)path
+{
+	// Custon GDBp paths are fine.
+	if ([[path substringToIndex:4] isEqualToString:@"gdbp"])
+		return path;
+	
+	// Create a temporary URL that will escape all the nasty characters.
+	NSURL* url = [NSURL fileURLWithPath:path];
+	NSString* urlString = [url absoluteString];
+	
+	// Remove the host because this is a file:// URL;
+	urlString = [urlString stringByReplacingOccurrencesOfString:[url host] withString:@""];
+	
+	// Escape % for use in printf-style NSString formatters.
+	urlString = [urlString stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
+	return urlString;
+}
+
+/**
+ * Returns the transaction_id from an NSXMLDocument.
+ */
+- (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response
+{
+	return [[[[response rootElement] attributeForName:@"transaction_id"] stringValue] intValue];
+}
+
+/**
+ * Scans a command string for the transaction ID component. If it is not found,
+ * returns NSNotFound.
+ */
+- (NSInteger)transactionIDFromCommand:(NSString*)command
+{
+	NSRange occurrence = [command rangeOfString:@"-i "];
+	if (occurrence.location == NSNotFound)
+		return NSNotFound;
+	NSString* transaction = [command substringFromIndex:occurrence.location + occurrence.length];
+	return [transaction intValue];
+}
+
+// Private /////////////////////////////////////////////////////////////////////
+#pragma mark Private
+
+/**
+ * Receives errors from the SocketWrapper and updates the display
+ */
+- (void)errorEncountered:(NSString*)error
+{
+	[delegate_ errorEncountered:error];
 }
 
 /**
@@ -449,20 +546,25 @@ void SocketAcceptCallback(CFSocketRef socket,
 	[self handleResponse:[xmlTest autorelease]];	
 }
 
-/**
- * Writes a command into the write stream. If the stream is ready for writing,
- * we do so immediately. If not, the command is queued and will be written
- * when the stream is ready.
- */
-- (void)send:(NSString*)command
+- (void)handleResponse:(NSXMLDocument*)response
 {
-	if (lastReadTransaction_ >= lastWrittenTransaction_ && CFWriteStreamCanAcceptBytes(writeStream_)) {
-		[self performSend:command];
-	} else {
-		[writeQueueLock_ lock];
-		[queuedWrites_ addObject:command];
-		[writeQueueLock_ unlock];
+	// Check and see if there's an error.
+	NSArray* error = [[response rootElement] elementsForName:@"error"];
+	if ([error count] > 0)
+	{
+		NSLog(@"Xdebug error: %@", error);
+		[delegate_ errorEncountered:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
+	}
+	
+	if ([[[response rootElement] name] isEqualToString:@"init"])
+	{
+		[delegate_ handleInitialResponse:response];
+		return;
 	}
+	
+	if ([delegate_ respondsToSelector:@selector(handleResponse:)])
+		[(NSObject*)delegate_ performSelectorOnMainThread:@selector(handleResponse:) withObject:response waitUntilDone:NO];
+	
 	[self sendQueuedWrites];
 }
 
@@ -523,50 +625,6 @@ void SocketAcceptCallback(CFSocketRef socket,
 	log.lastReadTransactionID = lastReadTransaction_;
 }
 
-- (void)handleResponse:(NSXMLDocument*)response
-{
-	// Check and see if there's an error.
-	NSArray* error = [[response rootElement] elementsForName:@"error"];
-	if ([error count] > 0)
-	{
-		NSLog(@"Xdebug error: %@", error);
-		[delegate_ errorEncountered:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
-	}
-	
-	if ([[[response rootElement] name] isEqualToString:@"init"])
-	{
-		[delegate_ handleInitialResponse:response];
-		return;
-	}
-
-	if ([delegate_ respondsToSelector:@selector(handleResponse:)])
-		[(NSObject*)delegate_ performSelectorOnMainThread:@selector(handleResponse:) withObject:response waitUntilDone:NO];
-
-	[self sendQueuedWrites];
-}
-
-#pragma mark Private
-
-/**
- * This will send a command to the debugger engine. It will append the
- * transaction ID automatically. It accepts a NSString command along with a
- * a variable number of arguments to substitute into the command, a la
- * +[NSString stringWithFormat:]. Returns the transaction ID as a NSNumber.
- */
-- (NSNumber*)sendCommandWithFormat:(NSString*)format, ...
-{
-	// Collect varargs and format command.
-	va_list args;
-	va_start(args, format);
-	NSString* command = [[NSString alloc] initWithFormat:format arguments:args];
-	va_end(args);
-	
-	NSNumber* callbackKey = [NSNumber numberWithInt:transactionID++];
-	[self send:[NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey]];
-	
-	return callbackKey;
-}
-
 /**
  * Checks if there are unsent commands in the |queuedWrites_| queue and sends
  * them if it's OK to do so. This will not block.
@@ -593,48 +651,4 @@ void SocketAcceptCallback(CFSocketRef socket,
 	[writeQueueLock_ unlock];
 }
 
-
-/**
- * Returns the transaction_id from an NSXMLDocument.
- */
-- (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response
-{
-	return [[[[response rootElement] attributeForName:@"transaction_id"] stringValue] intValue];
-}
-
-/**
- * Scans a command string for the transaction ID component. If it is not found,
- * returns NSNotFound.
- */
-- (NSInteger)transactionIDFromCommand:(NSString*)command
-{
-	NSRange occurrence = [command rangeOfString:@"-i "];
-	if (occurrence.location == NSNotFound)
-		return NSNotFound;
-	NSString* transaction = [command substringFromIndex:occurrence.location + occurrence.length];
-	return [transaction intValue];
-}
-
-/**
- * Given a file path, this returns a file:// URI and escapes any spaces for the
- * debugger engine.
- */
-- (NSString*)escapedURIPath:(NSString*)path
-{
-	// Custon GDBp paths are fine.
-	if ([[path substringToIndex:4] isEqualToString:@"gdbp"])
-		return path;
-	
-	// Create a temporary URL that will escape all the nasty characters.
-	NSURL* url = [NSURL fileURLWithPath:path];
-	NSString* urlString = [url absoluteString];
-	
-	// Remove the host because this is a file:// URL;
-	urlString = [urlString stringByReplacingOccurrencesOfString:[url host] withString:@""];
-	
-	// Escape % for use in printf-style NSString formatters.
-	urlString = [urlString stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
-	return urlString;
-}
-
 @end
-- 
2.43.5