/** AGSHtml ... a class to output html for a gsdoc file Copyright (C) 2001 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald 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/NSAutoreleasePool.h" #import "Foundation/NSArray.h" #import "Foundation/NSDictionary.h" #import "Foundation/NSSet.h" #import "Foundation/NSUserDefaults.h" #import "AGSHtml.h" #import "GNUstepBase/NSString+GNUstepBase.h" #import "GNUstepBase/NSMutableString+GNUstepBase.h" /* * Define constants for use if we are built with apple Foundation */ #ifndef GS_API_OSSPEC #define GS_API_OSSPEC 10000 #endif #ifndef GS_API_OPENSTEP #define GS_API_OPENSTEP 40000 #endif #ifndef GS_API_MACOSX #define GS_API_MACOSX 100000 #endif static int XML_ELEMENT_NODE; static int XML_ENTITY_REF_NODE; static int XML_TEXT_NODE; static GSXMLNode *firstElement(GSXMLNode *nodes) { if (nodes == nil) { return nil; } if ([nodes type] == XML_ELEMENT_NODE) { return nodes; } return [nodes nextElement]; } @implementation AGSHtml static NSMutableSet *textNodes = nil; static NSString *tocFont = nil; static NSString *mainFont = nil; + (void) initialize { if (self == [AGSHtml class]) { /* * Cache XML node information. */ XML_ELEMENT_NODE = [GSXMLNode typeFromDescription: @"XML_ELEMENT_NODE"]; XML_ENTITY_REF_NODE = [GSXMLNode typeFromDescription: @"XML_ENTITY_REF_NODE"]; XML_TEXT_NODE = [GSXMLNode typeFromDescription: @"XML_TEXT_NODE"]; textNodes = [NSMutableSet new]; [textNodes addObject: @"br"]; [textNodes addObject: @"code"]; [textNodes addObject: @"em"]; [textNodes addObject: @"email"]; [textNodes addObject: @"entry"]; [textNodes addObject: @"file"]; [textNodes addObject: @"label"]; [textNodes addObject: @"prjref"]; [textNodes addObject: @"ref"]; [textNodes addObject: @"site"]; [textNodes addObject: @"strong"]; [textNodes addObject: @"uref"]; [textNodes addObject: @"url"]; [textNodes addObject: @"var"]; [textNodes addObject: @"footnote"]; // default fonts tocFont = @"sans"; mainFont = @"serif"; } } - (void) dealloc { RELEASE(project); RELEASE(globalRefs); RELEASE(localRefs); RELEASE(projectRefs); RELEASE(indent); [super dealloc]; } - (void) decIndent { unsigned len = [indent length]; if (len >= 2) { [indent deleteCharactersInRange: NSMakeRange(len - 2, 2)]; } } - (void) incIndent { [indent appendString: @" "]; } - (id) init { indent = [[NSMutableString alloc] initWithCapacity: 64]; project = RETAIN([[NSUserDefaults standardUserDefaults] stringForKey: @"Project"]); return self; } /** * Calls -makeLink:ofType:isRef: or -makeLink:ofType:inUnit:isRef: to * create the first part of an anchor, and fills in the text content * of the anchor with n (the specified name). Returns an entire anchor * string as a result.
* This method is used to create all the anchors in the html output. */ - (NSString*) makeAnchor: (NSString*)r ofType: (NSString*)t name: (NSString*)n { NSString *s; if (n == nil) { n = @""; } if ([t isEqualToString: @"method"] || [t isEqualToString: @"ivariable"]) { s = [self makeLink: r ofType: t inUnit: nil isRef: NO]; } else { s = [self makeLink: r ofType: t isRef: NO]; } if (s != nil) { n = [s stringByAppendingFormat: @"%@", n]; } return n; } /** * Make a link for the element r with the specified type. Only the start of * the html element is returned (<a ...>). * If the boolean f is YES, then the link is a reference to somewhere, * and the method will return nil if the destination is not found in the index. * If f is NO, then the link is an anchor for some element being output, and * the method is guaranteed to succeed and return the link. */ - (NSString*) makeLink: (NSString*)r ofType: (NSString*)t isRef: (BOOL)f { NSString *s; NSString *kind = (f == YES) ? @"rel=\"gsdoc\" href" : @"name"; NSString *hash = (f == YES) ? @"#" : @""; if (f == NO || (s = [localRefs globalRef: r type: t]) != nil) { s = [NSString stringWithFormat: @"", kind, hash, t, r]; } else if ((s = [globalRefs globalRef: r type: t]) != nil) { s = [s stringByAppendingPathExtension: @"html"]; s = [NSString stringWithFormat: @"", kind, s, hash, t, r]; } return [s stringByReplacingString: @":" withString: @"$"]; } /** * Make a link for the element r, with the specified type t, * in a particular unit u. Only the start of * the html element is returned (<a ...>).
* If the boolean f is YES, then the link is a reference to somewhere, * otherwise the link is an anchor for some element being output.
* If there is an error, the method returns nil. */ - (NSString*) makeLink: (NSString*)r ofType: (NSString*)t inUnit: (NSString*)u isRef: (BOOL)f { NSString *s = nil; NSString *kind = (f == YES) ? @"rel=\"gsdoc\" href" : @"name"; NSString *hash = (f == YES) ? @"#" : @""; if (f == NO) { if (u == nil) { u = unit; s = base; } } else if (u == nil) { NSString *tmp = unit; s = [localRefs unitRef: r type: t unit: &tmp]; if (s == nil) { tmp = u; s = [localRefs unitRef: r type: t unit: &tmp]; if (s == nil) { tmp = unit; s = [globalRefs unitRef: r type: t unit: &tmp]; if (s == nil) { tmp = nil; s = [globalRefs unitRef: r type: t unit: &tmp]; } } } u = tmp; } if (s == nil) { NSString *tmp = u; /* * Simply look up the reference. */ s = [localRefs unitRef: r type: t unit: &u]; if (s == nil) { u = tmp; s = [globalRefs unitRef: r type: t unit: &u]; } } if (s != nil) { NSString *sep = @""; if ([t isEqual: @"ivariable"] == YES) { sep = @"*"; } if ([s isEqual: base] == YES) { s = [NSString stringWithFormat: @"
", kind, hash, t, u, sep, r]; } else { s = [s stringByAppendingPathExtension: @"html"]; s = [NSString stringWithFormat: @"", kind, s, hash, t, u, sep, r]; } } return [s stringByReplacingString: @":" withString: @"$"]; } - (NSString*) outputDocument: (GSXMLNode*)node { NSMutableString *buf; if (localRefs == nil) { localRefs = [AGSIndex new]; [localRefs makeRefs: node]; } buf = [NSMutableString stringWithCapacity: 4096]; /* Declaration */ [buf appendString: @"\n"]; [buf appendString: @"\n"]; [self incIndent]; [self outputNodeList: node to: buf]; [self decIndent]; [buf appendString: @"\n"]; return buf; } - (void) outputIndex: (NSString*)type scope: (NSString*)scope title: (NSString*)title style: (NSString*)style target: (NSString*)target to: (NSMutableString*)buf { NSDictionary *refs = [localRefs refs]; NSDictionary *dict; NSArray *a; unsigned c; unsigned i; BOOL isBareStyle = [@"bare" isEqualToString: style]; if (globalRefs != nil && [scope isEqual: @"global"] == YES) { refs = [globalRefs refs]; } else if (projectRefs != nil && [scope isEqual: @"project"] == YES) { refs = [projectRefs refs]; } if ([type isEqualToString: @"method"] == YES) { if (unit == nil) { refs = nil; // Can't index methods outside a unit. } dict = [refs objectForKey: @"unitmethods"]; dict = [dict objectForKey: unit]; } else if ([type isEqualToString: @"ivariable"] == YES) { if (unit == nil) { refs = nil; // Can't index instance variables outside a class. } dict = [refs objectForKey: @"classvars"]; dict = [dict objectForKey: unit]; } else { dict = [refs objectForKey: type]; } if ([dict count] > 1 && [type isEqual: @"title"] == YES) { if (!isBareStyle) { [buf appendString: indent]; [buf appendFormat: @"%@ Index\n", title]; [buf appendString: indent]; [buf appendString: @"\n"]; } } else if ([dict count] > 0) { NSString *sep = @""; NSString *u = unit; BOOL isInUnit = NO; if (unit != nil) { if ([type isEqual: @"method"] || [type isEqual: @"ivariable"]) { isInUnit = YES; if ([type isEqual: @"ivariable"]) { sep = @"*"; // List ivars in class } else if (classname != nil && category == nil) { NSArray *catNames; NSDictionary *d; /* * For a class, we want to list methods in any associated * categories as well as those of the class itself. */ d = [refs objectForKey: @"categories"]; d = [d objectForKey: classname]; catNames = [d allKeys]; if ((c = [catNames count]) > 0) { NSMutableDictionary *m = [dict mutableCopy]; NSDictionary *unitDict; unitDict = [refs objectForKey: @"unitmethods"]; for (i = 0; i < c; i++) { NSString *catName = [catNames objectAtIndex: i]; NSDictionary *catDict; NSString *cName; NSEnumerator *enumerator; NSString *mname; cName = [classname stringByAppendingFormat: @"(%@)", catName]; catDict = [unitDict objectForKey: cName]; enumerator = [catDict keyEnumerator]; /* * Add category references to the dictionary, * prefixing them with the category they belong to. */ while ((mname = [enumerator nextObject]) != nil) { NSString *file = [catDict objectForKey: mname]; NSString *ref = [NSString stringWithFormat: @"(%@)%@", catName, mname]; [m setObject: file forKey: ref]; } } dict = AUTORELEASE(m); } } } } [buf appendString: indent]; if (!isBareStyle) { [buf appendFormat: @"%@\n", title]; } [buf appendString: indent]; if (!isBareStyle) { [buf appendString: @""]; } [buf appendString: @"\n"]; } } - (void) outputNode: (GSXMLNode*)node to: (NSMutableString*)buf { #if GS_WITH_GC == 0 CREATE_AUTORELEASE_POOL(arp); #endif GSXMLNode *children = [node firstChild]; if ([node type] == XML_ELEMENT_NODE) { NSString *name = [node name]; NSDictionary *prop = [node attributes]; if ([name isEqual: @"back"] == YES) { // Open back division [buf appendString: indent]; [buf appendString: @"
\n"]; [self incIndent]; [self outputNodeList: children to: buf]; // Close back division [self decIndent]; [buf appendString: indent]; [buf appendString: @"
\n"]; } else if ([name isEqual: @"body"] == YES) { /* Should already be in html body */ [self outputNodeList: children to: buf]; [buf appendString: indent]; [buf appendString: @"
\n"]; if (prevFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Prev\n", prevFile]; } if (upFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Up\n", upFile]; } if (nextFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Next\n", nextFile]; } [self decIndent]; [buf appendString: indent]; [buf appendString: indent]; [buf appendString: @"\n"]; [buf appendString: @"\n"]; } else if ([name isEqual: @"br"] == YES) { [buf appendString: @"
"]; } else if ([name isEqual: @"category"] == YES) { NSString *s; category = [prop objectForKey: @"name"]; classname = [prop objectForKey: @"class"]; unit = [NSString stringWithFormat: @"%@(%@)", classname, category]; [buf appendString: indent]; [buf appendString: @"

"]; [buf appendString: [self typeRef: classname]]; [buf appendString: @"("]; s = [self makeAnchor: unit ofType: @"category" name: category]; [buf appendString: s]; [buf appendString: @")

\n"]; [self outputUnit: node to: buf]; unit = nil; classname = nil; category = nil; } else if ([name isEqual: @"chapter"] == YES) { heading = @"h1"; chap++; sect = 0; ssect = 0; sssect = 0; [self outputNodeList: children to: buf]; heading = nil; } else if ([name isEqual: @"class"] == YES) { NSString *sup = [prop objectForKey: @"super"]; classname = [prop objectForKey: @"name"]; unit = classname; [buf appendString: indent]; [buf appendString: @"

"]; [buf appendString: [self makeAnchor: classname ofType: @"class" name: classname]]; if (sup != nil) { sup = [self typeRef: sup]; if (sup != nil) { [buf appendString: @" : "]; [buf appendString: sup]; } } [buf appendString: @"

\n"]; [self outputUnit: node to: buf]; unit = nil; classname = nil; } else if ([name isEqual: @"code"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"constant"] == YES) { NSString *nam; NSString *str; NSString *s; nam = [prop objectForKey: @"name"]; str = [prop objectForKey: @"type"]; str = [self typeRef: str]; str = [str stringByAppendingFormat: @" %@", nam]; /* * Output heading. */ [buf appendString: indent]; [buf appendString: @"

"]; s = [self makeLink: nam ofType: @"constant" isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: nam]; [buf appendString: @""]; } else { [buf appendString: nam]; } [buf appendString: @"

\n"]; [buf appendString: indent]; [buf appendString: str]; [buf appendString: @";
\n"]; node = firstElement(children); if ([[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if ([[node name] isEqual: @"desc"]) { [self outputNode: node to: buf]; } [buf appendString: indent]; [buf appendString: @"
\n"]; } else if ([name isEqual: @"contents"] == YES) { NSDictionary *dict; dict = [[localRefs refs] objectForKey: @"contents"]; if ([dict count] > 1) { NSArray *a; unsigned i; unsigned l = 0; [buf appendString: indent]; [buf appendString: @"
\n"]; [buf appendString: indent]; [buf appendString: @"

Contents -

\n"]; a = [dict allKeys]; a = [a sortedArrayUsingSelector: @selector(compare:)]; for (i = 0; i < [a count]; i++) { NSString *k = [a objectAtIndex: i]; NSString *v = [dict objectForKey: k]; unsigned pos = 3; if ([k hasSuffix: @"000"] == YES) { pos = 2; if ([k hasSuffix: @"000000"] == YES) { pos = 1; if ([k hasSuffix: @"000"] == YES) { pos = 0; } } if (l == pos) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; } else { while (l > pos + 1) { [self decIndent]; [buf appendString: indent]; [buf appendString: @"\n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"
\n"]; l--; } if (l == pos + 1) { [self decIndent]; [buf appendString: indent]; [buf appendString: @"\n"]; l--; } } } [buf appendString: indent]; [buf appendString: @"
  • \n"]; [self incIndent]; [buf appendString: indent]; [buf appendFormat: @"%@\n", k, v]; if (pos == 3) { [self decIndent]; [buf appendString: indent]; [buf appendString: @"
  • \n"]; } else { l++; } } while (l > 0) { [self decIndent]; [buf appendString: indent]; [buf appendString: @"\n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"\n"]; l--; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } } else if ([name isEqual: @"declared"] == YES) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; [buf appendString: indent]; [buf appendString: @"
    Declared in:
    \n"]; [buf appendString: indent]; [buf appendString: @"
    "]; [self outputText: [node firstChild] to: buf]; [buf appendString: @"
    \n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"desc"] == YES) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; while (children != nil) { children = [self outputBlock: children to: buf inPara: YES]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"em"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"email"] == YES) { NSString *ename; ename = [prop objectForKey: @"address"]; if (ename == nil) { [buf appendString: @""]; } else { [buf appendFormat: @"", ename]; } // // [node firstChild] doesn't look like it points to // the mail address. // not sure _where_ it points to though... // #if 0 [self outputText: [node firstChild] to: buf]; #endif if (ename == nil) { [buf appendString: @""]; } else { [buf appendFormat: @"%@", ename]; } } else if ([name isEqual: @"embed"] == YES) { [self outputBlock: node to: buf inPara: NO]; } else if ([name isEqual: @"entry"]) { NSString *text; NSString *val; text = [children escapedContent]; val = [prop objectForKey: @"id"]; if (val == nil) { val = text; if (val == nil) val = @""; } [buf appendString: [self makeAnchor: val ofType: @"label" name: @""]]; } else if ([name isEqual: @"example"] == YES) { [self outputBlock: node to: buf inPara: NO]; } else if ([name isEqual: @"file"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"footnote"] == YES) { [buf appendString: @"
    "]; [self outputText: children to: buf]; [buf appendString: @"
    "]; } else if ([name isEqual: @"front"] == YES) { // Open front division [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; [self outputNodeList: children to: buf]; // Close front division [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"function"] == YES) { NSString *fun; NSString *str; NSString *s; GSXMLNode *tmp = children; BOOL hadArg = NO; fun = [prop objectForKey: @"name"]; str = [prop objectForKey: @"type"]; str = [self typeRef: str]; str = [str stringByAppendingFormat: @" %@(", fun]; children = nil; while (tmp != nil) { if ([tmp type] == XML_ELEMENT_NODE) { if ([[tmp name] isEqual: @"arg"] == YES) { GSXMLNode *t = [tmp firstChild]; NSString *s; if (hadArg == YES) { str = [str stringByAppendingString: @", "]; } s = [[tmp attributes] objectForKey: @"type"]; s = [self typeRef: s]; str = [str stringByAppendingString: s]; str = [str stringByAppendingString: @" "]; while (t != nil) { if ([t type] == XML_TEXT_NODE) { NSString *content = [t escapedContent]; if (content == nil) content = @""; str = [str stringByAppendingString: content]; } t = [t next]; } str = [str stringByAppendingString: @""]; hadArg = YES; } else if ([[tmp name] isEqual: @"vararg"] == YES) { if (hadArg == YES) { str = [str stringByAppendingString: @",..."]; } else { str = [str stringByAppendingString: @",..."]; } children = [tmp nextElement]; break; } else { children = tmp; break; } } tmp = [tmp nextElement]; } /* * Output function heading. */ [buf appendString: indent]; [buf appendString: @"

    "]; s = [self makeLink: fun ofType: @"function" isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: fun]; [buf appendString: @""]; } else { [buf appendString: fun]; } [buf appendString: @"

    \n"]; [buf appendString: indent]; [buf appendString: str]; [buf appendString: @");
    \n"]; node = firstElement(children); if ([[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if ([[node name] isEqual: @"desc"]) { [self outputNode: node to: buf]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"gsdoc"] == YES) { NSString *stylesheetURL = [prop objectForKey: @"stylesheeturl"]; base = [prop objectForKey: @"base"]; if (base == nil) { NSLog(@"No 'base' document name supplied in gsdoc element"); return; } nextFile = [prop objectForKey: @"next"]; nextFile = [nextFile stringByAppendingPathExtension: @"html"]; prevFile = [prop objectForKey: @"prev"]; prevFile = [prevFile stringByAppendingPathExtension: @"html"]; upFile = [prop objectForKey: @"up"]; upFile = [upFile stringByAppendingPathExtension: @"html"]; // special formatting for table-of-contents frames; ultimately // this should be moved to stylesheet isContentsDoc = ((stylesheetURL != nil) && ([stylesheetURL rangeOfString: @"gsdoc_contents"].length > 0)) ? YES : NO; [self outputNodeList: children to: buf]; } else if ([name isEqual: @"head"] == YES) { NSString *headerTag; [buf appendString: indent]; [buf appendString: @"\n"]; [self incIndent]; children = firstElement(children); [buf appendString: indent]; [buf appendString: @""]; [self incIndent]; [self outputText: [children firstChild] to: buf]; [self decIndent]; [buf appendString: @"\n"]; #if 0 /** Css : TODO print.css **/ [buf appendString:@"\n"]; [buf appendString:@"\n"]; /** Robots **/ [buf appendString:@"\n"]; #endif [self decIndent]; [buf appendString: indent]; [buf appendString: @"\n"]; [buf appendString: indent]; [buf appendString: @"\n"]; [self incIndent]; // special formatting for table-of-contents frames; ultimately // this should be moved to stylesheet if (isContentsDoc) { [buf appendString: indent]; [buf appendFormat: @"\n", tocFont]; } else { [buf appendString: indent]; [buf appendFormat: @"\n", mainFont]; } if (prevFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Prev\n", prevFile]; } if (upFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Up\n", upFile]; } if (nextFile != nil) { [buf appendString: indent]; [buf appendFormat: @"Next\n", nextFile]; } if (prevFile != nil || upFile != nil || nextFile != nil) { [buf appendString: indent]; [buf appendString: @"
    \n"]; } [buf appendString: indent]; if (isContentsDoc) { headerTag = @"h2"; } else { headerTag = @"h1"; } [buf appendFormat: @"<%@>", headerTag, base]; [self outputText: [children firstChild] to: buf]; [buf appendFormat: @"\n", headerTag]; children = [children nextElement]; if ([[children name] isEqual: @"author"] == YES) { [buf appendString: indent]; [buf appendString: @"

    Authors

    \n"]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; while ([[children name] isEqual: @"author"] == YES) { GSXMLNode *author = children; GSXMLNode *tmp; GSXMLNode *email = nil; GSXMLNode *url = nil; GSXMLNode *desc = nil; children = [children nextElement]; tmp = [author firstChildElement]; if ([[tmp name] isEqual: @"email"] == YES) { email = tmp; tmp = [tmp nextElement]; } if ([[tmp name] isEqual: @"url"] == YES) { url = tmp; tmp = [tmp nextElement]; } if ([[tmp name] isEqual: @"desc"] == YES) { desc = tmp; tmp = [tmp nextElement]; } [buf appendString: indent]; if (url == nil) { [buf appendString: @"
    "]; [buf appendString: [[[author attributes] objectForKey: @"name"] stringByEscapingXML]]; } else { [buf appendString: @"
    "]; [buf appendString: [[[author attributes] objectForKey: @"name"] stringByEscapingXML]]; [buf appendString: @""]; } if (email != nil) { // // Add a beautifier ' ' otherwise we'll get a //
    John Doe(\n"]; [buf appendString: indent]; [buf appendString: @"
    \n"]; if (desc != nil) { [self incIndent]; [self outputNode: desc to: buf]; desc = nil; [self decIndent]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } if ([[children name] isEqual: @"version"] == YES) { [buf appendString: indent]; [buf appendString: @"

    Version: "]; [self outputText: [children firstChild] to: buf]; [buf appendString: @"

    \n"]; children = [children nextElement]; } if ([[children name] isEqual: @"date"] == YES) { [buf appendString: indent]; [buf appendString: @"

    Date: "]; [self outputText: [children firstChild] to: buf]; [buf appendString: @"

    \n"]; children = [children nextElement]; } if ([[children name] isEqual: @"abstract"] == YES) { GSXMLNode *tmp = [children firstChild]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; while (tmp != nil) { tmp = [self outputBlock: tmp to: buf inPara: NO]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; children = [children nextElement]; } if ([[children name] isEqual: @"copy"] == YES) { [buf appendString: indent]; [buf appendString: @"

    Copyright: (C) "]; [self outputText: [children firstChild] to: buf]; [buf appendString: @"

    \n"]; children = [children nextElement]; } } else if ([name isEqual: @"heading"] == YES) { if (heading == nil) { } else { [buf appendString: indent]; [buf appendString: @"<"]; [buf appendString: heading]; [buf appendString: @">"]; [buf appendFormat: @"
    ", chap, sect, ssect, sssect]; [self outputText: children to: buf]; [buf appendString: @"\n"]; heading = nil; } } else if ([name isEqual: @"index"] == YES) { NSString *scope = [prop objectForKey: @"scope"]; NSString *type = [prop objectForKey: @"type"]; NSString *target = [prop objectForKey: @"target"]; NSString *title = [type capitalizedString]; NSString *style = [prop objectForKey: @"style"]; [self outputIndex: type scope: scope title: title style: style target: target to: buf ]; } else if ([name isEqual: @"ivar"] == YES) // %phrase { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"ivariable"] == YES) { NSString *n = [prop objectForKey: @"name"]; NSString *t = [prop objectForKey: @"type"]; NSString *v = [prop objectForKey: @"validity"]; NSString *s; GSXMLNode *tmp; tmp = children = firstElement(children); [buf appendString: indent]; [buf appendString: @"

    "]; s = [self makeLink: n ofType: @"ivariable" inUnit: nil isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: n]; [buf appendString: @""]; } else { [buf appendString: n]; } [buf appendString: @"

    \n"]; if (v == nil) { v = @"public"; } [buf appendFormat: @"%@@%@ %@ %@;
    \n", indent, v, t, n]; if ([[children name] isEqual: @"desc"] == YES) { children = [children nextElement]; } /* * List standards with which ivar complies */ [self outputVersion: prop to: buf]; if ([[tmp name] isEqual: @"desc"]) { [self outputNode: tmp to: buf]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"label"] == YES) // %anchor { NSString *text; NSString *val; text = [children escapedContent]; val = [prop objectForKey: @"id"]; if (val == nil) { val = text; if (val == nil) val = @""; } [buf appendString: [self makeAnchor: val ofType: @"label" name: text]]; } else if ([name isEqual: @"macro"] == YES) { NSString *mac; NSString *str; NSString *s; GSXMLNode *tmp = children; BOOL hadArg = NO; mac = [prop objectForKey: @"name"]; str = [NSString stringWithFormat: @" %@", mac]; children = nil; while (tmp != nil) { if ([tmp type] == XML_ELEMENT_NODE) { if ([[tmp name] isEqual: @"arg"] == YES) { GSXMLNode *t = [tmp firstChild]; if (hadArg == YES) { str = [str stringByAppendingString: @", "]; } else { str = [str stringByAppendingString: @"("]; } str = [str stringByAppendingString: @""]; while (t != nil) { if ([t type] == XML_TEXT_NODE) { NSString *content = [t escapedContent]; if (content == nil) content = @""; str = [str stringByAppendingString: content]; } t = [t next]; } str = [str stringByAppendingString: @""]; hadArg = YES; } else if ([[tmp name] isEqual: @"vararg"] == YES) { if (hadArg == YES) { str = [str stringByAppendingString: @",..."]; } else { str = [str stringByAppendingString: @"(..."]; } children = [tmp nextElement]; hadArg = YES; break; } else { children = tmp; break; } } tmp = [tmp nextElement]; } /* * Output macro heading. */ [buf appendString: indent]; [buf appendString: @"

    "]; s = [self makeLink: mac ofType: @"macro" isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: mac]; [buf appendString: @""]; } else { [buf appendString: mac]; } [buf appendString: @"

    \n"]; [buf appendString: indent]; [buf appendString: str]; if (hadArg == YES) { [buf appendString: @")"]; } [buf appendString: @"
    \n"]; node = firstElement(children); if ([[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if ([[node name] isEqual: @"desc"]) { [self outputNode: node to: buf]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"method"] == YES) { NSString *sel; NSString *str; GSXMLNode *tmp = children; BOOL hadArg = NO; [buf appendString:@"
    \n"]; sel = [prop objectForKey: @"factory"]; str = [prop objectForKey: @"type"]; if (sel != nil && [sel boolValue] == YES) { sel = @"+"; str = [NSString stringWithFormat: @"+ (%@) ", [self typeRef: str]]; } else { sel = @"-"; str = [NSString stringWithFormat: @"- (%@) ", [self typeRef: str]]; } children = nil; while (tmp != nil) { if ([tmp type] == XML_ELEMENT_NODE) { if ([[tmp name] isEqual: @"sel"] == YES) { GSXMLNode *t = [tmp firstChild]; str = [str stringByAppendingString: @""]; while (t != nil) { if ([t type] == XML_TEXT_NODE) { NSString *content = [t escapedContent]; if (content == nil) content = @""; sel = [sel stringByAppendingString: content]; // these nbsp added for readability, but must // be removed below when making href link sel = [sel stringByAppendingString: @" "]; if (hadArg == YES) { str = [str stringByAppendingString: @" "]; } str = [str stringByAppendingString: content]; } t = [t next]; } str = [str stringByAppendingString: @""]; } else if ([[tmp name] isEqual: @"arg"] == YES) { GSXMLNode *t = [tmp firstChild]; NSString *s; s = [[tmp attributes] objectForKey: @"type"]; s = [self typeRef: s]; str = [str stringByAppendingFormat: @" (%@)", s]; while (t != nil) { if ([t type] == XML_TEXT_NODE) { NSString *content = [t escapedContent]; if (content == nil) content = @""; str = [str stringByAppendingString: content]; } t = [t next]; } hadArg = YES; // Say we have found an arg. } else if ([[tmp name] isEqual: @"vararg"] == YES) { sel = [sel stringByAppendingString: @",..."]; str = [str stringByAppendingString: @",..."]; children = [tmp nextElement]; break; } else { children = tmp; break; } } tmp = [tmp nextElement]; } if ([sel length] > 1) { NSString *s; NSMutableString *linkRef; /* * Output selector heading. */ [buf appendString: indent]; [buf appendString: @"

    "]; // get rid of nbsps put in for readability above linkRef = [NSMutableString stringWithCapacity: [sel length]]; [linkRef setString:sel]; [linkRef replaceString: @" " withString: @""]; s = [self makeLink: linkRef ofType: @"method" inUnit: nil isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: [sel substringFromIndex: 1]]; [buf appendString: @""]; } else { [buf appendString: [sel substringFromIndex: 1]]; } [buf appendString: @"

    \n"]; [buf appendString: indent]; [buf appendString: str]; [buf appendString: @";
    \n"]; node = firstElement(children); /* * List standards with which method complies */ children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if ((str = [prop objectForKey: @"init"]) != nil && [str boolValue] == YES) { [buf appendString: @"This is a designated initialiser " @"for the class.
    \n"]; } str = [prop objectForKey: @"override"]; if ([str isEqual: @"subclass"] == YES) { [buf appendString: @"Subclasses must " @"override this method.
    \n"]; } else if ([str isEqual: @"dummy"] == YES) { [buf appendString: @"An empty method provided for subclasses " @"to override.
    \n"]; } else if ([str isEqual: @"never"] == YES) { [buf appendString: @"Subclasses must NOT " @"override this method.
    \n"]; } if ([[node name] isEqual: @"desc"]) { [self outputNode: node to: buf]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } [buf appendString:@"
    \n"]; } else if ([name isEqual: @"p"] == YES) { [self outputBlock: node to: buf inPara: NO]; } else if ([name isEqual: @"prjref"] == YES) { NSLog(@"Element '%@' not implemented", name); // FIXME } else if ([name isEqual: @"ref"] == YES) // %xref { NSString *type = [prop objectForKey: @"type"]; NSString *r = [prop objectForKey: @"id"]; GSXMLNode *tmp = [node firstChild]; NSString *c = [prop objectForKey: @"class"]; NSString *s; // fill in default value if ((type == nil) || [type isEqual: @""]) { type = @"label"; } if ([type isEqual: @"method"] || [type isEqual: @"ivariable"]) { s = [self makeLink: r ofType: type inUnit: c isRef: YES]; } else { s = [self makeLink: r ofType: type isRef: YES]; /** * As a special case, if we have a reference to a function, * and we can't find it, we check to see if there is actually * a macro of that name and refer to that instead. */ if (s == nil && [type isEqual: @"function"] == YES) { s = [self makeLink: r ofType: @"macro" isRef: YES]; } } if (s == nil) { if (c == nil) { NSLog(@"Location of %@ '%@' (referenced from %@) " @"not found or not unique.", type, r, base); } else { NSLog(@"Location of the %@ version of %@ '%@' (referenced " @"from %@) not found.", c, type, r, base); } if (tmp == nil) { [buf appendString: r]; } else { [self outputText: tmp to: buf]; } [buf appendString: @"\n"]; } else { [buf appendString: s]; if (tmp == nil) { [buf appendString: r]; } else { [self outputText: tmp to: buf]; } [buf appendString: @"\n"]; } } else if ([name isEqual: @"protocol"] == YES) { NSString *value = [prop objectForKey: @"name"]; unit = [NSString stringWithFormat: @"(%@)", value]; [buf appendString: indent]; [buf appendString: @"

    "]; [buf appendString: [self makeAnchor: unit ofType: @"protocol" name: value]]; [buf appendString: @"

    \n"]; [self outputUnit: node to: buf]; unit = nil; } else if ([name isEqual: @"EOEntity"] == YES || [name isEqual: @"EOModel"] == YES) { NSLog(@"Element '%@' not implemented", name); // FIXME } else if ([name isEqual: @"section"] == YES) { heading = @"h2"; sect++; ssect = 0; sssect = 0; [self outputNodeList: children to: buf]; heading = @"h1"; } else if ([name isEqual: @"site"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"standards"]) { } else if ([name isEqual: @"strong"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"subsect"] == YES) { heading = @"h3"; ssect++; sssect = 0; [self outputNodeList: children to: buf]; heading = @"h2"; } else if ([name isEqual: @"subsubsect"] == YES) { heading = @"h4"; sssect++; [self outputNodeList: children to: buf]; heading = @"h3"; } else if ([name isEqual: @"type"] == YES) { NSString *nam; NSString *str; NSString *s; nam = [prop objectForKey: @"name"]; str = [prop objectForKey: @"type"]; str = [self typeRef: str]; str = [NSString stringWithFormat: @"typedef %@ %@", str, nam]; /* * Output typedef heading. */ [buf appendString: indent]; [buf appendString: @"

    "]; s = [self makeLink: nam ofType: @"type" isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: nam]; [buf appendString: @""]; } else { [buf appendString: nam]; } [buf appendString: @"

    \n"]; [buf appendString: indent]; [buf appendString: str]; [buf appendString: @";
    \n"]; node = firstElement(children); if (node != nil && [[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if (node != nil && [[node name] isEqual: @"desc"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"uref"] == YES) { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"url"] == YES) { // create an HREF as before but use the URL itself as marker text [buf appendString: @""]; [buf appendString: [prop objectForKey: @"url"]]; [buf appendString: @""]; } else if ([name isEqual: @"var"] == YES) // %phrase { [buf appendString: @""]; [self outputText: children to: buf]; [buf appendString: @""]; } else if ([name isEqual: @"variable"] == YES) { NSString *nam; NSString *str; NSString *s; nam = [prop objectForKey: @"name"]; str = [prop objectForKey: @"type"]; str = [self typeRef: str]; str = [str stringByAppendingFormat: @" %@", nam]; /* * Output variable heading. */ [buf appendString: indent]; [buf appendString: @"

    "]; s = [self makeLink: nam ofType: @"variable" isRef: NO]; if (s != nil) { [buf appendString: s]; [buf appendString: nam]; [buf appendString: @""]; } else { [buf appendString: nam]; } [buf appendString: @"

    \n"]; [buf appendString: indent]; [buf appendString: str]; [buf appendString: @";
    \n"]; node = firstElement(children); if ([[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } children = node; if ([[children name] isEqual: @"standards"]) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if ([[node name] isEqual: @"desc"]) { [self outputNode: node to: buf]; } [buf appendString: indent]; [buf appendString: @"
    \n"]; } else { GSXMLNode *tmp; /* * Try outputting as any of the list elements. */ tmp = [self outputList: node to: buf]; if (tmp == node) { NSLog(@"Element '%@' not implemented", name); // FIXME } node = tmp; } } [arp drain]; } /** * Output all the nodes from this one onwards ... try to output * as text first, if not possible, call the main method to output * each node. */ - (void) outputNodeList: (GSXMLNode*)node to: (NSMutableString*)buf { while (node != nil) { GSXMLNode *next = [node nextElement]; GSXMLNode *tmp; tmp = [self outputText: node to: buf]; if (tmp == node) { [self outputNode: node to: buf]; node = next; } else { node = tmp; } } } /** * Outputs zero or more nodes at the same level as long as the nodes * are valid %block elements. Returns nil or the first node not output. * The value of flag is used to control paragraph nesting ... if YES * we close a paragraph before opening a new one, and open again once * the new paragraph closes. */ - (GSXMLNode*) outputBlock: (GSXMLNode*)node to: (NSMutableString*)buf inPara: (BOOL)flag { if (node != nil && [node type] == XML_ELEMENT_NODE) { GSXMLNode *tmp = node; NSString *n; node = [self outputList: node to: buf]; if (node != tmp) { return node; } n = [node name]; if ([n isEqual: @"p"] == YES) { if (flag == YES) { [self decIndent]; [buf appendString: indent]; [buf appendString: @"

    \n"]; } [buf appendString: indent]; [buf appendString: @"

    \n"]; [self incIndent]; [self outputText: [node firstChild] to: buf]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"

    \n"]; if (flag == YES) { [buf appendString: indent]; [buf appendString: @"

    \n"]; [self incIndent]; } return [node next]; } else if ([n isEqual: @"example"] == YES) { GSXMLNode *c = [node firstChild]; [buf appendString: @"

    "];
    	  [self outputText: c to: buf];
    	  [buf appendString: @"
    \n"]; return [node next]; } else if ([n isEqual: @"embed"] == YES) { NSLog(@"Element 'embed' not supported"); return [node next]; } else if ([n isEqual: @"index"] == YES) { [self outputNode: node to: buf]; return [node next]; } else if ([textNodes member: n] != nil) { [buf appendString: indent]; node = [self outputText: node to: buf]; [buf appendString: @"\n"]; return node; } else { NSLog(@"Non-block element '%@' in block ...", n); NSLog(@"%@",node); return nil; } } [buf appendString: indent]; node = [self outputText: node to: buf]; [buf appendString: @"\n"]; return node; } /** * Outputs a node as long as it is a * valid %list element. Returns next node at this level. */ - (GSXMLNode*) outputList: (GSXMLNode*)node to: (NSMutableString*)buf { NSString *name = [node name]; GSXMLNode *children = [node firstChildElement]; if ([name isEqual: @"list"] == YES) { [buf appendString: indent]; [buf appendString: @"
      \n"]; [self incIndent]; while (children != nil) { GSXMLNode *tmp = [children firstChild]; [buf appendString: indent]; [buf appendString: @"
    • \n"]; [self incIndent]; while (tmp != nil) { tmp = [self outputBlock: tmp to: buf inPara: NO]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    • \n"]; children = [children nextElement]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"enum"] == YES) { [buf appendString: indent]; [buf appendString: @"
      \n"]; [self incIndent]; while (children != nil) { GSXMLNode *tmp = [children firstChild]; [buf appendString: indent]; [buf appendString: @"
    1. \n"]; [self incIndent]; while (tmp != nil) { tmp = [self outputBlock: tmp to: buf inPara: NO]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    2. \n"]; children = [children nextElement]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"deflist"] == YES) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; while (children != nil) { GSXMLNode *tmp; [buf appendString: indent]; [buf appendString: @"
    "]; [self outputText: [children firstChild] to: buf]; [buf appendString: @"
    \n"]; children = [children nextElement]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; tmp = [children firstChild]; while (tmp != nil) { tmp = [self outputBlock: tmp to: buf inPara: NO]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; children = [children nextElement]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"qalist"] == YES) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; while (children != nil) { GSXMLNode *tmp; [buf appendString: indent]; [buf appendString: @"
    "]; [self outputText: [children firstChild] to: buf]; [buf appendString: @"
    \n"]; children = [children nextElement]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; tmp = [children firstChild]; while (tmp != nil) { tmp = [self outputBlock: tmp to: buf inPara: NO]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; children = [children nextElement]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } else if ([name isEqual: @"dictionary"] == YES) { [buf appendString: indent]; [buf appendString: @"
    {\n"]; [self incIndent]; // all children should be dictionaryItems w/key and value attributes; // if the value attribute is absent, the content is the value for (; children != nil; children = [children nextElement]) { GSXMLNode *dItem = children; NSDictionary *dProp = [dItem attributes]; NSString *value = [dProp objectForKey: @"value"]; GSXMLNode *dChild; if (![@"dictionaryItem" isEqualToString: [dItem name]]) { continue; } [buf appendString: indent]; [buf appendString: @"
    "]; [buf appendString: [[dProp objectForKey: @"key"] stringByEscapingXML]]; [buf appendString: @" =
    \n"]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; if (value != nil) { [buf appendString: [value stringByEscapingXML]]; } else { dChild = [dItem firstChildElement]; if ( dChild == nil ) { // no elements, just text contents dChild = [dItem firstChild]; [buf appendString: indent]; } dItem = [self outputBlock: dChild to: buf inPara: NO]; //PENDING should check that dItem is what it should be... } [buf appendString: @"\n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @";
    \n"]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    }\n"]; } else { return node; // Not a list } node = [node next]; return node; } /** * Outputs zero or more nodes at the same level as long as the nodes * are valid %text elements. Returns nil or the first node not output. */ - (GSXMLNode*) outputText: (GSXMLNode*)node to: (NSMutableString*)buf { while (node != nil) { if ([node type] == XML_TEXT_NODE) { NSString *str = [node escapedContent]; if (str == nil) str = @""; [buf appendString: str]; } else if ([node type] == XML_ENTITY_REF_NODE) { [buf appendString: @"&"]; [buf appendString: [node name]]; [buf appendString: @";"]; } else if ([node type] == XML_ELEMENT_NODE) { NSString *name = [node name]; if ([textNodes member: name] != nil) { [self outputNode: node to: buf]; } else { return node; // Not a text node. } } node = [node next]; } return node; } - (void) outputUnit: (GSXMLNode*)node to: (NSMutableString*)buf { NSArray *a; NSMutableString *ivarBuf = ivarsAtEnd ? (id)[NSMutableString stringWithCapacity: 1024] : nil; NSDictionary *prop = [node attributes]; node = [node firstChildElement]; if (node != nil && [[node name] isEqual: @"declared"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } if (node != nil && [[node name] isEqual: @"conform"] == YES) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self incIndent]; [buf appendString: indent]; [buf appendString: @"
    Conforms to:
    \n"]; while (node != nil && [[node name] isEqual: @"conform"] == YES) { NSString *text = [[node firstChild] escapedContent]; if (text == nil) text = @""; [buf appendString: indent]; [buf appendString: @"
    "]; [buf appendString: [self protocolRef: text]]; [buf appendString: @"
    \n"]; node = [node nextElement]; } [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; [self decIndent]; [buf appendString: indent]; [buf appendString: @"
    \n"]; } while (node != nil && [[node name] isEqual: @"desc"] == NO) { node = [node nextElement]; } [self outputVersion: prop to: buf]; if (node != nil && [[node name] isEqual: @"desc"] == YES) { [self outputNode: node to: buf]; node = [node nextElement]; } if (node != nil && [[node name] isEqual: @"ivariable"] == YES) { NSMutableString *ibuf = buf; /* * If want instance variables at end, throw it all into an alternate * buffer and just put a link here; later alt buf must be appended. */ if (ivarsAtEnd) { ibuf = ivarBuf; [buf appendString: indent]; [buf appendString: @"
    \n"]; [buf appendString: indent]; [buf appendFormat: @"Instance Variables\n", classname]; [buf appendString: indent]; [buf appendString: @"

    \n"]; [ibuf appendFormat: @"", classname]; } [ibuf appendString: indent]; [ibuf appendString: @"

    \n"]; [ibuf appendString: indent]; [ibuf appendFormat: @"

    Instance Variables for %@ Class

    \n", classname]; while (node != nil && [[node name] isEqual: @"ivariable"] == YES) { [self outputNode: node to: ibuf]; node = [node nextElement]; } [ibuf appendString: indent]; [ibuf appendString: @"


    \n"]; } a = [localRefs methodsInUnit: unit]; if ([a count] > 0) { [self outputIndex: @"method" scope: @"global" title: @"Method summary" style: nil target: nil to: buf]; [buf appendString: indent]; [buf appendString: @"
    \n"]; while (node != nil) { if ([[node name] isEqual: @"method"] == YES) { [self outputNode: node to: buf]; } node = [node nextElement]; } } // if had ivars docs, insert them now if (ivarsAtEnd) { [buf appendString: ivarBuf]; } } - (void) outputVersion: (NSDictionary*)prop to: (NSMutableString*)buf { NSString *ovadd = [prop objectForKey: @"ovadd"]; NSString *gvadd = [prop objectForKey: @"gvadd"]; NSString *ovdep = [prop objectForKey: @"ovdep"]; NSString *gvdep = [prop objectForKey: @"gvdep"]; NSString *ovrem = [prop objectForKey: @"ovrem"]; NSString *gvrem = [prop objectForKey: @"gvrem"]; const char *str; int maj; int min; int sub; if ([ovadd length] > 0) { int add; int dep; int rem; str = [ovadd UTF8String]; if (str != 0 && sscanf(str, "%d.%d.%d", &maj, &min, &sub) == 3) add = maj * 10000 + min * 100 + sub; else add = 0; str = [ovdep UTF8String]; if (str != 0 && sscanf(str, "%d.%d.%d", &maj, &min, &sub) == 3) dep = maj * 10000 + min * 100 + sub; else dep = 0; str = [ovrem UTF8String]; if (str != 0 && sscanf(str, "%d.%d.%d", &maj, &min, &sub) == 3) rem = maj * 10000 + min * 100 + sub; else rem = 0; [buf appendString: indent]; [buf appendString: @"
    \n"]; [buf appendString: @"Availability: "]; if (add < GS_API_OSSPEC) { [buf appendString: @"Not in OpenStep/MacOS-X"]; } else if (add < GS_API_OPENSTEP) { [buf appendString: @"OpenStep"]; } else if (add < GS_API_MACOSX) { [buf appendString: @"OPENSTEP "]; [buf appendString: ovadd]; } else { [buf appendString: @"MacOS-X "]; [buf appendString: ovadd]; } if (dep > add) { [buf appendString: @" deprecated at "]; if (dep < GS_API_MACOSX) { [buf appendString: @"OPENSTEP "]; [buf appendString: ovdep]; } else { [buf appendString: @"MacOS-X "]; [buf appendString: ovdep]; } } if (rem > add) { [buf appendString: @" removed at "]; if (rem < GS_API_MACOSX) { [buf appendString: @"OPENSTEP "]; [buf appendString: ovrem]; } else { [buf appendString: @"MacOS-X "]; [buf appendString: ovrem]; } } if ([gvadd length] > 0) { [buf appendString: @", "]; [buf appendString: project]; if ([gvadd isEqualToString: @"0.0.0"] == NO) { [buf appendString: @" "]; [buf appendString: gvadd]; } if ([gvdep length] > 0) { [buf appendString: @" deprecated at "]; [buf appendString: gvdep]; } if ([gvrem length] > 0) { [buf appendString: @" Likely to be changed/moved/removed at "]; [buf appendString: gvrem]; } } [buf appendString:@"
    \n"]; [buf appendString: @"
    \n"]; } else if ([gvadd length] > 0) { [buf appendString: indent]; [buf appendString: @"
    \n"]; [buf appendString: @"Availability: "]; [buf appendString: project]; if ([gvadd isEqualToString: @"0.0.0"] == NO) { [buf appendString: @" "]; [buf appendString: gvadd]; } if ([gvdep length] > 0) { [buf appendString: @" deprecated at "]; [buf appendString: gvdep]; } [buf appendString: @"
    \n"]; if ([gvrem length] > 0) { [buf appendString: @" Likely to be changed/moved/removed at "]; [buf appendString: gvrem]; } [buf appendString:@"
    \n"]; [buf appendString: @"
    \n"]; } } /** * Try to make a link to the documentation for the supplied protocol. */ - (NSString*) protocolRef: (NSString*)t { NSString *n; NSString *s; t = [t stringByTrimmingSpaces]; n = [NSString stringWithFormat: @"(%@)", t]; s = [self makeLink: n ofType: @"protocol" isRef: YES]; if (s != nil) { s = [s stringByAppendingString: t]; t = [s stringByAppendingString: @"
    "]; } return t; } - (void) setGlobalRefs: (AGSIndex*)r { ASSIGN(globalRefs, r); } - (void) setLocalRefs: (AGSIndex*)r { ASSIGN(localRefs, r); } - (void) setProjectRefs: (AGSIndex*)r { ASSIGN(projectRefs, r); } - (void) setInstanceVariablesAtEnd: (BOOL)val { ivarsAtEnd = val; } /** * Assuming that the supplied string contains type information (as used * in a method declaration or type cast), we make an attempt at extracting * the basic type, and seeing if we can find a documented declaration for * it. If we can, we return a modified version of the string containing * a link to the appropriate documentation. Otherwise, we just return the * plain type string. In all cases, we strip leading and trailing white space. */ - (NSString*) typeRef: (NSString*)t { NSString *str = [t stringByTrimmingSpaces]; NSString *s; unsigned end = [str length]; unsigned start; NSMutableString *ms = nil; NSRange er; NSRange sr; if (end == 0) { return nil; } t = str; while (end > 0) { unichar c = [t characterAtIndex: end-1]; if (c != '*' && !isspace(c)) { break; } end--; } start = end; while (start > 0) { unichar c = [t characterAtIndex: start-1]; if (c != '_' && !isalnum(c)) { break; } start--; } t = [str substringWithRange: NSMakeRange(start, end - start)]; s = [self makeLink: t ofType: @"type" isRef: YES]; if (s == nil) { s = [self makeLink: t ofType: @"class" isRef: YES]; } s = [s stringByAppendingFormat: @"%@", t]; if (s != nil && [str length] == [t length]) { return s; } /* * Look for protocol spec. */ sr = [str rangeOfString: @"<"]; if (sr.length == 0) { sr = [str rangeOfString: @"<"]; } if (sr.length == 0) { sr = [str rangeOfString: @"<"]; } er = [str rangeOfString: @">"]; if (er.length == 0) { er = [str rangeOfString: @">"]; } if (er.length == 0) { er = [str rangeOfString: @">"]; } /* * Substitute in protocol references. */ if (sr.length > 0 && er.length > 0 && er.location > sr.location) { NSString *pString; NSRange r; NSArray *protocols; unsigned i; r = NSMakeRange(NSMaxRange(sr), er.location - NSMaxRange(sr)); pString = [str substringWithRange: r]; protocols = [pString componentsSeparatedByString: @","]; ms = [str mutableCopy]; pString = @""; for (i = 0; i < [protocols count]; i++) { NSString *p = [protocols objectAtIndex: i]; NSString *l; l = [self makeLink: [NSString stringWithFormat: @"(%@)", p] ofType: @"protocol" isRef: YES]; if (l != nil) { p = [l stringByAppendingFormat: @"%@", p]; } if (i > 0) { pString = [pString stringByAppendingString: @","]; } pString = [pString stringByAppendingString: p]; } [ms replaceCharactersInRange: r withString: pString]; } /* * Substitute in basic type reference. */ if (s != nil) { if (ms == nil) { ms = [str mutableCopy]; } [ms replaceCharactersInRange: NSMakeRange(start, end - start) withString: s]; } if (ms != nil) { str = AUTORELEASE(ms); } return str; } @end