// // P4Port.m // P4ObjectLayer // // Created by Michael Bishop on 9/1/10. // Copyright 2010 Numerical Garden, LLC. All rights reserved. // #import "P4ClientApi.h" #import "P4Port.h" #import "P4ConnectionPool.h" #import "P4Response.h" #import "NGAReachability.h" #import "NGAUtilities.h" @interface P4Port () -(void)startReachability; +(NSMutableDictionary*)ports; @property (nonatomic,readwrite,copy) NSString * GMTOffsetString; @property (nonatomic,readwrite,copy) NSDictionary * info; @property (nonatomic,readwrite,assign) BOOL reachable; @property (nonatomic,readwrite,copy) NSString * portString; @property (nonatomic,readonly) NSString * remoteHost; @property (nonatomic,readwrite,retain) NSError * lastError; @property (readwrite,assign) BOOL refreshing; @end @implementation P4Port @synthesize info = _info , portString = _portString , reachable = _reachable , GMTOffsetString = _GMTOffsetString , lastError = _lastError , refreshing = _isRefreshing ; + (NSMutableDictionary*)ports { static dispatch_once_t pred; static NSMutableDictionary *sharedInstance = nil; dispatch_once(&pred, ^{ sharedInstance = [[NSMutableDictionary allocWithZone:NULL] init]; }); return sharedInstance; } -(P4Port*)initWithP4PortString:(NSString *)p4port { if ( (self = [super init]) == nil ) return nil; self.portString = p4port; [self startReachability]; _completionBlocksForRefresh = [[NSMutableArray alloc] init]; [self registerAllAutoObservationMethods ]; return self; } -(void)startReachability { DECLARE_UNRETAINED_SELF(P4Port*); [_reachability release]; _reachability = [[NGAReachability alloc] initWithName:unretainedSelf.remoteHost]; [_reachability startWithBlock:^(SCNetworkReachabilityFlags flags) { unretainedSelf.reachable = [_reachability isReachableWithoutRequiringConnection:flags]; }]; } DEALLOC( [self unregisterAllAutoObservationMethods]; RELEASE( _portString ); RELEASE( _info ); RELEASE( _completionBlocksForRefresh ); RELEASE( _reachability ); ) -(NSString*)description { return [NSString stringWithFormat:@"P4Port '%@'", _portString]; } +(P4Port*)defaultPort { return [P4Port portWithPortString:nil]; } +(P4Port*)portWithPortString:(NSString *)p4port { // P4SpecManagers are retained by the application as a singleton P4Port * p4portInstance = nil; NSMutableDictionary * ports = [P4Port ports]; @synchronized(ports) { p4portInstance = [ports objectForKey:p4port]; if ( !p4portInstance ) { p4portInstance = [[[P4Port alloc] initWithP4PortString:(p4port ? p4port : [P4RawConnection defaultP4Port])] autorelease]; [ports setObject:p4portInstance forKey:[p4portInstance portString]]; } } return p4portInstance; } -(BOOL)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context updateBlock:(UpdateBlock)update completionBlock:(void(^)(P4Response*))completion { return [self runArguments:arguments withContext:context content:nil updateBlock:update completionBlock:completion]; } -(BOOL)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context content:(NSString*)content updateBlock:(UpdateBlock)update completionBlock:(void(^)(P4Response*))completion { if ( !self.reachable ) return NO; return [[P4ConnectionPool sharedPool] runArguments:arguments onPort:self.portString withContext:context content:content updateBlock:update completionBlock:completion]; } -(P4Response*)runArguments:(NSArray*)arguments withContext:(NSDictionary*)context content:(NSString*)content { return [[P4ConnectionPool sharedPool] runArguments:arguments onPort:self.portString withContext:context content:content]; } -(NSString*)remoteHost { NSRange prefix = [self.portString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@":"]]; if ( prefix.location == NSNotFound ) return nil; if ( prefix.location == 0 ) return @"perforce"; prefix.length = prefix.location; prefix.location = 0; return [self.portString substringWithRange:prefix]; } -(IBAction)refresh:(id)sender { if ( self.refreshing ) return; DECLARE_UNRETAINED_SELF(P4Port*); self.refreshing = [self runArguments:[NSArray arrayWithObject:@"info"] withContext:nil updateBlock:nil completionBlock: ^(P4Response * response) { unretainedSelf.info = response.result; unretainedSelf.lastError = response.error; unretainedSelf.refreshing = NO; NSArray * completionBlocks = nil; @synchronized(_completionBlocksForRefresh) { completionBlocks = [_completionBlocksForRefresh copy]; [_completionBlocksForRefresh removeAllObjects]; } for (PortRefreshCompletionBlock completion in completionBlocks) completion( response.error ); [completionBlocks release]; }]; } -(void)refreshWithCompletion:(PortRefreshCompletionBlock)completion { @synchronized(_completionBlocksForRefresh) { [_completionBlocksForRefresh addObject:[[completion copy] autorelease]]; } // No one else has refreshed this yet if ( [_completionBlocksForRefresh count] == 1 ) [self refresh:self]; } // SAMPLE OUTPUT //... userName mbishop //... clientName *unknown* //... clientCwd /Users/mbishop/dev/mercurial_repo //... clientHost MichaelBook.local //... clientAddress 127.0.0.1:50042 //... serverName Play //... serverDescription Just for fun and play to test against. //... serverAddress localhost:1999 //... serverRoot /Users/Shared/Perforce Server Data/localhost_1999 //... serverDate 2010/09/01 13:37:19 -0400 EDT //... serverUptime 03:16:26 //... serverVersion P4D/DARWIN80U/2010.1/251161 (2010/06/16) //... serverLicense none //... caseHandling insensitive -(NSDictionary*)info { if ( !_info ) [self refresh:self]; return _info; } -(void)valueDidChangeForInfo { // @"2010/09/01 13:37:19 -0400 EDT" NSString * serverDate = [self.info objectForKey:@"serverDate"]; if ( !serverDate ) { self.GMTOffsetString = nil; return; } // 2010/09/01 // 13:37:19 // -0400 // EDT NSArray * dateComponents = [serverDate componentsSeparatedByString:@" "]; self.GMTOffsetString = [dateComponents objectAtIndex:2]; } -(void)valueDidChangeForReachable:(NSDictionary*)change { BOOL newValue = [[change valueForKey:NSKeyValueChangeNewKey] boolValue]; if ( newValue == YES ) [self refresh:self]; // LOG_DEBUG( @"%@ %@ reachable", self, newValue ? @"is" : @"is NOT" ); } -(NSString*)GMTOffsetString { return [[_GMTOffsetString retain] autorelease]; } @end