PerforceFile.m #6

  • //
  • guest/
  • jeff_argast/
  • P4Cocoa/
  • source/
  • Perforce/
  • PerforceFile.m
  • View
  • Commits
  • Open Download .zip Download (18 KB)
/*

Copyright (C) 2002-2003, Jeffrey D. Argast.

The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose.  This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.

Permission is hereby granted to use, copy, modify, and distribute this
software or portions thereof for any purpose, without fee, subject to these
conditions:

(1) If any part of the source code is distributed, then this
statement must be included, with this copyright and no-warranty notice
unaltered.

(2) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.

*/

#import "PerforceFile.h"
#import "IconController.h"
#import "IconDefs.h"
#import "PerforceAction.h"
#import "PerforceActionSync.h"
#import "PerforceActionFstat.h"
#import "PerforceActionFilelog.h"
#import "PerforceActionEdit.h"
#import "PerforceActionRevert.h"
#import "PerforceActionDelete.h"
#import "PerforceActionPrint.h"
#import "PerforceActionDiff.h"

#import "AppUtils.h"
#import "AppDefaults.h"
#import "MessageDefs.h"
#import "RevisionsController.h"

enum 
{
    kIsHeadRevision		= 0,
    kIsOldRevision		= 1,
    kIsMissingRevision	= 2
};

static NSString* kFstatPrefixTag	= @"... ";
static NSString* kIndentTag			= @"... ... ";
static NSString* kDepotFileTag		= @"depotFile";
static NSString* kClientFileTag		= @"clientFile";
static NSString* kHeadActionTag		= @"headAction";
static NSString* kHeadTypeTag		= @"headType";
static NSString* kHeadRevTag		= @"headRev";
static NSString* kHeadChangeTag		= @"headChange";
static NSString* kHaveRevTag		= @"haveRev";
static NSString* kActionTag			= @"action";
static NSString* kChangeTag			= @"change";
static NSString* kUnresolvedTag		= @"unresolved";
static NSString* kOtherOpenTag		= @"otherOpen";
static NSString* kOtherOpenSpaceTag = @"otherOpen ";
static NSString* kOtherLockTag		= @"otherLock";
static NSString* kOurLockTag		= @"ourLock";
static NSString* kOtherActionTag	= @"otherAction";

static NSString* kDeleteStatus_Have = @"<-deleted->";
static NSString* kDeleteStatus_Old  = @"<-head rev deleted->";

static NSString* kDeleteAction		= @"delete";
static NSString* kAddAction			= @"add";
static NSString* kEditAction		= @"edit";

static NSString* kSpaceTag			= @" ";
static NSString* kNewlineTag		= @"\n";

static NSString* kZeroRev			= @"0";
static NSString* kEmptyString		= @"";

@interface PerforceFile (Private)

- (void) releaseData;

@end

@implementation PerforceFile

+ (PerforceFile*) fileWithString: (NSString*) string
{
	return [[[PerforceFile alloc] initWithString: string] autorelease];
}

- (id)  initWithString: (NSString*) string
{
	NSArray* split = [string componentsSeparatedByString: kNewlineTag];
	
	NSMutableDictionary*	fileDictionary		= [NSMutableDictionary dictionaryWithCapacity:[split count]];
	NSMutableArray*			otherOpenArray		= [NSMutableArray array];
	NSMutableArray*			otherActionArray	= [NSMutableArray array];
	
	NSString* keyString;
	NSString* valueString;
	
	NSString* s;
	NSEnumerator* e = [split objectEnumerator];
	
	while ( s = [e nextObject] )
	{
		NSScanner* scanner = [NSScanner scannerWithString: s];
		
		// First check for the indent
		if ( [scanner scanString:kIndentTag intoString:nil] )
		{
			// we only care about otherOpen# where # is one or more digits
			if ( ![scanner scanString:kOtherOpenSpaceTag intoString:nil] )
			{
				NSMutableArray* targetArray = otherOpenArray;
				
				if ( [scanner scanString:kOtherActionTag intoString:nil] )
				{
					targetArray = otherActionArray;
				}

				[scanner scanUpToString:kSpaceTag intoString:nil];
				[scanner scanString:kSpaceTag intoString:nil];
				[scanner scanUpToString:kNewlineTag intoString:&valueString];
				
				[targetArray addObject:valueString];
			}
		}
		else if ( [scanner scanString:kFstatPrefixTag intoString:nil] )
		{
			[scanner scanUpToString:kSpaceTag intoString:&keyString];
			[scanner scanString:kSpaceTag intoString:nil];
			[scanner scanUpToString:kNewlineTag intoString:&valueString];
			
			[fileDictionary setObject:valueString forKey:keyString];
		}
		
	}		
	
    return [self initWithDict:fileDictionary otherOpen:otherOpenArray otherAction:otherActionArray];
}

- (id) initWithDict: (NSDictionary*) fileDictionary
       otherOpen: (NSArray*) otherOpenArray
	   otherAction: (NSArray*) otherActionArray
{
    BOOL otherEdit   = NO;
    BOOL otherDelete = NO;
    
    NSMutableString* displayString = nil;

	int revType = 0;

	NSString* valueString = nil;

	self = [super init];
		
    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kDepotFileTag];
	
	if ( valueString )
	{
		fDepotPath = [valueString retain];
	}
    else
    {
        fDepotPath = [[NSString alloc] initWithString:kEmptyString];
    }

    fFileName = ExtractLastPathComponent (fDepotPath);
        
    [fFileName retain];

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kClientFileTag];
	
	if ( valueString )
	{
		fClientPath = [valueString retain];
	}
    else
    {
        fClientPath = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kHeadActionTag];
	
	if ( valueString )
	{
		fHeadAction = [valueString retain];
	}
    else
    {
        fHeadAction = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kHeadTypeTag];
	
	if ( valueString )
	{
		fHeadType = [valueString retain];
	}
    else
    {
        fHeadType = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kHeadRevTag];
	
	if ( valueString )
	{
		fHeadRev = [valueString retain];
	}
    else
    {
        fHeadRev = [[NSString alloc] initWithString:kEmptyString]; 
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kHeadChangeTag];
	
	if ( valueString )
	{
		fHeadChange = [valueString retain];
	}
    else
    {
        fHeadChange = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kHaveRevTag];
	
	if ( valueString )
	{
		fHaveRev = [valueString retain];
	}
    else
    {
        fHaveRev = [[NSString alloc] initWithString:kZeroRev]; // NOTE: the zero instead of empty string
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kActionTag];
	
	if ( valueString )
	{
		fAction = [valueString retain];
	}
    else
    {
        fAction = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kChangeTag];
	
	if ( valueString )
	{
		fChange = [valueString retain];
	}
    else
    {
        fChange = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kUnresolvedTag];
	
	if ( valueString )
	{
		fUnresolved = [valueString retain];
	}
    else
    {
        fUnresolved = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kOtherOpenTag];
	
	if ( valueString )
	{
		fOtherOpen = [valueString retain];
	}
    else
    {
        fOtherOpen = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kOtherLockTag];
	
	if ( valueString )
	{
		fOtherLock = [valueString retain];
	}
    else
    {
        fOtherLock = [[NSString alloc] initWithString:kEmptyString];
    }

    ///////////////////////////////
	valueString = [fileDictionary objectForKey:kOurLockTag];
	
	if ( valueString )
	{
		fOurLock = [valueString retain];
	}
    else
    {
        fOurLock = [[NSString alloc] initWithString:kEmptyString];
    }

	fOtherUsers = [otherOpenArray retain];
	
	fOtherActions = [otherActionArray retain];
	

    // Is this file equal to head, out of date, or not on clients machine?
    if ( [fHaveRev isEqualToString:fHeadRev] )
    {
        revType = kIsHeadRevision;
    }
    else if ( [fHaveRev isEqualToString:kZeroRev] )
    {
        revType = kIsMissingRevision;
    }
    else
    {
        revType = kIsOldRevision;
    }

    // Build the display string
    displayString = [[NSMutableString alloc] initWithString:fFileName];
        
    [displayString appendString:@" #"];
        
    [displayString appendString:fHaveRev];
        
    [displayString appendString:@"/"];
        
    [displayString appendString:fHeadRev];
        
    [displayString appendString:@" <"];
        
    [displayString appendString:fHeadType];
        
    [displayString appendString:@"> "];
        
    if ( [fHeadAction isEqualToString:kDeleteAction] )
    {
        if ( revType == kIsOldRevision )
        {
            [displayString appendString:kDeleteStatus_Old];
        }
        else
        {
            [displayString appendString:kDeleteStatus_Have];
        }
    }
        
    // force into an NSString so that later when we return it
    // it is faster (assuming Cocoa implemented NSString copy
    // as a simple retain)
    fDisplayString = [[NSString alloc] initWithCString:[displayString cString]];
    
    if ( fOtherActions )
    {
        int numActions = [fOtherActions count];
        int n;
        
        for ( n = 0; (n < numActions) && !(otherEdit && otherDelete); n++ )
        {
            NSString* otherAction = [fOtherActions objectAtIndex:n];
            
            if ( !otherEdit && [otherAction isEqualTo:kEditAction] )
            {
                otherEdit = YES;
            }
            
            if ( !otherDelete && [otherAction isEqualTo:kDeleteAction] )
            {
                otherDelete = YES;
            }
        }
        
    }
    
	int iconID = 0;
	
    // Figure out icon id
    if ( revType == kIsHeadRevision )
    {
        if ( [fAction isEqualTo:kEditAction] )
        {
            if ( otherDelete )
            {
                iconID = kHaveFileOurLockOtherDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kHaveFileBothLockIcon;
            }
            else
            {
                iconID = kHaveFileOurLockIcon;
            }
        }
        else if ( [fAction isEqualTo:kDeleteAction] )
        {
            if ( otherDelete )
            {
                iconID = kHaveFileBothDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kHaveFileOurDeleteOtherLockIcon;
            }
            else
            {
                iconID = kHaveFileDeleteIcon;
            }
        }
        else
        {
            if ( otherDelete )
            {
                iconID = kHaveFileOtherDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kHaveFileOtherLockIcon;
            }
            else
            {
                iconID = kHaveFileIcon;
            }
        }
    }
    else if ( revType == kIsOldRevision )
    {
        if ( [fAction isEqualTo:kEditAction] )
        {
            if ( otherDelete )
            {
                iconID = kOldFileOurLockOtherDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kOldFileBothLockIcon;
            }
            else
            {
                iconID = kOldFileOurLockIcon;
            }
        }
        else if ( [fAction isEqualTo:kDeleteAction] )
        {
            if ( otherDelete )
            {
                iconID = kOldFileBothDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kOldFileOurDeleteOtherLockIcon;
            }
            else
            {
                iconID = kOldFileDeleteIcon;
            }
        }
        else
        {
            if ( otherDelete )
            {
                iconID = kOldFileOtherDeleteIcon;
            }
            else if ( otherEdit )
            {
                iconID = kOldFileOtherLockIcon;
            }
            else
            {
                iconID = kOldFileIcon;
            }
        }
    }
    else
    {
        if ( otherDelete )
        {
            iconID = kMissingFileOtherDeleteIcon;
        }
        else if ( otherEdit )
        {
            iconID = kMissingFileOtherLockIcon;
        }
        else
        {
            iconID = kMissingFileIcon;
        }
    }

	
    IconController* iconController = [IconController defaultIconController];

    fIcon = [[iconController getIcon:iconID] retain];
	
	return self;
}

- (void) copyFrom: (PerforceFile*) fromFile
{
    [self releaseData];
    
    fFileName		= [fromFile->fFileName retain];
    fDepotPath		= [fromFile->fDepotPath retain];
    fClientPath		= [fromFile->fClientPath retain];
    fHeadAction		= [fromFile->fHeadAction retain];
    fHeadType		= [fromFile->fHeadType retain];
    fHeadRev		= [fromFile->fHeadRev retain];
    fHeadChange		= [fromFile->fHeadChange retain];
    fHaveRev		= [fromFile->fHaveRev retain];
    
    fAction			= [fromFile->fAction retain];
    fChange			= [fromFile->fChange retain];
    fUnresolved		= [fromFile->fUnresolved retain];
    fOtherOpen		= [fromFile->fOtherOpen retain];
    fOtherLock		= [fromFile->fOtherLock retain];
    fOurLock		= [fromFile->fOurLock retain];
    
    fOtherUsers		= [fromFile->fOtherUsers retain];
    fOtherActions	= [fromFile->fOtherActions retain];

    fDisplayString	= [fromFile->fDisplayString retain];
    
    fIcon			= [fromFile->fIcon retain];
}


- (void) dealloc 
{	
    [self releaseData];
    
    [super dealloc];
}

- (BOOL) isDeleted
{
	if ( [fHeadAction isEqualToString:kDeleteAction] && [fHaveRev isEqualToString:kZeroRev] )
	{
		return YES;
	}
	
	return NO;
}

- (BOOL) isAdd
{
	return [fAction isEqualToString:kAddAction];
}

- (int)	getNumberOfChildren
{
    return 0;
}

- (BOOL) hasChildren
{
    return NO;
}

- (id) getChildAtIndex: (int) index
{
    return nil;
}

- (id) getCellText
{
    return fDisplayString;
}

- (id) getCellImage
{
	return fIcon;
}

//
// Perforce Protocol
//

//
// data
//

//
// Enablement
//
- (BOOL) canSyncToHead
{
    if ( [fHaveRev isEqualTo:fHeadRev] )
    {
        return NO;
    }
    
    if ( [fHeadAction isEqualTo:kDeleteAction] && [fHaveRev isEqualTo:kZeroRev] )
    {
        return NO;
    }
    
    return YES;
}

- (BOOL) canSyncToNone
{
    if ( ![fAction isEqualTo:kEmptyString] )
    {
        return NO;
    }
    
    if ( [fHaveRev isEqualTo:kZeroRev] )
    {
        return NO;
    }
    
    return YES;
}

- (BOOL) canEdit
{
    if ( ![fAction isEqualTo:kEmptyString] )
    {
        return NO;
    }
    
    if ( [fHaveRev isEqualTo:kZeroRev] )
    {
        return NO;
    }
    
    return YES;
}

- (BOOL) canDelete
{
    if ( ![fAction isEqualTo:kEmptyString] )
    {
        return NO;
    }
    
    if ( [fHaveRev isEqualTo:kZeroRev] )
    {
        return NO;
    }
    
    return YES;
}

- (BOOL) canRevert
{
    if ( ![fAction isEqualTo:kEmptyString] )
    {
        return YES;
    }

    return NO;
}

- (BOOL) canDiff
{
    if ( [fAction isEqualTo:kEditAction] )
    {
        return YES;
    }
    
    return NO;
}

- (BOOL) canGetRevisions
{
    return YES;
}

- (BOOL) canViewInEditor
{
    if ( [fHaveRev isEqualTo:kZeroRev] && [fHeadAction isEqualTo:kDeleteAction] )
    {
        return NO;
    }
    
    return YES;
}


//
// Actions
//
- (void) resultFromPerforceFstat: (PerforceActionFstat*) action
{
	if ( [action wasSuccess] )
	{
		[self copyFrom:[[action getFiles] objectAtIndex:0]];

		SendNotificationWithObject (kDepotControllerChildChanged, self);
	}
}

- (void) resultFromActionDoFstat: (id) nothing
{	
	[PerforceActionFstat defaultRunFor:self selector:@selector(resultFromPerforceFstat:) withPath:fDepotPath includeDeletes:YES];

}

- (void) resultFromPerforceFilelog: (PerforceActionFilelog*) action
{
	if ( [action wasSuccess] )
	{
		[RevisionsController showWithRevisions:[action getRevisions]];
	}
}

- (void) showRevisionHistory
{
	[PerforceActionFilelog defaultRunFor:self selector:@selector(resultFromPerforceFilelog:) withPath:fDepotPath];	
}

- (void) resultFromActionDoDiff: (NSString*) outputPath
{
	DiffFiles (fClientPath, outputPath);
}

- (void) diff
{
	[PerforceActionDiff depotPath:fDepotPath];
}

- (void) viewFromTempFile
{
	NSString* fileName = GetTempFileName (fFileName, fHeadRev);
	
	[PerforceActionPrint defaultRunFor:self 
						 selector:@selector(viewAfterPrint:) 
						 withPath:fDepotPath
						 outputPath:fileName];
}

- (void) viewInEditor
{
    if ( [fHaveRev isEqualTo:kZeroRev] )
    {
		[self viewFromTempFile];
    }
	else
	{	
		ViewFileInEditor (fClientPath);
	}
}

- (NSString*) getPasteboardData
{
	return fDepotPath;
}


- (void) viewAfterPrint:(PerforceActionPrint*) action
{
	if ( [action wasSuccess] )
	{
		ViewFileInEditor ([action getOutputPath]);
	}
}

- (void) pathChanged:(NSString*)depotPath theFile:(PerforceFile**)theFile
{
	if ( [depotPath isEqualToString:fDepotPath] )
	{
		*theFile = self;
	}
}

- (NSString*) getDepotPath
{
	return fDepotPath;
}

- (NSString*) getPerforceActionPath
{
	return fDepotPath;
}

- (void) expandPath:(NSString*)depotPath
{
	// do nothing
}

@end

@implementation PerforceFile(Private)

- (void) releaseData
{
    [fFileName release];    
    [fDepotPath release];    
    [fClientPath release];    
    [fHeadAction release];    
    [fHeadType release];    
    [fHeadRev release];    
    [fHeadChange release];    
    [fHaveRev release];    
    [fAction release];    
    [fChange release];    
    [fUnresolved release];    
    [fOtherOpen release];    
    [fOtherUsers release];
    [fOtherActions release];
    [fOtherLock release];    
    [fOurLock release];    
    [fDisplayString release];  
	[fIcon release];  
}

@end


# Change User Description Committed
#9 4277 Jeff Argast Fixed two bugs and updated version to 0.23
#8 4225 Jeff Argast Added resolve support, reveal in finder, drag and drop edit,
show local files, and showing added files.
#7 3980 Jeff Argast Added distributed protocol
#6 3941 Jeff Argast Simplified fstat, branches, and job parsing.
#5 3134 Jeff Argast Added copy/paste support in the depot view
Added expand path to depot view
Added bookmarks
#4 3113 Jeff Argast Reduced the times the depot view completely collapses.
Now it won't collapse on refresh views or submit, but
still collapses when the defaults change.
#3 3111 Jeff Argast Made multiple selection smarter by operating on the entire selection as an atomic
operation with the server. Also partially fixed the read only window to not wrap
at the window boundary.  I did the same for the editable window, but now the problem
appears to be that p4 change -o is breaking its output at some character location
before the string gets into the editor (at least I think that is the problem).
#2 2741 Jeff Argast Fixed viewing files that are not in client
#1 2732 Jeff Argast Initial submission of P4Cocoa