Skip to content

Instantly share code, notes, and snippets.

@dasto
Created December 9, 2010 17:53
Show Gist options
  • Save dasto/735057 to your computer and use it in GitHub Desktop.
Save dasto/735057 to your computer and use it in GitHub Desktop.
proposal for overhaul of cappuccinos document architecture
@implementation CPOverhauledDocument : CPDocument
{
id _documentPersistenceAgent;
Object _persistenceOperationInfo;
}
/*
-> this might be the right place to obtain _documentPersistenceAgent based on document type
-> uses the base class to determine the right class
*/
- (void)setFileType:(CPString)aType
{
[super setFileType:aType];
_documentPersistenceAgent = [[[CPDocumentPersistenceAgent persistenceAgentClassForFileType:aType] alloc] initWithDocument:self];
}
/*
-> sending CPDocumentWillSaveNotification has moved to "saveToURL:..."
*/
- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
{
if (_fileURL)
{
//[[CPNotificationCenter defaultCenter]
// postNotificationName:CPDocumentWillSaveNotification
// object:self];
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
}
else
[self _saveDocumentAsWithDelegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
}
/*
-> instead of using a fixed save panel, retrieve it from _documentPersistenceAgent
-> fixed missing delegate message in CPDocument when savePanel is aborted
-> sending CPDocumentWillSaveNotification has moved to "saveToURL:..."
*/
- (void)_saveDocumentAsWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
{
//var savePanel = [CPSavePanel savePanel],
var savePanel = [_documentPersistenceAgent savePanel],
response = [savePanel runModal];
if (!response)
{
objj_msgSend(delegate, didSaveSelector, self, NO, contextInfo);
return;
}
//if (!response)
// return;
var saveURL = [savePanel URL];
//[[CPNotificationCenter defaultCenter]
// postNotificationName:CPDocumentWillSaveNotification
// object:self];
[self saveToURL:saveURL ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
}
/*
-> instead of executing the persistence operation itself delegate to _documentPersistenceAgent!
-> only allow one persistence operation at a time! (maybe not strictly necessary)
-> do not update change count in advance, instead cache the current value and update after the operation has succeeded!
-> sending CPDocumentWillSaveNotification has moved here (but only if it definitely gets executed)
*/
- (void)saveToURL:(CPURL)anAbsoluteURL ofType:(CPString)aType forSaveOperation:(CPSaveOperationType)aSaveOperation delegate:(id)aDelegate didSaveSelector:(SEL)aDidSaveSelector contextInfo:(id)aContextInfo
{
if(_persistenceOperationInfo)
objj_msgSend(aDelegate, aDidSaveSelector, self, NO, aContextInfo);
else
{
_persistenceOperationInfo = { operationURL:anAbsoluteURL, delegate:aDelegate, finishSelector:aDidSaveSelector, context:aContextInfo, operationType:aSaveOperation, operationChangeCount:_changeCount };
[self _sendDocumentWillSaveNotification];
[_documentPersistenceAgent saveDocumentToURL:anAbsoluteURL ofType:aType forSaveOperation:aSaveOperation];
}
/*
var data = [self dataOfType:[self fileType] error:nil],
oldChangeCount = _changeCount;
_writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
// FIXME: THIS IS WRONG! We need a way to decide
if ([CPPlatform isBrowser])
[_writeRequest setHTTPMethod:@"POST"];
else
[_writeRequest setHTTPMethod:@"PUT"];
[_writeRequest setHTTPBody:[data rawString]];
[_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
if (aSaveOperation === CPSaveOperation)
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
if (aSaveOperation !== CPSaveToOperation)
[self updateChangeCount:CPChangeCleared];
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
connection.session = _CPSaveSessionMake(anAbsoluteURL, aSaveOperation, oldChangeCount, aDelegate, aDidSaveSelector, aContextInfo, connection);
*/
}
- (void)_sendDocumentWillSaveNotification
{
[[CPNotificationCenter defaultCenter] postNotificationName:CPDocumentWillSaveNotification object:self];
}
/*
-> new callback for _documentPersistenceAgent
-> performs delegate message (formerly buried in CPURLConnection`s delegate methods)
-> sets fileURL (formerly buried in CPURLConnection`s delegate methods)
-> update change count only if the operation succeeded! (formerly updated in advance and corrected afterwards if operation failed)
-> all generic alerts and confirms have been removed (can be implemented in subclasses of CPOverhauledDocument)
-> FIXME: currently implemented no "overwrite" functionality
*/
- (void)_saveOperationFinishedWithSuccess:(BOOL)aFlag
{
var operationType = _persistenceOperationInfo.operationType;
if(aFlag && (operationType != CPSaveToOperation))
{
_changeCount -= _persistenceOperationInfo.operationChangeCount;
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
if(operationType != CPSaveOperation)
[self setFileURL:_persistenceOperationInfo.operationURL];
}
[self _sendDocumentSavedNotification:aFlag];
objj_msgSend(_persistenceOperationInfo.delegate, _persistenceOperationInfo.finishSelector, self, aFlag, _persistenceOperationInfo.context);
_persistenceOperationInfo = nil;
}
/*
-> instead of executing the persistence operation itself delegate to _documentPersistenceAgent!
-> only allow one persistence operation at a time! (maybe not strictly necessary)
*/
- (void)readFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
{
if(_persistenceOperationInfo)
objj_msgSend(aDelegate, aDidReadSelector, self, NO, aContextInfo);
else
{
_persistenceOperationInfo = { operationURL:anAbsoluteURL, delegate:aDelegate, finishSelector:aDidReadSelector, context:aContextInfo, };
[_documentPersistenceAgent readDocumentFromURL:anAbsoluteURL ofType:aType];
}
/*
[_readConnection cancel];
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
_readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
_readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
*/
}
/*
-> new callback for _documentPersistenceAgent
-> performs delegate message formerly buried in CPURLConnection`s delegate methods
-> readFromData: is called by _documentPersistenceAgent
*/
- (void)_readOperationFinishedWithSuccess:(BOOL)aFlag
{
var operationType = _persistenceOperationInfo.operationType;
//for read operations the fileURL has been set in advance, so if read does not succeed, reset it to nil...
if(aFlag == NO)
[self setFileURL:nil];
objj_msgSend(_persistenceOperationInfo.delegate, _persistenceOperationInfo.finishSelector, self, aFlag, _persistenceOperationInfo.context);
_persistenceOperationInfo = nil;
}
@end
@implementation CPOverhauledDocumentController : CPDocumentController
{
}
/*
-> instead of using a fixed save panel, retrieve it from CPDocumentPersistenceAgent class for default document type
*/
- (void)openDocument:(id)aSender
{
var docPersistenceAgentClass = [CPDocumentPersistenceAgent persistenceAgentClassForFileType:[self defaultType]];
var openPanel = [docPersistenceAgentClass openPanelForFileType:[self defaultType]];
//var openPanel = [CPOpenPanel openPanel];
[openPanel runModal];
var URLs = [openPanel URLs],
index = 0,
count = [URLs count];
for (; index < count; ++index)
[self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil];
}
@end
@implementation CPDocumentPersistenceAgent : CPObject
{
CPOverhauledDocument _persistedDocument;
}
+ (id)persistenceAgentClassForFileType:(CPString)aFileType
{
var documentTypes = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
var documentType, documentTypeEnumerator = [documentTypes objectEnumerator];
while(documentType = [documentTypeEnumerator nextObject])
{
if([documentType objectForKey:@"CPBundleTypeName"] == aFileType)
{
var docPersistenceAgentClassName = [documentType objectForKey:@"CPDocumentPersistenceAgentClass"];
return CPClassFromString(docPersistenceAgentClassName);
}
}
[CPException raise:@"CPDocumentPersistenceAgentException" reason:@"no valid 'CPDocumentPersistenceAgentClass' set in Info.plist"]
}
+ (id)openPanelForFileType:(CPString)aFileType
{
[CPException raise:@"CPDocumentPersistenceAgentException" reason:@"'openPanelForFileType' is abstract in CPDocumentPersistenceAgent"]
}
- (id)savePanel
{
[CPException raise:@"CPDocumentPersistenceAgentException" reason:@"'savePanel' is abstract in CPDocumentPersistenceAgent"]
}
- (id)initWithDocument:(CPDocument)aDocument
{
if(self = [self init])
_persistedDocument = aDocument;
return self;
}
- (void)saveDocumentToURL:(CPURL)anAbsoluteURL ofType:(CPString)aType forSaveOperation:(CPSaveOperationType)aSaveOperation
{
[CPException raise:@"CPDocumentPersistenceAgentException" reason:@"'saveDocumentToURL:ofType:forSaveOperation:' is abstract in CPDocumentPersistenceAgent"]
}
- (void)readDocumentFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType
{
[CPException raise:@"CPDocumentPersistenceAgentException" reason:@"'readDocumentFromURL:ofType:' is abstract in CPDocumentPersistenceAgent"]
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment