1/* GormClassManager.m
2 *
3 * Copyright (C) 1999 Free Software Foundation, Inc.
4 *
5 * Author:	Richard Frith-Macdonald <richard@brainstrom.co.uk>
6 * Author:	Gregory John Casamento <greg_casamento@yahoo.com>
7 * Date:	1999, 2002
8 *
9 * This file is part of GNUstep.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
24 */
25
26#include "GormPrivate.h"
27#include "GormCustomView.h"
28#include "GormDocument.h"
29#include "GormFilesOwner.h"
30#include "GormPalettesManager.h"
31#include <InterfaceBuilder/IBEditors.h>
32#include <InterfaceBuilder/IBPalette.h>
33#include <Foundation/NSValue.h>
34#include <Foundation/NSException.h>
35
36#include <GormObjCHeaderParser/OCHeaderParser.h>
37#include <GormObjCHeaderParser/OCClass.h>
38#include <GormObjCHeaderParser/OCMethod.h>
39#include <GormObjCHeaderParser/OCIVar.h>
40
41/**
42 * Just a few definitions to start things out.  To increase efficiency,
43 * so that Gorm doesn't need to constantly derive the method list for
44 * each class, it is necessary to cache some information.  Here is the
45 * way it works.
46 *
47 * Actions = All actions on that class, excluding superclass methods.
48 * AllActions = All actions on that class including superclass methods.
49 * ExtraActions = All actions added during this session.
50 *
51 * Outlets = All actions on that class, excluding superclass methods.
52 * AllOutlets = All actions on that class including superclass methods.
53 * ExtraOutlets = All actions added during this session.
54 */
55
56/** Private methods not accesible from outside */
57@interface GormClassManager (Private)
58- (NSMutableDictionary*) classInfoForClassName: (NSString*)className;
59- (NSMutableDictionary*) classInfoForObject: (id)anObject;
60- (void) touch;
61- (void) convertDictionary: (NSMutableDictionary *)dict;
62@end
63
64@interface NSMutableArray (Private)
65- (void) mergeObject: (id)object;
66- (void) mergeObjectsFromArray: (NSArray *)array;
67@end
68
69@implementation NSMutableArray (Private)
70- (void) mergeObject: (id)object
71{
72  if ([self containsObject: object] == NO)
73    {
74      [self addObject: object];
75      [self sortUsingSelector: @selector(compare:)];
76    }
77}
78
79- (void) mergeObjectsFromArray: (NSArray *)array
80{
81  id            obj = nil;
82
83  if(array != nil)
84    {
85      NSEnumerator	*enumerator = [array objectEnumerator];
86      while ((obj = [enumerator nextObject]) != nil)
87	{
88	  [self mergeObject: obj];
89	}
90    }
91}
92@end
93
94@implementation GormClassManager
95
96- (id) initWithDocument: (id)aDocument
97{
98  self = [super init];
99  if (self != nil)
100    {
101      NSBundle			*bundle = [NSBundle mainBundle];
102      NSString			*path;
103
104      document = aDocument;  // the document retains us, this is for convenience
105
106      path = [bundle pathForResource: @"ClassInformation" ofType: @"plist"];
107      if (path == nil)
108	{
109	  NSLog(@"ClassInformation.plist missing from resources");
110	}
111      else
112	{
113	  GormPalettesManager *palettesManager = [(id<Gorm>)NSApp palettesManager];
114	  NSDictionary *importedClasses = [palettesManager importedClasses];
115	  NSEnumerator *en = [importedClasses objectEnumerator];
116	  NSDictionary *description = nil;
117
118	  // load the classes, initialize the custom class array and map..
119	  if([self loadFromFile: path])
120	    {
121	      NSMutableDictionary *classDict = [classInformation objectForKey: @"FirstResponder"];
122	      NSMutableArray *firstResponderActions = [classDict objectForKey: @"Actions"];
123
124	      customClasses = [[NSMutableArray alloc] initWithCapacity: 1];
125	      customClassMap = [[NSMutableDictionary alloc] initWithCapacity: 10];
126	      categoryClasses = [[NSMutableArray alloc] initWithCapacity: 1];
127
128	      // add the imported classes to the class information list...
129	      [classInformation addEntriesFromDictionary: importedClasses];
130
131	      // add all of the actions to the FirstResponder
132	      while((description = [en nextObject]) != nil)
133		{
134		  NSArray *actions = [description objectForKey: @"Actions"];
135		  NSEnumerator *aen = [actions objectEnumerator];
136		  NSString *actionName = nil;
137
138		  // add the actions to the first responder...
139		  while((actionName = [aen nextObject]) != nil)
140		    {
141		      if(![firstResponderActions containsObject: actionName])
142			{
143			  [firstResponderActions addObject: [actionName copy]];
144			}
145		    }
146		}
147
148	      // incorporate the added actions into the list and sort.
149	      [self allActionsForClassNamed: @"FirstResponder"];
150	    }
151	}
152    }
153
154  return self;
155}
156
157- (void) touch
158{
159  [[NSNotificationCenter defaultCenter]
160    postNotificationName: GormDidModifyClassNotification
161    object: self];
162
163  [document touch];
164}
165
166- (void) convertDictionary: (NSMutableDictionary *)dict
167{
168  [dict removeObjectsForKeys: [classInformation allKeys]];
169}
170
171- (NSString *) uniqueClassNameFrom: (NSString *)name
172{
173  NSString *search = [NSString stringWithString: name];
174  NSInteger i = 1;
175
176  while([classInformation objectForKey: search])
177    {
178      search = [name stringByAppendingString: [NSString stringWithFormat: @"%ld",(long)i++]];
179    }
180
181  return search;
182}
183
184- (NSString *) addClassWithSuperClassName: (NSString*)name
185{
186  if (([self isRootClass: name] || [classInformation objectForKey: name] != nil)
187      && [name isEqual: @"FirstResponder"] == NO)
188    {
189      NSMutableDictionary	*classInfo;
190      NSMutableArray		*outlets;
191      NSMutableArray		*actions;
192      NSString			*className = [self uniqueClassNameFrom: @"NewClass"];
193
194      classInfo = [[NSMutableDictionary alloc] initWithCapacity: 3];
195      outlets = [[NSMutableArray alloc] initWithCapacity: 0];
196      actions = [[NSMutableArray alloc] initWithCapacity: 0];
197
198      [classInfo setObject: outlets forKey: @"Outlets"];
199      [classInfo setObject: actions forKey: @"Actions"];
200      [classInfo setObject: name forKey: @"Super"];
201
202      [classInformation setObject: classInfo forKey: className];
203      [customClasses addObject: className];
204
205      [self touch];
206
207      [[NSNotificationCenter defaultCenter]
208	postNotificationName: GormDidAddClassNotification
209	object: self];
210
211      return className;
212    }
213
214  return nil;
215}
216
217- (NSString *) addNewActionToClassNamed: (NSString *)name
218{
219  NSArray *combined = [self allActionsForClassNamed: name];
220  NSString *newAction = @"newAction";
221  NSString *search = [newAction stringByAppendingString: @":"];
222  NSString *new = nil;
223  NSInteger i = 1;
224
225  while ([combined containsObject: search])
226    {
227      new = [newAction stringByAppendingFormat: @"%ld", (long)i++];
228      search = [new stringByAppendingString: @":"];
229    }
230
231  [self addAction: search forClassNamed: name];
232  return search;
233}
234
235- (NSString *) addNewOutletToClassNamed: (NSString *)name
236{
237  NSArray *combined = [self allOutletsForClassNamed: name];
238  NSString *newOutlet = @"newOutlet";
239  NSString *new = newOutlet;
240  NSInteger i = 1;
241
242  while ([combined containsObject: new])
243    {
244      new = [newOutlet stringByAppendingFormat: @"%ld", (long)i++];
245    }
246
247  [self addOutlet: new forClassNamed: name];
248  return new;
249}
250
251- (BOOL) addClassNamed: (NSString *)className
252   withSuperClassNamed: (NSString *)superClassName
253	   withActions: (NSArray *)actions
254	   withOutlets: (NSArray *)outlets
255{
256  return [self addClassNamed: className
257	       withSuperClassNamed: superClassName
258	       withActions: actions
259	       withOutlets: outlets
260	       isCustom: YES];
261}
262
263- (BOOL) addClassNamed: (NSString *)className
264   withSuperClassNamed: (NSString *)superClassName
265	   withActions: (NSArray *)actions
266	   withOutlets: (NSArray *)outlets
267	      isCustom: (BOOL) isCustom
268{
269  BOOL result = NO;
270  NSString *classNameCopy = [NSString stringWithString: className];
271  NSString *superClassNameCopy = (superClassName != nil)?[NSString stringWithString: superClassName]:nil;
272  NSMutableArray *actionsCopy = (actions != nil)?[NSMutableArray arrayWithArray: actions]:[NSMutableArray array];
273  NSMutableArray *outletsCopy = (outlets != nil)?[NSMutableArray arrayWithArray: outlets]:[NSMutableArray array];
274
275  // We make an autoreleased copy of all of the inputs.  This prevents changes
276  // to the original objects from reflecting here. GJC
277
278  if ([self isRootClass: superClassNameCopy] ||
279      ([classInformation objectForKey: superClassNameCopy] != nil &&
280       [superClassNameCopy isEqualToString: @"FirstResponder"] == NO))
281    {
282      NSMutableDictionary	*classInfo;
283
284      if (![classInformation objectForKey: classNameCopy])
285	{
286	  NSEnumerator *e = [actionsCopy objectEnumerator];
287	  id action = nil;
288	  NSArray *superActions = [self allActionsForClassNamed: superClassNameCopy];
289	  NSArray *superOutlets = [self allOutletsForClassNamed: superClassNameCopy];
290
291	  [self touch];
292	  classInfo = [[NSMutableDictionary alloc] initWithCapacity: 3];
293
294	  // if an outlet/action is defined on the superclass before this
295	  // class is added, the superclass' entry takes precedence.
296	  [actionsCopy removeObjectsInArray: superActions];
297	  [outletsCopy removeObjectsInArray: superOutlets];
298
299	  [classInfo setObject: outletsCopy forKey: @"Outlets"];
300	  [classInfo setObject: actionsCopy forKey: @"Actions"];
301	  if(superClassNameCopy != nil)
302	    {
303	      [classInfo setObject: superClassNameCopy forKey: @"Super"];
304	    }
305	  [classInformation setObject: classInfo forKey: classNameCopy];
306
307	  // if it's a custom class add it to the list.
308	  if(isCustom)
309	    {
310	      [customClasses addObject: classNameCopy];
311	    }
312
313	  // copy all actions from the class imported to the first responder
314	  while((action = [e nextObject]))
315	    {
316	      [self addAction: action forClassNamed: @"FirstResponder"];
317	    }
318
319	  result = YES;
320
321	  // post the notification
322	  [[NSNotificationCenter defaultCenter]
323	    postNotificationName: GormDidAddClassNotification
324	    object: self];
325	}
326      else
327	{
328	  NSDebugLog(@"Class already exists");
329	  result = NO;
330	}
331    }
332
333  return result;
334}
335
336- (void) addAction: (NSString *)anAction forObject: (id)anObject
337{
338  [self addAction: anAction forClassNamed: [anObject className]];
339}
340
341- (void) addAction: (NSString *)action forClassNamed: (NSString *)className
342{
343  NSMutableDictionary *info = [classInformation objectForKey: className];
344  NSMutableArray *extraActions = [info objectForKey: @"ExtraActions"];
345  NSMutableArray *allActions = [info objectForKey: @"AllActions"];
346  NSString *anAction = [action copy];
347  NSArray *subClasses = [self allSubclassesOf: className];
348  NSEnumerator *en = [subClasses objectEnumerator];
349  NSString *subclassName = nil;
350
351  // check all
352  if ([allActions containsObject: anAction])
353    {
354      return;
355    }
356
357  if ([self isNonCustomClass: className])
358    {
359      if([categoryClasses containsObject: className] == NO)
360	{
361	  [categoryClasses addObject: className];
362	}
363    }
364
365  if (extraActions == nil)
366    {
367      extraActions = [[NSMutableArray alloc] initWithCapacity: 1];
368      [info setObject: extraActions forKey: @"ExtraActions"];
369    }
370
371  [extraActions mergeObject: anAction];
372  [allActions mergeObject: anAction];
373
374  if(![className isEqualToString: @"FirstResponder"])
375    {
376      [self addAction: anAction forClassNamed: @"FirstResponder"];
377    }
378
379  while((subclassName = [en nextObject]) != nil)
380    {
381      NSDictionary *subInfo = [classInformation objectForKey: subclassName];
382      NSMutableArray *subAll = [subInfo objectForKey: @"AllActions"];
383      [subAll mergeObject: anAction];
384    }
385
386  [self touch];
387}
388
389- (void) addOutlet: (NSString *)outlet forObject: (id)anObject
390{
391  [self addOutlet: outlet forClassNamed: [anObject className]];
392}
393
394- (void) addOutlet: (NSString *)outlet forClassNamed: (NSString *)className
395{
396  NSMutableDictionary *info = [classInformation objectForKey: className];
397  NSMutableArray *extraOutlets = [info objectForKey: @"ExtraOutlets"];
398  NSMutableArray *allOutlets = [info objectForKey: @"AllOutlets"];
399  NSString *anOutlet = [outlet copy];
400  NSArray *subClasses = [self allSubclassesOf: className];
401  NSEnumerator *en = [subClasses objectEnumerator];
402  NSString *subclassName = nil;
403
404  // check all
405  if ([allOutlets containsObject: anOutlet])
406    {
407      return;
408    }
409
410  if (extraOutlets == nil)
411    {
412      extraOutlets = [[NSMutableArray alloc] initWithCapacity: 1];
413      [info setObject: extraOutlets forKey: @"ExtraOutlets"];
414    }
415
416  [extraOutlets mergeObject: anOutlet];
417  [allOutlets mergeObject: anOutlet];
418
419  while((subclassName = [en nextObject]) != nil)
420    {
421      NSDictionary *subInfo = [classInformation objectForKey: subclassName];
422      NSMutableArray *subAll = [subInfo objectForKey: @"AllOutlets"];
423      [subAll mergeObject: anOutlet];
424    }
425
426  [self touch];
427}
428
429- (void) replaceAction: (NSString *)oldAction
430	    withAction: (NSString *)aNewAction
431	 forClassNamed: (NSString *)className
432{
433  NSMutableDictionary *info = [classInformation objectForKey: className];
434  NSMutableArray *extraActions = [info objectForKey: @"ExtraActions"];
435  NSMutableArray *actions = [info objectForKey: @"Actions"];
436  NSMutableArray *allActions = [info objectForKey: @"AllActions"];
437  NSString *newAction = AUTORELEASE([aNewAction copy]);
438  NSEnumerator *en = [[self subClassesOf: className] objectEnumerator];
439  NSString *subclassName = nil;
440
441  if ([allActions containsObject: newAction]
442    || [extraActions containsObject: newAction])
443    {
444      return;
445    }
446
447  // replace the action in the appropriate places.
448  if ([extraActions containsObject: oldAction])
449    {
450      NSInteger extra_index = [extraActions indexOfObject: oldAction];
451      [extraActions replaceObjectAtIndex: extra_index withObject: newAction];
452    }
453
454  if ([actions containsObject: oldAction])
455    {
456      NSInteger actions_index = [actions indexOfObject: oldAction];
457      [actions replaceObjectAtIndex: actions_index withObject: newAction];
458    }
459
460  if ([allActions containsObject: oldAction])
461    {
462      NSInteger all_index = [allActions indexOfObject: oldAction];
463      [allActions replaceObjectAtIndex: all_index withObject: newAction];
464    }
465
466  [self touch];
467
468  // add the action to all of the subclasses, in the "AllActions" section...
469  while((subclassName = [en nextObject]) != nil)
470    {
471      [self replaceAction: oldAction withAction: newAction forClassNamed: subclassName];
472    }
473
474  if(![className isEqualToString: @"FirstResponder"])
475    {
476      [self replaceAction: oldAction withAction: newAction forClassNamed: @"FirstResponder"];
477    }
478}
479
480- (void) replaceOutlet: (NSString *)oldOutlet
481	    withOutlet: (NSString *)aNewOutlet
482	 forClassNamed: (NSString *)className
483{
484  NSMutableDictionary *info = [classInformation objectForKey: className];
485  NSMutableArray *extraOutlets = [info objectForKey: @"ExtraOutlets"];
486  NSMutableArray *outlets = [info objectForKey: @"Outlets"];
487  NSMutableArray *allOutlets = [info objectForKey: @"AllOutlets"];
488  NSString *newOutlet = AUTORELEASE([aNewOutlet copy]);
489  NSEnumerator *en = [[self subClassesOf: className] objectEnumerator];
490  NSString *subclassName = nil;
491
492  if ([allOutlets containsObject: newOutlet]
493    || [extraOutlets containsObject: newOutlet])
494    {
495      return;
496    }
497
498  // replace outlets in appropriate places...
499  if ([extraOutlets containsObject: oldOutlet])
500    {
501      NSInteger extraIndex = [extraOutlets indexOfObject: oldOutlet];
502      [extraOutlets replaceObjectAtIndex: extraIndex withObject: newOutlet];
503    }
504
505  if ([outlets containsObject: oldOutlet])
506    {
507      NSInteger outletsIndex = [outlets indexOfObject: oldOutlet];
508      [outlets replaceObjectAtIndex: outletsIndex withObject: newOutlet];
509    }
510
511  if ([allOutlets containsObject: oldOutlet])
512    {
513      NSInteger allIndex = [allOutlets indexOfObject: oldOutlet];
514      [allOutlets replaceObjectAtIndex: allIndex withObject: newOutlet];
515    }
516
517  [self touch];
518
519  // add the action to all of the subclasses, in the "AllActions" section...
520  while((subclassName = [en nextObject]) != nil)
521    {
522      [self replaceOutlet: oldOutlet withOutlet: newOutlet forClassNamed: subclassName];
523    }
524}
525
526- (void) removeAction: (NSString *)anAction forObject: (id)anObject
527{
528  [self removeAction: anAction fromClassNamed: [anObject className]];
529}
530
531- (void) removeAction: (NSString *)anAction
532       fromClassNamed: (NSString *)className
533{
534  NSMutableDictionary	*info = [classInformation objectForKey: className];
535  NSMutableArray	*extraActions = [info objectForKey: @"ExtraActions"];
536  NSMutableArray        *allActions = [info objectForKey: @"AllActions"];
537  NSEnumerator *en = [[self subClassesOf: className] objectEnumerator];
538  NSString *subclassName = nil;
539
540  if ([extraActions containsObject: anAction] == YES ||
541      [allActions containsObject: anAction] == YES)
542    {
543      NSString	*superName = [info objectForKey: @"Super"];
544
545      if (superName != nil)
546	{
547	  NSArray	*superActions;
548
549	  /*
550	   * If this action is new in this class (ie not overriding an
551	   * action in a parent) then we remove it from the list of all
552	   * actions that the object responds to.
553	   */
554	  superActions = [self allActionsForClassNamed: superName];
555	  if ([superActions containsObject: anAction] == NO)
556	    {
557	      NSMutableArray	*array = [info objectForKey: @"AllActions"];
558	      NSMutableArray    *actions = [info objectForKey: @"Actions"];
559	      [array removeObject: anAction];
560	      [actions removeObject: anAction];
561	    }
562	}
563      else
564	{
565	  NSMutableArray *array = [info objectForKey: @"AllActions"];
566	  NSMutableArray *actions = [info objectForKey: @"Actions"];
567	  [array removeObject: anAction];
568	  [actions removeObject: anAction];
569	}
570
571      [extraActions removeObject: anAction];
572      [self touch];
573    }
574
575  if([categoryClasses containsObject: className] && [extraActions count] == 0)
576    {
577      [categoryClasses removeObject: className];
578    }
579
580  if(![className isEqualToString: @"FirstResponder"])
581    {
582      [self removeAction: anAction fromClassNamed: @"FirstResponder"];
583    }
584
585  while((subclassName = [en nextObject]) != nil)
586    {
587      [self removeAction: anAction fromClassNamed: subclassName];
588    }
589}
590
591- (void) removeOutlet: (NSString *)anOutlet forObject: (id)anObject
592{
593  [self removeOutlet: anOutlet fromClassNamed: [anObject className]];
594}
595
596- (void) removeOutlet: (NSString *)anOutlet fromClassNamed: (NSString *)className
597{
598  NSMutableDictionary	*info = [classInformation objectForKey: className];
599  NSMutableArray	*extraOutlets = [info objectForKey: @"ExtraOutlets"];
600  NSMutableArray	*allOutlets = [info objectForKey: @"AllOutlets"];
601  NSEnumerator *en = [[self subClassesOf: className] objectEnumerator];
602  NSString *subclassName = nil;
603
604  if ([extraOutlets containsObject: anOutlet] == YES
605    || [allOutlets containsObject: anOutlet] == YES)
606    {
607      NSString	*superName = [info objectForKey: @"Super"];
608
609      if (superName != nil)
610	{
611	  NSArray	*superOutlets;
612
613	  // remove the outlet from the other arrays...
614	  superOutlets = [self allOutletsForClassNamed: superName];
615	  if ([superOutlets containsObject: anOutlet] == NO)
616	    {
617	      NSMutableArray	*array = [info objectForKey: @"AllOutlets"];
618	      NSMutableArray    *actions = [info objectForKey: @"Outlets"];
619	      [array removeObject: anOutlet];
620	      [actions removeObject: anOutlet];
621	    }
622	}
623      else
624	{
625	  NSMutableArray *array = [info objectForKey: @"AllOutlets"];
626	  NSMutableArray *actions = [info objectForKey: @"Outlets"];
627	  [array removeObject: anOutlet];
628	  [actions removeObject: anOutlet];
629	}
630
631      [extraOutlets removeObject: anOutlet];
632      [self touch];
633    }
634
635  while((subclassName = [en nextObject]) != nil)
636    {
637      [self removeOutlet: anOutlet fromClassNamed: subclassName];
638    }
639}
640
641
642- (NSArray *) allActionsForObject: (id)obj
643{
644  NSString	*className;
645  NSArray	*actions;
646  Class		 theClass = [obj class];
647  NSString      *customClassName = [self customClassForObject: obj];
648
649  NSDebugLog(@"** ACTIONS");
650  NSDebugLog(@"Object: %@",obj);
651  NSDebugLog(@"Custom class: %@",customClassName);
652  if (customClassName != nil)
653    {
654      // if the object has been mapped to a custom class, then
655      // get the information for it.
656      className = customClassName;
657    }
658  else if (theClass == [GormFirstResponder class])
659    {
660      className = @"FirstResponder";
661    }
662  else if (theClass == [GormFilesOwner class])
663    {
664      className = [(GormFilesOwner*)obj className];
665    }
666  else if ([obj isKindOfClass: [GSNibItem class]] == YES)
667    {
668      // this adds support for custom objects
669      className = [obj className];
670    }
671  else if ([obj isKindOfClass: [GormClassProxy class]] == YES)
672    {
673      // this adds support for class proxies
674      className = [obj className];
675    }
676  else if ([obj isKindOfClass: [GormCustomView class]] == YES)
677    {
678      // this adds support for custom views
679      className = [obj className];
680    }
681  else
682    {
683      className = NSStringFromClass(theClass);
684    }
685  if (className == nil)
686    {
687      // NSLog(@"attempt to get actions for non-existent class (%@)",
688      //	[obj class]);
689      return nil;
690    }
691
692  actions = [self allActionsForClassNamed: className];
693  while (actions == nil && (theClass = class_getSuperclass(theClass)) != nil
694    && theClass != [NSObject class])
695    {
696      className = NSStringFromClass(theClass);
697      actions = [self allActionsForClassNamed: className];
698    }
699
700  NSDebugLog(@"class=%@ actions=%@",className,actions);
701  return actions;
702}
703
704- (NSArray *) allActionsForClassNamed: (NSString *)className
705{
706  NSMutableDictionary	*info = [classInformation objectForKey: className];
707
708  if (info != nil)
709    {
710      NSMutableArray	*allActions = [info objectForKey: @"AllActions"];
711
712      if (allActions == nil)
713	{
714	  NSString	*superName = [info objectForKey: @"Super"];
715	  NSArray	*actions = [info objectForKey: @"Actions"];
716	  NSArray       *extraActions = [info objectForKey: @"ExtraActions"];
717	  NSArray	*superActions;
718
719	  if (superName == nil || [className isEqual: @"FirstResponder"])
720	    {
721	      superActions = nil;
722	    }
723	  else
724	    {
725	      superActions = [self allActionsForClassNamed: superName];
726	    }
727
728	  if (superActions == nil)
729	    {
730	      if (actions == nil)
731		{
732		  allActions = [[NSMutableArray alloc] init];
733		}
734	      else
735		{
736		  allActions = [actions mutableCopy];
737		}
738
739	      [allActions mergeObjectsFromArray: extraActions];
740	    }
741	  else
742	    {
743	      allActions = [superActions mutableCopy];
744	      [allActions mergeObjectsFromArray: actions];
745	      [allActions mergeObjectsFromArray: extraActions];
746	    }
747
748	  [info setObject: allActions forKey: @"AllActions"];
749	  RELEASE(allActions);
750	}
751      return AUTORELEASE([allActions copy]);
752    }
753  return nil;
754}
755
756- (NSArray *) allCustomClassNames
757{
758  // return [customClassMap allKeys];
759  return customClasses;
760}
761
762- (NSArray *) allClassNames
763{
764  return [[classInformation allKeys] sortedArrayUsingSelector: @selector(compare:)];
765}
766
767- (NSArray *) allOutletsForObject: (id)obj
768{
769  NSString	*className;
770  NSArray	*outlets;
771  Class		theClass = [obj class];
772  NSString      *customClassName = [self customClassForObject: obj];
773
774  if (customClassName != nil)
775    {
776      // if the object has been mapped to a custom class, then
777      // get the information for it.
778      className = customClassName;
779    }
780  else if (theClass == [GormFirstResponder class])
781    {
782      return nil;
783    }
784  else if (theClass == [GormFilesOwner class])
785    {
786      className = [(GormFilesOwner*)obj className];
787    }
788  else if ([obj isKindOfClass: [GSNibItem class]] == YES)
789    {
790      // this adds support for custom objects
791      className = [(id)obj className];
792    }
793  else if ([obj isKindOfClass: [GormClassProxy class]] == YES)
794    {
795      // this adds support for class proxies
796      className = [(id)obj className];
797    }
798  else if ([obj isKindOfClass: [GormCustomView class]] == YES)
799    {
800      // this adds support for custom views
801      className = [(id)obj className];
802    }
803  else
804    {
805      className = NSStringFromClass(theClass);
806    }
807
808  if (className == nil)
809    {
810      NSLog(@"attempt to get outlets for non-existent class (%@)",
811      	[obj class]);
812      return nil;
813    }
814
815  outlets = [self allOutletsForClassNamed: className];
816  while (outlets == nil && (theClass = class_getSuperclass(theClass)) != nil
817    && theClass != [NSObject class])
818    {
819      className = NSStringFromClass(theClass);
820      outlets = [self allOutletsForClassNamed: className];
821    }
822  return outlets;
823}
824
825- (NSArray *) allOutletsForClassNamed: (NSString *)className;
826{
827  NSMutableDictionary	*info = [classInformation objectForKey: className];
828
829  if (info != nil)
830    {
831      NSMutableArray	*allOutlets = [info objectForKey: @"AllOutlets"];
832
833      if (allOutlets == nil)
834	{
835	  NSString	*superName = [info objectForKey: @"Super"];
836	  NSArray	*outlets = [info objectForKey: @"Outlets"];
837	  NSArray       *extraOutlets = [info objectForKey: @"ExtraOutlets"];
838	  NSArray	*superOutlets;
839
840	  if (superName == nil)
841	    {
842	      superOutlets = nil;
843	    }
844	  else
845	    {
846	      superOutlets = [self allOutletsForClassNamed: superName];
847	    }
848
849	  if (superOutlets == nil)
850	    {
851	      if (outlets == nil)
852		{
853		  allOutlets = [[NSMutableArray alloc] init];
854		}
855	      else
856		{
857		  allOutlets = [outlets mutableCopy];
858		}
859
860	      [allOutlets mergeObjectsFromArray: extraOutlets];
861	    }
862	  else
863	    {
864	      allOutlets = [superOutlets mutableCopy];
865	      [allOutlets mergeObjectsFromArray: outlets];
866	      [allOutlets mergeObjectsFromArray: extraOutlets];
867	    }
868
869	  [info setObject: allOutlets forKey: @"AllOutlets"];
870	  RELEASE(allOutlets);
871	}
872      return AUTORELEASE([allOutlets copy]);
873    }
874  return nil;
875}
876
877- (NSMutableDictionary*) classInfoForClassName: (NSString *)className
878{
879  NSMutableDictionary	*info;
880
881  info = [classInformation objectForKey: className];
882  if (info == nil)
883    {
884      Class	theClass = NSClassFromString(className);
885
886      if (theClass != nil)
887	{
888	  theClass = class_getSuperclass(theClass);
889	  if (theClass != nil && theClass != [NSObject class])
890	    {
891	      NSString			*name;
892	      NSMutableDictionary	*dict;
893
894	      name = NSStringFromClass(theClass);
895	      dict = [self classInfoForClassName: name];
896	      if (dict != nil)
897		{
898		  id	o;
899
900		  info = [[NSMutableDictionary alloc] initWithCapacity: 3];
901		  [info setObject: name forKey: @"Super"];
902		  o = [[self allActionsForClassNamed: name] mutableCopy];
903		  [info setObject: o forKey: @"AllActions"];
904		  o = [[self allOutletsForClassNamed: name] mutableCopy];
905		  [info setObject: o forKey: @"AllOutlets"];
906		  [classInformation setObject: info forKey: className];
907		}
908	    }
909	}
910    }
911  return info;
912}
913
914- (NSMutableDictionary*) classInfoForObject: (id)obj
915{
916  NSString		*className;
917  Class			theClass = [obj class];
918
919  if (theClass == [GormFilesOwner class])
920    {
921      className = [(GormFilesOwner*)obj className];
922    }
923  else if ([obj isKindOfClass: [GSNibItem class]] == YES)
924    {
925      // this adds support for custom objects
926      className = [(id)obj className];
927    }
928  else if ([obj isKindOfClass: [GormClassProxy class]] == YES)
929    {
930      // this adds support for class proxies
931      className = [(id)obj className];
932    }
933  else if ([obj isKindOfClass: [GormCustomView class]] == YES)
934    {
935      // this adds support for custom views
936      className = [(id)obj className];
937    }
938  else
939    {
940      className = NSStringFromClass(theClass);
941    }
942
943  if (className == nil)
944    {
945      NSLog(@"attempt to get outlets for non-existent class (%@)",
946      	[obj class]);
947      return nil;
948    }
949  return [self classInfoForClassName: className];
950}
951
952- (BOOL) actionExists: (NSString *)action
953	 onClassNamed: (NSString *)className
954{
955  NSArray *actions = [self allActionsForClassNamed: className];
956  return [actions containsObject: action];
957}
958
959- (BOOL) outletExists: (NSString *)outlet
960	 onClassNamed: (NSString *)className
961{
962  NSArray *outlets = [self allOutletsForClassNamed: className];
963  return [outlets containsObject: outlet];
964}
965
966- (void) dealloc
967{
968  RELEASE(classInformation);
969  RELEASE(customClassMap);
970  [super dealloc];
971}
972
973- (NSArray *) extraActionsForObject: (id)anObject
974{
975  NSMutableDictionary	*info = [self classInfoForObject: anObject];
976
977  return [info objectForKey: @"ExtraActions"];
978}
979
980- (NSArray *) extraOutletsForObject: (id)anObject
981{
982  NSMutableDictionary	*info = [self classInfoForObject: anObject];
983
984  return [info objectForKey: @"ExtraOutlets"];
985}
986
987- (void) allSubclassesOf: (NSString *)superclass
988      referenceClassList: (NSArray *)classList
989	       intoArray: (NSMutableArray *)array
990{
991  NSEnumerator *cen   = [classList objectEnumerator];
992  id object = nil;
993
994  while ((object = [cen nextObject]))
995    {
996      NSDictionary *dictForClass = [classInformation objectForKey: object];
997      NSString *superClassName = [dictForClass objectForKey: @"Super"];
998      if ([superClassName isEqual: superclass] ||
999	  (superClassName == nil && superclass == nil))
1000	{
1001	  [array addObject: object];
1002	  [self allSubclassesOf: object
1003		referenceClassList: classList
1004		intoArray: array];
1005	}
1006    }
1007}
1008
1009- (NSArray *) allSubclassesOf: (NSString *)superClass
1010{
1011  NSMutableArray *array = [NSMutableArray array];
1012
1013  [self allSubclassesOf: superClass
1014	referenceClassList: [classInformation allKeys]
1015	intoArray: array];
1016
1017  return [array sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1018}
1019
1020- (NSArray *) allCustomSubclassesOf: (NSString *)superClass
1021{
1022  NSMutableArray *array = [NSMutableArray array];
1023
1024  [self allSubclassesOf: superClass
1025	referenceClassList: customClasses
1026	intoArray: array];
1027
1028  return [array sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1029}
1030
1031- (NSArray *) customSubClassesOf: (NSString *)superclass
1032{
1033  NSEnumerator *cen   = [customClasses objectEnumerator];
1034  id object = nil;
1035  NSMutableArray *subclasses = [NSMutableArray array];
1036
1037  while ((object = [cen nextObject]))
1038    {
1039      NSDictionary *dictForClass = [classInformation objectForKey: object];
1040
1041      if ([[dictForClass objectForKey: @"Super"] isEqual: superclass])
1042	{
1043	  [subclasses addObject: object];
1044	}
1045    }
1046
1047  return subclasses;
1048}
1049
1050- (NSArray *) subClassesOf: (NSString *)superclass
1051{
1052  NSArray *allClasses = [classInformation allKeys];
1053  NSEnumerator *cen   = [allClasses objectEnumerator];
1054  id object = nil;
1055  NSMutableArray *subclasses = [NSMutableArray array];
1056
1057  while ((object = [cen nextObject]))
1058    {
1059      NSDictionary *dictForClass = [classInformation objectForKey: object];
1060      NSString *superClassName = [dictForClass objectForKey: @"Super"];
1061      if ([superClassName isEqual: superclass] ||
1062	  (superClassName == nil && superclass == nil))
1063	{
1064	  [subclasses addObject: object];
1065	}
1066    }
1067
1068  return [subclasses sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1069}
1070
1071- (void) removeClassNamed: (NSString *)className
1072{
1073  if ([customClasses containsObject: className])
1074    {
1075      NSEnumerator *en = [customClassMap keyEnumerator];
1076      id object = nil;
1077      id owner = nil;
1078
1079      [customClasses removeObject: className];
1080
1081      while((object = [en nextObject]) != nil)
1082	{
1083	  id customClassName = [customClassMap objectForKey: object];
1084	  if(customClassName != nil)
1085	    {
1086	      if([className isEqualToString: customClassName])
1087		{
1088		  NSDebugLog(@"Deleting object -> customClass association %@ -> %@",object,customClassName);
1089		  [customClassMap removeObjectForKey: object];
1090		}
1091	    }
1092	}
1093
1094      // get the owner and reset the class name to NSApplication.
1095      owner = [document objectForName: @"NSOwner"];
1096      if([className isEqual: [owner className]])
1097	{
1098	  [owner setClassName: @"NSApplication"];
1099	}
1100    }
1101
1102  [classInformation removeObjectForKey: className];
1103  [self touch];
1104
1105  [[NSNotificationCenter defaultCenter]
1106    postNotificationName: GormDidDeleteClassNotification
1107    object: self];
1108}
1109
1110- (BOOL) renameClassNamed: (NSString *)oldName newName: (NSString *)newName
1111{
1112  id classInfo = [classInformation objectForKey: oldName];
1113  NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
1114  NSString *name = [newName copy];
1115
1116  NSDebugLog(@"Old name %@, new name %@",oldName,name);
1117
1118  if (classInfo != nil && [classInformation objectForKey: name] == nil)
1119    {
1120      NSUInteger index = 0;
1121      NSArray *subclasses = [self subClassesOf: oldName];
1122
1123      RETAIN(classInfo); // prevent loss of the information...
1124      [classInformation removeObjectForKey: oldName];
1125      [classInformation setObject: classInfo forKey: name];
1126      RELEASE(classInfo); // release our hold on it.
1127
1128      if ((index = [customClasses indexOfObject: oldName]) != NSNotFound)
1129	{
1130	  NSEnumerator *en = [customClassMap keyEnumerator];
1131	  NSEnumerator *cen = [subclasses objectEnumerator];
1132	  id sc = nil;
1133	  id object = nil;
1134
1135	  NSDebugLog(@"replacing object with %@, %@",name, customClasses);
1136	  [customClasses replaceObjectAtIndex: index withObject: name];
1137	  NSDebugLog(@"replaced object with %@, %@",name, customClasses);
1138
1139	  // show the class map before...
1140	  NSDebugLog(@"customClassMap = %@",customClassMap);
1141	  while((object = [en nextObject]) != nil)
1142	    {
1143	      id customClassName = [customClassMap objectForKey: object];
1144	      if(customClassName != nil)
1145		{
1146		  if([oldName isEqualToString: customClassName])
1147		    {
1148		      NSDebugLog(@"Replacing object -> customClass association %@ -> %@",object,customClassName);
1149		      [customClassMap setObject: name forKey: object];
1150		    }
1151		}
1152	    }
1153	  NSDebugLog(@"New customClassMap = %@",customClassMap); // and after
1154
1155	  // Iterate over the list of subclasses and replace their referece with the new
1156	  // name.
1157	  while((sc = [cen nextObject]) != nil)
1158	    {
1159	      [self setSuperClassNamed: name
1160		    forClassNamed: sc];
1161	    }
1162
1163	  [self touch];
1164	}
1165      else
1166	NSLog(@"customClass not found %@",oldName);
1167
1168      [nc postNotificationName: IBClassNameChangedNotification object: self];
1169      return YES;
1170    }
1171  else return NO;
1172}
1173
1174- (NSString *)parentOfClass: (NSString *)aClass
1175{
1176  NSDictionary *dictForClass = [classInformation objectForKey: aClass];
1177  return [dictForClass objectForKey: @"Super"];
1178}
1179
1180- (NSData *) nibData
1181{
1182  NSMutableDictionary	*dict = nil;
1183  NSMutableArray        *classes = nil;
1184  NSEnumerator		*enumerator = nil;
1185  NSMutableArray        *cats = [NSMutableArray arrayWithArray: categoryClasses];
1186  id			name = nil;
1187
1188  // save all custom classes....
1189  dict = [NSMutableDictionary dictionary];
1190  [dict setObject: @"1" forKey: @"IBVersion"];
1191  classes = [NSMutableArray array];
1192
1193  // build IBClasses...
1194  enumerator = [customClasses objectEnumerator];
1195  while ((name = [enumerator nextObject]) != nil)
1196    {
1197      NSDictionary		*classInfo;
1198      NSMutableDictionary	*newInfo;
1199      id			obj;
1200      id                        extraObj;
1201
1202      // get the info...
1203      classInfo = [classInformation objectForKey: name];
1204
1205      newInfo = [[NSMutableDictionary alloc] init];
1206      [newInfo setObject: name forKey: @"CLASS"];
1207
1208      // superclass...
1209      obj = [classInfo objectForKey: @"Super"];
1210      if (obj != nil)
1211	{
1212	  [newInfo setObject: obj forKey: @"SUPERCLASS"];
1213	}
1214
1215      // outlets...
1216      obj = [classInfo objectForKey: @"Outlets"];
1217      extraObj = [classInfo objectForKey: @"ExtraOutlets"];
1218      if (obj && extraObj)
1219	{
1220	  obj = [obj arrayByAddingObjectsFromArray: extraObj];
1221	}
1222      else if (extraObj)
1223	{
1224	  obj = extraObj;
1225	}
1226      if (obj != nil && [obj count] > 0)
1227	{
1228	  NSMutableDictionary *outletDict = [NSMutableDictionary dictionary];
1229	  NSEnumerator *oen = [obj objectEnumerator];
1230	  id outlet = nil;
1231
1232	  while((outlet = [oen nextObject]) != nil)
1233	    {
1234	      [outletDict setObject: @"id" forKey: outlet];
1235	    }
1236
1237	  [newInfo setObject: outletDict forKey: @"OUTLETS"];
1238	}
1239
1240      // actions...
1241      obj = [classInfo objectForKey: @"Actions"];
1242      extraObj = [classInfo objectForKey: @"ExtraActions"];
1243      if (obj && extraObj)
1244	{
1245	  obj = [obj arrayByAddingObjectsFromArray: extraObj];
1246	}
1247      else if (extraObj)
1248	{
1249	  obj = extraObj;
1250	}
1251      if (obj != nil && [obj count] > 0)
1252	{
1253	  NSMutableDictionary *actionDict = [NSMutableDictionary dictionary];
1254	  NSEnumerator *aen = [obj objectEnumerator];
1255	  id action = nil;
1256
1257	  while((action = [aen nextObject]) != nil)
1258	    {
1259	      NSString *actionName = nil;
1260	      NSScanner *scanner = [NSScanner scannerWithString: action];
1261
1262	      if ([scanner scanUpToString: @":" intoString: &actionName])
1263          [actionDict setObject: @"id" forKey: actionName];
1264	    }
1265
1266	  [newInfo setObject: actionDict forKey: @"ACTIONS"];
1267	}
1268
1269      [newInfo setObject: @"ObjC" forKey: @"LANGUAGE"];
1270
1271      [classes addObject: newInfo];
1272    }
1273
1274  // Save all categories on existing, non-custom classes....
1275  // Always save the FirstResponder....
1276  if([cats containsObject: @"FirstResponder"] == NO)
1277    {
1278      [cats addObject: @"FirstResponder"];
1279    }
1280  enumerator = [cats objectEnumerator];
1281  while((name = [enumerator nextObject]) != nil)
1282    {
1283      NSDictionary  *classInfo;
1284      NSMutableDictionary  *newInfo;
1285      id obj;
1286
1287      // get the info...
1288      classInfo = [classInformation objectForKey: name];
1289      newInfo = [NSMutableDictionary dictionary];
1290      [newInfo setObject: name forKey: @"CLASS"];
1291
1292      // superclass...
1293      obj = [classInfo objectForKey: @"Super"];
1294      if (obj != nil)
1295	{
1296	  [newInfo setObject: obj forKey: @"SUPERCLASS"];
1297	}
1298
1299      // actions...
1300      obj = [classInfo objectForKey: @"ExtraActions"];
1301      if (obj != nil && [obj count] > 0)
1302	{
1303	  NSMutableDictionary *actionDict = [NSMutableDictionary dictionary];
1304	  NSEnumerator *aen = [obj objectEnumerator];
1305	  id action = nil;
1306
1307	  while((action = [aen nextObject]) != nil)
1308	    {
1309	      NSString *actionName = nil;
1310	      NSScanner *scanner = [NSScanner scannerWithString: action];
1311
1312	      if ([scanner scanUpToString: @":" intoString: &actionName])
1313          [actionDict setObject: @"id" forKey: actionName];
1314	    }
1315
1316	  [newInfo setObject: actionDict forKey: @"ACTIONS"];
1317	}
1318
1319      [newInfo setObject: @"ObjC" forKey: @"LANGUAGE"];
1320
1321      [classes addObject: newInfo];
1322    }
1323
1324  [dict setObject: classes forKey: @"IBClasses"];
1325
1326  return [NSPropertyListSerialization dataFromPropertyList: dict
1327				      format: NSPropertyListOpenStepFormat
1328				      errorDescription: NULL];
1329}
1330
1331- (NSData *) data
1332{
1333  NSMutableDictionary	*ci = nil;
1334  NSEnumerator		*enumerator = nil;
1335  id			key = nil;
1336
1337  // save all custom classes....
1338  ci = [NSMutableDictionary dictionary];
1339  enumerator = [customClasses objectEnumerator];
1340  while ((key = [enumerator nextObject]) != nil)
1341    {
1342      NSDictionary		*classInfo;
1343      NSMutableDictionary	*newInfo;
1344      id			obj;
1345      id                        extraObj;
1346
1347      // get the info...
1348      classInfo = [classInformation objectForKey: key];
1349      newInfo = [[NSMutableDictionary alloc] init];
1350      [ci setObject: newInfo forKey: key];
1351
1352      // superclass...
1353      obj = [classInfo objectForKey: @"Super"];
1354      if (obj != nil)
1355	{
1356	  [newInfo setObject: obj forKey: @"Super"];
1357	}
1358
1359      // outlets...
1360      obj = [classInfo objectForKey: @"Outlets"];
1361      extraObj = [classInfo objectForKey: @"ExtraOutlets"];
1362      if (obj && extraObj)
1363	{
1364	  obj = [obj arrayByAddingObjectsFromArray: extraObj];
1365	}
1366      else if (extraObj)
1367	{
1368	  obj = extraObj;
1369	}
1370      if (obj != nil)
1371	{
1372	  [newInfo setObject: obj forKey: @"Outlets"];
1373	}
1374
1375      // actions...
1376      obj = [classInfo objectForKey: @"Actions"];
1377      extraObj = [classInfo objectForKey: @"ExtraActions"];
1378      if (obj && extraObj)
1379	{
1380	  obj = [obj arrayByAddingObjectsFromArray: extraObj];
1381	}
1382      else if (extraObj)
1383	{
1384	  obj = extraObj;
1385	}
1386      if (obj != nil)
1387	{
1388	  [newInfo setObject: obj forKey: @"Actions"];
1389	}
1390    }
1391
1392  // save all categories on existing, non-custom classes....
1393  enumerator = [categoryClasses objectEnumerator];
1394  while((key = [enumerator nextObject]) != nil)
1395    {
1396      NSDictionary  *classInfo;
1397      NSMutableDictionary  *newInfo;
1398      id obj;
1399
1400      // get the info...
1401      classInfo = [classInformation objectForKey: key];
1402      newInfo = [NSMutableDictionary dictionary];
1403      [ci setObject: newInfo forKey: key];
1404
1405      // superclass...
1406      obj = [classInfo objectForKey: @"Super"];
1407      if (obj != nil)
1408	{
1409	  [newInfo setObject: obj forKey: @"Super"];
1410	}
1411
1412      // actions...
1413      obj = [classInfo objectForKey: @"ExtraActions"];
1414      if (obj != nil)
1415	{
1416	  [newInfo setObject: obj forKey: @"Actions"];
1417	}
1418    }
1419
1420  // add the extras...
1421  [ci setObject: @"Do NOT change this file, Gorm maintains it"
1422      forKey: @"## Comment"];
1423
1424  return [NSPropertyListSerialization dataFromPropertyList: ci
1425				      format: NSPropertyListOpenStepFormat
1426				      errorDescription: NULL];
1427}
1428
1429- (BOOL) saveToFile: (NSString *)path
1430{
1431  return [[self data] writeToFile: path atomically: YES];
1432}
1433
1434- (BOOL) loadFromFile: (NSString *)path
1435{
1436  NSDictionary 	        *dict;
1437  NSEnumerator		*enumerator;
1438  NSString		*key;
1439
1440  NSDebugLog(@"Load from file %@",path);
1441
1442  dict = [NSDictionary dictionaryWithContentsOfFile: path];
1443  if (dict == nil)
1444    {
1445      NSLog(@"Could not load classes dictionary");
1446      return NO;
1447    }
1448
1449  /*
1450   * Convert property-list data into a mutable structure.
1451   */
1452  ASSIGN(classInformation, [[NSMutableDictionary alloc] init]);
1453
1454  // iterate over all entries..
1455  enumerator = [dict keyEnumerator];
1456  while ((key = [enumerator nextObject]) != nil)
1457    {
1458      NSDictionary	    *classInfo = [dict objectForKey: key];
1459      NSMutableDictionary   *newInfo;
1460      id		    obj;
1461
1462      newInfo = [[NSMutableDictionary alloc] init];
1463
1464      [classInformation setObject: newInfo forKey: key];
1465
1466      // superclass
1467      obj = [classInfo objectForKey: @"Super"];
1468      if (obj != nil)
1469	{
1470	  [newInfo setObject: obj forKey: @"Super"];
1471	}
1472
1473      // outlets
1474      obj = [classInfo objectForKey: @"Outlets"];
1475      if (obj != nil)
1476	{
1477	  obj = [obj mutableCopy];
1478	  [obj sortUsingSelector: @selector(compare:)];
1479	  [newInfo setObject: obj forKey: @"Outlets"];
1480	  RELEASE(obj);
1481	}
1482
1483      // actions
1484      obj = [classInfo objectForKey: @"Actions"];
1485      if (obj != nil)
1486	{
1487	  obj = [obj mutableCopy];
1488	  [obj sortUsingSelector: @selector(compare:)];
1489	  [newInfo setObject: obj forKey: @"Actions"];
1490	  RELEASE(obj);
1491	}
1492    }
1493  return YES;
1494}
1495
1496- (BOOL) loadNibFormatCustomClassesWithDict: (NSDictionary *)dict
1497{
1498  NSArray *classes = [dict objectForKey: @"IBClasses"];
1499  NSEnumerator *en = [classes objectEnumerator];
1500  BOOL result = NO;
1501  id cls = nil;
1502
1503  // If there are no classes to add, return gracefully.
1504  if([classes count] == 0)
1505    {
1506      return YES;
1507    }
1508
1509  while((cls = [en nextObject]) != nil)
1510    {
1511      NSString *className = [cls objectForKey: @"CLASS"];
1512      NSString *superClass = [cls objectForKey: @"SUPERCLASS"];
1513      NSDictionary *actionDict = [cls objectForKey: @"ACTIONS"];
1514      NSDictionary *outletDict = [cls objectForKey: @"OUTLETS"];
1515      NSMutableArray *actions = [NSMutableArray array];
1516      NSArray *outlets = [outletDict allKeys];
1517      NSEnumerator *aen = [actionDict keyEnumerator];
1518      id action = nil;
1519
1520      //
1521      // Convert action format.
1522      //
1523      while((action = [aen nextObject]) != nil)
1524	{
1525	  NSString *aname = [action stringByAppendingString: @":"];
1526	  [actions addObject: aname];
1527	}
1528
1529      //
1530      // If the class is known, add the actions/outlets, if it's
1531      // not, then add all of the information.
1532      //
1533      if([self isKnownClass: className])
1534	{
1535	  [self addActions: actions forClassNamed: className];
1536	  [self addOutlets: outlets forClassNamed: className];
1537	  result = YES;
1538	}
1539      else
1540	{
1541	  result = [self addClassNamed: className
1542			 withSuperClassNamed: superClass
1543			 withActions: actions
1544			 withOutlets: outlets];
1545	}
1546    }
1547
1548  return result;
1549}
1550
1551- (BOOL) loadNibFormatCustomClassesWithData: (NSData *)data
1552{
1553  NSString *dictString = AUTORELEASE([[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding]);
1554  NSDictionary *dict = [dictString propertyList];
1555  return [self loadNibFormatCustomClassesWithDict: dict];
1556}
1557
1558// this method will load the custom classes and merge them with the
1559// Class information loaded at initialization time.
1560- (BOOL) loadCustomClasses: (NSString *)path
1561{
1562  NSMutableDictionary  *dict;
1563  BOOL result = NO;
1564
1565  NSDebugLog(@"Load custom classes from file %@",path);
1566
1567  dict = [NSMutableDictionary dictionaryWithContentsOfFile: path];
1568  if (dict == nil)
1569    {
1570      NSLog(@"Could not load custom classes dictionary");
1571      return NO;
1572    }
1573
1574  if (classInformation == nil)
1575    {
1576      NSLog(@"Default classes file not loaded");
1577      return NO;
1578    }
1579
1580  if([path isEqualToString: @"data.classes"])
1581    {
1582      result = [self loadCustomClassesWithDict: dict];
1583    }
1584  else if([path isEqualToString: @"classes.nib"])
1585    {
1586      result = [self loadNibFormatCustomClassesWithDict: dict];
1587    }
1588  return result;
1589}
1590
1591- (BOOL) loadCustomClassesWithData: (NSData *)data
1592{
1593  NSString *dictString = AUTORELEASE([[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding]);
1594  NSDictionary *dict = [dictString propertyList];
1595  return [self loadCustomClassesWithDict: dict];
1596}
1597
1598- (BOOL) loadCustomClassesWithDict: (NSDictionary *)dict
1599{
1600  NSEnumerator *en = nil;
1601  id            key = nil;
1602
1603  // Iterate over the set of classes, if it's in the classInformation
1604  // list, it's a category, if it's not it's a custom class.
1605  en = [dict keyEnumerator];
1606  while((key = [en nextObject]) != nil)
1607    {
1608      id class_dict = [dict objectForKey: key];
1609
1610      // Class information is always a dictionary, other information, such as
1611      // comments or version numbers, will appear as strings.
1612      if([class_dict isKindOfClass: [NSDictionary class]])
1613	{
1614	  NSMutableDictionary *classDict = (NSMutableDictionary *)class_dict;
1615	  NSMutableDictionary *info = [classInformation objectForKey: key];
1616	  if(info == nil)
1617	    {
1618	      [customClasses addObject: key];
1619	      [classInformation setObject: classDict forKey: key];
1620	    }
1621	  else
1622	    {
1623	      NSMutableArray *actions = [classDict objectForKey: @"Actions"];
1624	      NSMutableArray *origActions = [info objectForKey: @"Actions"];
1625	      NSMutableArray *allActions = nil;
1626
1627	      // remove any duplicate actions...
1628	      if(origActions != nil)
1629		{
1630		  allActions = [NSMutableArray arrayWithArray: origActions];
1631
1632		  [actions removeObjectsInArray: origActions];
1633		  [allActions addObjectsFromArray: actions];
1634		  [info setObject: allActions forKey: @"AllActions"];
1635		}
1636
1637	      // if there are any action methods left after the process above,
1638	      // add it, otherwise don't.
1639	      if([actions count] > 0)
1640		{
1641		  [categoryClasses addObject: key];
1642		  [info setObject: actions forKey: @"ExtraActions"];
1643		}
1644	    }
1645	}
1646    }
1647
1648  return YES;
1649}
1650
1651- (BOOL) isCustomClass: (NSString *)className
1652{
1653  return ([customClasses indexOfObject: className] != NSNotFound);
1654}
1655
1656- (BOOL) isNonCustomClass: (NSString *)className
1657{
1658  return !([self isCustomClass: className]);
1659}
1660
1661- (BOOL) isCategoryForClass: (NSString *)className
1662{
1663  return ([categoryClasses indexOfObject: className] != NSNotFound);
1664}
1665
1666- (BOOL) isAction: (NSString *)actionName onCategoryForClassNamed: (NSString *)className
1667{
1668  NSDictionary *info = [classInformation objectForKey: className];
1669  BOOL result = NO;
1670
1671  if([self isCategoryForClass: className])
1672    {
1673      if(info != nil)
1674	{
1675	  NSArray *extra = [info objectForKey: @"ExtraActions"];
1676	  if(extra != nil)
1677	    {
1678	      result = [extra containsObject: actionName];
1679	    }
1680	}
1681    }
1682
1683  return result;
1684}
1685
1686- (BOOL) isKnownClass: (NSString *)className
1687{
1688  return ([classInformation objectForKey: className] != nil);
1689}
1690
1691- (BOOL) setSuperClassNamed: (NSString *)superclass
1692	      forClassNamed: (NSString *)subclass
1693{
1694  NSArray *cn = [self allClassNames];
1695
1696  if (superclass != nil
1697      && subclass != nil
1698      && [cn containsObject: subclass]
1699      && ([cn containsObject: superclass] || [self isRootClass: superclass])
1700      && [self isSuperclass: subclass linkedToClass: superclass] == NO)
1701    {
1702      NSMutableDictionary	*info;
1703
1704      info = [classInformation objectForKey: subclass];
1705      if (info != nil)
1706	{
1707	  // remove actions/outlets inherited from superclasses...
1708	  [info removeObjectForKey: @"AllActions"];
1709	  [info removeObjectForKey: @"AllOutlets"];
1710
1711	  // change the parent of the class...
1712	  [info setObject: superclass forKey: @"Super"];
1713
1714	  // recalculate the actions/outlets...
1715	  [self allActionsForClassNamed: subclass];
1716	  [self allOutletsForClassNamed: subclass];
1717
1718	  // return success.
1719	  return YES;
1720	}
1721      else
1722	{
1723	  return NO;
1724	}
1725    }
1726
1727  return NO;
1728}
1729
1730- (NSString *) superClassNameForClassNamed: (NSString *)className
1731{
1732  NSMutableDictionary	*info = [classInformation objectForKey: className];
1733  NSString		*superName = nil;
1734
1735  if (info != nil)
1736    {
1737      superName = [info objectForKey: @"Super"];
1738    }
1739
1740  return superName;
1741}
1742
1743- (BOOL) isSuperclass: (NSString *)superclass linkedToClass: (NSString *)subclass
1744{
1745  NSString *ssclass;
1746
1747  if (superclass == nil || subclass == nil)
1748    {
1749      return NO;
1750    }
1751
1752  ssclass = [self superClassNameForClassNamed: subclass];
1753  if ([superclass isEqualToString: ssclass])
1754    {
1755      return YES;
1756    }
1757
1758  return [self isSuperclass: superclass linkedToClass: ssclass];
1759}
1760
1761- (NSDictionary *) dictionaryForClassNamed: (NSString *)className
1762{
1763  NSMutableDictionary *info = [NSMutableDictionary dictionaryWithDictionary: [classInformation objectForKey: className]];
1764
1765  if(info != nil)
1766    {
1767      [info removeObjectForKey: @"AllActions"];
1768      [info removeObjectForKey: @"AllOutlets"];
1769    }
1770
1771  return info;
1772}
1773
1774
1775/*
1776 *  create .m & .h files for a class
1777 */
1778- (BOOL) makeSourceAndHeaderFilesForClass: (NSString *)className
1779				 withName: (NSString *)sourcePath
1780				      and: (NSString *)headerPath
1781{
1782  NSMutableString	*headerFile;
1783  NSMutableString	*sourceFile;
1784  NSData		*headerData;
1785  NSData		*sourceData;
1786  NSMutableArray	*outlets;
1787  NSMutableArray	*actions;
1788  NSString		*actionName;
1789  int			i;
1790  int			n;
1791  NSDictionary          *classInfo = [classInformation objectForKey: className];
1792
1793  headerFile = [NSMutableString stringWithCapacity: 200];
1794  sourceFile = [NSMutableString stringWithCapacity: 200];
1795
1796  // add all outlets and actions for the current class to the list...
1797  outlets = [[classInfo objectForKey: @"Outlets"] mutableCopy];
1798  [outlets addObjectsFromArray: [classInfo objectForKey: @"ExtraOutlets"]];
1799  actions = [[classInfo objectForKey: @"Actions"] mutableCopy];
1800  [actions addObjectsFromArray: [classInfo objectForKey: @"ExtraActions"]];
1801
1802  // header file comments...
1803  [headerFile appendString: @"/* All Rights reserved */\n\n"];
1804  [sourceFile appendString: @"/* All Rights reserved */\n\n"];
1805  [headerFile appendString: @"#include <AppKit/AppKit.h>\n\n"];
1806  [sourceFile appendString: @"#include <AppKit/AppKit.h>\n"];
1807  if ([[headerPath stringByDeletingLastPathComponent]
1808    isEqualToString: [sourcePath stringByDeletingLastPathComponent]])
1809    {
1810      [sourceFile appendFormat: @"#include \"%@\"\n\n",
1811	[headerPath lastPathComponent]];
1812    }
1813  else
1814    {
1815      [sourceFile appendFormat: @"#include \"%@\"\n\n",
1816	headerPath];
1817    }
1818  [headerFile appendFormat: @"@interface %@ : %@\n{\n", className,
1819    [self superClassNameForClassNamed: className]];
1820  [sourceFile appendFormat: @"@implementation %@\n\n", className];
1821
1822  n = [outlets count];
1823  for (i = 0; i < n; i++)
1824    {
1825      [headerFile appendFormat: @"  id %@;\n", [outlets objectAtIndex: i]];
1826    }
1827  [headerFile appendFormat: @"}\n"];
1828
1829  n = [actions count];
1830  for (i = 0; i < n; i++)
1831    {
1832      actionName = [actions objectAtIndex: i];
1833      [headerFile appendFormat: @"- (void) %@ (id)sender;\n", actionName];
1834      [sourceFile appendFormat:
1835	@"\n"
1836	@"- (void) %@ (id)sender\n"
1837	@"{\n"
1838	@"  /* insert your code here */\n"
1839	@"}\n"
1840	@"\n"
1841	, [actions objectAtIndex: i]];
1842    }
1843  [headerFile appendFormat: @"@end\n"];
1844  [sourceFile appendFormat: @"@end\n"];
1845
1846  headerData = [headerFile dataUsingEncoding:
1847    [NSString defaultCStringEncoding]];
1848  sourceData = [sourceFile dataUsingEncoding:
1849    [NSString defaultCStringEncoding]];
1850
1851  [headerData writeToFile: headerPath atomically: NO];
1852
1853  [[NSDistributedNotificationCenter defaultCenter]
1854    postNotificationName: @"GormCreateFileNotification"
1855    object: headerPath];
1856
1857  [sourceData writeToFile: sourcePath atomically: NO];
1858
1859  [[NSDistributedNotificationCenter defaultCenter]
1860    postNotificationName: @"GormCreateFileNotification"
1861    object: sourcePath];
1862
1863  return YES;
1864}
1865
1866- (BOOL) parseHeader: (NSString *)headerPath
1867{
1868  OCHeaderParser *ochp = AUTORELEASE([[OCHeaderParser alloc] initWithContentsOfFile: headerPath]);
1869  BOOL result = NO;
1870
1871  if(ochp != nil)
1872    {
1873      result = [ochp parse];
1874      if(result)
1875	{
1876	  NSArray *classes = [ochp classes];
1877	  NSEnumerator *en = [classes objectEnumerator];
1878	  OCClass *cls = nil;
1879
1880	  while((cls = (OCClass *)[en nextObject]) != nil)
1881	    {
1882	      NSArray *methods = [cls methods];
1883	      NSArray *ivars = [cls ivars];
1884	      NSString *superClass = [cls superClassName];
1885	      NSString *className = [cls className];
1886	      NSEnumerator *ien = [ivars objectEnumerator];
1887	      NSEnumerator *men = [methods objectEnumerator];
1888	      OCMethod *method = nil;
1889	      OCIVar *ivar = nil;
1890	      NSMutableArray *actions = [NSMutableArray array];
1891	      NSMutableArray *outlets = [NSMutableArray array];
1892
1893	      // skip it, if it's category...  for now.  TODO: make categories work...
1894	      while((method = (OCMethod *)[men nextObject]) != nil)
1895		{
1896		  if([method isAction])
1897		    {
1898		      [actions addObject: [method name]];
1899		    }
1900		}
1901
1902	      while((ivar = (OCIVar *)[ien nextObject]) != nil)
1903		{
1904		  if([ivar isOutlet])
1905		    {
1906		      [outlets addObject: [ivar name]];
1907		    }
1908		}
1909
1910	      if(([self isKnownClass: superClass] || superClass == nil) &&
1911		 [cls isCategory] == NO)
1912		{
1913		  if([self isKnownClass: className])
1914		    {
1915		      NSString *title = [NSString stringWithFormat:
1916						    _(@"Reparsing Class")];
1917		      NSString *msg = [NSString stringWithFormat:
1918						  _(@"This may break connections to "
1919						    @"actions/outlets to instances of class '%@' "
1920						    @"and it's subclasses.  Continue?"),
1921						className];
1922		      NSInteger retval = NSRunAlertPanel(title, msg,_(@"OK"),_(@"Cancel"), nil, nil);
1923
1924		      if (retval == NSAlertDefaultReturn)
1925			{
1926			  // get the owner and reset the class name to NSApplication.
1927			  GormFilesOwner *owner = [document objectForName: @"NSOwner"];
1928			  NSString *ownerClassName = [owner className];
1929
1930			  // Retain this, in case we're dealing with the NSOwner...
1931			  RETAIN(ownerClassName);
1932
1933			  // delete the class..
1934			  [self removeClassNamed: className];
1935
1936			  // re-add it.
1937			  [self addClassNamed: className
1938				withSuperClassNamed: superClass
1939				withActions: actions
1940				withOutlets: outlets];
1941
1942			  // Set the owner back to the class name, if needed.
1943			  if([className isEqualToString: ownerClassName])
1944			    {
1945			      [owner setClassName: className];
1946			    }
1947
1948			  // refresh the connections.
1949			  [document refreshConnectionsForClassNamed: className];
1950
1951			  // Release the owner classname...
1952			  RELEASE(ownerClassName);
1953			}
1954		    }
1955		  else
1956		    {
1957		      [self addClassNamed: className
1958			    withSuperClassNamed: superClass
1959			    withActions: actions
1960			    withOutlets: outlets];
1961		    }
1962		}
1963	      else if([cls isCategory] && [self isKnownClass: className])
1964		{
1965		  [self addActions: actions forClassNamed: className];
1966		}
1967	      else if(superClass != nil && [self isKnownClass: superClass] == NO)
1968		{
1969		  result = NO;
1970		  [NSException raise: NSGenericException
1971			       format: @"The superclass %@ of class %@ is not known, please parse it.",
1972			       superClass, className];
1973		}
1974	    }
1975	}
1976    }
1977
1978  return result;
1979}
1980
1981- (BOOL) isAction: (NSString *)name ofClass: (NSString *)className
1982{
1983  BOOL result = NO;
1984  NSDictionary *classInfo = [classInformation objectForKey: className];
1985
1986  if (classInfo != nil)
1987    {
1988      NSArray *array = [classInfo objectForKey: @"Actions"];
1989      NSArray *extra_array = [classInfo objectForKey: @"ExtraActions"];
1990      NSMutableArray *combined = [NSMutableArray array];
1991
1992      [combined addObjectsFromArray: array];
1993      [combined addObjectsFromArray: extra_array];
1994      result = ([combined indexOfObject: name] != NSNotFound);
1995    }
1996
1997  return result;
1998}
1999
2000- (BOOL) isOutlet: (NSString *)name ofClass: (NSString *)className
2001{
2002  BOOL result = NO;
2003  NSDictionary *classInfo = [classInformation objectForKey: className];
2004
2005  if (classInfo != nil)
2006    {
2007      NSArray *array = [classInfo objectForKey: @"Outlets"];
2008      NSArray *extra_array = [classInfo objectForKey: @"ExtraOutlets"];
2009      NSMutableArray *combined = [NSMutableArray array];
2010
2011      [combined addObjectsFromArray: array];
2012      [combined addObjectsFromArray: extra_array];
2013      result = ([combined indexOfObject: name] != NSNotFound);
2014    }
2015
2016  return result;
2017}
2018
2019// custom class support...
2020- (NSString *) customClassForName: (NSString *)name
2021{
2022  NSString *result = [customClassMap objectForKey: name];
2023  return result;
2024}
2025
2026- (NSString *) customClassForObject: (id)object
2027{
2028  NSString *name = [document nameForObject: object];
2029  NSString *result = [self customClassForName: name];
2030  NSDebugLog(@"in customClassForObject: object = %@, name = %@, result = %@, customClassMap = %@",
2031	     object, name, result, customClassMap);
2032  return result;
2033}
2034
2035- (NSString *) classNameForObject: (id)object
2036{
2037  NSString *className = [self customClassForObject: object];
2038  if(className == nil)
2039    {
2040      className = [object className];
2041    }
2042  return className;
2043}
2044
2045- (void) setCustomClass: (NSString *)className
2046                forName: (NSString *)name
2047{
2048  [customClassMap setObject: className forKey: name];
2049}
2050
2051- (void) removeCustomClassForName: (NSString *)name
2052{
2053  [customClassMap removeObjectForKey: name];
2054}
2055
2056- (NSMutableDictionary *) customClassMap
2057{
2058  return customClassMap;
2059}
2060
2061- (void) setCustomClassMap: (NSMutableDictionary *)dict
2062{
2063  // copy the dictionary..
2064  NSDebugLog(@"dictionary = %@",dict);
2065  ASSIGN(customClassMap, [dict mutableCopy]);
2066  RETAIN(customClassMap); // released in dealloc
2067}
2068
2069- (BOOL) isCustomClassMapEmpty
2070{
2071  return ([customClassMap count] == 0);
2072}
2073
2074- (BOOL) isRootClass: (NSString *)className
2075{
2076  return ([self superClassNameForClassNamed: className] == nil);
2077}
2078
2079- (NSString *) nonCustomSuperClassOf: (NSString *)className
2080{
2081  NSString *result = className;
2082
2083  if(![self isCustomClass: className] && ![self isRootClass: className])
2084    {
2085      result = [self superClassNameForClassNamed: result];
2086    }
2087  else
2088    {
2089      // iterate up the chain until a non-custom superclass is found...
2090      while ([self isCustomClass: result])
2091	{
2092	  NSDebugLog(@"result = %@",result);
2093	  result = [self superClassNameForClassNamed: result];
2094	}
2095    }
2096
2097  return result;
2098}
2099
2100- (NSArray *) allSuperClassesOf: (NSString *)className
2101{
2102  NSMutableArray *classes = [NSMutableArray array];
2103
2104  while (![self isRootClass: className] && className != nil)
2105    {
2106      NSDictionary *dict = [self classInfoForClassName: className];
2107      if(dict != nil)
2108	{
2109	  className = [dict objectForKey: @"Super"];
2110	  if(className != nil)
2111	    {
2112	      [classes insertObject: className atIndex: 0];
2113	    }
2114	}
2115      else
2116	{
2117	  NSLog(@"Unable to find class named (%@), check that all palettes properly export classes to Gorm.",className);
2118	  break;
2119	}
2120    }
2121
2122  return classes;
2123}
2124
2125- (void) addActions: (NSArray *)actions forClassNamed: (NSString *)className
2126{
2127  id action = nil;
2128  NSEnumerator *e = [actions objectEnumerator];
2129  while((action = [e nextObject]))
2130    {
2131      [self addAction: action forClassNamed: className];
2132    }
2133}
2134
2135- (void) addOutlets: (NSArray *)outlets forClassNamed: (NSString *)className
2136{
2137  id action = nil;
2138  NSEnumerator *e = [outlets objectEnumerator];
2139  while((action = [e nextObject]))
2140    {
2141      [self addOutlet: action forClassNamed: className];
2142    }
2143}
2144
2145// There are some classes which can't be instantiated directly
2146// in Gorm.  These are they.. (GJC)
2147- (BOOL) canInstantiateClassNamed: (NSString *)className
2148{
2149  if([self isSuperclass: @"NSApplication" linkedToClass: className] ||
2150     [className isEqualToString: @"NSApplication"])
2151    {
2152      return NO;
2153    }
2154  else if([self isSuperclass: @"NSCell" linkedToClass: className] ||
2155	  [className isEqualToString: @"NSCell"])
2156    {
2157      return NO;
2158    }
2159  else if([className isEqualToString: @"NSDocument"])
2160    {
2161      return NO;
2162    }
2163  else if([className isEqualToString: @"NSDocumentController"])
2164    {
2165      return NO;
2166    }
2167  else if([className isEqualToString: @"NSFontManager"])
2168    {
2169      return NO;
2170    }
2171  else if([className isEqualToString: @"NSHelpManager"])
2172    {
2173      return NO;
2174    }
2175  else if([className isEqualToString: @"NSImage"])
2176    {
2177      return NO;
2178    }
2179  else if([self isSuperclass: @"NSMenuItem" linkedToClass: className] ||
2180	  [className isEqualToString: @"NSMenuItem"])
2181    {
2182      return NO;
2183    }
2184  else if([className isEqualToString: @"NSResponder"])
2185    {
2186      return NO;
2187    }
2188  else if([self isSuperclass: @"NSSound" linkedToClass: className] ||
2189	  [className isEqualToString: @"NSSound"])
2190    {
2191      return NO;
2192    }
2193  else if([self isSuperclass: @"NSTableColumn" linkedToClass: className] ||
2194	  [className isEqualToString: @"NSTableColumn"])
2195    {
2196      return NO;
2197    }
2198  else if([self isSuperclass: @"NSTableViewItem" linkedToClass: className] ||
2199	  [className isEqualToString: @"NSTableViewItem"])
2200    {
2201      return NO;
2202    }
2203  else if([self isSuperclass: @"NSView" linkedToClass: className] ||
2204	  [className isEqualToString: @"NSView"])
2205    {
2206      return NO;
2207    }
2208  else if([self isSuperclass: @"NSWindow" linkedToClass: className] ||
2209	  [className isEqualToString: @"NSWindow"])
2210    {
2211      return NO;
2212    }
2213  else if([self isSuperclass: @"FirstResponder" linkedToClass: className] ||
2214	  [className isEqualToString: @"FirstResponder"])
2215    {
2216      // special case, FirstResponder.
2217      return NO;
2218    }
2219
2220  return YES;
2221}
2222
2223- (NSString *) findClassByName: (NSString *)name
2224{
2225  NSArray *classNames = [self allClassNames];
2226  NSEnumerator *en = [classNames objectEnumerator];
2227  NSString *className = nil;
2228  NSInteger namelen = [name length];
2229
2230  while((className = [en nextObject]) != nil)
2231    {
2232      NSInteger classlen = [className length];
2233      if(namelen < classlen)
2234	{
2235	  NSComparisonResult result =
2236	    [className compare: name
2237		       options: NSCaseInsensitiveSearch
2238		       range: ((NSRange){0, namelen})];
2239	  if(result == NSOrderedSame)
2240	    {
2241	      break;
2242	    }
2243	}
2244      else if(namelen == classlen)
2245	{
2246	  if([className caseInsensitiveCompare: name] == NSOrderedSame)
2247	    {
2248	      break;
2249	    }
2250	}
2251    }
2252
2253  return className;
2254}
2255
2256- (NSString *) description
2257{
2258  return [NSString stringWithFormat: @"<%s: %lx> = %@",
2259 		   GSClassNameFromObject(self),
2260		   (unsigned long)self,
2261 		   customClassMap];
2262}
2263
2264/** Helpful for debugging */
2265- (NSString *) dumpClassInformation
2266{
2267  return [classInformation description];
2268}
2269@end
2270