| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244 | {    FPDoc  -  Free Pascal Documentation Tool    Copyright (C) 2000 - 2002 by      Areca Systems GmbH / Sebastian Guenther, [email protected]    * Global declarations    * Link list management    * Document node tree    * Main engine    See the file COPYING, included in this distribution,    for details about the copyright.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.}{$MODE objfpc}{$H+}unit dGlobals;interfaceuses Classes, DOM, PasTree, PParser;Var  LEOL : Integer;resourcestring  // Output strings  SDocPackageTitle           = 'Reference for package ''%s''';  SDocPrograms               = 'Programs';  SDocUnits                  = 'Units';  SDocUnitTitle              = 'Reference for unit ''%s''';  SDocInterfaceSection       = 'Interface section';  SDocImplementationSection  = 'Implementation section';  SDocUsedUnits              = 'Used units';  SDocUsedUnitsByUnitXY      = 'Used units by unit ''%s''';  SDocConstsTypesVars        = 'Constants, types and variables';  SDocResStrings             = 'Resource strings';  SDocTypes                  = 'Types';  SDocConstants              = 'Constants';  SDocClasses                = 'Classes';  SDocProceduresAndFunctions = 'Procedures and functions';  SDocVariables              = 'Variables';  SDocUnitOverview           = 'Overview of unit ''%s''';  SDocOverview               = 'Overview';  SDocSearch                 = 'Search';  SDocDeclaration            = 'Declaration';  SDocDescription            = 'Description';  SDocErrors                 = 'Errors';  SDocSeeAlso                = 'See also';  SDocExample                = 'Example';  SDocArguments              = 'Arguments';  SDocFunctionResult         = 'Function result';  SDocRemark                 = 'Remark:   ';  SDocMethodOverview         = 'Method overview';  SDocPropertyOverview       = 'Property overview';  SDocPage                   = 'Page';  SDocMethod                 = 'Method';  SDocProperty               = 'Property';  SDocAccess                 = 'Access';  SDocInheritance            = 'Inheritance';  SDocProperties             = 'Properties';  SDocMethods                = 'Methods';  SDocEvents                 = 'Events';  SDocByName                 = 'by Name';  SDocValue                  = 'Value';  SDocExplanation            = 'Explanation';  SDocProcedure              = 'Procedure';  SDocValuesForEnum          = 'Enumeration values for type %s';  SDocSourcePosition         = 'Source position: %s line %d';  SDocSynopsis               = 'Synopsis';  SDocVisibility             = 'Visibility';  SDocOpaque                 = 'Opaque type';    // Topics  SDocRelatedTopics = 'Related topics';  SDocUp            = 'Up';  SDocNext          = 'Next';  SDocPrevious      = 'Previous';  // Various backend constants  SDocChapter    = 'Chapter';  SDocSection    = 'Section';  SDocSubSection = 'Subsection';  SDocTable      = 'Table';  SDocListing    = 'Listing';  // Man page usage  SManUsageManSection         = 'Use ASection as the man page section';  SManUsageNoUnitPrefix       = 'Do not prefix man pages with unit name.';  SManUsageWriterDescr        = 'UNIX man page output.';  SManUsagePackageDescription = 'Use descr as the description of man pages';    // HTML usage  SHTMLUsageFooter = 'Append xhtml from file as footer to html page';  STitle           = 'FPDoc - Free Pascal Documentation Tool';  SVersion         = 'Version %s [%s]';  SCopyright       = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, [email protected]';  SCmdLineHelp     = 'Usage: %s [options]';  SUsageOption010  = '--content         Create content file for package cross-references';  SUsageOption020  = '--cputarget=value Set the target CPU for the scanner.';  SUsageOption030  = '--descr=name      use name as description file. ';  SUsageOption040  = '                  This option is allowed more than once';  SUsageOption050  = '--format=fmt      Select output format.';  SUsageOption060  = '--help            Show this help.';  SUsageOption070  = '--hide-protected  Do not show protected methods in overview';  SUsageOption080  = '--import=file     Import content file for package cross-references';  SUsageOption090  = '--input=cmd       use cmd as input for the parser.';  SUsageOption100  = '                  At least one input option is required.';  SUsageOption110  = '--lang=lng        Select output language.';  SUsageOption120  = '--ostarget=value  Set the target OS for the scanner.';  SUsageOption130  = '--output=name     use name as the output name.';  SUsageOption140  = '                  Each backend interpretes this as needed.';  SUsageOption150  = '--package=name    Set the package name for which to create output';  SUsageOption160  = '--show-private    Show private methods.';  SUsageOption170  = '--warn-no-node    Warn if no documentation node was found.';  SUsageFormats        = 'The following output formats are supported by this fpdoc:';  SUsageBackendHelp    = 'Specify an output format, combined with --help to get more help for this backend.';  SUsageFormatSpecific = 'Output format "%s" supports the following options:';  SCmdLineInvalidOption       = 'Ignoring unknown option "%s"';  SCmdLineInvalidFormat       = 'Invalid format "%s" specified';  SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=<filename>';  SWritingPages               = 'Writing %d pages...';  SNeedPackageName            = 'No package name specified. Please specify one using the --package option.';  SDone                       = 'Done.';  SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';  SErrCouldNotCreateFile      = 'Could not create file "%s": %s';Const  SVisibility: array[TPasMemberVisibility] of string =       ('Default', 'Private', 'Protected', 'Public',       'Published', 'Automated');type  // Assumes a list of TObject instances and frees them on destruction  TObjectList = class(TList)  public    destructor Destroy; override;  end;  { Link entry tree    TFPDocEngine stores the root of the entry tree in its property    "RootLinkNode". The root has one child node for each package, for which    documentation links are available. The children of a package node    are module nodes; and the children of a module node are the top-level    declarations of this module; the next level in the tree stores e.g. record    members, and so on...  }  TLinkNode = class  private    FFirstChild, FNextSibling: TLinkNode;    FName: String;    FLink: String;  public    constructor Create(const AName, ALink: String);    destructor Destroy; override;    function FindChild(const APathName: String): TLinkNode;    function CreateChildren(const APathName, ALinkTo: String): TLinkNode;    // Properties for tree structure    property FirstChild: TLinkNode read FFirstChild;    property NextSibling: TLinkNode read FNextSibling;    // Link properties    property Name: String read FName;    property Link: String read FLink;  end;  { Documentation entry tree    TFPDocEngine stores the root of the entry tree in its property    "RootDocNode". The root has one child node for each package, for which    documentation is being provided by the user. The children of a package node    are module nodes; and the children of a module node are the top-level    declarations of this module; the next level in the tree stores e.g. record    members, and so on...  }  TDocNode = class  private    FFirstChild, FNextSibling: TDocNode;    FName: String;    FNode: TDOMElement;    FIsSkipped: Boolean;    FShortDescr: TDOMElement;    FDescr: TDOMElement;    FErrorsDoc: TDOMElement;    FSeeAlso: TDOMElement;    FFirstExample: TDOMElement;    FLink: String;    FTopicNode : Boolean;  public    constructor Create(const AName: String; ANode: TDOMElement);    destructor Destroy; override;    function FindChild(const APathName: String): TDocNode;    function CreateChildren(const APathName: String): TDocNode;    // Properties for tree structure    property FirstChild: TDocNode read FFirstChild;    property NextSibling: TDocNode read FNextSibling;    // Basic properties    property Name: String read FName;    property Node: TDOMElement read FNode;    // Data fetched from the XML document    property IsSkipped: Boolean read FIsSkipped;    property ShortDescr: TDOMElement read FShortDescr;    property Descr: TDOMElement read FDescr;    property ErrorsDoc: TDOMElement read FErrorsDoc;    property SeeAlso: TDOMElement read FSeeAlso;    property FirstExample: TDOMElement read FFirstExample;    property Link: String read FLink;    Property TopicNode : Boolean Read FTopicNode;  end;  // The main FPDoc engine  TFPDocEngine = class(TPasTreeContainer)  protected    DescrDocs: TObjectList;             // List of XML documents    DescrDocNames: TStringList;         // Names of the XML documents    FRootLinkNode: TLinkNode;    FRootDocNode: TDocNode;    FPackages: TList;                   // List of TFPPackage objects    CurModule: TPasModule;    CurPackageDocNode: TDocNode;  public    constructor Create;    destructor Destroy; override;    procedure SetPackageName(const APackageName: String);    procedure ReadContentFile(const AFilename, ALinkPrefix: String);    procedure WriteContentFile(const AFilename: String);    function CreateElement(AClass: TPTreeElement; const AName: String;      AParent: TPasElement; AVisibility: TPasMemberVisibility;      const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;      override;    function FindElement(const AName: String): TPasElement; override;    function FindModule(const AName: String): TPasModule; override;    // Link tree support    procedure AddLink(const APathName, ALinkTo: String);    function FindAbsoluteLink(const AName: String): String;    function ResolveLink(AModule: TPasModule; const ALinkDest: String): String;    // Documentation file support    procedure AddDocFile(const AFilename: String);    // Documentation retrieval    function FindDocNode(AElement: TPasElement): TDocNode;    function FindDocNode(ARefModule: TPasModule; const AName: String): TDocNode;    function FindShortDescr(AElement: TPasElement): TDOMElement;    function FindShortDescr(ARefModule: TPasModule;      const AName: String): TDOMElement;    function GetExampleFilename(const ExElement: TDOMElement): String;    property RootLinkNode: TLinkNode read FRootLinkNode;    property RootDocNode: TDocNode read FRootDocNode;    property Package: TPasPackage read FPackage;    Output: String;    HasContentFile: Boolean;    HidePrivate: Boolean;       // Hide private class members in output?    HideProtected: Boolean;     // Hide protected class members in output?    WarnNoNode : Boolean;       // Warn if no description node found for element.  end;procedure TranslateDocStrings(const Lang: String);Function IsLinkNode(Node : TDomNode) : Boolean;Function IsExampleNode(Example : TDomNode) : Boolean;// returns true is link is an absolute URIFunction IsLinkAbsolute(ALink: String): boolean;implementationuses SysUtils, Gettext, XMLRead;const  AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');{ TObjectList }destructor TObjectList.Destroy;var  i: Integer;begin  for i := 0 to Count - 1 do    TObject(Items[i]).Free;  inherited Destroy;end;{ TLinkNode }constructor TLinkNode.Create(const AName, ALink: String);begin  inherited Create;  FName := AName;  FLink := ALink;end;destructor TLinkNode.Destroy;begin  if Assigned(FirstChild) then    FirstChild.Free;  if Assigned(NextSibling) then    NextSibling.Free;  inherited Destroy;end;function TLinkNode.FindChild(const APathName: String): TLinkNode;var  DotPos: Integer;  ChildName: String;  Child: TLinkNode;begin  if Length(APathName) = 0 then    Result := Self  else  begin    DotPos := Pos('.', APathName);    if DotPos = 0 then      ChildName := APathName    else      ChildName := Copy(APathName, 1, DotPos - 1);    Child := FirstChild;    while Assigned(Child) do    begin      if CompareText(Child.Name, ChildName) = 0 then      begin        if DotPos = 0 then          Result := Child        else          Result := Child.FindChild(            Copy(APathName, DotPos + 1, Length(APathName)));        exit;      end;      Child := Child.NextSibling;    end;    Result := nil;  end;end;function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;var  DotPos: Integer;  ChildName: String;  Child, LastChild: TLinkNode;begin  if Length(APathName) = 0 then    Result := Self  else  begin    DotPos := Pos('.', APathName);    if DotPos = 0 then      ChildName := APathName    else      ChildName := Copy(APathName, 1, DotPos - 1);    Child := FirstChild;    LastChild := nil;    while Assigned(Child) do    begin      if CompareText(Child.Name, ChildName) = 0 then      begin        if DotPos = 0 then          Result := Child        else          Result := Child.CreateChildren(            Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);        exit;      end;      LastChild := Child;      Child := Child.NextSibling;    end;    { No child found, let's create one if we are at the end of the path }    if DotPos > 0 then      // !!!: better throw an exception      WriteLn('Link path does not exist: ', APathName);    Result := TLinkNode.Create(ChildName, ALinkTo);    if Assigned(LastChild) then      LastChild.FNextSibling := Result    else      FFirstChild := Result;  end;end;{ TDocNode }constructor TDocNode.Create(const AName: String; ANode: TDOMElement);begin  inherited Create;  FName := AName;  FNode := ANode;end;destructor TDocNode.Destroy;begin  if Assigned(FirstChild) then    FirstChild.Free;  if Assigned(NextSibling) then    NextSibling.Free;  inherited Destroy;end;function TDocNode.FindChild(const APathName: String): TDocNode;var  DotPos: Integer;  ChildName: String;  Child: TDocNode;begin  if Length(APathName) = 0 then    Result := Self  else  begin    DotPos := Pos('.', APathName);    if DotPos = 0 then      ChildName := APathName    else      ChildName := Copy(APathName, 1, DotPos - 1);    Child := FirstChild;    while Assigned(Child) do    begin      if CompareText(Child.Name, ChildName) = 0 then      begin        if DotPos = 0 then          Result := Child        else          Result := Child.FindChild(            Copy(APathName, DotPos + 1, Length(APathName)));        exit;      end;      Child := Child.NextSibling;    end;    Result := nil;  end;end;function TDocNode.CreateChildren(const APathName: String): TDocNode;var  DotPos: Integer;  ChildName: String;  Child: TDocNode;begin  if Length(APathName) = 0 then    Result := Self  else  begin    DotPos := Pos('.', APathName);    if DotPos = 0 then      ChildName := APathName    else      ChildName := Copy(APathName, 1, DotPos - 1);    Child := FirstChild;    while Assigned(Child) do    begin      if CompareText(Child.Name, ChildName) = 0 then      begin        if DotPos = 0 then          Result := Child        else          Result := Child.CreateChildren(            Copy(APathName, DotPos + 1, Length(APathName)));        exit;      end;      Child := Child.NextSibling;    end;    // No child found, let's create one    Result := TDocNode.Create(ChildName, nil);    if Assigned(FirstChild) then    begin      Result.FNextSibling := FirstChild;      FFirstChild := Result;    end else      FFirstChild := Result;    if DotPos > 0 then      Result := Result.CreateChildren(        Copy(APathName, DotPos + 1, Length(APathName)));  end;end;{ TFPDocEngine }constructor TFPDocEngine.Create;begin  inherited Create;  DescrDocs := TObjectList.Create;  DescrDocNames := TStringList.Create;  FRootLinkNode := TLinkNode.Create('', '');  FRootDocNode := TDocNode.Create('', nil);  HidePrivate := True;  FPackages := TList.Create;end;destructor TFPDocEngine.Destroy;var  i: Integer;begin  for i := 0 to FPackages.Count - 1 do    TPasPackage(FPackages[i]).Release;  FPackages.Free;  FRootDocNode.Free;  FRootLinkNode.Free;  DescrDocNames.Free;  DescrDocs.Free;  inherited Destroy;end;procedure TFPDocEngine.SetPackageName(const APackageName: String);begin  ASSERT(not Assigned(Package));  FPackage := TPasPackage(inherited CreateElement(TPasPackage,    '#' + APackageName, nil, '', 0));  FPackages.Add(FPackage);  CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);end;procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);var  f: Text;  procedure ReadLinkTree;  var    s: String;    PrevSpaces, ThisSpaces, i, StackIndex: Integer;    CurParent, PrevSibling, NewNode: TLinkNode;    ParentStack, SiblingStack: array[0..7] of TLinkNode;  begin    PrevSpaces := 0;    CurParent := RootLinkNode;    PrevSibling := CurParent.FirstChild;    if assigned(PrevSibling) then      while assigned(PrevSibling.NextSibling) do        PrevSibling := PrevSibling.NextSibling;    StackIndex := 0;    while True do    begin      ReadLn(f, s);      if Length(s) = 0 then        break;      ThisSpaces := 0;      while s[ThisSpaces + 1] = ' ' do        Inc(ThisSpaces);      if ThisSpaces <> PrevSpaces then      begin        if ThisSpaces > PrevSpaces then        begin          { Dive down one level }          ParentStack[StackIndex] := CurParent;          SiblingStack[StackIndex] := PrevSibling;          Inc(StackIndex);          CurParent := PrevSibling;          PrevSibling := nil;        end else          while PrevSpaces > ThisSpaces do          begin            Dec(StackIndex);            CurParent := ParentStack[StackIndex];            PrevSibling := SiblingStack[StackIndex];            Dec(PrevSpaces);          end;        PrevSpaces := ThisSpaces;      end;      i := ThisSpaces + 1;      while s[i] <> ' ' do        Inc(i);      NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),        ALinkPrefix + Copy(s, i + 1, Length(s)));      if Assigned(PrevSibling) then        PrevSibling.FNextSibling := NewNode      else        CurParent.FFirstChild := NewNode;      PrevSibling := NewNode;    end;  end;  procedure ReadClasses;    function CreateClass(const AName: String): TPasClassType;    var      DotPos, DotPos2, i: Integer;      s: String;      HPackage: TPasPackage;      Module: TPasModule;    begin      // Find or create package      DotPos := Pos('.', AName);      s := Copy(AName, 1, DotPos - 1);      HPackage := nil;      for i := 0 to FPackages.Count - 1 do        if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then        begin          HPackage := TPasPackage(FPackages[i]);          break;        end;      if not Assigned(HPackage) then      begin        HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,          '', 0));        FPackages.Add(HPackage);      end;      // Find or create module      DotPos2 := DotPos;      repeat        Inc(DotPos2);      until AName[DotPos2] = '.';      s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);      Module := nil;      for i := 0 to HPackage.Modules.Count - 1 do        if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then        begin          Module := TPasModule(HPackage.Modules[i]);          break;        end;      if not Assigned(Module) then      begin        Module := TPasModule.Create(s, HPackage);        Module.InterfaceSection := TPasSection.Create('', Module);        HPackage.Modules.Add(Module);      end;      // Create node for class      Result := TPasClassType.Create(Copy(AName, DotPos2 + 1, Length(AName)),        Module.InterfaceSection);      Result.ObjKind := okClass;      Module.InterfaceSection.Declarations.Add(Result);      Module.InterfaceSection.Classes.Add(Result);    end;  var    s, Name: String;    CurClass: TPasClassType;    i: Integer;    Member: TPasElement;  begin    CurClass := nil;    while True do    begin      ReadLn(f, s);      if Length(s) = 0 then        break;      if s[1] = '#' then      begin        // New class        i := Pos(' ', s);        CurClass := CreateClass(Copy(s, 1, i - 1));      end else      begin        i := Pos(' ', s);        if i = 0 then          Name := Copy(s, 3, Length(s))        else          Name := Copy(s, 3, i - 3);        case s[2] of          'M':            Member := TPasProcedure.Create(Name, CurClass);          'P':            begin              Member := TPasProperty.Create(Name, CurClass);              if i > 0 then                while i <= Length(s) do                begin                  case s[i] of                    'r':                      TPasProperty(Member).ReadAccessorName := '<dummy>';                    'w':                      TPasProperty(Member).WriteAccessorName := '<dummy>';                    's':                      TPasProperty(Member).StoredAccessorName := '<dummy>';                  end;                  Inc(i);                end;            end;          'V':            Member := TPasVariable.Create(Name, CurClass);          else            raise Exception.Create('Invalid member type: ' + s[2]);        end;        CurClass.Members.Add(Member);      end;    end;  end;var  s: String;begin  if not FileExists(AFileName) then    raise EInOutError.Create('File not found: ' + AFileName);  Assign(f, AFilename);  Reset(f);  while not EOF(f) do  begin    ReadLn(f, s);    if (Length(s) = 0) or (s[1] = '#') then      continue;    if s = ':link tree' then      ReadLinkTree    else if s = ':classes' then      ReadClasses    else      repeat        ReadLn(f, s);      until EOF(f) or (Length(s) = 0);  end;  Close(f);end;procedure TFPDocEngine.WriteContentFile(const AFilename: String);var  ContentFile: Text;  procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);  var    ChildNode: TLinkNode;  begin    WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);    ChildNode := ALinkNode.FirstChild;    while Assigned(ChildNode) do    begin      ProcessLinkNode(ChildNode, AIdent + ' ');      ChildNode := ChildNode.NextSibling;    end;  end;var  LinkNode: TLinkNode;  i, j, k: Integer;  Module: TPasModule;  ClassDecl: TPasClassType;  Member: TPasElement;  s: String;begin  Assign(ContentFile, AFilename);  Rewrite(ContentFile);  try    WriteLn(ContentFile, '# FPDoc Content File');    WriteLn(ContentFile, ':link tree');    LinkNode := RootLinkNode.FirstChild;    while Assigned(LinkNode) do    begin      if LinkNode.Name = Package.Name then      begin        ProcessLinkNode(LinkNode, '');      end;      LinkNode := LinkNode.NextSibling;    end;  if Assigned(Package) then  begin    WriteLn(ContentFile);    WriteLn(ContentFile, ':classes');    for i := 0 to Package.Modules.Count - 1 do    begin      Module := TPasModule(Package.Modules[i]);      for j := 0 to Module.InterfaceSection.Classes.Count - 1 do      begin        ClassDecl := TPasClassType(Module.InterfaceSection.Classes[j]);        Write(ContentFile, ClassDecl.PathName, ' ');        if Assigned(ClassDecl.AncestorType) then          WriteLn(ContentFile, ClassDecl.AncestorType.PathName)        else if ClassDecl.ObjKind = okClass then          WriteLn(ContentFile, '.TObject');        for k := 0 to ClassDecl.Members.Count - 1 do        begin          Member := TPasElement(ClassDecl.Members[k]);          Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));          SetLength(s, 0);          if Member.ClassType = TPasVariable then            Write(ContentFile, 'V')          else if Member.ClassType = TPasProperty then          begin            Write(ContentFile, 'P');            if Length(TPasProperty(Member).ReadAccessorName) > 0 then              s := s + 'r';            if Length(TPasProperty(Member).WriteAccessorName) > 0 then              s := s + 'w';            if Length(TPasProperty(Member).StoredAccessorName) > 0 then              s := s + 's';          end else            Write(ContentFile, 'M');    // Member must be a method          Write(ContentFile, Member.Name);          if Length(s) > 0 then            WriteLn(ContentFile, ' ', s)          else            WriteLn(ContentFile);        end;      end;    end;  end;  finally    Close(ContentFile);  end;end;function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;  AParent: TPasElement; AVisibility: TPasMemberVisibility;  const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;begin  Result := AClass.Create(AName, AParent);  Result.Visibility := AVisibility;  if AClass.InheritsFrom(TPasModule) then    CurModule := TPasModule(Result);  Result.SourceFilename := ASourceFilename;  Result.SourceLinenumber := ASourceLinenumber;end;function TFPDocEngine.FindElement(const AName: String): TPasElement;  function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;  var    l: TList;    i: Integer;  begin    l := AModule.InterfaceSection.Declarations;    for i := 0 to l.Count - 1 do    begin      Result := TPasElement(l[i]);      if CompareText(Result.Name, LocalName) = 0 then        exit;    end;    Result := nil; end;var  i: Integer;  //ModuleName, LocalName: String;  Module: TPasElement;begin{!!!: Don't know if we ever will have to use the following:  i := Pos('.', AName);  if i <> 0 then  begin    WriteLn('Dot found in name: ', AName);    Result := nil;  end else  begin}    Result := FindInModule(CurModule, AName);    if not Assigned(Result) then      for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do      begin        Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);        if Module.ClassType = TPasModule then        begin          Result := FindInModule(TPasModule(Module), AName);          if Assigned(Result) then            exit;        end;      end;  {end;}end;function TFPDocEngine.FindModule(const AName: String): TPasModule;  function FindInPackage(APackage: TPasPackage): TPasModule;  var    i: Integer;  begin    for i := 0 to APackage.Modules.Count - 1 do    begin      Result := TPasModule(APackage.Modules[i]);      if CompareText(Result.Name, AName) = 0 then        exit;    end;    Result := nil;  end;var  i: Integer;begin  Result := FindInPackage(Package);  if not Assigned(Result) then    for i := FPackages.Count - 1 downto 0 do    begin      if TPasPackage(FPackages[i]) = Package then        continue;      Result := FindInPackage(TPasPackage(FPackages[i]));      if Assigned(Result) then        exit;    end;end;procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);begin  RootLinkNode.CreateChildren(APathName, ALinkTo);end;function TFPDocEngine.FindAbsoluteLink(const AName: String): String;var  LinkNode: TLinkNode;begin  LinkNode := RootLinkNode.FindChild(AName);  if Assigned(LinkNode) then    Result := LinkNode.Link  else    SetLength(Result, 0);end;function TFPDocEngine.ResolveLink(AModule: TPasModule;  const ALinkDest: String): String;var  i: Integer;  ThisPackage: TLinkNode;  UnitList: TList;begin//WriteLn('ResolveLink(', ALinkDest, ')... ');  if Length(ALinkDest) = 0 then  begin    SetLength(Result, 0);    exit;  end;  if (ALinkDest[1] = '#') or (not assigned(AModule)) then    Result := FindAbsoluteLink(ALinkDest)  else  begin    Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest);    if Length(Result) > 0 then      exit;    { Try all packages }    SetLength(Result, 0);    ThisPackage := RootLinkNode.FirstChild;    while Assigned(ThisPackage) do    begin      Result := ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest);      if Length(Result) > 0 then        exit;      ThisPackage := ThisPackage.NextSibling;    end;    if Length(Result) = 0 then    begin      { Okay, then we have to try all imported units of the current module }      UnitList := AModule.InterfaceSection.UsesList;      for i := UnitList.Count - 1 downto 0 do      begin        { Try all packages }        ThisPackage := RootLinkNode.FirstChild;        while Assigned(ThisPackage) do        begin          Result := ResolveLink(AModule, ThisPackage.Name + '.' +            TPasType(UnitList[i]).Name + '.' + ALinkDest);          if Length(Result) > 0 then            exit;          ThisPackage := ThisPackage.NextSibling;        end;      end;    end;  end;  if Length(Result) = 0 then    for i := Length(ALinkDest) downto 1 do      if ALinkDest[i] = '.' then      begin        Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1));        exit;      end;end;procedure TFPDocEngine.AddDocFile(const AFilename: String);  function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;  var    Subnode: TDOMNode;  begin    if OwnerDocNode = RootDocNode then      Result := OwnerDocNode.CreateChildren('#' + Element['name'])    else      Result := OwnerDocNode.CreateChildren(Element['name']);    Result.FNode := Element;    Result.FLink := Element['link'];    Result.FIsSkipped := Element['skip'] = '1';    Subnode := Element.FirstChild;    while Assigned(Subnode) do    begin      if Subnode.NodeType = ELEMENT_NODE then      begin        if Subnode.NodeName = 'short' then          Result.FShortDescr := TDOMElement(Subnode)        else if Subnode.NodeName = 'descr' then          Result.FDescr := TDOMElement(Subnode)        else if Subnode.NodeName = 'errors' then          Result.FErrorsDoc := TDOMElement(Subnode)        else if Subnode.NodeName = 'seealso' then          Result.FSeeAlso := TDOMElement(Subnode)        else if (Subnode.NodeName = 'example') and          not Assigned(Result.FirstExample) then          Result.FFirstExample := TDOMElement(Subnode);      end;      Subnode := Subnode.NextSibling;    end;  end;  Procedure ReadTopics(TopicNode : TDocNode);  Var    SubNode : TDOMNode;  begin    SubNode:=TopicNode.FNode.FirstChilD;    While Assigned(SubNode) do      begin      If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then        With ReadNode(TopicNode,TDomElement(SubNode)) do          // We could allow recursion here, but we won't, because it doesn't work on paper.          FTopicNode:=True;      SubNode:=Subnode.NextSibling;      end;  end;var  i: Integer;  Node, Subnode, Subsubnode: TDOMNode;  Element: TDOMElement;  Doc: TXMLDocument;  PackageDocNode, TopicNode,ModuleDocNode: TDocNode;begin  ReadXMLFile(Doc, AFilename);  DescrDocs.Add(Doc);  DescrDocNames.Add(AFilename);  Node := Doc.DocumentElement.FirstChild;  while Assigned(Node) do    begin    if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then      begin      PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));      // Scan all 'module' elements within this package element      Subnode := Node.FirstChild;      while Assigned(Subnode) do        begin        if (Subnode.NodeType = ELEMENT_NODE) then          begin          If (Subnode.NodeName = 'module') then            begin            ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));            // Scan all 'element' elements within this module element            Subsubnode := Subnode.FirstChild;            while Assigned(Subsubnode) do              begin              if (Subsubnode.NodeType = ELEMENT_NODE) then                begin                if (Subsubnode.NodeName = 'element') then                  ReadNode(ModuleDocNode, TDOMElement(Subsubnode))                else if (SubSubNode.NodeName='topic') then                  begin                  TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));                  TopicNode.FTopicNode:=True;                  ReadTopics(TopicNode);                  end;                end;              Subsubnode := Subsubnode.NextSibling;              end;            end          else if (SubNode.NodeName='topic') then            begin            TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));            TopicNode.FTopicNode:=True;            ReadTopics(TopicNode);            end;          end;        Subnode := Subnode.NextSibling;      end;    end;    Node := Node.NextSibling;  end;end;function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;begin  Result:=Nil;  If Assigned(AElement) then    begin    if AElement.InheritsFrom(TPasUnresolvedTypeRef) then      Result := FindDocNode(AElement.GetModule, AElement.Name)    else      Result := RootDocNode.FindChild(AElement.PathName);    if (Result=Nil) and       WarnNoNode and       (Length(AElement.PathName)>0) and       (AElement.PathName[1]='#') then      Writeln('No documentation node found for identifier : ',AElement.PathName);    end;end;function TFPDocEngine.FindDocNode(ARefModule: TPasModule;  const AName: String): TDocNode;var  CurPackage: TDocNode;  UnitList: TList;  i: Integer;begin  if Length(AName) = 0 then    Result := nil  else  begin    if AName[1] = '#' then      Result := RootDocNode.FindChild(AName)    else      Result := RootDocNode.FindChild(Package.Name + '.' + AName);    if (not Assigned(Result)) and Assigned(ARefModule) then      Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);    if (not Assigned(Result)) and (AName[1] <> '#') then    begin      CurPackage := RootDocNode.FirstChild;      while Assigned(CurPackage) do      begin        Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);        if Assigned(Result) then          break;        CurPackage := CurPackage.NextSibling;      end;      if not Assigned(Result) then      begin        { Okay, then we have to try all imported units of the current module }        UnitList := CurModule.InterfaceSection.UsesList;        for i := UnitList.Count - 1 downto 0 do        begin          { Try all packages }          CurPackage := RootDocNode.FirstChild;          while Assigned(CurPackage) do          begin            Result := RootDocNode.FindChild(CurPackage.Name + '.' +              TPasType(UnitList[i]).Name + '.' + AName);            if Assigned(Result) then              break;            CurPackage := CurPackage.NextSibling;          end;        end;      end;    end;  end;end;function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;var  DocNode: TDocNode;begin  DocNode := FindDocNode(AElement);  if Assigned(DocNode) then    Result := DocNode.ShortDescr  else    Result := nil;end;function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;  const AName: String): TDOMElement;var  DocNode: TDocNode;begin  DocNode := FindDocNode(ARefModule, AName);  if Assigned(DocNode) then    Result := DocNode.ShortDescr  else    Result := nil;end;function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;var  i: Integer;  fn : String;  begin  Result:='';  for i := 0 to DescrDocs.Count - 1 do    begin    Fn:=ExElement['file'];    if (FN<>'') and (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then      begin      Result := ExtractFilePath(DescrDocNames[i]) + FN;      if (ExtractFileExt(Result)='') then        Result:=Result+'.pp';      end;    end;  end;{ Global helpers }procedure TranslateDocStrings(const Lang: String);var  mo: TMOFile;begin{$IFDEF Unix}  mo := TMOFile.Create(Format('/usr/local/share/locale/%s/LC_MESSAGES/dglobals.mo', [Lang]));{$ELSE}  mo := TMOFile.Create(Format('intl/dglobals.%s.mo', [Lang]));{$ENDIF}  try    TranslateResourceStrings(mo);  finally    mo.Free;  end;end;Function IsLinkNode(Node : TDomNode) : Boolean;begin  Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');end;Function IsExampleNode(Example : TDomNode) : Boolean;begin  Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')end;function IsLinkAbsolute(ALink: String): boolean;var  i: integer;begin  Result := false;  for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do    if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin      Result := true;      break;    end;end;initialization  LEOL:=Length(LineEnding);end.
 |