3 * Copyright (c) 2013, Blue Static <http://www.bluestatic.org>
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.
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.
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
17 #import "ProtocolClient.h"
19 @interface ProtocolClient (Private
)
20 - (void)postReceivedMessage
:(NSXMLDocument
*)message
;
23 @implementation ProtocolClient
25 - (id)initWithDelegate
:(NSObject
<ProtocolClientDelegate
>*)delegate
{
26 if ((self = [super init
])) {
28 _delegateThread
= [NSThread currentThread
];
29 _lock
= [[NSRecursiveLock alloc
] init
];
40 return [_messageQueue isConnected
];
43 - (void)connectOnPort
:(NSUInteger
)port
{
44 assert(!_messageQueue
);
45 _messageQueue
= [[MessageQueue alloc
] initWithPort
:port delegate
:self];
46 [_messageQueue connect
];
50 [_messageQueue disconnect
];
53 - (NSNumber
*)sendCommandWithFormat
:(NSString
*)format
, ...
{
54 // Collect varargs and format command.
56 va_start(args
, format
);
57 NSString
* command
= [[NSString alloc
] initWithFormat
:format arguments
:args
];
60 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
61 NSString
* taggedCommand
= [NSString stringWithFormat
:@
"%@ -i %@", [command autorelease
], callbackKey
];
63 assert(_messageQueue
);
64 [_messageQueue sendMessage
:taggedCommand
];
68 - (NSNumber
*)sendCustomCommandWithFormat
:(NSString
*)format
, ...
{
69 // Collect varargs and format command.
71 va_start(args
, format
);
72 NSString
* command
= [[[NSString alloc
] initWithFormat
:format arguments
:args
] autorelease
];
75 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
76 NSString
* taggedCommand
= [command stringByReplacingOccurrencesOfString
:@
"{txn}"
77 withString
:[callbackKey stringValue
]];
79 [_messageQueue sendMessage
:taggedCommand
];
83 - (NSInteger
)transactionIDFromResponse
:(NSXMLDocument
*)response
{
84 return [[[[response rootElement
] attributeForName
:@
"transaction_id"] stringValue
] intValue
];
87 - (NSInteger
)transactionIDFromCommand
:(NSString
*)command
{
88 NSRange occurrence
= [command rangeOfString
:@
"-i "];
89 if (occurrence.location
== NSNotFound
)
91 NSString
* transaction
= [command substringFromIndex
:occurrence.location
+ occurrence.length
];
92 return [transaction intValue
];
95 // MessageQueueDelegate ////////////////////////////////////////////////////////
97 - (void)messageQueueError
:(NSError
*)error
{
98 NSLog(@
"error = %@", error
);
101 - (void)clientDidConnect
:(MessageQueue
*)queue
{
108 [_delegate performSelector
:@selector(debuggerEngineConnected
:)
109 onThread
:_delegateThread
114 - (void)clientDidDisconnect
:(MessageQueue
*)queue
{
115 [_delegate performSelector
:@selector(debuggerEngineDisconnected
:)
116 onThread
:_delegateThread
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
{
125 BOOL r
= _lastReadID
>= _lastWrittenID
;
130 // Callback for when a message has been sent.
131 - (void)didSendMessage
:(NSString
*)message
{
132 NSInteger tag
= [self transactionIDFromCommand
:message
];
134 _lastWrittenID
= tag
;
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
146 [self messageQueueError
:error
];
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
);
157 if (transaction
!= _lastWrittenID
) {
158 NSLog(@
"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.",
159 transaction
, _lastReadID
, _lastWrittenID
);
162 _lastReadID
= transaction
;
164 [self performSelector
:@selector(postReceivedMessage
:)
165 onThread
:_delegateThread
168 modes
:@
[ NSDefaultRunLoopMode
]];
171 // Private /////////////////////////////////////////////////////////////////////
173 - (void)postReceivedMessage
:(NSXMLDocument
*)message
{
174 [_delegate debuggerEngine
:self receivedMessage
:message
];