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