From ec6158f0bb22ab944218dea66e1bd1b92e939f77 Mon Sep 17 00:00:00 2001 From: fedor Date: Thu, 28 Jan 1999 18:34:31 +0000 Subject: [PATCH] Move GMArchiver files so gui doesnt depend on extenions git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@3614 72102866-910b-0410-8b05-ffd578937521 --- Headers/gnustep/gui/GMAppKit.h | 2 +- Headers/gnustep/gui/GMArchiver.h | 196 ++++++ Model/GMArchiveObjects.m | 168 +++++ Model/GMArchiver.m | 1096 ++++++++++++++++++++++++++++++ Model/GNUmakefile | 7 +- Model/GNUmakefile.postamble | 3 + Model/IBClasses.m | 2 +- Model/IMConnectors.m | 16 +- Model/IMCustomObject.m | 2 +- Model/IMLoading.m | 2 +- Model/Translator.m | 2 +- Source/NSPanel.m | 2 +- Source/NSSavePanel.m | 2 +- 13 files changed, 1488 insertions(+), 12 deletions(-) create mode 100644 Headers/gnustep/gui/GMArchiver.h create mode 100644 Model/GMArchiveObjects.m create mode 100644 Model/GMArchiver.m diff --git a/Headers/gnustep/gui/GMAppKit.h b/Headers/gnustep/gui/GMAppKit.h index 2b26baac4..923da2f25 100644 --- a/Headers/gnustep/gui/GMAppKit.h +++ b/Headers/gnustep/gui/GMAppKit.h @@ -27,7 +27,7 @@ #define _GNUstep_H_GMAppKit #include -#import +#import @interface NSApplication (GMArchiverMethods) @end diff --git a/Headers/gnustep/gui/GMArchiver.h b/Headers/gnustep/gui/GMArchiver.h new file mode 100644 index 000000000..0c997bae7 --- /dev/null +++ b/Headers/gnustep/gui/GMArchiver.h @@ -0,0 +1,196 @@ +/* + GMArchiver.h + + Author: Ovidiu Predescu + Date: October 1997 + + Copyright (C) 1997 Free Software Foundation, Inc. + All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Portions of the code are based on NSArchiver from libFoundation. See the + COPYING file from libFoundation for copyright information. */ + +#ifndef __GMArchiver_h__ +#define __GMArchiver_h__ + +#import +#import +#import +#import + +@class NSString; +@class NSData; +@class NSArray; +@class NSDictionary; +@class NSMutableArray; +@class NSMutableDictionary; + +@class GMArchiver; +@class GMUnarchiver; + + +/* The objects that want to be archived the following protocol. */ + +@protocol ModelCoding + +/* These methods are much like those from the NSCoding protocol. + The difference is that you can specify names for the instance + variables or attributes you encode. The recommended way is not to encode + instance variables but attributes so that an archive file does not + depend on the particular version of a class or on different + instance variable names of the class from another implementation. */ + +- (void)encodeWithModelArchiver:(GMArchiver*)archiver; +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver; +@end + + +@interface NSObject (ModelArchivingMethods) +- (id)replacementObjectForModelArchiver:(GMArchiver*)archiver; +- (Class)classForModelArchiver; ++ (id)createObjectForModelUnarchiver:(GMUnarchiver*)unarchiver; +@end + + +@interface GMArchiver : NSObject +{ + NSMutableDictionary* propertyList; + NSMutableArray* topLevelObjects; + id lastObjectRepresentation; + NSMapTable* objects; // object -> name + NSHashTable* conditionals; // conditional objects + NSMapTable* classes; // real classname -> class info + int counter; + int level; + BOOL writingRoot; // YES if encodeRootObject:withName: was sent + BOOL findingConditionals; // YES if finding conditionals +} + +/* Initializing an GMArchiver */ +- (id)init; + +/* Archiving Data */ ++ (BOOL)archiveRootObject:(id)rootObject + toFile:(NSString*)path; +- (BOOL)writeToFile:(NSString*)path; + +/* Getting the property list representation from the GMArchiver */ +- (id)propertyList; + +/* Encoding objects */ +- (id)encodeRootObject:(id)rootObject withName:(NSString*)name; +- (id)encodeConditionalObject:(id)object withName:(NSString*)name; +- (id)encodeObject:(id)anObject withName:(NSString*)name; +- (id)encodeString:(NSString*)anObject withName:(NSString*)name; +- (id)encodeArray:(NSArray*)array withName:(NSString*)name; +- (id)encodeDictionary:(NSDictionary*)dictionary withName:(NSString*)name; +- (id)encodeData:(NSData*)anObject withName:(NSString*)name; +- (id)encodeClass:(Class)class withName:(NSString*)name; +- (id)encodeSelector:(SEL)selector withName:(NSString*)name; + +/* Encoding the most common C types */ +- (void)encodeChar:(char)value withName:(NSString*)name; +- (void)encodeUnsignedChar:(unsigned char)value withName:(NSString*)name; +- (void)encodeBOOL:(BOOL)value withName:(NSString*)name; +- (void)encodeShort:(short)value withName:(NSString*)name; +- (void)encodeUnsignedShort:(unsigned short)value withName:(NSString*)name; +- (void)encodeInt:(int)value withName:(NSString*)name; +- (void)encodeUnsignedInt:(unsigned int)value withName:(NSString*)name; +- (void)encodeLong:(long)value withName:(NSString*)name; +- (void)encodeUnsignedLong:(unsigned long)value withName:(NSString*)name; +- (void)encodeFloat:(float)value withName:(NSString*)name; +- (void)encodeDouble:(double)value withName:(NSString*)name; + +/* Encoding geometry types */ +- (void)encodePoint:(NSPoint)point withName:(NSString*)name; +- (void)encodeSize:(NSSize)size withName:(NSString*)name; +- (void)encodeRect:(NSRect)rect withName:(NSString*)name; + +/* Substituting One Class for Another */ +- (NSString*)classNameEncodedForTrueClassName:(NSString*)trueName; +- (void)encodeClassName:(NSString*)trueName + intoClassName:(NSString*)inArchiveName; + +@end /* GMArchiver */ + + +@interface GMUnarchiver : NSObject +{ + NSMutableDictionary* propertyList; + id currentDecodedObjectRepresentation; + NSMutableDictionary* namesToObjects; // object name -> object + int level; + int version; + NSZone* objectZone; +} + +/* Initializing an GMUnarchiver */ ++ (id)unarchiverWithContentsOfFile:(NSString*)filename; +- (id)initForReadingWithPropertyList:(id)propertyList; + +/* Decoding Objects */ ++ (id)unarchiveObjectWithName:(NSString*)name + fromPropertyList:(id)propertyList; ++ (id)unarchiveObjectWithName:(NSString*)name fromFile:(NSString*)path; + +/* Decoding objects */ +- (id)decodeObjectWithName:(NSString*)name; +- (NSString*)decodeStringWithName:(NSString*)name; +- (NSArray*)decodeArrayWithName:(NSString*)name; +- (NSDictionary*)decodeDictionaryWithName:(NSString*)name; +- (NSData*)decodeDataWithName:(NSString*)name; +- (Class)decodeClassWithName:(NSString*)name; +- (SEL)decodeSelectorWithName:(NSString*)name; + +/* Decoding the most common C types */ +- (char)decodeCharWithName:(NSString*)name; +- (unsigned char)decodeUnsignedCharWithName:(NSString*)name; +- (BOOL)decodeBOOLWithName:(NSString*)name; +- (short)decodeShortWithName:(NSString*)name; +- (unsigned short)decodeUnsignedShortWithName:(NSString*)name; +- (int)decodeIntWithName:(NSString*)name; +- (unsigned int)decodeUnsignedIntWithName:(NSString*)name; +- (long)decodeLongWithName:(NSString*)name; +- (unsigned long)decodeUnsignedLongWithName:(NSString*)name; +- (float)decodeFloatWithName:(NSString*)name; +- (double)decodeDoubleWithName:(NSString*)name; + +/* Decoding geometry types */ +- (NSPoint)decodePointWithName:(NSString*)name; +- (NSSize)decodeSizeWithName:(NSString*)name; +- (NSRect)decodeRectWithName:(NSString*)name; + +/* Managing an GMUnarchiver */ +- (BOOL)isAtEnd; +- (NSZone*)objectZone; +- (void)setObjectZone:(NSZone*)zone; +- (unsigned int)systemVersion; + +/* Substituting One Class for Another */ ++ (NSString*)classNameDecodedForArchiveClassName:(NSString*)nameInArchive; ++ (void)decodeClassName:(NSString*)nameInArchive + asClassName:(NSString*)trueName; +- (NSString*)classNameDecodedForArchiveClassName:(NSString*)nameInArchive; +- (void)decodeClassName:(NSString*)nameInArchive + asClassName:(NSString*)trueName; +- (unsigned int)versionForClassName:(NSString*)className; + +@end + +#endif /* __GMArchiver_h__ */ diff --git a/Model/GMArchiveObjects.m b/Model/GMArchiveObjects.m new file mode 100644 index 000000000..ec63ea738 --- /dev/null +++ b/Model/GMArchiveObjects.m @@ -0,0 +1,168 @@ +/* + GMArchiveObjects.m + + Author: Ovidiu Predescu + Date: October 1997 + + Copyright (C) 1997 Free Software Foundation, Inc. + All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This file declares categories to various OpenStep classes so they get + archived correctly by GMArchiver. The main things deal with encoding in + place of some objects like imutable strings, arrays, dictionaries and + data objects (basically the property list classes). + + This file is included by GMArchiver rather than compiled as a separate file + because of the linking problems with categories (they are not linked into + the executable even if you refer a method from category; you should refer a + symbol from the category's file in order to force it link. + */ + +#import + + +@implementation NSObject (ModelArchivingMethods) +- (id)replacementObjectForModelArchiver:(GMArchiver*)archiver +{ + return [self replacementObjectForCoder:nil]; +} + +- (Class)classForModelArchiver +{ + return [self classForCoder]; +} + ++ (id)createObjectForModelUnarchiver:(GMUnarchiver*)unarchiver +{ + return [[[self allocWithZone:[unarchiver objectZone]] init] autorelease]; +} +@end + + +@implementation NSString (ModelArchivingMethods) +- (void)encodeWithModelArchiver:(id)archiver +{ + [archiver encodeString:self withName:@"string"]; +} + +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver +{ + return [unarchiver decodeStringWithName:@"string"]; +} + +- (Class)classForModelArchiver +{ + return [NSString class]; +} +@end + + +@implementation NSMutableString (ModelArchivingMethods) +- (Class)classForModelArchiver +{ + return [NSMutableString class]; +} + +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver +{ + return [[[unarchiver decodeStringWithName:@"string"] mutableCopy] + autorelease]; +} +@end + + +@implementation NSArray (ModelArchivingMethods) +- (void)encodeWithModelArchiver:(id)archiver +{ + [archiver encodeArray:self withName:@"elements"]; +} + +- (Class)classForModelArchiver +{ + return [NSMutableArray class]; +} +@end + + +@implementation NSMutableArray (ModelArchivingMethods) +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver +{ + id array = [unarchiver decodeArrayWithName:@"elements"]; + int i, count; + + for (i = 0, count = [array count]; i < count; i++) + [self addObject:[array objectAtIndex:i]]; + + return self; +} +@end + + +@implementation NSDictionary (ModelArchivingMethods) +- (void)encodeWithModelArchiver:(id)archiver +{ + [archiver encodeDictionary:self withName:@"elements"]; +} + +- (Class)classForModelArchiver +{ + return [NSMutableDictionary class]; +} +@end + + +@implementation NSMutableDictionary (ModelArchivingMethods) +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver +{ + id dictionary = [unarchiver decodeDictionaryWithName:@"elements"]; + id enumerator = [dictionary keyEnumerator]; + id key, value; + + while ((key = [enumerator nextObject])) { + value = [dictionary objectForKey:key]; + [self setObject:value forKey:key]; + } + + return self; +} +@end + + +@implementation NSData (ModelArchivingMethods) +- (void)encodeWithModelArchiver:(id)archiver +{ + [archiver encodeData:self withName:@"data"]; +} + +- (Class)classForModelArchiver +{ + return [NSMutableData class]; +} +@end + + +@implementation NSMutableData (ModelArchivingMethods) +- (id)initWithModelUnarchiver:(GMUnarchiver*)unarchiver +{ + id data = [unarchiver decodeDataWithName:@"data"]; + + [self appendData:data]; + return self; +} +@end diff --git a/Model/GMArchiver.m b/Model/GMArchiver.m new file mode 100644 index 000000000..c6c122fed --- /dev/null +++ b/Model/GMArchiver.m @@ -0,0 +1,1096 @@ +/* + GMArchiver.m + + Author: Ovidiu Predescu + Date: October 1997 + + Copyright (C) 1997 Free Software Foundation, Inc. + All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Portions of the code are based on NSArchiver from libFoundation. See the + COPYING file from libFoundation for copyright information. */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#include + +@interface GMClassInfo : NSObject +{ + NSString* className; + int version; + BOOL written; +} + ++ classInfoWithClassName:(NSString*)className version:(int)version; + +- (NSString*)className; +- (int)version; +- (void)setWasWritten:(BOOL)flag; +- (BOOL)wasWritten; + +@end + + +@implementation GMClassInfo + ++ classInfoWithClassName:(NSString*)name version:(int)_version +{ + GMClassInfo* object = [[self new] autorelease]; + + object->className = [name retain]; + object->version = _version; + + return object; +} + +- (NSString*)className { return className; } +- (int)version { return version; } +- (void)setWasWritten:(BOOL)flag { written = flag; } +- (BOOL)wasWritten { return written; } + +@end + + +@implementation GMArchiver + ++ (BOOL)archiveRootObject:(id)rootObject + toFile:(NSString*)path +{ + GMArchiver* archiver = [[self new] autorelease]; + BOOL result; + + [archiver encodeRootObject:rootObject withName:@"RootObject"]; + result = [archiver writeToFile:path]; + + return result; +} + +- (id)init +{ + propertyList = [NSMutableDictionary new]; + topLevelObjects = [NSMutableArray new]; + [propertyList setObject:topLevelObjects forKey:@"TopLevelObjects"]; + lastObjectRepresentation = propertyList; + + objects = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks, + NSObjectMapValueCallBacks, 119); + conditionals = NSCreateHashTable (NSNonRetainedObjectHashCallBacks, 19); + classes = NSCreateMapTable (NSObjectMapKeyCallBacks, + NSObjectMapValueCallBacks, 19); + [propertyList setObject:@"1" forKey:@"Version"]; + + return self; +} + +- (void)dealloc +{ + [propertyList release]; + [topLevelObjects release]; + NSFreeMapTable(objects); + NSFreeHashTable(conditionals); + NSFreeMapTable(classes); + + return [super dealloc]; +} + +- (NSString*)newLabel +{ + return [NSString stringWithFormat:@"Object%5d", ++counter]; +} + +- (BOOL)writeToFile:(NSString*)path +{ + return [propertyList writeToFile:path atomically:YES]; +} + +- (id)encodeRootObject:(id)rootObject withName:(NSString*)name +{ + id originalPList = propertyList; + int oldCounter = counter; + id label; + + if (writingRoot) +#if 1 + [NSException raise: NSInconsistentArchiveException + format: @"CoderHasAlreadyWrittenRootObject"]; +#else + THROW([CoderHasAlreadyWrittenRootObjectException new]); +#endif + + writingRoot = YES; + + /* + * Prepare for writing the graph objects for which `rootObject' is the root + * node. The algorithm consists from two passes. In the first pass it + * determines the nodes so-called 'conditionals' - the nodes encoded *only* + * with -encodeConditionalObject:. They represent nodes that are not + * related directly to the graph. In the second pass objects are encoded + * normally, except for the conditional objects which are encoded as nil. + */ + + /* First pass. */ + findingConditionals = YES; + lastObjectRepresentation = propertyList = nil; + NSResetHashTable(conditionals); + NSResetMapTable(objects); + [self encodeObject:rootObject withName:name]; + + /* Second pass. */ + findingConditionals = NO; + counter = oldCounter; + lastObjectRepresentation = propertyList = originalPList; + NSResetMapTable(objects); + label = [self encodeObject:rootObject withName:name]; + + writingRoot = NO; + + return label; +} + +- (id)encodeConditionalObject:(id)anObject withName:(NSString*)name +{ +#if 0 + if (!writingRoot) + THROW([RootObjectHasNotBeenWrittenException new]); +#endif + + if (findingConditionals) { + /* + * This is the first pass of the determining the conditionals + * algorithm. We traverse the graph and insert into the `conditionals' + * set. In the second pass all objects that are still in this set will + * be encoded as nil when they receive -encodeConditionalObject:. An + * object is removed from this set when it receives -encodeObject:. + */ + id value; + + if (!anObject) + return nil; + + /* Lookup anObject into the `conditionals' set. If it is then the + object is still a conditional object. */ + value = (id)NSHashGet(conditionals, anObject); + if (value) + return value; + + /* + * Maybe it has received -encodeObject: + * and now is in the `objects' set. + */ + value = (id)NSMapGet(objects, anObject); + if (value) + return value; + + /* anObject was not written previously. */ + NSHashInsert(conditionals, anObject); + } + else { + /* If anObject is in the `conditionals' set, it is encoded as nil. */ + if (!anObject || NSHashGet(conditionals, anObject)) + return [self encodeObject:nil withName:name]; + else + return [self encodeObject:anObject withName:name]; + } + + return nil; +} + +- (id)encodeObject:(id)anObject withName:(NSString*)name +{ + if (!anObject) { + if (!findingConditionals && name) + [lastObjectRepresentation setObject:@"nil" forKey:name]; + return @"nil"; + } + else { + id label; + id upperObjectRepresentation; + + label = NSMapGet(objects, anObject); + + if (findingConditionals && !label) { + /* + * Look-up the object in the `conditionals' set. If the object is + * there, then remove it because it is no longer a conditional one. + */ + label = NSHashGet(conditionals, anObject); + if (label) { + NSHashRemove(conditionals, anObject); + NSMapInsert(objects, anObject, [self newLabel]); + return label; + } + } + + if (!label) { + Class archiveClass; + + /* If the object gets encoded on the top level, set the label to be + `name'. */ + if (!level) { + if (!name) { + NSLog (@"Can't encode top level object with a nil name!"); + return nil; + } + label = name; + } + else + label = [self newLabel]; + + NSMapInsert(objects, anObject, label); + + /* Temporary save the last object into upperObjectRepresentation so we + can restore the stack of objects being encoded after anObject is + encoded. */ + upperObjectRepresentation = lastObjectRepresentation; + + anObject = [anObject replacementObjectForModelArchiver:self]; + archiveClass = [anObject classForModelArchiver]; + + if (!findingConditionals) { + NSMutableDictionary* objectPList = [NSMutableDictionary dictionary]; + + /* If anObject is the first object in the graph that receives the + -encodeObject:withName: message, save its label into the + topLevelObjects array. */ + if (!level) + [topLevelObjects addObject:(name ? name : label)]; + + lastObjectRepresentation = objectPList; + + if (level) { + /* Encode 'name = label' in the object's representation and put the + description of anObject on the top level like 'label = object'. + */ + if (name) + [upperObjectRepresentation setObject:label forKey:name]; + [propertyList setObject:objectPList forKey:label]; + } + else { + /* The encoded object is on the top level so encode it and put it + under the key 'name'. */ + if (name) + label = name; + [propertyList setObject:objectPList + forKey:label]; + } + + [objectPList setObject:NSStringFromClass(archiveClass) + forKey:@"isa"]; + } + else { + /* + * This is the first pass of determining the conditionals + * objects algorithm. Remove anObject from the `conditionals' + * set if it is there and insert it into the `objects' set. + */ + NSHashRemove(conditionals, anObject); + } + + level++; + [anObject encodeWithModelArchiver:self]; + level--; + + lastObjectRepresentation = upperObjectRepresentation; + } + else if (!findingConditionals) { + if (name) + [lastObjectRepresentation setObject:label forKey:name]; + } + + return label; + } +} + +- (id)encodeString:(NSString*)anObject withName:(NSString*)name +{ + if (!findingConditionals) { + if (!anObject) { + if (name) + [lastObjectRepresentation setObject:@"nil" forKey:name]; + return @"nil"; + } + else { + if (name) + [lastObjectRepresentation setObject:anObject forKey:name]; + return anObject; + } + } + + return @"nil"; +} + +- (id)encodeData:(NSData*)anObject withName:(NSString*)name +{ + if (!findingConditionals) { + if (!anObject) { + if (name) + [lastObjectRepresentation setObject:@"nil" forKey:name]; + return @"nil"; + } + else { + if (name) + [lastObjectRepresentation setObject:anObject forKey:name]; + return anObject; + } + } + + return @"nil"; +} + +- (id)encodeArray:(NSArray*)array withName:(NSString*)name +{ + if (array) { + int i, count = [array count]; + NSMutableArray* description = [NSMutableArray arrayWithCapacity:count]; + + for (i = 0; i < count; i++) { + id object = [array objectAtIndex:i]; + [description addObject:[self encodeObject:object withName:nil]]; + } + if (name) + [lastObjectRepresentation setObject:description forKey:name]; + return description; + } + else { + if (name) + [lastObjectRepresentation setObject:@"nil" forKey:name]; + return @"nil"; + } +} + +- (id)encodeDictionary:(NSDictionary*)dictionary withName:(NSString*)name +{ + if (dictionary) { + id enumerator, key; + NSMutableDictionary* description + = [NSMutableDictionary dictionaryWithCapacity:[dictionary count]]; + + enumerator = [dictionary keyEnumerator]; + + while ((key = [enumerator nextObject])) { + id value = [dictionary objectForKey:key]; + id keyDesc = [self encodeObject:key withName:nil]; + id valueDesc = [self encodeObject:value withName:nil]; + + [description setObject:valueDesc forKey:keyDesc]; + } + if (name) + [lastObjectRepresentation setObject:description forKey:name]; + return description; + } + else { + if (name) + [lastObjectRepresentation setObject:@"nil" forKey:name]; + return @"nil"; + } +} + +- (id)propertyList +{ + return propertyList; +} + +- (id)encodeClass:(Class)class withName:(NSString*)name +{ + if (class) + return [self encodeString:NSStringFromClass(class) withName:name]; + else + return [self encodeString:nil withName:name]; +} + +- (id)encodeSelector:(SEL)selector withName:(NSString*)name +{ + if (selector) + return [self encodeString:NSStringFromSelector(selector) withName:name]; + else + return [self encodeString:nil withName:name]; +} + +- (void)encodeChar:(char)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%c", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeUnsignedChar:(unsigned char)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%uc", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeBOOL:(BOOL)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%s", value ? "YES" : "NO"]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeShort:(short)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%s", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeUnsignedShort:(unsigned short)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%us", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeInt:(int)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%i", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeUnsignedInt:(unsigned int)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%u", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeLong:(long)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%l", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeUnsignedLong:(unsigned long)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%lu", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeFloat:(float)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%f", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeDouble:(double)value withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = [NSString stringWithFormat:@"%f", value]; + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodePoint:(NSPoint)point withName:(NSString*)name +{ + if (!findingConditionals && name) { + id valueString = NSStringFromPoint (point); + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeSize:(NSSize)size withName:(NSString*)name +{ + if (!findingConditionals) { + id valueString = NSStringFromSize (size); + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (void)encodeRect:(NSRect)rect withName:(NSString*)name +{ + if (!findingConditionals) { + id valueString = NSStringFromRect (rect); + + [lastObjectRepresentation setObject:valueString forKey:name]; + } +} + +- (NSString*)classNameEncodedForTrueClassName:(NSString*)trueName +{ + id inArchiveName = [(id)NSMapGet(classes, trueName) className]; + + return inArchiveName ? inArchiveName : trueName; +} + +/* In the following method the version of class named trueName is written as + version for class named inArchiveName. Is this right? It is possible for + the inArchiveName class that it could not be linked in the running process + at the time the archive is written. */ +- (void)encodeClassName:(NSString*)trueName + intoClassName:(NSString*)inArchiveName +{ + id classInfo = [GMClassInfo classInfoWithClassName:inArchiveName + version:[NSClassFromString(trueName) version]]; + + NSMapInsert(classes, trueName, classInfo); +} + +@end /* GMArchiver */ + + +@implementation GMUnarchiver + +static NSMutableDictionary* classToAliasMappings = nil; + ++ (void)initialize +{ + classToAliasMappings = [NSMutableDictionary new]; +} + ++ (id)unarchiverWithContentsOfFile:(NSString*)path +{ + id plist = [[NSString stringWithContentsOfFile:path] propertyList]; + GMUnarchiver* unarchiver; + + if (!plist) + return nil; + + unarchiver = [[[self alloc] initForReadingWithPropertyList:plist] + autorelease]; + return unarchiver; +} + ++ (id)unarchiveObjectWithName:(NSString*)name + fromPropertyList:(id)plist +{ + GMUnarchiver* unarchiver + = [[[self alloc] initForReadingWithPropertyList:plist] autorelease]; + + return [unarchiver decodeObjectWithName:name]; +} + ++ (id)unarchiveObjectWithName:(NSString*)name fromFile:(NSString*)path +{ + GMUnarchiver* unarchiver = [self unarchiverWithContentsOfFile:path]; + return [unarchiver decodeObjectWithName:name]; +} + +- init +{ + return [self initForReadingWithPropertyList:nil]; +} + +- (id)initForReadingWithPropertyList:(id)plist +{ + NSString* versionString; + + propertyList = [plist copy]; + currentDecodedObjectRepresentation = propertyList; + namesToObjects = [NSMutableDictionary dictionaryWithCapacity:119]; + + /* Decode version information */ + versionString = [propertyList objectForKey:@"Version"]; + [[NSScanner scannerWithString:versionString] scanInt:&version]; + + objectZone = NSDefaultMallocZone (); + + return self; +} + +- (id)decodeObjectWithName:(NSString*)name +{ + id object, label, representation, className; + id upperObjectRepresentation; + BOOL objectOnTopLevel = NO; + id newObject; + Class class; + + if (!name) + return nil; + + if (level) { + /* First try to see if the object has been already decoded */ + if ((object = [namesToObjects objectForKey:name])) + return object; + } + + /* The object has not been decoded yet. Read its label from the current + object dictionary representation. */ + label = [currentDecodedObjectRepresentation objectForKey:name]; + + if (label) { + /* Try to see if the object has been decoded using `label' as name */ + if ((object = [namesToObjects objectForKey:label])) + return object; + } + else { + /* Try to find the object on the top level */ + label = [propertyList objectForKey:name]; + + if (label) + objectOnTopLevel = YES; + else { + /* There is no object with this name within the current object or on the + top level. */ +#if 0 + NSLog (@"No object named '%@' in object representation '%@'", + name, currentDecodedObjectRepresentation); +#endif + return nil; + } + } + + /* If we are on the top level the description is really the representation of + the object. Otherwise the value is the name of an object on the top level. + */ + if (currentDecodedObjectRepresentation != propertyList + && !objectOnTopLevel) { + NSAssert1 ([label isKindOfClass:[NSString class]], + @"label is not a string: '%@'!", label); + + /* label is either a name of an object on the top level dictionary or the + string "nil" which means the object has the nil value. */ + if ([label isEqual:@"nil"]) + return nil; + + representation = [propertyList objectForKey:label]; + } + else { + representation = label; + label = name; + } + + if (!representation) { + /* There is no object with such a label on the top level dictionary */ + NSLog (@"No object object named '%@' on the top level dictionary! (error " + @"within object representation '%@')", + label, currentDecodedObjectRepresentation); + return nil; + } + + /* Temporary save the current object representation */ + upperObjectRepresentation = currentDecodedObjectRepresentation; + currentDecodedObjectRepresentation = representation; + + /* Create the object */ + className = [representation objectForKey:@"isa"]; + class = NSClassFromString(className); + object = [class createObjectForModelUnarchiver:self]; + + if (!class) { + NSLog (@"Class %@ not linked into application!", className); + return nil; + } + + /* Push it into the dictionary of known objects */ + [namesToObjects setObject:object forKey:label]; + + /* Read it from dictionary */ + level++; + newObject = [object initWithModelUnarchiver:self]; + level--; + + if (newObject != object) { + object = newObject; + [namesToObjects setObject:object forKey:label]; + } + + /* Restore the current object representation */ + currentDecodedObjectRepresentation = upperObjectRepresentation; + + return object; +} + +- (NSString*)decodeStringWithName:(NSString*)name +{ + id string; + + if (!name) + return nil; + + string = [currentDecodedObjectRepresentation objectForKey:name]; + if (!string) { +#if 0 + NSLog (@"Couldn't find the string value for key '%@' (object '%@')", + name, currentDecodedObjectRepresentation); +#endif + return nil; + } + + if (![string isKindOfClass:[NSString class]]) { + NSLog (@"Decoded object is not a string: '%@'! (key '%@', object '%@')", + string, name, currentDecodedObjectRepresentation); + return nil; + } + + return string; +} + +- (NSData*)decodeDataWithName:(NSString*)name +{ + id data; + + if (!name) + return nil; + + data = [currentDecodedObjectRepresentation objectForKey:name]; + if (!data) { + NSLog (@"Couldn't find the data value for key '%@' (object '%@')", + name, currentDecodedObjectRepresentation); + return nil; + } + + if (![data isKindOfClass:[NSData class]]) { + NSLog (@"Decoded object is not a data: '%@'! (key '%@', object '%@')", + data, name, currentDecodedObjectRepresentation); + return nil; + } + + return data; +} + +- (NSArray*)decodeArrayWithName:(NSString*)name +{ + id array, decodedArray; + int i, count; + + if (!name) + return nil; + + array = [currentDecodedObjectRepresentation objectForKey:name]; + if (!array) { + NSLog (@"Couldn't find the array value for key '%@' (object '%@')", + name, currentDecodedObjectRepresentation); + return nil; + } + + if (![array isKindOfClass:[NSArray class]]) { + NSLog (@"Decoded object is not an array: '%@'! (key '%@', object '%@')", + array, name, currentDecodedObjectRepresentation); + return nil; + } + + count = [array count]; + decodedArray = [NSMutableArray arrayWithCapacity:count]; + for (i = 0; i < count; i++) { + id label = [array objectAtIndex:i]; + id objectDescription = [propertyList objectForKey:label]; + + if (!objectDescription) { + NSLog (@"warning: couldn't find the description for object labeled '%@' " + @"in the array description '%@ = %@'!", label, name, array); + continue; + } + + [decodedArray addObject:[self decodeObjectWithName:label]]; + } + + return decodedArray; +} + +- (NSDictionary*)decodeDictionaryWithName:(NSString*)name +{ + id dictionary, decodedDictionary; + id enumerator, keyLabel, valueLabel; + + if (!name) + return nil; + + dictionary = [currentDecodedObjectRepresentation objectForKey:name]; + if (!dictionary) { + NSLog (@"Couldn't find the dictionary value for key '%@' (object '%@')", + name, currentDecodedObjectRepresentation); + return nil; + } + + if (![dictionary isKindOfClass:[NSDictionary class]]) { + NSLog (@"Decoded object is not a dictionary: '%@'! (key '%@', object '%@')", + dictionary, name, currentDecodedObjectRepresentation); + return nil; + } + + decodedDictionary + = [NSMutableDictionary dictionaryWithCapacity:[dictionary count]]; + enumerator = [dictionary keyEnumerator]; + while ((keyLabel = [enumerator nextObject])) { + id key, value, objectDescription; + + objectDescription = [propertyList objectForKey:keyLabel]; + if (!objectDescription) { + NSLog (@"warning: couldn't find the description for object labeled '%@' " + @"in the dictionary description '%@ = %@'!", + keyLabel, name, dictionary); + continue; + } + key = [self decodeObjectWithName:keyLabel]; + + valueLabel = [dictionary objectForKey:keyLabel]; + objectDescription = [propertyList objectForKey:valueLabel]; + if (!objectDescription) { + NSLog (@"warning: couldn't find the description for object labeled '%@' " + @"in the dictionary description '%@ = %@'!", + valueLabel, name, dictionary); + continue; + } + value = [self decodeObjectWithName:valueLabel]; + + [decodedDictionary setObject:value forKey:key]; + } + + return decodedDictionary; +} + +- (Class)decodeClassWithName:(NSString*)name +{ + NSString* className = [self decodeStringWithName:name]; + + return className ? NSClassFromString (className) : Nil; +} + +- (SEL)decodeSelectorWithName:(NSString*)name +{ + NSString* selectorName = [self decodeStringWithName:name]; + + return selectorName ? NSSelectorFromString (selectorName) : NULL; +} + +- (char)decodeCharWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return 0; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return 0; + + return *[valueString cString]; +} + +- (unsigned char)decodeUnsignedCharWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return 0; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return 0; + + return *[valueString cString]; +} + +- (BOOL)decodeBOOLWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return NO; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return NO; + + return [valueString compare:@"YES" options:NSCaseInsensitiveSearch] + == NSOrderedSame; +} + +- (short)decodeShortWithName:(NSString*)name +{ + return [self decodeIntWithName:name]; +} + +- (unsigned short)decodeUnsignedShortWithName:(NSString*)name +{ + return [self decodeIntWithName:name]; +} + +- (int)decodeIntWithName:(NSString*)name +{ + NSString* valueString; + int value; + + if (!name) + return 0; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return 0; + + if (![[NSScanner scannerWithString:valueString] scanInt:&value]) { + NSLog (@"Cannot scan integer value '%@' from object '%@' under key '%@'", + valueString, currentDecodedObjectRepresentation, name); + return 0; + } + + return value; +} + +- (unsigned int)decodeUnsignedIntWithName:(NSString*)name +{ + return [self decodeIntWithName:name]; +} + +- (long)decodeLongWithName:(NSString*)name +{ + return [self decodeIntWithName:name]; +} + +- (unsigned long)decodeUnsignedLongWithName:(NSString*)name +{ + return [self decodeIntWithName:name]; +} + +- (float)decodeFloatWithName:(NSString*)name +{ + NSString* valueString; + float value; + + if (!name) + return 0; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return 0; + + if (![[NSScanner scannerWithString:valueString] scanFloat:&value]) { + NSLog (@"Cannot scan float value '%@' from object '%@' under key '%@'", + valueString, currentDecodedObjectRepresentation, name); + return 0; + } + + return value; +} + +- (double)decodeDoubleWithName:(NSString*)name +{ + return [self decodeDoubleWithName:name]; +} + +- (NSPoint)decodePointWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return NSZeroPoint; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return NSZeroPoint; + + return NSPointFromString (valueString); +} + +- (NSSize)decodeSizeWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return NSZeroSize; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return NSZeroSize; + + return NSSizeFromString (valueString); +} + +- (NSRect)decodeRectWithName:(NSString*)name +{ + NSString* valueString; + + if (!name) + return NSZeroRect; + + valueString = [currentDecodedObjectRepresentation objectForKey:name]; + if (!valueString) + return NSZeroRect; + + return NSRectFromString (valueString); +} + +- (BOOL)isAtEnd +{ + // TODO + return NO; +} + +- (void)setObjectZone:(NSZone*)zone { objectZone = zone; } +- (unsigned int)systemVersion { return version; } +- (NSZone*)objectZone { return objectZone; } + ++ (NSString*)classNameDecodedForArchiveClassName:(NSString*)nameInArchive +{ + NSString* className = [classToAliasMappings objectForKey:nameInArchive]; + + return className ? className : nameInArchive; +} + ++ (void)decodeClassName:(NSString*)nameInArchive + asClassName:(NSString*)trueName +{ + [classToAliasMappings setObject:trueName forKey:nameInArchive]; +} + +- (NSString*)classNameDecodedForArchiveClassName:(NSString*)nameInArchive +{ + return nameInArchive; +} + +- (void)decodeClassName:(NSString*)nameInArchive + asClassName:(NSString*)trueName +{ +} + +- (unsigned int)versionForClassName:(NSString*)className +{ + return 1; +} + +@end /* GMUnarchiver */ + + +#include "GMArchiveObjects.m" diff --git a/Model/GNUmakefile b/Model/GNUmakefile index 7fc8d2a2c..f816b2f75 100644 --- a/Model/GNUmakefile +++ b/Model/GNUmakefile @@ -37,7 +37,8 @@ endif #APP_NAME = test -libgmodel_OBJC_FILES = IMCustomObject.m IMConnectors.m IMLoading.m GMAppKit.m +libgmodel_OBJC_FILES = IMCustomObject.m IMConnectors.m IMLoading.m GMAppKit.m \ + GMArchiver.m libgmodel_HEADER_FILES_DIR = ../Headers/AppKit libgmodel_HEADER_FILES_INSTALL_DIR = /gnustep/gui/AppKit libgmodel_HEADER_FILES = IMConnectors.h IMCustomObject.h IMLoading.h GMAppKit.h @@ -59,7 +60,7 @@ ADDITIONAL_CPPFLAGS += $(RUNTIME_DEFINE) $(GUI_DEFINE) $(BACKEND_DEFINE) ADDITIONAL_LIB_DIRS += -L$(GNUSTEP_OBJ_DIR) -ADDITIONAL_TOOL_LIBS += -lgmodel -lFoundationExt +ADDITIONAL_TOOL_LIBS += -lgmodel ADDITIONAL_OBJC_FLAGS += $(BACKEND_DEFINE) # Additional include directories the compiler should search @@ -69,7 +70,7 @@ ADDITIONAL_INCLUDE_DIRS = -I../Headers -I../Headers/gnustep # systems where building a shared library requires to pass to the linker # all the libraries the target library depends upon. -LIBRARIES_DEPEND_UPON = -lFoundationExt $(FND_LIBS) $(GUI_LIBS) \ +LIBRARIES_DEPEND_UPON = $(FND_LIBS) $(GUI_LIBS) \ $(BACKEND_LIBS) $(SYSTEM_LIBS) -include GNUmakefile.local diff --git a/Model/GNUmakefile.postamble b/Model/GNUmakefile.postamble index 756d9ee9c..002c733dd 100644 --- a/Model/GNUmakefile.postamble +++ b/Model/GNUmakefile.postamble @@ -27,3 +27,6 @@ before-libgmodel-all:: \ ../Headers/gnustep/gui/%.h: %.h cp $^ $@ + +# Additional dependencies +$(GNUSTEP_OBJ_DIR)/GMArchiver.o: GMArchiveObjects.m diff --git a/Model/IBClasses.m b/Model/IBClasses.m index 1ab27c54c..3c080bd26 100644 --- a/Model/IBClasses.m +++ b/Model/IBClasses.m @@ -28,7 +28,7 @@ #import #import #import -#import +#import #import "IBClasses.h" #import "Translator.h" #import "IMConnectors.h" diff --git a/Model/IMConnectors.m b/Model/IMConnectors.m index fc38b6877..7c6f58dc5 100644 --- a/Model/IMConnectors.m +++ b/Model/IMConnectors.m @@ -26,11 +26,23 @@ #include #import -#include -#include +#include #include "AppKit/IMCustomObject.h" #include "IMConnectors.h" +#if GNU_RUNTIME + +#include +#include +#include + +#else /* NeXT_RUNTIME */ + +#include +#include +#include +#endif + static void object_set_instance_variable (id anObject, const char* variableName, diff --git a/Model/IMCustomObject.m b/Model/IMCustomObject.m index 895624654..807d1d430 100644 --- a/Model/IMCustomObject.m +++ b/Model/IMCustomObject.m @@ -24,7 +24,7 @@ */ #import -#include +#include #include "AppKit/IMCustomObject.h" @implementation NSObject(ModelUnarchiving) diff --git a/Model/IMLoading.m b/Model/IMLoading.m index 284704e78..2429aa36a 100644 --- a/Model/IMLoading.m +++ b/Model/IMLoading.m @@ -28,7 +28,7 @@ #import #import -#include +#include #include "AppKit/IMLoading.h" #include "AppKit/IMCustomObject.h" diff --git a/Model/Translator.m b/Model/Translator.m index b0f844774..cf39799de 100644 --- a/Model/Translator.m +++ b/Model/Translator.m @@ -32,7 +32,7 @@ #import #import -#import +#import #import "AppKit/IMLoading.h" #import "IBClasses.h" #import "Translator.h" diff --git a/Source/NSPanel.m b/Source/NSPanel.m index a369b07b9..889873d9e 100644 --- a/Source/NSPanel.m +++ b/Source/NSPanel.m @@ -47,7 +47,7 @@ #include #include -#include +#include diff --git a/Source/NSSavePanel.m b/Source/NSSavePanel.m index 606c2cd7d..181ea6104 100644 --- a/Source/NSSavePanel.m +++ b/Source/NSSavePanel.m @@ -44,7 +44,7 @@ #include #include #include -#include +#include // toDo: // - interactive directory creation in SavePanel