Perform -postReceivedMessage: only in the default mode.
[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 @interface ProtocolClient (Private)
20 - (void)postReceivedMessage:(NSXMLDocument*)message;
21 @end
22
23 @implementation ProtocolClient
24
25 - (id)initWithDelegate:(NSObject<ProtocolClientDelegate>*)delegate {
26 if ((self = [super init])) {
27 _delegate = delegate;
28 _delegateThread = [NSThread currentThread];
29 _lock = [[NSRecursiveLock alloc] init];
30 }
31 return self;
32 }
33
34 - (void)dealloc {
35 [_lock release];
36 [super dealloc];
37 }
38
39 - (BOOL)isConnected {
40 return [_messageQueue isConnected];
41 }
42
43 - (void)connectOnPort:(NSUInteger)port {
44 assert(!_messageQueue);
45 _messageQueue = [[MessageQueue alloc] initWithPort:port delegate:self];
46 [_messageQueue connect];
47 }
48
49 - (void)disconnect {
50 [_messageQueue disconnect];
51 }
52
53 - (NSNumber*)sendCommandWithFormat:(NSString*)format, ... {
54 // Collect varargs and format command.
55 va_list args;
56 va_start(args, format);
57 NSString* command = [[NSString alloc] initWithFormat:format arguments:args];
58 va_end(args);
59
60 NSNumber* callbackKey = [NSNumber numberWithInt:_nextID++];
61 NSString* taggedCommand = [NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey];
62
63 assert(_messageQueue);
64 [_messageQueue sendMessage:taggedCommand];
65 return callbackKey;
66 }
67
68 - (NSNumber*)sendCustomCommandWithFormat:(NSString*)format, ... {
69 // Collect varargs and format command.
70 va_list args;
71 va_start(args, format);
72 NSString* command = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
73 va_end(args);
74
75 NSNumber* callbackKey = [NSNumber numberWithInt:_nextID++];
76 NSString* taggedCommand = [command stringByReplacingOccurrencesOfString:@"{txn}"
77 withString:[callbackKey stringValue]];
78
79 [_messageQueue sendMessage:taggedCommand];
80 return callbackKey;
81 }
82
83 - (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response {
84 return [[[[response rootElement] attributeForName:@"transaction_id"] stringValue] intValue];
85 }
86
87 - (NSInteger)transactionIDFromCommand:(NSString*)command {
88 NSRange occurrence = [command rangeOfString:@"-i "];
89 if (occurrence.location == NSNotFound)
90 return NSNotFound;
91 NSString* transaction = [command substringFromIndex:occurrence.location + occurrence.length];
92 return [transaction intValue];
93 }
94
95 // MessageQueueDelegate ////////////////////////////////////////////////////////
96
97 - (void)messageQueueError:(NSError*)error {
98 NSLog(@"error = %@", error);
99 }
100
101 - (void)clientDidConnect:(MessageQueue*)queue {
102 [_lock lock];
103 _nextID = 0;
104 _lastReadID = 0;
105 _lastWrittenID = 0;
106 [_lock unlock];
107
108 [_delegate performSelector:@selector(debuggerEngineConnected:)
109 onThread:_delegateThread
110 withObject:self
111 waitUntilDone:NO];
112 }
113
114 - (void)clientDidDisconnect:(MessageQueue*)queue {
115 [_delegate performSelector:@selector(debuggerEngineDisconnected:)
116 onThread:_delegateThread
117 withObject:self
118 waitUntilDone:NO];
119 }
120
121 // If the write stream is ready, the delegate controls whether or not the next
122 // pending message should be sent via the result of this method.
123 - (BOOL)shouldSendMessage {
124 [_lock lock];
125 BOOL r = _lastReadID >= _lastWrittenID;
126 [_lock unlock];
127 return r;
128 }
129
130 // Callback for when a message has been sent.
131 - (void)didSendMessage:(NSString*)message {
132 NSInteger tag = [self transactionIDFromCommand:message];
133 [_lock lock];
134 _lastWrittenID = tag;
135 [_lock unlock];
136 }
137
138 // Callback with the message content when one has been receieved.
139 - (void)didReceiveMessage:(NSString*)message {
140 // Test if we can convert it into an NSXMLDocument.
141 NSError* error = nil;
142 NSXMLDocument* xml = [[NSXMLDocument alloc] initWithXMLString:message
143 options:NSXMLDocumentTidyXML
144 error:&error];
145 if (error) {
146 [self messageQueueError:error];
147 return;
148 }
149
150 // Validate the transaction.
151 NSInteger transaction = [self transactionIDFromResponse:xml];
152 if (transaction < _lastReadID) {
153 NSLog(@"Transaction #%d is out of date (lastRead = %d). Dropping packet: %@",
154 transaction, _lastReadID, message);
155 return;
156 }
157 if (transaction != _lastWrittenID) {
158 NSLog(@"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.",
159 transaction, _lastReadID, _lastWrittenID);
160 }
161
162 _lastReadID = transaction;
163
164 [self performSelector:@selector(postReceivedMessage:)
165 onThread:_delegateThread
166 withObject:xml
167 waitUntilDone:NO
168 modes:@[ NSDefaultRunLoopMode ]];
169 }
170
171 // Private /////////////////////////////////////////////////////////////////////
172
173 - (void)postReceivedMessage:(NSXMLDocument*)message {
174 [_delegate debuggerEngine:self receivedMessage:message];
175 }
176
177 @end