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 [_messageQueue sendMessage
:taggedCommand
];
67 - (NSNumber
*)sendCustomCommandWithFormat
:(NSString
*)format
, ...
{
68 // Collect varargs and format command.
70 va_start(args
, format
);
71 NSString
* command
= [[[NSString alloc
] initWithFormat
:format arguments
:args
] autorelease
];
74 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
75 NSString
* taggedCommand
= [command stringByReplacingOccurrencesOfString
:@
"{txn}"
76 withString
:[callbackKey stringValue
]];
78 [_messageQueue sendMessage
:taggedCommand
];
82 - (NSInteger
)transactionIDFromResponse
:(NSXMLDocument
*)response
{
83 return [[[[response rootElement
] attributeForName
:@
"transaction_id"] stringValue
] intValue
];
86 - (NSInteger
)transactionIDFromCommand
:(NSString
*)command
{
87 NSRange occurrence
= [command rangeOfString
:@
"-i "];
88 if (occurrence.location
== NSNotFound
)
90 NSString
* transaction
= [command substringFromIndex
:occurrence.location
+ occurrence.length
];
91 return [transaction intValue
];
94 // MessageQueueDelegate ////////////////////////////////////////////////////////
96 - (void)messageQueueError
:(NSError
*)error
{
97 NSLog(@
"error = %@", error
);
100 - (void)clientDidConnect
:(MessageQueue
*)queue
{
107 [_delegate performSelector
:@selector(debuggerEngineConnected
:)
108 onThread
:_delegateThread
113 - (void)clientDidDisconnect
:(MessageQueue
*)queue
{
114 [_delegate performSelector
:@selector(debuggerEngineDisconnected
:)
115 onThread
:_delegateThread
120 // If the write stream is ready, the delegate controls whether or not the next
121 // pending message should be sent via the result of this method.
122 - (BOOL)shouldSendMessage
{
124 BOOL r
= _lastReadID
>= _lastWrittenID
;
129 // Callback for when a message has been sent.
130 - (void)didSendMessage
:(NSString
*)message
{
131 NSInteger tag
= [self transactionIDFromCommand
:message
];
133 _lastWrittenID
= tag
;
137 // Callback with the message content when one has been receieved.
138 - (void)didReceiveMessage
:(NSString
*)message
{
139 // Test if we can convert it into an NSXMLDocument.
140 NSError
* error
= nil;
141 NSXMLDocument
* xml
= [[NSXMLDocument alloc
] initWithXMLString
:message
142 options
:NSXMLDocumentTidyXML
145 [self messageQueueError
:error
];
149 // Validate the transaction.
150 NSInteger transaction
= [self transactionIDFromResponse
:xml
];
151 if (transaction
< _lastReadID
) {
152 NSLog(@
"Transaction #%d is out of date (lastRead = %d). Dropping packet: %@",
153 transaction
, _lastReadID
, message
);
156 if (transaction
!= _lastWrittenID
) {
157 NSLog(@
"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.",
158 transaction
, _lastReadID
, _lastWrittenID
);
161 _lastReadID
= transaction
;
163 [self performSelector
:@selector(postReceivedMessage
:)
164 onThread
:_delegateThread
169 // Private /////////////////////////////////////////////////////////////////////
171 - (void)postReceivedMessage
:(NSXMLDocument
*)message
{
172 [_delegate debuggerEngine
:self receivedMessage
:message
];