diff --git a/ChangeLog b/ChangeLog index 7ad119729..33818e031 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2000-09-21 Richard Frith-Macdonald + + * Headers/gnustep/base/NSURLHandle.h: tidy up. + * Source/NSURLHandle.m: Implement class and add simple implementation + of a concrete subclass for handling file URLs. + 2000-09-20 Richard Frith-Macdonald * Headers/gnustep/base/GSXML.h: Added ([-parser:]) and removed diff --git a/Headers/gnustep/base/NSURLHandle.h b/Headers/gnustep/base/NSURLHandle.h index c677f7669..ebb725531 100644 --- a/Headers/gnustep/base/NSURLHandle.h +++ b/Headers/gnustep/base/NSURLHandle.h @@ -24,6 +24,10 @@ #ifndef _NSURLHandle_h__ #define _NSURLHandle_h__ +@class NSData; +@class NSString; +@class NSMutableArray; +@class NSMutableData; @class NSURLHandle; @class NSURL; @@ -57,48 +61,40 @@ typedef enum //============================================================================= @interface NSURLHandle: NSObject { + NSMutableData *_data; NSMutableArray *_clients; - id _data; + NSString *_failure; NSURLHandleStatus _status; } ++ (NSURLHandle*) cachedHandleForURL: (NSURL*)url; ++ (BOOL) canInitWithURL: (NSURL*)url; + (void) registerURLHandleClass: (Class)urlHandleSubclass; + (Class) URLHandleClassForURL: (NSURL*)url; -- (id) initWithURL: (NSURL*)url - cached: (BOOL)cached; - -- (NSURLHandleStatus) status; -- (NSString*) failureReason; - - (void) addClient: (id )client; -- (void) removeClient: (id )client; - -- (void) loadInBackground; -- (void) cancelLoadInBackground; - -- (NSData*) resourceData; - (NSData*) availableResourceData; - -- (void) flushCachedData; - - (void) backgroundLoadDidFailWithReason: (NSString*)reason; +- (void) beginLoadInBackground; +- (void) cancelLoadInBackground; - (void) didLoadBytes: (NSData*)newData loadComplete: (BOOL)loadComplete; - - -+ (BOOL) canInitWithURL: (NSURL*)url; -+ (NSURLHandle*) cachedHandleForURL: (NSURL*)url; - +- (void) endLoadInBackground; +- (NSString*) failureReason; +- (void) flushCachedData; +- (id) initWithURL: (NSURL*)url + cached: (BOOL)cached; +- (void) loadInBackground; +- (NSData*) loadInForeground; - (id) propertyForKey: (NSString*)propertyKey; - (id) propertyForKeyIfAvailable: (NSString*)propertyKey; +- (void) removeClient: (id )client; +- (NSData*) resourceData; +- (NSURLHandleStatus) status; +- (BOOL) writeData: (NSData*)data; - (BOOL) writeProperty: (id)propertyValue forKey: (NSString*)propertyKey; -- (BOOL) writeData: (NSData*)data; -- (NSData*) loadInForeground; -- (void) beginLoadInBackground; -- (void) endLoadInBackground; @end diff --git a/Source/NSURLHandle.m b/Source/NSURLHandle.m index 1d97653f9..a3106b477 100644 --- a/Source/NSURLHandle.m +++ b/Source/NSURLHandle.m @@ -2,7 +2,9 @@ Copyright (C) 1999 Free Software Foundation, Inc. Written by: Manuel Guesdon - Date: Jan 1999 + Date: Jan 1999 + Update: Richard Frith-Macdonald + Date: Sep 2000 This file is part of the GNUstep Library. @@ -21,15 +23,11 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ -/* -Note from Manuel Guesdon: -* functions are not implemented. If someone has documentation or ideas on -how it should work... -*/ - #include #include #include +#include +#include #include #include #include @@ -37,39 +35,71 @@ how it should work... #include #include -//============================================================================= +@class GSFileURLHandle; + @implementation NSURLHandle -static NSMapTable *cache = 0; static NSMutableArray *registry = nil; ++ (NSURLHandle*) cachedHandleForURL: (NSURL*)url +{ + /* + * Each subclass is supposed to do its own caching, so we must + * find the correct subclass and ask it for its cached handle. + */ + if (self == [NSURLHandle class]) + { + Class c = [self URLHandleClassForURL: url]; + + return [c cachedHandleForURL: url]; + } + else + { + [self subclassResponsibility: _cmd]; + return nil; + } +} + ++ (BOOL) canInitWithURL: (NSURL*)url +{ + /* + * The semi-abstract base class can't handle ANY scheme + */ + return NO; +} + + (void) initialize { if (self == [NSURLHandle class]) { - cache = NSCreateMapTable(NSObjectMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); registry = [NSMutableArray new]; + [self registerURLHandleClass: [GSFileURLHandle class]]; } } -+ (void) registerURLHandleClass: (Class)_urlHandleSubclass ++ (void) registerURLHandleClass: (Class)urlHandleSubclass { - if ([registry indexOfObjectIdenticalTo: _urlHandleSubclass] == NSNotFound) - { - [registry addObject: _urlHandleSubclass]; - } + /* + * Maintain a registry of classes that handle various schemes + * Re-adding a class moves it to the end of the registry - so it will + * be used in preference to any class added earlier. + */ + [registry removeObjectIdenticalTo: urlHandleSubclass]; + [registry addObject: urlHandleSubclass]; } -+ (Class) URLHandleClassForURL: (NSURL*)_url ++ (Class) URLHandleClassForURL: (NSURL*)url { unsigned count = [registry count]; + /* + * Find a class to handle the URL, try most recently registered first. + */ while (count-- > 0) { id found = [registry objectAtIndex: count]; - if ([found canInitWithURL: _url] == YES) + if ([found canInitWithURL: url] == YES) { return (Class)found; } @@ -77,185 +107,335 @@ static NSMutableArray *registry = nil; return 0; } -- (id) initWithURL: (NSURL*)_url - cached: (BOOL)_cached +- (void) addClient: (id )client { - Class concreteSubclass; - NSURLHandle *instance; - - if (_cached == YES) - { - instance = (id)NSMapGet(cache, (void*)_url); - if (instance != nil) - { - RELEASE(self); - return instance; - } - } - concreteSubclass = [NSURLHandle URLHandleClassForURL: _url]; - if (concreteSubclass == 0) - { - NSLog(@"Attempt to init NSURLHandle with unsupported URL schema"); - RELEASE(self); - } - RELEASE(self); - instance = [concreteSubclass alloc]; - instance = [instance initWithURL: _url cached: _cached]; - if (instance != nil) - { - NSMapInsert(cache, (void*)_url, (void*)instance); - } - return instance; + [_clients addObject: client]; } -//----------------------------------------------------------------------------- -- (NSURLHandleStatus) status -{ - //FIXME - [self notImplemented: _cmd]; - return (NSURLHandleStatus)0; -} - -//----------------------------------------------------------------------------- -- (NSString*) failureReason -{ - //FIXME - [self notImplemented: _cmd]; - return nil; -} - -//----------------------------------------------------------------------------- -- (void) addClient: (id )_client -{ - //FIXME - [self notImplemented: _cmd]; -} - -//----------------------------------------------------------------------------- -- (void) removeClient: (id )_client -{ - //FIXME - [self notImplemented: _cmd]; -} - -//----------------------------------------------------------------------------- -- (void) loadInBackground -{ - //FIXME - [self notImplemented: _cmd]; -} - -//----------------------------------------------------------------------------- -- (void) cancelLoadInBackground -{ - //FIXME - [self notImplemented: _cmd]; -} - -//----------------------------------------------------------------------------- -- (NSData*) resourceData -{ - //FIXME - [self notImplemented: _cmd]; - return nil; -} - -//----------------------------------------------------------------------------- - (NSData*) availableResourceData { - //FIXME - [self notImplemented: _cmd]; - return nil; + return AUTORELEASE([_data copy]); } -//----------------------------------------------------------------------------- -- (void) flushCachedData -{ - NSResetMapTable(cache); -} - -//----------------------------------------------------------------------------- - (void) backgroundLoadDidFailWithReason: (NSString*)reason { - //FIXME - [self notImplemented: _cmd]; + NSEnumerator *enumerator = [_clients objectEnumerator]; + id client; + + _status = NSURLHandleLoadFailed; + [_data setLength: 0]; + ASSIGNCOPY(_failure, reason); + + while ((client = [enumerator nextObject]) != nil) + { + [client URLHandle: self resourceDidFailLoadingWithReason: _failure]; + } } -//----------------------------------------------------------------------------- -- (void) didLoadBytes: (NSData*)newData - loadComplete: (BOOL)_loadComplete -{ - //FIXME - [self notImplemented: _cmd]; -} - -//----------------------------------------------------------------------------- -+ (BOOL) canInitWithURL: (NSURL*)_url -{ - //FIXME - [self notImplemented: _cmd]; - return NO; -} - -//----------------------------------------------------------------------------- -+ (NSURLHandle*) cachedHandleForURL: (NSURL*)_url -{ - return (NSURLHandle*) NSMapGet(cache, (void*)_url); -} - -//----------------------------------------------------------------------------- -- (id) propertyForKey: (NSString*)propertyKey -{ - //FIXME - [self notImplemented: _cmd]; - return nil; -} - -//----------------------------------------------------------------------------- -- (id) propertyForKeyIfAvailable: (NSString*)propertyKey -{ - //FIXME - [self notImplemented: _cmd]; - return nil; -} - -//----------------------------------------------------------------------------- -- (BOOL) writeProperty: (id)propertyValue - forKey: (NSString*)propertyKey -{ - //FIXME - [self notImplemented: _cmd]; - return NO; -} - -//----------------------------------------------------------------------------- -- (BOOL) writeData: (NSData*)data -{ - //FIXME - [self notImplemented: _cmd]; - return NO; -} - -//----------------------------------------------------------------------------- -- (NSData*) loadInForeground -{ - //FIXME - [self notImplemented: _cmd]; - return nil; -} - -//----------------------------------------------------------------------------- - (void) beginLoadInBackground { - //FIXME - [self notImplemented: _cmd]; + _status = NSURLHandleLoadInProgress; + [_data setLength: 0]; + [_clients makeObjectsPerformSelector: + @selector(URLHandleResourceDidBeginLoading:) + withObject: self]; +} + +- (void) cancelLoadInBackground +{ + _status = NSURLHandleNotLoaded; + [_data setLength: 0]; + [_clients makeObjectsPerformSelector: + @selector(URLHandleResourceDidCancelLoading:) + withObject: self]; + [self endLoadInBackground]; +} + +- (void) dealloc +{ + RELEASE(_data); + RELEASE(_failure); + RELEASE(_clients); + [super dealloc]; +} + +/* + * Mathod called by subclasses during process of loading a resource. + * The base class maintains a copy of the data being read in and + * accumulates separate parts of the data. + */ +- (void) didLoadBytes: (NSData*)newData + loadComplete: (BOOL)loadComplete +{ + NSEnumerator *enumerator; + id client; + + /* + * Let clients know we are starting loading (unless this has already been + * done). + */ + if (_status != NSURLHandleLoadInProgress) + { + _status = NSURLHandleLoadInProgress; + [_data setLength: 0]; + [_clients makeObjectsPerformSelector: + @selector(URLHandleResourceDidBeginLoading:) + withObject: self]; + } + + /* + * If we have been given nil data, there must have been a failure! + */ + if (newData == nil) + { + [self backgroundLoadDidFailWithReason: @"nil data"]; + return; + } + + /* + * Let clients know we have read some data. + */ + enumerator = [_clients objectEnumerator]; + while ((client = [enumerator nextObject]) != nil) + { + [client URLHandle: self resourceDataDidBecomeAvailable: newData]; + } + + /* + * Accumulate data in cache. + */ + [_data appendData: newData]; + + if (loadComplete == YES) + { + /* + * Let clients know we have finished loading. + */ + _status = NSURLHandleLoadSucceeded; + [_clients makeObjectsPerformSelector: + @selector(URLHandleResourceDidFinishLoading:) + withObject: self]; + } } -//----------------------------------------------------------------------------- - (void) endLoadInBackground { - //FIXME - [self notImplemented: _cmd]; + _status = NSURLHandleNotLoaded; + [_data setLength: 0]; +} + +- (NSString*) failureReason +{ + if (_status == NSURLHandleLoadFailed) + return _failure; + else + return nil; +} + +- (void) flushCachedData +{ + [_data setLength: 0]; +} + +- (id) init +{ + _status = NSURLHandleNotLoaded; + _clients = [NSMutableArray new]; + _data = [NSMutableData new]; + return self; +} + +- (id) initWithURL: (NSURL*)url + cached: (BOOL)cached +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (void) loadInBackground +{ + [self subclassResponsibility: _cmd]; +} + +- (NSData*) loadInForeground +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (id) propertyForKey: (NSString*)propertyKey +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (id) propertyForKeyIfAvailable: (NSString*)propertyKey +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (void) removeClient: (id )client +{ + [_clients removeObjectIdenticalTo: client]; +} + +- (NSData*) resourceData +{ + if (_status == NSURLHandleLoadSucceeded) + { + return [self availableResourceData]; + } + else + { + NSData *d = [self loadInForeground]; + + if (d != nil) + { + [_data setData: d]; + } + return d; + } +} + +- (NSURLHandleStatus) status +{ + return _status; +} + +- (BOOL) writeData: (NSData*)data +{ + [self subclassResponsibility: _cmd]; + return NO; +} + +- (BOOL) writeProperty: (id)propertyValue + forKey: (NSString*)propertyKey +{ + [self subclassResponsibility: _cmd]; + return NO; } @end + +@interface GSFileURLHandle : NSURLHandle +{ + NSString *_path; +} +@end + +@implementation GSFileURLHandle + +static NSMutableDictionary *fileCache = nil; + ++ (NSURLHandle*) cachedHandleForURL: (NSURL*)url +{ + NSURLHandle *obj = nil; + + if ([url isFileURL] == YES) + { + NSString *path = [url path]; + + path = [path stringByStandardizingPath]; + obj = [fileCache objectForKey: path]; + } + return obj; +} + ++ (BOOL) canInitWithURL: (NSURL*)url +{ + if ([url isFileURL] == YES) + { + return YES; + } + return NO; +} + ++ (void) initialize +{ + fileCache = [NSMutableDictionary new]; +} + +- (void) dealloc +{ + RELEASE(_path); + [super dealloc]; +} + +- (id) initWithURL: (NSURL*)url + cached: (BOOL)cached +{ + NSString *path; + + if ([url isFileURL] == NO) + { + NSLog(@"Attempt to init GSFileURLHandle with bad URL"); + RELEASE(self); + return nil; + } + path = [url path]; + path = [path stringByStandardizingPath]; + + if (cached == YES) + { + id obj; + + obj = [fileCache objectForKey: path]; + if (obj != nil) + { + RELEASE(self); + self = RETAIN(obj); + return self; + } + } + self = [super init]; + if (self != nil) + { + _path = [path copy]; + if (cached == YES) + { + [fileCache setObject: self forKey: _path]; + RELEASE(self); + } + } + return self; +} + +- (void) loadInBackground +{ + [self loadInForeground]; +} + +- (NSData*) loadInForeground +{ + NSData *d = [NSData dataWithContentsOfFile: _path]; + + [self didLoadBytes: d loadComplete: YES]; + return d; +} + +- (id) propertyForKey: (NSString*)propertyKey +{ + return nil; +} + +- (id) propertyForKeyIfAvailable: (NSString*)propertyKey +{ + return nil; +} + +- (BOOL) writeData: (NSData*)data +{ + /* FIXME */ + [self notImplemented: _cmd]; + return NO; +} + +- (BOOL) writeProperty: (id)propertyValue + forKey: (NSString*)propertyKey +{ + return NO; +} + +@end +