mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-20 06:51:08 +00:00
941 lines
22 KiB
Objective-C
941 lines
22 KiB
Objective-C
/**
|
|
|
|
<title>AGSIndex ... a class to create references for a gsdoc file</title>
|
|
Copyright (C) 2001 Free Software Foundation, Inc.
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
Created: October 2001
|
|
|
|
This file is part of the GNUstep Project
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License along with this program; see the file COPYINGv3.
|
|
If not, write to the Free Software Foundation,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#import "common.h"
|
|
|
|
#import "Foundation/NSArray.h"
|
|
#import "Foundation/NSAutoreleasePool.h"
|
|
#import "Foundation/NSDictionary.h"
|
|
#import "Foundation/NSUserDefaults.h"
|
|
#import "AGSIndex.h"
|
|
#import "GNUstepBase/NSString+GNUstepBase.h"
|
|
#import "GNUstepBase/NSMutableString+GNUstepBase.h"
|
|
|
|
static int XML_ELEMENT_NODE;
|
|
static int XML_TEXT_NODE;
|
|
|
|
static void
|
|
mergeDictionaries(NSMutableDictionary *dst, NSDictionary *src, BOOL override)
|
|
{
|
|
static NSMutableArray *stack = nil;
|
|
NSEnumerator *e = [src keyEnumerator];
|
|
NSString *k;
|
|
id s;
|
|
id d;
|
|
|
|
if (stack == nil)
|
|
{
|
|
stack = [[NSMutableArray alloc] initWithCapacity: 8];
|
|
}
|
|
while ((k = [e nextObject]) != nil)
|
|
{
|
|
if ([k isEqualToString: @"contents"] == YES)
|
|
{
|
|
continue; // Makes no sense to merge file contents.
|
|
}
|
|
s = [src objectForKey: k];
|
|
d = [dst objectForKey: k];
|
|
|
|
[stack addObject: k];
|
|
if (d == nil)
|
|
{
|
|
if ([s isKindOfClass: [NSString class]])
|
|
{
|
|
[dst setObject: s forKey: k];
|
|
}
|
|
else if ([s isKindOfClass: [NSArray class]])
|
|
{
|
|
[dst setObject: s forKey: k];
|
|
}
|
|
else if ([s isKindOfClass: [NSDictionary class]])
|
|
{
|
|
d = [[NSMutableDictionary alloc] initWithCapacity: [s count]];
|
|
[dst setObject: d forKey: k];
|
|
RELEASE(d);
|
|
/* Fall through to populate the new dictionary.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"Unexpected class in merge %@ ignored", stack);
|
|
}
|
|
}
|
|
|
|
if (d != nil)
|
|
{
|
|
if ([d isKindOfClass: [NSString class]])
|
|
{
|
|
if ([s isKindOfClass: [NSString class]] == NO)
|
|
{
|
|
NSLog(@"Class mismatch in merge for %@.", stack);
|
|
}
|
|
else if ([d isEqual: s] == NO)
|
|
{
|
|
if (override == YES)
|
|
{
|
|
[dst setObject: s forKey: k];
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"String mismatch in merge for %@. S:%@, D:%@",
|
|
stack, s, d);
|
|
[dst setObject: s forKey: k];
|
|
}
|
|
}
|
|
}
|
|
else if ([d isKindOfClass: [NSArray class]])
|
|
{
|
|
if ([s isKindOfClass: [NSArray class]] == NO)
|
|
{
|
|
NSLog(@"Class mismatch in merge for %@.", stack);
|
|
}
|
|
else if ([d isEqual: s] == NO)
|
|
{
|
|
if ([[stack firstObject] isEqualToString: @"author"])
|
|
{
|
|
NSMutableArray *m = AUTORELEASE([s mutableCopy]);
|
|
NSUInteger c = [d count];
|
|
|
|
while (c-- > 0)
|
|
{
|
|
NSString *e = [d objectAtIndex: c];
|
|
|
|
if (NO == [m containsObject: e])
|
|
{
|
|
[m addObject: e];
|
|
}
|
|
}
|
|
[dst setObject: m forKey: k];
|
|
}
|
|
else if (override == YES)
|
|
{
|
|
[dst setObject: s forKey: k];
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"Array mismatch in merge for %@. S:%@, D:%@",
|
|
stack, s, d);
|
|
}
|
|
}
|
|
}
|
|
else if ([d isKindOfClass: [NSDictionary class]])
|
|
{
|
|
if ([s isKindOfClass: [NSDictionary class]] == NO)
|
|
{
|
|
NSLog(@"Class mismatch in merge for %@.", stack);
|
|
}
|
|
else
|
|
{
|
|
mergeDictionaries(d, s, override);
|
|
}
|
|
}
|
|
}
|
|
[stack removeLastObject];
|
|
}
|
|
}
|
|
|
|
static void
|
|
setDirectory(NSMutableDictionary *dict, NSString *path)
|
|
{
|
|
NSArray *a = [dict allKeys];
|
|
NSEnumerator *e = [a objectEnumerator];
|
|
NSString *k;
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
{
|
|
id o = [dict objectForKey: k];
|
|
|
|
if ([o isKindOfClass: [NSString class]] == YES)
|
|
{
|
|
o = [path stringByAppendingPathComponent: [o lastPathComponent]];
|
|
[dict setObject: o forKey: k];
|
|
}
|
|
else if ([o isKindOfClass: [NSDictionary class]] == YES)
|
|
{
|
|
setDirectory(o, path);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class is used to build and manipulate a dictionary of
|
|
* cross-reference information.<br />
|
|
* The references are held in a nested dictionary
|
|
* with strings at the leaves (persisted to 'projectName'.igsdoc) -<br />
|
|
* method : method-name - { class-name file-name }<br />
|
|
* method : method-name - { protocol-name file-name }<br />
|
|
* ivariable : variable-name - { class-name file-name }<br />
|
|
* class : class-name - file-name<br />
|
|
* category : category-name - file-name<br />
|
|
* protocol : protocol-name - file-name<br />
|
|
* function : function-name - file-name<br />
|
|
* type : type-name - file-name<br />
|
|
* constant : constant-name - file-name<br />
|
|
* variable : variable-name - file-name<br />
|
|
* entry : entry-name - { file-name ref }<br />
|
|
* label : label-name - { file-name ref }<br />
|
|
* contents : ref - text<br />
|
|
* super : class-name - superclass-name<br />
|
|
* categories : class-name - { category-name file-name }<br />
|
|
* unitmethods : unit-name - method-name<br />
|
|
* classvars : class-name - variables-name<br />
|
|
* title : file-name - text<br />
|
|
* source : file-name - array-of-source-files<br />
|
|
*/
|
|
@implementation AGSIndex
|
|
|
|
+ (void) initialize
|
|
{
|
|
if (self == [AGSIndex class])
|
|
{
|
|
/*
|
|
* Cache XML node information.
|
|
*/
|
|
XML_ELEMENT_NODE = [GSXMLNode typeFromDescription: @"XML_ELEMENT_NODE"];
|
|
XML_TEXT_NODE = [GSXMLNode typeFromDescription: @"XML_TEXT_NODE"];
|
|
}
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
RELEASE(refs);
|
|
[super dealloc];
|
|
}
|
|
|
|
static void
|
|
findKey(id refs, NSString *key, NSMutableArray *path, NSMutableArray *found)
|
|
{
|
|
if ([refs isKindOfClass: [NSDictionary class]])
|
|
{
|
|
if ([refs objectForKey: key])
|
|
{
|
|
[found addObject: AUTORELEASE([path copy])];
|
|
}
|
|
else
|
|
{
|
|
NSEnumerator *e = [refs keyEnumerator];
|
|
NSString *k;
|
|
|
|
while ((k = [e nextObject]) != nil)
|
|
{
|
|
[path addObject: k];
|
|
findKey([refs objectForKey: k], key, path, found);
|
|
[path removeLastObject];
|
|
}
|
|
}
|
|
}
|
|
else if ([refs isKindOfClass: [NSArray class]])
|
|
{
|
|
if ([refs containsObject: key])
|
|
{
|
|
[found addObject: AUTORELEASE([path copy])];
|
|
}
|
|
}
|
|
else if ([refs isEqual: key])
|
|
{
|
|
[found addObject: AUTORELEASE([path copy])];
|
|
}
|
|
}
|
|
|
|
/** Looks for a string as a key or value in the index,
|
|
* and returns the paths to any occurrences found.
|
|
* Returns nil if the string is not found.
|
|
*/
|
|
- (NSArray*) find: (NSString*)key
|
|
{
|
|
NSMutableArray *path = [NSMutableArray arrayWithCapacity: 10];
|
|
NSMutableArray *found = [NSMutableArray arrayWithCapacity: 10];
|
|
|
|
findKey(refs, key, path, found);
|
|
return ([found count] ? found : nil);
|
|
}
|
|
|
|
- (NSString*) globalRef: (NSString*)ref type: (NSString*)type
|
|
{
|
|
NSDictionary *t;
|
|
|
|
t = [refs objectForKey: type];
|
|
return [t objectForKey: ref];
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
refs = [[NSMutableDictionary alloc] initWithCapacity: 8];
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
* Given the root node of a gsdoc document, we traverse the tree
|
|
* looking for interesting nodes, and recording their names in a
|
|
* dictionary of references.
|
|
*/
|
|
- (void) makeRefs: (GSXMLNode*)node
|
|
{
|
|
GSXMLNode *children = [node firstChild];
|
|
GSXMLNode *next = [node next];
|
|
BOOL newUnit = NO;
|
|
|
|
if ([node type] == XML_ELEMENT_NODE)
|
|
{
|
|
NSString *name = [node name];
|
|
NSDictionary *prop = [node attributes];
|
|
|
|
// special case- if has an id of "_main" this gsdoc is from a tool
|
|
// file; add an entry to the index to indicate so
|
|
if (([name isEqual: @"chapter"] || [name isEqual: @"section"])
|
|
&& [@"_main" isEqual: [prop objectForKey: @"id"]] == YES)
|
|
{
|
|
[self setGlobalRef: base type: @"tool"];
|
|
}
|
|
|
|
if ([name isEqual: @"author"] == YES)
|
|
{
|
|
NSString *author = [prop objectForKey: @"name"];
|
|
|
|
if ([author length] > 0)
|
|
{
|
|
GSXMLNode *tmp = [node firstChild];
|
|
|
|
[self setEmail: nil forAuthor: author];
|
|
while (tmp)
|
|
{
|
|
if ([[tmp name] isEqual: @"email"])
|
|
{
|
|
NSDictionary *prop = [tmp attributes];
|
|
|
|
name = [prop objectForKey: @"address"];
|
|
if ([name length] > 0)
|
|
{
|
|
[self setEmail: name forAuthor: author];
|
|
}
|
|
}
|
|
tmp = [tmp next];
|
|
}
|
|
}
|
|
}
|
|
else if ([name isEqual: @"category"] == YES)
|
|
{
|
|
newUnit = YES;
|
|
classname = [prop objectForKey: @"class"];
|
|
category = [prop objectForKey: @"name"];
|
|
unit = classname;
|
|
/*
|
|
* Add this category to the list of those for the class.
|
|
*/
|
|
[self setUnitRef: category type: @"categories"];
|
|
unit = [NSString stringWithFormat: @"%@(%@)", classname, category];
|
|
[self setGlobalRef: unit type: @"category"];
|
|
}
|
|
else if ([name isEqual: @"chapter"] == YES)
|
|
{
|
|
chap++;
|
|
sect = 0;
|
|
ssect = 0;
|
|
sssect = 0;
|
|
}
|
|
else if ([name isEqual: @"class"] == YES)
|
|
{
|
|
NSString *tmp;
|
|
|
|
newUnit = YES;
|
|
classname = [prop objectForKey: @"name"];
|
|
unit = classname;
|
|
tmp = [prop objectForKey: @"super"];
|
|
if (tmp != nil)
|
|
{
|
|
[self setRelationship: @"super" from: unit to: tmp];
|
|
}
|
|
[self setGlobalRef: unit type: @"class"];
|
|
}
|
|
else if ([name isEqual: @"gsdoc"] == YES)
|
|
{
|
|
base = [prop objectForKey: @"base"];
|
|
if (base == nil)
|
|
{
|
|
NSLog(@"No 'base' document name supplied in gsdoc element");
|
|
return;
|
|
}
|
|
}
|
|
else if ([name isEqual: @"heading"] == YES)
|
|
{
|
|
NSMutableDictionary *d;
|
|
NSString *k;
|
|
NSString *c;
|
|
|
|
d = [refs objectForKey: @"contents"];
|
|
if (d == nil)
|
|
{
|
|
d = [[NSMutableDictionary alloc] initWithCapacity: 8];
|
|
[refs setObject: d forKey: @"contents"];
|
|
RELEASE(d);
|
|
}
|
|
|
|
k = [NSString stringWithFormat: @"%03u%03u%03u%03u",
|
|
chap, sect, ssect, sssect];
|
|
c = [[children content] stringByTrimmingSpaces];
|
|
if (c == nil) c = @"";
|
|
[d setObject: c forKey: k];
|
|
children = nil;
|
|
}
|
|
else if ([name isEqual: @"ivariable"] == YES)
|
|
{
|
|
NSString *tmp = [prop objectForKey: @"name"];
|
|
|
|
[self setUnitRef: tmp type: @"ivariable"];
|
|
[self setUnitRef: tmp type: @"classvars"];
|
|
}
|
|
else if ([name isEqual: @"entry"] || [name isEqual: @"label"])
|
|
{
|
|
NSMutableDictionary *all;
|
|
NSString *text;
|
|
NSString *val;
|
|
|
|
text = [[children content] stringByTrimmingSpaces];
|
|
if (text == nil) text = @"";
|
|
children = nil;
|
|
all = [refs objectForKey: name];
|
|
if (all == nil)
|
|
{
|
|
all = [[NSMutableDictionary alloc] initWithCapacity: 8];
|
|
[refs setObject: all forKey: name];
|
|
RELEASE(all);
|
|
}
|
|
val = [prop objectForKey: @"id"];
|
|
if (val == nil)
|
|
{
|
|
val = text;
|
|
}
|
|
[all setObject: base forKey: val];
|
|
}
|
|
else if ([name isEqual: @"method"] == YES)
|
|
{
|
|
NSString *sel = @"";
|
|
GSXMLNode *tmp = children;
|
|
|
|
sel = [prop objectForKey: @"factory"];
|
|
if (sel != nil && [sel boolValue] == YES)
|
|
{
|
|
sel = @"+";
|
|
}
|
|
else
|
|
{
|
|
sel = @"-";
|
|
}
|
|
children = nil;
|
|
while (tmp != nil)
|
|
{
|
|
if ([tmp type] == XML_ELEMENT_NODE)
|
|
{
|
|
if ([[tmp name] isEqual: @"sel"] == YES)
|
|
{
|
|
GSXMLNode *t = [tmp firstChild];
|
|
|
|
while (t != nil)
|
|
{
|
|
if ([t type] == XML_TEXT_NODE)
|
|
{
|
|
NSString *s;
|
|
|
|
s = [[t content] stringByTrimmingSpaces];
|
|
if (s == nil) s = @"";
|
|
sel = [sel stringByAppendingString: s];
|
|
}
|
|
t = [t next];
|
|
}
|
|
}
|
|
else if ([[tmp name] isEqual: @"vararg"] == YES)
|
|
{
|
|
sel = [sel stringByAppendingString: @",..."];
|
|
children = [tmp next];
|
|
break;
|
|
}
|
|
else if ([[tmp name] isEqual: @"arg"] == NO)
|
|
{
|
|
children = tmp;
|
|
break;
|
|
}
|
|
}
|
|
tmp = [tmp next];
|
|
}
|
|
if ([sel length] > 1)
|
|
{
|
|
[self setUnitRef: sel type: @"method"];
|
|
[self setUnitRef: sel type: @"unitmethods"];
|
|
}
|
|
}
|
|
else if ([name isEqual: @"protocol"] == YES)
|
|
{
|
|
newUnit = YES;
|
|
unit = [NSString stringWithFormat: @"(%@)",
|
|
[prop objectForKey: @"name"]];
|
|
[self setGlobalRef: unit type: @"protocol"];
|
|
}
|
|
else if ([name isEqual: @"constant"] == YES
|
|
|| [name isEqual: @"EOEntity"] == YES
|
|
|| [name isEqual: @"EOModel"] == YES
|
|
|| [name isEqual: @"function"] == YES
|
|
|| [name isEqual: @"macro"] == YES
|
|
|| [name isEqual: @"type"] == YES
|
|
|| [name isEqual: @"variable"] == YES)
|
|
{
|
|
NSString *tmp = [prop objectForKey: @"name"];
|
|
|
|
[self setGlobalRef: tmp type: name];
|
|
children = nil;
|
|
}
|
|
else if ([name isEqual: @"section"] == YES)
|
|
{
|
|
//FIXME- this info needs to be placed into the "label" refs somehow
|
|
sect++;
|
|
ssect = 0;
|
|
sssect = 0;
|
|
}
|
|
else if ([name isEqual: @"subsect"] == YES)
|
|
{
|
|
ssect++;
|
|
sssect = 0;
|
|
}
|
|
else if ([name isEqual: @"subsubsect"] == YES)
|
|
{
|
|
sssect++;
|
|
}
|
|
else if ([name isEqual: @"title"] == YES)
|
|
{
|
|
NSMutableDictionary *d;
|
|
NSString *s;
|
|
|
|
d = [refs objectForKey: @"title"];
|
|
if (d == nil)
|
|
{
|
|
d = [[NSMutableDictionary alloc] initWithCapacity: 8];
|
|
[refs setObject: d forKey: @"title"];
|
|
RELEASE(d);
|
|
}
|
|
|
|
s = [[children content] stringByTrimmingSpaces];
|
|
if (s == nil) s = @"";
|
|
[d setObject: s forKey: base];
|
|
children = nil;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
|
|
if (children != nil)
|
|
{
|
|
[self makeRefs: children];
|
|
}
|
|
if (newUnit == YES)
|
|
{
|
|
unit = nil;
|
|
category = nil;
|
|
classname = nil;
|
|
}
|
|
if (next != nil)
|
|
{
|
|
[self makeRefs: next];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merge a dictionary containing references into the current
|
|
* index. The flag may be used to specify that references
|
|
* being merged in should override any pre-existing values.
|
|
*/
|
|
- (void) mergeRefs: (NSDictionary*)more override: (BOOL)flag
|
|
{
|
|
mergeDictionaries(refs, more, flag);
|
|
}
|
|
|
|
/**
|
|
* Informal protocols are not explicitly marked in source, but are
|
|
* inferred to be those categories of NSObject that receive no
|
|
* implementation. [AGSOutput] finds and accumulates them; autogsdoc
|
|
* passes them here, where each entry is found in the 'category'
|
|
* section of our refs and copied over to the protocol section.
|
|
*/
|
|
- (void) addInformalProtocols: (NSArray *)protocolNames
|
|
{
|
|
NSString *name;
|
|
NSString *file;
|
|
NSEnumerator *pnames = [protocolNames objectEnumerator];
|
|
|
|
//PENDING, should we worry about not overriding entries?
|
|
while ((name = [pnames nextObject]) != nil)
|
|
{
|
|
NSMutableDictionary *d;
|
|
|
|
d = [refs objectForKey: @"category"];
|
|
file = [d objectForKey: name];
|
|
if (file != nil)
|
|
{
|
|
d = [refs objectForKey: @"protocol"];
|
|
[d setObject: file forKey: name];
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"Category entry not found for informal protocol '%@'", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (NSArray*) methodsInUnit: (NSString*)aUnit
|
|
{
|
|
NSDictionary *d = [refs objectForKey: @"unitmethods"];
|
|
|
|
d = [d objectForKey: aUnit];
|
|
return [d allKeys];
|
|
}
|
|
|
|
/**
|
|
* Return a list of output files for the header
|
|
*/
|
|
- (NSMutableArray*) outputsForHeader: (NSString*)h
|
|
{
|
|
NSDictionary *dict = [refs objectForKey: @"output"];
|
|
NSArray *array = [dict objectForKey: h];
|
|
|
|
if (array == nil)
|
|
{
|
|
return [NSMutableArray arrayWithCapacity: 2];
|
|
}
|
|
return AUTORELEASE([array mutableCopy]);
|
|
}
|
|
|
|
- (NSMutableDictionary*) refs
|
|
{
|
|
return refs;
|
|
}
|
|
|
|
- (void) setDirectory: (NSString*)path
|
|
{
|
|
if (path != nil)
|
|
{
|
|
ENTER_POOL
|
|
setDirectory(refs, path);
|
|
LEAVE_POOL
|
|
}
|
|
}
|
|
|
|
- (NSDictionary*) authors
|
|
{
|
|
return [refs objectForKey: @"author"];
|
|
}
|
|
|
|
/**
|
|
* Set up an array of email addresses for author.
|
|
*/
|
|
- (void) setEmail: (NSString*)address forAuthor: (NSString*)name
|
|
{
|
|
NSMutableDictionary *dict;
|
|
NSMutableArray *array;
|
|
|
|
dict = [refs objectForKey: @"author"];
|
|
if (dict == nil)
|
|
{
|
|
dict = [NSMutableDictionary new];
|
|
[refs setObject: dict forKey: @"author"];
|
|
RELEASE(dict);
|
|
}
|
|
if (nil == (array = [dict objectForKey: name]))
|
|
{
|
|
array = [NSMutableArray array];
|
|
[dict setObject: array forKey: name];
|
|
}
|
|
address = [address lowercaseString];
|
|
if (address != nil && NO == [array containsObject: address])
|
|
{
|
|
[array addObject: address];
|
|
}
|
|
}
|
|
|
|
- (void) setGlobalRef: (NSString*)ref
|
|
type: (NSString*)type
|
|
{
|
|
NSMutableDictionary *t;
|
|
NSString *old;
|
|
|
|
t = [refs objectForKey: type];
|
|
if (t == nil)
|
|
{
|
|
t = [NSMutableDictionary new];
|
|
[refs setObject: t forKey: type];
|
|
RELEASE(t);
|
|
}
|
|
old = [t objectForKey: ref];
|
|
if (old != nil && [old isEqual: base] == NO)
|
|
{
|
|
NSLog(@"Warning ... %@ %@ appears in %@ and %@ ... using the latter",
|
|
type, ref, old, base);
|
|
}
|
|
[t setObject: base forKey: ref];
|
|
}
|
|
|
|
/**
|
|
* Set up an array listing the output files for a particular header.
|
|
*/
|
|
- (void) setOutputs: (NSArray*)a forHeader: (NSString*)h
|
|
{
|
|
NSMutableDictionary *dict;
|
|
|
|
dict = [refs objectForKey: @"output"];
|
|
if (dict == nil)
|
|
{
|
|
dict = [NSMutableDictionary new];
|
|
[refs setObject: dict forKey: @"output"];
|
|
RELEASE(dict);
|
|
}
|
|
[dict setObject: a forKey: h];
|
|
}
|
|
|
|
- (void) setRelationship: (NSString*)r from: (NSString*)from to: (NSString*)to
|
|
{
|
|
NSMutableDictionary *dict;
|
|
|
|
dict = [refs objectForKey: r];
|
|
if (dict == nil)
|
|
{
|
|
dict = [NSMutableDictionary new];
|
|
[refs setObject: dict forKey: r];
|
|
RELEASE(dict);
|
|
}
|
|
[dict setObject: to forKey: from];
|
|
}
|
|
|
|
/**
|
|
* Set up an array listing the source files for a particular header.
|
|
*/
|
|
- (void) setSources: (NSArray*)a forHeader: (NSString*)h
|
|
{
|
|
NSMutableDictionary *dict;
|
|
|
|
dict = [refs objectForKey: @"source"];
|
|
if (dict == nil)
|
|
{
|
|
dict = [NSMutableDictionary new];
|
|
[refs setObject: dict forKey: @"source"];
|
|
RELEASE(dict);
|
|
}
|
|
[dict setObject: a forKey: h];
|
|
}
|
|
|
|
/**
|
|
* Set up a reference for something inside a unit (class, category or protocol)
|
|
* We store 'method' and 'ivariable' by ref then unit (class),
|
|
* but we store 'unitmethods' * and 'classvars' by unit then ref.
|
|
*/
|
|
- (void) setUnitRef: (NSString*)ref
|
|
type: (NSString*)type
|
|
{
|
|
NSMutableDictionary *t;
|
|
NSMutableDictionary *r;
|
|
NSString *u = unit;
|
|
NSString *old;
|
|
|
|
if ([type isEqualToString: @"method"] || [type isEqualToString: @"ivariable"])
|
|
{
|
|
// type ... ref ... unit ... file
|
|
}
|
|
else
|
|
{
|
|
u = ref;
|
|
ref = unit;
|
|
}
|
|
|
|
t = [refs objectForKey: type];
|
|
if (t == nil)
|
|
{
|
|
t = [NSMutableDictionary new];
|
|
[refs setObject: t forKey: type];
|
|
RELEASE(t);
|
|
}
|
|
r = [t objectForKey: ref];
|
|
if (r == nil)
|
|
{
|
|
r = [NSMutableDictionary new];
|
|
[t setObject: r forKey: ref];
|
|
RELEASE(r);
|
|
}
|
|
old = [r objectForKey: u];
|
|
if (old != nil && [old isEqual: base] == NO)
|
|
{
|
|
NSLog(@"Warning ... %@ %@ %@ appears in %@ and %@ ... using the latter",
|
|
type, ref, u, old, base);
|
|
}
|
|
[r setObject: base forKey: u];
|
|
}
|
|
|
|
/**
|
|
* Return a list of source files for the header.
|
|
*/
|
|
- (NSMutableArray*) sourcesForHeader: (NSString*)h
|
|
{
|
|
NSDictionary *dict = [refs objectForKey: @"source"];
|
|
NSArray *array = [dict objectForKey: h];
|
|
|
|
if (array == nil)
|
|
{
|
|
return [NSMutableArray arrayWithCapacity: 2];
|
|
}
|
|
return AUTORELEASE([array mutableCopy]);
|
|
}
|
|
|
|
/**
|
|
* Return a dictionary containing info on all the units containing the
|
|
* specified method or instance variable.
|
|
*/
|
|
- (NSDictionary*) unitRef: (NSString*)ref type: (NSString*)type
|
|
{
|
|
NSDictionary *t;
|
|
|
|
t = [refs objectForKey: type];
|
|
return [t objectForKey: ref];
|
|
}
|
|
|
|
/**
|
|
* Return the name of the file containing the ref and return
|
|
* the unit name in which it was found. If not found, return nil for both.
|
|
*/
|
|
- (NSString*) unitRef: (NSString*)ref type: (NSString*)type unit: (NSString**)u
|
|
{
|
|
NSString *s;
|
|
NSDictionary *t;
|
|
|
|
/**
|
|
* If ref does not occur in the index, this method returns nil.
|
|
*/
|
|
t = [self unitRef: ref type: type];
|
|
if (t == nil)
|
|
{
|
|
*u = nil;
|
|
return nil;
|
|
}
|
|
if (*u == nil)
|
|
{
|
|
NSEnumerator *e;
|
|
NSString *n;
|
|
unsigned count = 0;
|
|
|
|
/**
|
|
* If the method was given no unit to look in, then it will succeed
|
|
* and return a value if (and only if) the required reference is
|
|
* defined only in one unit (excluding protocols).
|
|
* In the case where it is in two units (one of them a protocol)
|
|
* the class is taken in preference to the protocol.
|
|
*/
|
|
e = [t keyEnumerator];
|
|
while ((n = [e nextObject]) != nil)
|
|
{
|
|
*u = n;
|
|
if ([n hasPrefix: @"("] == NO)
|
|
{
|
|
if (count++ > 0)
|
|
{
|
|
*u = nil; // More than one match
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (*u != nil)
|
|
{
|
|
n = [t objectForKey: *u];
|
|
}
|
|
else
|
|
{
|
|
n = nil;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* If ref exists in the unit specified, the method will succeed and
|
|
* return the name of the file in which the reference is located.
|
|
*/
|
|
s = [t objectForKey: *u];
|
|
if (s != nil)
|
|
{
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* If the unit that the method has been asked to look in is a
|
|
* protocol which is not found, the lookup must fail.
|
|
*/
|
|
if ([*u hasPrefix: @"("] == YES)
|
|
{
|
|
*u = nil;
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
* If unit is a category, method was probably indexed under the class,
|
|
* so work with the class instead of the category.
|
|
*/
|
|
if ([*u length] > 0 && [*u characterAtIndex: [*u length] - 1] == ')')
|
|
{
|
|
*u = [*u substringToIndex: [*u rangeOfString: @"("].location];
|
|
if ((s = [t objectForKey: *u]) != nil)
|
|
{
|
|
return s;
|
|
}
|
|
}
|
|
|
|
/* For a method lookup, try an informal protocol of the class
|
|
*/
|
|
if ([*u length] > 0 && [type isEqual: @"method"])
|
|
{
|
|
NSString *p = [NSString stringWithFormat: @"(%@)", *u];
|
|
|
|
if ((s = [t objectForKey: p]) != nil)
|
|
{
|
|
if ([[NSUserDefaults standardUserDefaults] boolForKey: @"Warn"])
|
|
{
|
|
NSLog(@"Warning - found %@ only in informal protocol of %@",
|
|
ref, *u);
|
|
}
|
|
return s;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try all superclasses in turn.
|
|
*/
|
|
while (*u != nil)
|
|
{
|
|
*u = [self globalRef: *u type: @"super"];
|
|
if (*u != nil)
|
|
{
|
|
s = [t objectForKey: *u];
|
|
if (s != nil)
|
|
{
|
|
return s;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
@end
|
|
|