diff --git a/ChangeLog b/ChangeLog index 4991062a3..6166e4734 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2000-03-24 Jonathan Gapen + * Source/NSFileWrapper.m: First implementation of NSFileWrapper + * Headers/gnustep/gui/NSFileWrapper.h: Added ivars and enum + 2000-03-21 22:00 Fred Kiefer * Source/NSText.m: Changed to use a text storage instead of an attributed string. diff --git a/Headers/gnustep/gui/NSFileWrapper.h b/Headers/gnustep/gui/NSFileWrapper.h index 0f80bf33a..9c8bd289a 100644 --- a/Headers/gnustep/gui/NSFileWrapper.h +++ b/Headers/gnustep/gui/NSFileWrapper.h @@ -29,49 +29,62 @@ #ifndef _GNUstep_H_NSFileWrapper #define _GNUstep_H_NSFileWrapper +#include +#include @class NSImage; -@class NSFont; @class NSString; +typedef enum +{ + GSFileWrapperDirectoryType, + GSFileWrapperRegularFileType, + GSFileWrapperSymbolicLinkType +} GSFileWrapperType; + @interface NSFileWrapper : NSObject -{ +{ + NSString *_filename; + NSString *_preferredFilename; + NSMutableDictionary *_fileAttributes; + GSFileWrapperType _wrapperType; + id _wrapperData; + NSImage *_iconImage; } // // Initialization // - // Init instance of directory type + +// Init instance of directory type - (id)initDirectoryWithFileWrappers:(NSDictionary *)docs; - // Init instance of regular file type +// Init instance of regular file type - (id)initRegularFileWithContents:(NSData *)data; - // Init instance of symbolic link type +// Init instance of symbolic link type - (id)initSymbolicLinkWithDestination:(NSString *)path; - // Init an instance from the file, - // directory, or symbolic link at path. - // This can create a tree of instances +// Init an instance from the file, directory, or symbolic link at path. +// This can create a tree of instances - (id)initWithPath:(NSString *)path; // with a directory instance at the top - // Init an instance from data in std - // serial format. Serial format is the - // same as that used by NSText's - // RTFDFromRange: method. This can - // create a tree of instances with a - // directory instance at the top +// Init an instance from data in standard serial format. Serial format +// is the same as that used by NSText's RTFDFromRange: method. This can +// create a tree of instances with a directory instance at the top - (id)initWithSerializedRepresentation:(NSData *)data; // // General methods -// // write instace to disk -- (BOOL)writeToFile:(NSString *)path // at path. if directory - atomically:(BOOL)atomicFlag // type this method is - updateFilenames:(BOOL)updateFilenamesFlag; // recursive, if flag is - // YES the wrapper will be - // updated with the name - // used in writing the file +// + +// write instace to disk at path. if directory type, this method is recursive +// if flag is YES, the wrapper will be updated with the name used in writing +// the file +- (BOOL)writeToFile:(NSString *)path + atomically:(BOOL)atomicFlag + updateFilenames:(BOOL)updateFilenamesFlag; + - (NSData *)serializedRepresentation; - (void)setFilename:(NSString *)filename; @@ -95,25 +108,29 @@ // // Directory type methods -// -- (NSString *)addFileWrapper:(NSFileWrapper *)doc; // methods for use -- (void)removeFileWrapper:(NSFileWrapper *)doc; // with directory -- (NSDictionary *)fileWrappers; // type instances -- (NSString *)keyForFileWrapper:(NSFileWrapper *)doc; // types they raise -- (NSString *)addFileWithPath:(NSString *)path; // an exception. -- (NSString *)addRegularFileWithContents:(NSData *)data - preferredFilename:(NSString *)filename; -- (NSString *)addSymbolicLinkWithDestination:(NSString *)path - preferredFilename:(NSString *)filename; +// -// +// these messages raise an exception when sent to non-directory type wrappers! +- (NSString *)addFileWrapper:(NSFileWrapper *)doc; +- (void)removeFileWrapper:(NSFileWrapper *)doc; +- (NSDictionary *)fileWrappers; +- (NSString *)keyForFileWrapper:(NSFileWrapper *)doc; +- (NSString *)addFileWithPath:(NSString *)path; +- (NSString *)addRegularFileWithContents:(NSData *)data + preferredFilename:(NSString *)filename; +- (NSString *)addSymbolicLinkWithDestination:(NSString *)path + preferredFilename:(NSString *)filename; + +// // Regular file type methods -// +// + - (NSData *)regularFileContents; -// +// // Symbolic link type methods -// +// + - (NSString *)symbolicLinkDestination; @end diff --git a/Source/NSFileWrapper.m b/Source/NSFileWrapper.m index 841e80345..49d8f9a96 100644 --- a/Source/NSFileWrapper.m +++ b/Source/NSFileWrapper.m @@ -7,6 +7,8 @@ Author: Felipe A. Rodriguez Date: Sept 1998 + Author: Jonathan Gapen + Date: Dec 1999 This file is part of the GNUstep GUI Library. @@ -30,6 +32,11 @@ #include #include +#include +#include +#include +#include +#include @implementation NSFileWrapper @@ -37,150 +44,388 @@ // // Initialization // -- (id)initDirectoryWithFileWrappers:(NSDictionary *)docs // Init instance of -{ // directory type - return nil; + +// Init instance of directory type +- (id)initDirectoryWithFileWrappers:(NSDictionary *)docs +{ + NSEnumerator *enumerator; + id key; + NSFileWrapper *wrapper; + [super init]; + + _wrapperType = GSFileWrapperDirectoryType; + _wrapperData = [NSMutableDictionary dictionaryWithCapacity: [docs count]]; + + enumerator = [docs keyEnumerator]; + while ((key = [enumerator nextObject])) + { + wrapper = (NSFileWrapper *)[docs objectForKey: key]; + + if (![wrapper preferredFilename]) + [wrapper setPreferredFilename: key]; + + [_wrapperData setObject: wrapper forKey: key]; + } + + return self; } - -- (id)initRegularFileWithContents:(NSData *)data // Init instance of -{ // regular file type - return nil; + +// Init instance of regular file type +- (id)initRegularFileWithContents:(NSData *)data +{ + [super init]; + + _wrapperData = [data copyWithZone: [self zone]]; + _wrapperType = GSFileWrapperRegularFileType; + + return self; } - -- (id)initSymbolicLinkWithDestination:(NSString *)path // Init instance of -{ // symbolic link type - return nil; + +// Init instance of symbolic link type +- (id)initSymbolicLinkWithDestination:(NSString *)path +{ + [super init]; + + _wrapperData = path; + _wrapperType = GSFileWrapperSymbolicLinkType; + + return self; } // Init an instance from the file, - // directory, or symbolic link at path. -- (id)initWithPath:(NSString *)path // This can create a tree of instances -{ // with a directory instance at the top - return nil; +// directory, or symbolic link at path. +// This can create a tree of instances +// with a directory instance at the top + +- (id)initWithPath:(NSString *)path +{ + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *fileType; + + NSDebugLLog(@"NSFileWrapper", @"initWithPath: %@", path); + + [self setFilename: path]; + [self setPreferredFilename: [path lastPathComponent]]; + [self setFileAttributes: [fm fileAttributesAtPath: path traverseLink: NO]]; + + fileType = [[self fileAttributes] fileType]; + if ([fileType isEqualToString: @"NSFileTypeDirectory"]) + { + NSString *filename; + NSMutableArray *fileWrappers = [NSMutableArray new]; + NSArray *filenames = [fm directoryContentsAtPath: path]; + NSEnumerator *enumerator = [filenames objectEnumerator]; + + while ((filename = [enumerator nextObject])) + { + [fileWrappers addObject: + [[NSFileWrapper alloc] initWithPath: + [path stringByAppendingPathComponent: filename]]]; + } + self = [self initDirectoryWithFileWrappers: + [NSDictionary dictionaryWithObjects: fileWrappers forKeys: filenames]]; + } + else if ([fileType isEqualToString: @"NSFileTypeRegular"]) + { + self = [self initRegularFileWithContents: + [[NSData alloc] initWithContentsOfFile: path]]; + } + else if ([fileType isEqualToString: @"NSFileTypeSymbolicLink"]) + { + self = [self initSymbolicLinkWithDestination: + [fm pathContentOfSymbolicLinkAtPath: path]]; + } + + return self; } // Init an instance from data in std - // serial format. Serial format is the - // same as that used by NSText's - // RTFDFromRange: method. This can - // create a tree of instances with a - // directory instance at the top +// serial format. Serial format is the +// same as that used by NSText's +// RTFDFromRange: method. This can +// create a tree of instances with a +// directory instance at the top + +// FIXME - implement - (id)initWithSerializedRepresentation:(NSData *)data { - return nil; + return nil; +} + +- (void)dealloc +{ + if (_filename) + [_filename release]; + if (_preferredFilename) + [_preferredFilename release]; + if (_wrapperData) + [_wrapperData release]; + if (_iconImage) + [_iconImage release]; } // // General methods -// // write instance to disk -- (BOOL)writeToFile:(NSString *)path // at path. if directory - atomically:(BOOL)atomicFlag // type, this method is - updateFilenames:(BOOL)updateFilenamesFlag // recursive, if flag is -{ // YES the wrapper will be - return NO; // updated with the name -} // used in writing the file - +// + +// write instance to disk at path; if directory type, this +// method is recursive; if updateFilenamesFlag is YES, the wrapper +// will be updated with the name used in writing the file + +- (BOOL)writeToFile:(NSString *)path + atomically:(BOOL)atomicFlag + updateFilenames:(BOOL)updateFilenamesFlag +{ + NSFileManager *fm = [NSFileManager defaultManager]; + BOOL pathExists = [fm fileExistsAtPath: path]; + + NSDebugLLog(@"NSFileWrapper", + @"writeToFile: %@ atomically: updateFilenames:", path); + + // don't overwrite existing paths + if (pathExists && atomicFlag) + return NO; + + if (updateFilenamesFlag == YES) + [self setFilename: [path lastPathComponent]]; + + switch (_wrapperType) + { + case GSFileWrapperDirectoryType: + { + // FIXME - more robust save proceedure when atomicFlag set + NSEnumerator *enumerator = [_wrapperData keyEnumerator]; + NSString *key; + + [fm createDirectoryAtPath: path attributes: _fileAttributes]; + while ((key = (NSString *)[enumerator nextObject])) + { + NSString *newPath = + [path stringByAppendingPathComponent: key]; + [[_wrapperData objectForKey: key] writeToFile: newPath + atomically: atomicFlag + updateFilenames: updateFilenamesFlag]; + } + return YES; + } + case GSFileWrapperRegularFileType: + { + return [_wrapperData writeToFile: path atomically: atomicFlag]; + } + case GSFileWrapperSymbolicLinkType: + { + return [fm createSymbolicLinkAtPath: path pathContent: _wrapperData]; + } + } + return NO; +} + +// FIXME - implement - (NSData *)serializedRepresentation { - return nil; + return nil; } - (void)setFilename:(NSString *)filename { + if (filename == nil || [filename isEqualToString: @""]) + [NSException raise: NSInternalInconsistencyException + format: @"Empty or nil argument to setFilename:"]; + else + ASSIGN(_filename, filename); } - (NSString *)filename { - return nil; + return _filename; } - (void)setPreferredFilename:(NSString *)filename { + if (filename == nil || [filename isEqualToString: @""]) + [NSException raise: NSInternalInconsistencyException + format: @"Empty or nil argument to setPreferredFilename:"]; + else + ASSIGN(_preferredFilename, filename); } - (NSString *)preferredFilename; { - return nil; + return _preferredFilename; } - (void)setFileAttributes:(NSDictionary *)attributes { + if (_fileAttributes == nil) + _fileAttributes = [NSMutableDictionary new]; + + [_fileAttributes addEntriesFromDictionary: attributes]; } - (NSDictionary *)fileAttributes { - return nil; + return _fileAttributes; } - (BOOL)isRegularFile { - return NO; + if (_wrapperType == GSFileWrapperRegularFileType) + return YES; + else + return NO; } - (BOOL)isDirectory { - return NO; + if (_wrapperType == GSFileWrapperDirectoryType) + return YES; + else + return NO; } - (BOOL)isSymbolicLink { - return NO; + if (_wrapperType == GSFileWrapperSymbolicLinkType) + return YES; + else + return NO; } - (void)setIcon:(NSImage *)icon { + ASSIGN(_iconImage, icon); } - (NSImage *)icon; { - return nil; + if (!_iconImage) + return [[NSWorkspace sharedWorkspace] iconForFile: [self filename]]; + else + return _iconImage; } +// FIXME - for directory wrappers - (BOOL)needsToBeUpdatedFromPath:(NSString *)path { - return NO; + NSFileManager *fm = [NSFileManager defaultManager]; + + switch (_wrapperType) + { + case GSFileWrapperRegularFileType: + if ([[self fileAttributes] + isEqualToDictionary: [fm fileAttributesAtPath: path + traverseLink: NO]]) + return NO; + break; + case GSFileWrapperSymbolicLinkType: + if ([_wrapperData isEqualToString: + [fm pathContentOfSymbolicLinkAtPath: path]]) + return NO; + break; + case GSFileWrapperDirectoryType: + break; + } + + return YES; } +// FIXME - implement - (BOOL)updateFromPath:(NSString *)path { - return NO; + return NO; } -// -// Directory type methods -// +// +// Directory type methods +// + +#define GSFileWrapperDirectoryTypeCheck() \ + if (_wrapperType != GSFileWrapperDirectoryType) \ + [NSException raise: NSInternalInconsistencyException \ + format: @"Can't invoke %@ on a file wrapper that" \ + @" does not wrap a directory!", _cmd]; + +// FIXME - handle duplicate names - (NSString *)addFileWrapper:(NSFileWrapper *)doc { - return nil; + NSString *key; + + GSFileWrapperDirectoryTypeCheck(); + + key = [doc preferredFilename]; + if (key == nil || [key isEqualToString: @""]) + { + [NSException raise: NSInvalidArgumentException + format: @"Adding file wrapper with no preferred filename."]; + return nil; + } + + [_wrapperData setObject: doc forKey: key]; + + return key; } - (void)removeFileWrapper:(NSFileWrapper *)doc { + GSFileWrapperDirectoryTypeCheck(); + + [_wrapperData removeObjectsForKeys: [_wrapperData allKeysForObject: doc]]; } -- (NSDictionary *)fileWrappers; +- (NSDictionary *)fileWrappers { - return nil; + GSFileWrapperDirectoryTypeCheck(); + + return _wrapperData; } -- (NSString *)keyForFileWrapper:(NSFileWrapper *)doc +- (NSString *)keyForFileWrapper:(NSFileWrapper *)doc { - return nil; + GSFileWrapperDirectoryTypeCheck(); + + return [[_wrapperData allKeysForObject: doc] objectAtIndex: 0]; } -- (NSString *)addFileWithPath:(NSString *)path +- (NSString *)addFileWithPath:(NSString *)path { - return nil; + NSFileWrapper *wrapper; + GSFileWrapperDirectoryTypeCheck(); + + wrapper = [[NSFileWrapper alloc] initWithPath: path]; + if (wrapper != nil) + return [self addFileWrapper: wrapper]; + else + return nil; } - (NSString *)addRegularFileWithContents:(NSData *)data - preferredFilename:(NSString *)filename + preferredFilename:(NSString *)filename { - return nil; + NSFileWrapper *wrapper; + GSFileWrapperDirectoryTypeCheck(); + + wrapper = [[NSFileWrapper alloc] initRegularFileWithContents: data]; + if (wrapper != nil) + { + [wrapper setPreferredFilename: filename]; + return [self addFileWrapper: wrapper]; + } + else + return nil; } - (NSString *)addSymbolicLinkWithDestination:(NSString *)path - preferredFilename:(NSString *)filename + preferredFilename:(NSString *)filename { - return nil; + NSFileWrapper *wrapper; + GSFileWrapperDirectoryTypeCheck(); + + wrapper = [[NSFileWrapper alloc] initSymbolicLinkWithDestination: path]; + if (wrapper != nil) + { + [wrapper setPreferredFilename: filename]; + return [self addFileWrapper: wrapper]; + } + else + return nil; } // @@ -188,7 +433,13 @@ // - (NSData *)regularFileContents { - return nil; + if (_wrapperType == GSFileWrapperRegularFileType) + return _wrapperData; + else + [NSException raise: NSInternalInconsistencyException + format: @"File wrapper does not wrap regular file."]; + + return nil; } // @@ -196,12 +447,22 @@ // - (NSString *)symbolicLinkDestination { - return nil; + if (_wrapperType == GSFileWrapperSymbolicLinkType) + return _wrapperData; + else + [NSException raise: NSInternalInconsistencyException + format: @"File wrapper does not wrap symbolic link."]; + + return nil; } // // Archiving -// +// + +// The MacOS X docs do not say that NSFileWrapper conforms to the +// NSCoding protocol. Should it nonetheless? + - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder];