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