Connect ProtocolClient to the LoggingController.
[macgdbp.git] / Source / ProtocolClient.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2013, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "ProtocolClient.h"
18
19 #import "AppDelegate.h"
20 #import "LoggingController.h"
21
22 @implementation ProtocolClient
23
24 - (id)initWithDelegate:(NSObject<ProtocolClientDelegate>*)delegate {
25 if ((self = [super init])) {
26 _delegate = delegate;
27 _delegateThread = [NSThread currentThread];
28 _lock = [[NSRecursiveLock alloc] init];
29 }
30 return self;
31 }
32
33 - (void)dealloc {
34 [_lock release];
35 [super dealloc];
36 }
37
38 - (BOOL)isConnected {
39 return [_messageQueue isConnected];
40 }
41
42 - (void)connectOnPort:(NSUInteger)port {
43 assert(!_messageQueue);
44 _messageQueue = [[MessageQueue alloc] initWithPort:port delegate:self];
45 [_messageQueue connect];
46 }
47
48 - (void)disconnect {
49 [_messageQueue disconnect];
50 }
51
52 - (NSNumber*)sendCommandWithFormat:(NSString*)format, ... {
53 // Collect varargs and format command.
54 va_list args;
55 va_start(args, format);
56 NSString* command = [[NSString alloc] initWithFormat:format arguments:args];
57 va_end(args);
58
59 NSNumber* callbackKey = [NSNumber numberWithInt:_nextID++];
60 NSString* taggedCommand = [NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey];
61
62 assert(_messageQueue);
63 [_messageQueue sendMessage:taggedCommand];
64 return callbackKey;
65 }
66
67 - (NSNumber*)sendCustomCommandWithFormat:(NSString*)format, ... {
68 // Collect varargs and format command.
69 va_list args;
70 va_start(args, format);
71 NSString* command = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
72 va_end(args);
73
74 NSNumber* callbackKey = [NSNumber numberWithInt:_nextID++];
75 NSString* taggedCommand = [command stringByReplacingOccurrencesOfString:@"{txn}"
76 withString:[callbackKey stringValue]];
77
78 [_messageQueue sendMessage:taggedCommand];
79 return callbackKey;
80 }
81
82 - (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response {
83 return [[[[response rootElement] attributeForName:@"transaction_id"] stringValue] intValue];
84 }
85
86 - (NSInteger)transactionIDFromCommand:(NSString*)command {
87 NSRange occurrence = [command rangeOfString:@"-i "];
88 if (occurrence.location == NSNotFound)
89 return NSNotFound;
90 NSString* transaction = [command substringFromIndex:occurrence.location + occurrence.length];
91 return [transaction intValue];
92 }
93
94 // MessageQueueDelegate ////////////////////////////////////////////////////////
95
96 - (void)messageQueue:(MessageQueue*)queue error:(NSError*)error {
97 NSLog(@"error = %@", error);
98 }
99
100 - (void)messageQueueDidConnect:(MessageQueue*)queue {
101 [_lock lock];
102 _nextID = 0;
103 _lastReadID = 0;
104 _lastWrittenID = 0;
105 [_lock unlock];
106
107 [_delegate debuggerEngineConnected:self];
108 }
109
110 - (void)messageQueueDidDisconnect:(MessageQueue*)queue {
111 [_messageQueue release];
112 _messageQueue = nil;
113 [_delegate debuggerEngineDisconnected:self];
114 }
115
116 // If the write stream is ready, the delegate controls whether or not the next
117 // pending message should be sent via the result of this method.
118 - (BOOL)shouldSendMessage {
119 [_lock lock];
120 BOOL r = _lastReadID >= _lastWrittenID;
121 [_lock unlock];
122 return r;
123 }
124
125 // Callback for when a message has been sent.
126 - (void)messageQueue:(MessageQueue*)queue didSendMessage:(NSString*)message
127 {
128 NSInteger tag = [self transactionIDFromCommand:message];
129 [_lock lock];
130 _lastWrittenID = tag;
131 [_lock unlock];
132
133 LoggingController* logger = [[AppDelegate instance] loggingController];
134 LogEntry* entry = [LogEntry newSendEntry:message];
135 entry.lastReadTransactionID = _lastReadID;
136 entry.lastWrittenTransactionID = _lastWrittenID;
137 [logger recordEntry:entry];
138 }
139
140 // Callback with the message content when one has been receieved.
141 - (void)messageQueue:(MessageQueue*)queue didReceiveMessage:(NSString*)message
142 {
143 LoggingController* logger = [[AppDelegate instance] loggingController];
144 LogEntry* entry = [LogEntry newReceiveEntry:message];
145 entry.lastReadTransactionID = _lastReadID;
146 entry.lastWrittenTransactionID = _lastWrittenID;
147 [logger recordEntry:entry];
148
149 // Test if we can convert it into an NSXMLDocument.
150 NSError* error = nil;
151 NSXMLDocument* xml = [[NSXMLDocument alloc] initWithXMLString:message
152 options:NSXMLDocumentTidyXML
153 error:&error];
154 if (error) {
155 [self messageQueue:queue error:error];
156 return;
157 }
158
159 // Validate the transaction.
160 NSInteger transaction = [self transactionIDFromResponse:xml];
161 if (transaction < _lastReadID) {
162 NSLog(@"Transaction #%d is out of date (lastRead = %d). Dropping packet: %@",
163 transaction, _lastReadID, message);
164 return;
165 }
166 if (transaction != _lastWrittenID) {
167 NSLog(@"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.",
168 transaction, _lastReadID, _lastWrittenID);
169 }
170
171 _lastReadID = transaction;
172 entry.lastReadTransactionID = _lastReadID;
173
174 [_delegate debuggerEngine:self receivedMessage:xml];
175 }
176
177 @end