dglobals.pp 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2002 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. * Global declarations
  6. * Link list management
  7. * Document node tree
  8. * Main engine
  9. See the file COPYING, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. }
  15. {$MODE objfpc}
  16. {$H+}
  17. unit dGlobals;
  18. interface
  19. uses Classes, DOM, PasTree, PParser, StrUtils,uriparser;
  20. Const
  21. CacheSize = 20;
  22. ContentBufSize = 4096 * 8;
  23. Var
  24. LEOL : Integer;
  25. modir : string;
  26. resourcestring
  27. // Output strings
  28. SDocPackageTitle = 'Reference for package ''%s''';
  29. SDocPrograms = 'Programs';
  30. SDocUnits = 'Units';
  31. SDocUnitTitle = 'Reference for unit ''%s''';
  32. SDocInheritanceHierarchy = 'Inheritance Hierarchy';
  33. SDocInterfaceSection = 'Interface section';
  34. SDocImplementationSection = 'Implementation section';
  35. SDocUsedUnits = 'Used units';
  36. SDocUsedUnitsByUnitXY = 'Used units by unit ''%s''';
  37. SDocConstsTypesVars = 'Constants, types and variables';
  38. SDocResStrings = 'Resource strings';
  39. SDocTypes = 'Types';
  40. SDocConstants = 'Constants';
  41. SDocClasses = 'Classes';
  42. SDocProceduresAndFunctions = 'Procedures and functions';
  43. SDocVariables = 'Variables';
  44. SDocIdentifierIndex = 'Index';
  45. SDocPackageClassHierarchy = 'Class hierarchy';
  46. SDocModuleIndex = 'Index of all identifiers in unit ''%s''';
  47. SDocPackageIndex = 'Index of all identifiers in package ''%s''';
  48. SDocUnitOverview = 'Overview of unit ''%s''';
  49. SDocOverview = 'Overview';
  50. SDocSearch = 'Search';
  51. SDocDeclaration = 'Declaration';
  52. SDocDescription = 'Description';
  53. SDocErrors = 'Errors';
  54. SDocVersion = 'Version info';
  55. SDocSeeAlso = 'See also';
  56. SDocExample = 'Example';
  57. SDocArguments = 'Arguments';
  58. SDocFunctionResult = 'Function result';
  59. SDocRemark = 'Remark: ';
  60. SDocMethodOverview = 'Method overview';
  61. SDocPropertyOverview = 'Property overview';
  62. SDocInterfacesOverview = 'Interfaces overview';
  63. SDocInterface = 'Interfaces';
  64. SDocPage = 'Page';
  65. SDocMethod = 'Method';
  66. SDocProperty = 'Property';
  67. SDocAccess = 'Access';
  68. SDocInheritance = 'Inheritance';
  69. SDocProperties = 'Properties';
  70. SDocMethods = 'Methods';
  71. SDocEvents = 'Events';
  72. SDocByName = 'by Name';
  73. SDocValue = 'Value';
  74. SDocExplanation = 'Explanation';
  75. SDocProcedure = 'Procedure';
  76. SDocValuesForEnum = 'Enumeration values for type %s';
  77. SDocSourcePosition = 'Source position: %s line %d';
  78. SDocSynopsis = 'Synopsis';
  79. SDocVisibility = 'Visibility';
  80. SDocOpaque = 'Opaque type';
  81. SDocDateGenerated = 'Documentation generated on: %s';
  82. // The next line requires leading/trailing space due to XML comment layout:
  83. SDocGeneratedByComment = ' Generated using FPDoc - (c) 2000-2012 FPC contributors and Sebastian Guenther, [email protected] ';
  84. SDocNotes = 'Notes';
  85. // Topics
  86. SDocRelatedTopics = 'Related topics';
  87. SDocUp = 'Up';
  88. SDocNext = 'Next';
  89. SDocPrevious = 'Previous';
  90. // Various backend constants
  91. SDocChapter = 'Chapter';
  92. SDocSection = 'Section';
  93. SDocSubSection = 'Subsection';
  94. SDocTable = 'Table';
  95. SDocListing = 'Listing';
  96. // Man page usage
  97. SManUsageManSection = 'Use ASection as the man page section';
  98. SManUsageNoUnitPrefix = 'Do not prefix man pages with unit name.';
  99. SManUsageWriterDescr = 'UNIX man page output.';
  100. SManUsagePackageDescription = 'Use descr as the description of man pages';
  101. // HTML usage
  102. SHTMLUsageFooter = 'Append xhtml from file as footer to html page';
  103. SHTMLUsageFooterDate = 'Append footer with date. fmt is Optional format for FormatDateTime';
  104. SHTMLUsageCharset = 'Set the HTML character set';
  105. SHTMLHtmlSearch = 'Add search page with given name to the menu bar';
  106. SHTMLIndexColcount = 'Use N columns in the identifier index pages';
  107. SHTMLImageUrl = 'Prefix image URLs with url';
  108. SHTMLDisableMenuBrackets = 'Disable ''['' and '']'' characters around menu items at the top of the page. Useful for custom css';
  109. // CHM usage
  110. SCHMUsageTOC = 'Use [File] as the table of contents. Usually a .hhc file.';
  111. SCHMUsageIndex = 'Use [File] as the index. Usually a .hhk file.';
  112. SCHMUsageDefPage = 'Set the "Home" page relative to where it lives in the chm. i.e. "/index.html"';
  113. SCHMUsageOtrFiles= 'A txt file containing a list of files to be added relative to the working directory.';
  114. SCHMUsageCSSFile = 'Filename of a .css file to be included in the chm.';
  115. SCHMUsageAutoTOC = 'Automatically generate a Table of Contents. Ignores --toc-file';
  116. SCHMUsageAutoIDX = 'Automatically generate an Index. Ignores --index-file';
  117. SCHMUsageMakeSearch = 'Automatically generate a Search Index from filenames that match *.htm*';
  118. SCHMUsageChmTitle= 'Title of the chm. Defaults to the value from --package';
  119. // Linear usage
  120. SLinearUsageDupLinkedDocsP1 = 'Duplicate linked element documentation in';
  121. SLinearUsageDupLinkedDocsP2 = 'descendant classes.';
  122. STitle = 'FPDoc - Free Pascal Documentation Tool';
  123. SVersion = 'Version %s [%s]';
  124. SCopyright1 = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, [email protected]';
  125. SCopyright2 = '(c) 2005 - 2012 various FPC contributors';
  126. SCmdLineHelp = 'Usage: %s [options]';
  127. SUsageOption010 = '--content Create content file for package cross-references';
  128. SUsageOption020 = '--cputarget=value Set the target CPU for the scanner.';
  129. SUsageOption030 = '--descr=file use file as description file, e.g.: ';
  130. SUsageOption035 = ' --descr=c:\WIP\myzipperdoc.xml';
  131. SUsageOption040 = ' This option is allowed more than once';
  132. SUsageOption050 = '--descr-dir=Dir Add All XML files in Dir to list of description files';
  133. SUsageOption060 = '--format=fmt Select output format.';
  134. SUsageOption070 = '--help Show this help.';
  135. SUsageOption080 = '--hide-protected Do not show protected methods in overview';
  136. SUsageOption090 = '--import=file Import content file for package cross-references';
  137. SUsageOption100 = '--input=cmd use cmd as input for the parser, e.g.:';
  138. SUsageOption110 = ' --input=C:\fpc\packages\paszlib\src\zipper.pp';
  139. SUsageOption120 = ' At least one input option is required.';
  140. SUsageOption130 = '--input-dir=Dir Add All *.pp and *.pas files in Dir to list of input files';
  141. SUsageOption140 = '--lang=lng Select output language.';
  142. SUsageOption150 = '--ostarget=value Set the target OS for the scanner.';
  143. SUsageOption160 = '--output=name use name as the output name.';
  144. SUsageOption170 = ' Each backend interpretes this as needed.';
  145. SUsageOption180 = '--package=name Set the package name for which to create output,';
  146. SUsageOption190 = ' e.g. --package=fcl';
  147. SUsageOption200 = '--project=file Use file as project file';
  148. SUsageOption210 = '--show-private Show private methods.';
  149. SUsageOption220 = '--warn-no-node Warn if no documentation node was found.';
  150. SUsageOption230 = '--mo-dir=dir Set directory where language files reside to dir';
  151. SUsageOption240 = '--parse-impl (Experimental) try to parse implementation too';
  152. SUsageOption250 = '--dont-trim Do not trim XML contents. Useful for preserving';
  153. SUsageOption260 = ' formatting inside e.g <pre> tags';
  154. SUsageOption270 = '--write-project=file';
  155. SUsageOption280 = ' Do not write documentation, create project file instead';
  156. SUsageOption290 = '--verbose Write more information on the screen';
  157. SUsageOption300 = '--dry-run Only parse sources and XML, do not create output';
  158. SUsageOption310 = '--write-project=file';
  159. SUsageOption320 = ' Write all command-line options to a project file';
  160. SUsageFormats = 'The following output formats are supported by this fpdoc:';
  161. SUsageBackendHelp = 'Specify an output format, combined with --help to get more help for this backend.';
  162. SUsageFormatSpecific = 'Output format "%s" supports the following options:';
  163. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  164. SCmdLineInvalidFormat = 'Invalid format "%s" specified';
  165. SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=<filename>';
  166. SWritingPages = 'Writing %d pages...';
  167. SNeedPackageName = 'No package name specified. Please specify one using the --package option.';
  168. SAvailablePackages = 'Available packages: ';
  169. SDone = 'Done.';
  170. SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';
  171. SErrCouldNotCreateFile = 'Could not create file "%s": %s';
  172. SSeeURL = '(See %s)'; // For linear text writers.
  173. SParsingUsedUnit = 'Parsing used unit "%s" with commandLine "%s"';
  174. Const
  175. SVisibility: array[TPasMemberVisibility] of string =
  176. ('Default', 'Private', 'Protected', 'Public',
  177. 'Published', 'Automated','Strict Private','Strict Protected');
  178. type
  179. // Assumes a list of TObject instances and frees them on destruction
  180. TObjectList = class(TFPList)
  181. public
  182. destructor Destroy; override;
  183. end;
  184. TPasExternalClassType = Class(TPasClassType);
  185. TPasExternalModule = Class(TPasModule);
  186. { Link entry tree
  187. TFPDocEngine stores the root of the entry tree in its property
  188. "RootLinkNode". The root has one child node for each package, for which
  189. documentation links are available. The children of a package node
  190. are module nodes; and the children of a module node are the top-level
  191. declarations of this module; the next level in the tree stores e.g. record
  192. members, and so on...
  193. }
  194. TLinkNode = class
  195. private
  196. FFirstChild, FNextSibling: TLinkNode;
  197. FName: String;
  198. FLink: String;
  199. public
  200. constructor Create(const AName, ALink: String);
  201. destructor Destroy; override;
  202. function FindChild(const APathName: String): TLinkNode;
  203. function CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  204. // Properties for tree structure
  205. property FirstChild: TLinkNode read FFirstChild;
  206. property NextSibling: TLinkNode read FNextSibling;
  207. // Link properties
  208. property Name: String read FName;
  209. property Link: String read FLink;
  210. end;
  211. { Documentation entry tree
  212. TFPDocEngine stores the root of the entry tree in its property
  213. "RootDocNode". The root has one child node for each package, for which
  214. documentation is being provided by the user. The children of a package node
  215. are module nodes; and the children of a module node are the top-level
  216. declarations of this module; the next level in the tree stores e.g. record
  217. members, and so on...
  218. }
  219. { TDocNode }
  220. TDocNode = class
  221. private
  222. FFirstChild, FNextSibling: TDocNode;
  223. FName: String;
  224. FNode: TDOMElement;
  225. FIsSkipped: Boolean;
  226. FShortDescr: TDOMElement;
  227. FDescr: TDOMElement;
  228. FErrorsDoc: TDOMElement;
  229. FSeeAlso: TDOMElement;
  230. FFirstExample: TDOMElement;
  231. FNotes : TDomElement;
  232. FLink: String;
  233. FTopicNode : Boolean;
  234. FRefCount : Integer;
  235. FVersion: TDomElement;
  236. public
  237. constructor Create(const AName: String; ANode: TDOMElement);
  238. destructor Destroy; override;
  239. Function IncRefcount : Integer;
  240. function FindChild(const APathName: String): TDocNode;
  241. function CreateChildren(const APathName: String): TDocNode;
  242. // Properties for tree structure
  243. property FirstChild: TDocNode read FFirstChild;
  244. property NextSibling: TDocNode read FNextSibling;
  245. // Basic properties
  246. property Name: String read FName;
  247. property Node: TDOMElement read FNode;
  248. // Data fetched from the XML document
  249. property IsSkipped: Boolean read FIsSkipped;
  250. property ShortDescr: TDOMElement read FShortDescr;
  251. property Descr: TDOMElement read FDescr;
  252. property ErrorsDoc: TDOMElement read FErrorsDoc;
  253. Property Version : TDomElement Read FVersion;
  254. property SeeAlso: TDOMElement read FSeeAlso;
  255. property FirstExample: TDOMElement read FFirstExample;
  256. property Notes : TDOMElement read FNotes;
  257. property Link: String read FLink;
  258. Property TopicNode : Boolean Read FTopicNode;
  259. Property RefCount : Integer Read FRefCount;
  260. end;
  261. // The main FPDoc engine
  262. TFPDocLogLevel = (dleWarnNoNode);
  263. TFPDocLogLevels = set of TFPDocLogLevel;
  264. TOnParseUnitEvent = Procedure (Sender : TObject; Const AUnitName : String; Out AInputFile,OSTarget,CPUTarget : String) of Object;
  265. { TFPDocEngine }
  266. TFPDocEngine = class(TPasTreeContainer)
  267. private
  268. FDocLogLevels: TFPDocLogLevels;
  269. FOnParseUnit: TOnParseUnitEvent;
  270. function ResolveLinkInPackages(AModule: TPasModule; const ALinkDest: String; Strict: Boolean=False): String;
  271. function ResolveLinkInUsedUnits(AModule: TPasModule; const ALinkDest: String; Strict: Boolean=False): String;
  272. protected
  273. FAlwaysVisible : TStringList;
  274. DescrDocs: TObjectList; // List of XML documents
  275. DescrDocNames: TStringList; // Names of the XML documents
  276. FRootLinkNode: TLinkNode;
  277. FRootDocNode: TDocNode;
  278. FPackages: TFPList; // List of TFPPackage objects
  279. CurModule: TPasModule;
  280. CurPackageDocNode: TDocNode;
  281. function ParseUsedUnit(AName, AInputLine,AOSTarget,ACPUTarget: String): TPasModule; virtual;
  282. Function LogEvent(E : TFPDocLogLevel) : Boolean;
  283. Procedure DoLog(Const Msg : String);overload;
  284. Procedure DoLog(Const Fmt : String; Args : Array of const);overload;
  285. public
  286. Output: String;
  287. HasContentFile: Boolean;
  288. HidePrivate: Boolean; // Hide private class members in output?
  289. HideProtected: Boolean; // Hide protected class members in output?
  290. WarnNoNode : Boolean; // Warn if no description node found for element.
  291. constructor Create;
  292. destructor Destroy; override;
  293. procedure SetPackageName(const APackageName: String);
  294. procedure ReadContentFile(const AFilename, ALinkPrefix: String);
  295. procedure WriteContentFile(const AFilename: String);
  296. function CreateElement(AClass: TPTreeElement; const AName: String;
  297. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  298. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  299. override;
  300. function FindElement(const AName: String): TPasElement; override;
  301. function FindModule(const AName: String): TPasModule; override;
  302. Function HintsToStr(Hints : TPasMemberHints) : String;
  303. // Link tree support
  304. procedure AddLink(const APathName, ALinkTo: String);
  305. function FindAbsoluteLink(const AName: String): String;
  306. function ResolveLink(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  307. function FindLinkedNode(ANode: TDocNode): TDocNode;
  308. Function ShowElement(El : TPasElement) : Boolean; inline;
  309. // Call this before documenting.
  310. Procedure StartDocumenting; virtual;
  311. // Documentation file support
  312. procedure AddDocFile(const AFilename: String;DontTrim:boolean=false);
  313. // Documentation retrieval
  314. function FindDocNode(AElement: TPasElement): TDocNode;
  315. function FindDocNode(ARefModule: TPasModule; const AName: String): TDocNode;
  316. function FindShortDescr(AElement: TPasElement): TDOMElement;
  317. function FindShortDescr(ARefModule: TPasModule; const AName: String): TDOMElement;
  318. function GetExampleFilename(const ExElement: TDOMElement): String;
  319. property RootLinkNode: TLinkNode read FRootLinkNode;
  320. property RootDocNode: TDocNode read FRootDocNode;
  321. Property DocLogLevels : TFPDocLogLevels Read FDocLogLevels Write FDocLogLevels;
  322. Property OnParseUnit : TOnParseUnitEvent Read FOnParseUnit Write FOnParseUnit;
  323. end;
  324. procedure TranslateDocStrings(const Lang: String);
  325. Function IsLinkNode(Node : TDomNode) : Boolean;
  326. Function IsExampleNode(Example : TDomNode) : Boolean;
  327. // returns true is link is an absolute URI
  328. Function IsLinkAbsolute(ALink: String): boolean;
  329. implementation
  330. uses SysUtils, Gettext, XMLRead;
  331. const
  332. AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');
  333. { TObjectList }
  334. destructor TObjectList.Destroy;
  335. var
  336. i: Integer;
  337. begin
  338. for i := 0 to Count - 1 do
  339. TObject(Items[i]).Free;
  340. inherited Destroy;
  341. end;
  342. { TLinkNode }
  343. constructor TLinkNode.Create(const AName, ALink: String);
  344. begin
  345. inherited Create;
  346. FName := AName;
  347. FLink := ALink;
  348. end;
  349. destructor TLinkNode.Destroy;
  350. begin
  351. if Assigned(FirstChild) then
  352. FirstChild.Free;
  353. if Assigned(NextSibling) then
  354. NextSibling.Free;
  355. inherited Destroy;
  356. end;
  357. function TLinkNode.FindChild(const APathName: String): TLinkNode;
  358. var
  359. DotPos: Integer;
  360. ChildName: String;
  361. Child: TLinkNode;
  362. begin
  363. if Length(APathName) = 0 then
  364. Result := Self
  365. else
  366. begin
  367. DotPos := Pos('.', APathName);
  368. if DotPos = 0 then
  369. ChildName := APathName
  370. else
  371. ChildName := Copy(APathName, 1, DotPos - 1);
  372. Child := FirstChild;
  373. while Assigned(Child) do
  374. begin
  375. if CompareText(Child.Name, ChildName) = 0 then
  376. begin
  377. if DotPos = 0 then
  378. Result := Child
  379. else
  380. Result := Child.FindChild(
  381. Copy(APathName, DotPos + 1, Length(APathName)));
  382. exit;
  383. end;
  384. Child := Child.NextSibling;
  385. end;
  386. Result := nil;
  387. end;
  388. end;
  389. function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  390. var
  391. DotPos: Integer;
  392. ChildName: String;
  393. Child, LastChild: TLinkNode;
  394. begin
  395. if Length(APathName) = 0 then
  396. Result := Self
  397. else
  398. begin
  399. DotPos := Pos('.', APathName);
  400. if DotPos = 0 then
  401. ChildName := APathName
  402. else
  403. ChildName := Copy(APathName, 1, DotPos - 1);
  404. Child := FirstChild;
  405. LastChild := nil;
  406. while Assigned(Child) do
  407. begin
  408. if CompareText(Child.Name, ChildName) = 0 then
  409. begin
  410. if DotPos = 0 then
  411. Result := Child
  412. else
  413. Result := Child.CreateChildren(
  414. Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);
  415. exit;
  416. end;
  417. LastChild := Child;
  418. Child := Child.NextSibling;
  419. end;
  420. Result := TLinkNode.Create(ChildName, ALinkTo);
  421. if Assigned(LastChild) then
  422. LastChild.FNextSibling := Result
  423. else
  424. FFirstChild := Result;
  425. end;
  426. end;
  427. { TDocNode }
  428. constructor TDocNode.Create(const AName: String; ANode: TDOMElement);
  429. begin
  430. inherited Create;
  431. FName := AName;
  432. FNode := ANode;
  433. end;
  434. destructor TDocNode.Destroy;
  435. begin
  436. if Assigned(FirstChild) then
  437. FirstChild.Free;
  438. if Assigned(NextSibling) then
  439. NextSibling.Free;
  440. inherited Destroy;
  441. end;
  442. Function TDocNode.IncRefcount : Integer;
  443. begin
  444. Inc(FRefCount);
  445. Result:=FRefCount;
  446. end;
  447. function TDocNode.FindChild(const APathName: String): TDocNode;
  448. var
  449. DotPos: Integer;
  450. ChildName: String;
  451. Child: TDocNode;
  452. begin
  453. if Length(APathName) = 0 then
  454. Result := Self
  455. else
  456. begin
  457. DotPos := Pos('.', APathName);
  458. if DotPos = 0 then
  459. ChildName := APathName
  460. else
  461. ChildName := Copy(APathName, 1, DotPos - 1);
  462. Child := FirstChild;
  463. while Assigned(Child) do
  464. begin
  465. if CompareText(Child.Name, ChildName) = 0 then
  466. begin
  467. if DotPos = 0 then
  468. Result := Child
  469. else
  470. Result := Child.FindChild(
  471. Copy(APathName, DotPos + 1, Length(APathName)));
  472. exit;
  473. end;
  474. Child := Child.NextSibling;
  475. end;
  476. Result := nil;
  477. end;
  478. end;
  479. function TDocNode.CreateChildren(const APathName: String): TDocNode;
  480. var
  481. DotPos: Integer;
  482. ChildName: String;
  483. Child: TDocNode;
  484. begin
  485. if Length(APathName) = 0 then
  486. Result := Self
  487. else
  488. begin
  489. DotPos := Pos('.', APathName);
  490. if DotPos = 0 then
  491. ChildName := APathName
  492. else
  493. ChildName := Copy(APathName, 1, DotPos - 1);
  494. Child := FirstChild;
  495. while Assigned(Child) do
  496. begin
  497. if CompareText(Child.Name, ChildName) = 0 then
  498. begin
  499. if DotPos = 0 then
  500. Result := Child
  501. else
  502. Result := Child.CreateChildren(
  503. Copy(APathName, DotPos + 1, Length(APathName)));
  504. exit;
  505. end;
  506. Child := Child.NextSibling;
  507. end;
  508. // No child found, let's create one
  509. Result := TDocNode.Create(ChildName, nil);
  510. if Assigned(FirstChild) then
  511. begin
  512. Result.FNextSibling := FirstChild;
  513. FFirstChild := Result;
  514. end else
  515. FFirstChild := Result;
  516. if DotPos > 0 then
  517. Result := Result.CreateChildren(
  518. Copy(APathName, DotPos + 1, Length(APathName)));
  519. end;
  520. end;
  521. { TFPDocEngine }
  522. function TFPDocEngine.LogEvent(E: TFPDocLogLevel): Boolean;
  523. begin
  524. Result:=E in FDocLogLevels;
  525. end;
  526. procedure TFPDocEngine.DoLog(const Msg: String);
  527. begin
  528. If Assigned(OnLog) then
  529. OnLog(Self,Msg);
  530. end;
  531. procedure TFPDocEngine.DoLog(const Fmt: String; Args: array of const);
  532. begin
  533. DoLog(Format(Fmt,Args));
  534. end;
  535. constructor TFPDocEngine.Create;
  536. begin
  537. inherited Create;
  538. DescrDocs := TObjectList.Create;
  539. FAlwaysVisible := TStringList.Create;
  540. FAlwaysVisible.CaseSensitive:=True;
  541. DescrDocNames := TStringList.Create;
  542. FRootLinkNode := TLinkNode.Create('', '');
  543. FRootDocNode := TDocNode.Create('', nil);
  544. HidePrivate := True;
  545. InterfaceOnly:=True;
  546. FPackages := TFPList.Create;
  547. end;
  548. destructor TFPDocEngine.Destroy;
  549. var
  550. i: Integer;
  551. begin
  552. for i := 0 to FPackages.Count - 1 do
  553. TPasPackage(FPackages[i]).Release;
  554. FreeAndNil(FRootDocNode);
  555. FreeAndNil(FRootLinkNode);
  556. FreeAndNil(DescrDocNames);
  557. FreeAndNil(DescrDocs);
  558. FreeAndNil(FAlwaysVisible);
  559. FreeAndNil(FPackages);
  560. inherited Destroy;
  561. end;
  562. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  563. begin
  564. ASSERT(not Assigned(Package));
  565. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  566. '#' + APackageName, nil, '', 0));
  567. FPackages.Add(FPackage);
  568. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  569. If Assigned(CurPackageDocNode) then
  570. CurPackageDocNode.IncRefCount;
  571. end;
  572. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  573. var
  574. f: Text;
  575. inheritanceinfo : TStringlist;
  576. procedure ReadLinkTree;
  577. var
  578. s: String;
  579. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  580. CurParent, PrevSibling, NewNode: TLinkNode;
  581. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  582. begin
  583. PrevSpaces := 0;
  584. CurParent := RootLinkNode;
  585. PrevSibling := CurParent.FirstChild;
  586. if assigned(PrevSibling) then
  587. while assigned(PrevSibling.NextSibling) do
  588. PrevSibling := PrevSibling.NextSibling;
  589. StackIndex := 0;
  590. while True do
  591. begin
  592. ReadLn(f, s);
  593. if Length(s) = 0 then
  594. break;
  595. ThisSpaces := 0;
  596. while s[ThisSpaces + 1] = ' ' do
  597. Inc(ThisSpaces);
  598. if ThisSpaces <> PrevSpaces then
  599. begin
  600. if ThisSpaces > PrevSpaces then
  601. begin
  602. { Dive down one level }
  603. ParentStack[StackIndex] := CurParent;
  604. SiblingStack[StackIndex] := PrevSibling;
  605. Inc(StackIndex);
  606. CurParent := PrevSibling;
  607. PrevSibling := nil;
  608. end else
  609. while PrevSpaces > ThisSpaces do
  610. begin
  611. Dec(StackIndex);
  612. CurParent := ParentStack[StackIndex];
  613. PrevSibling := SiblingStack[StackIndex];
  614. Dec(PrevSpaces);
  615. end;
  616. PrevSpaces := ThisSpaces;
  617. end;
  618. i := ThisSpaces + 1;
  619. while s[i] <> ' ' do
  620. Inc(i);
  621. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  622. ALinkPrefix + Copy(s, i + 1, Length(s)));
  623. if pos(' ',newnode.link)>0 then
  624. writeln(stderr,'Bad format imported node: name="',newnode.name,'" link="',newnode.link,'"');
  625. if Assigned(PrevSibling) then
  626. PrevSibling.FNextSibling := NewNode
  627. else
  628. CurParent.FFirstChild := NewNode;
  629. PrevSibling := NewNode;
  630. end;
  631. end;
  632. function ResolvePackageModule(AName:String;out pkg:TPasPackage;out module:TPasModule;createnew:boolean):String;
  633. var
  634. DotPos, DotPos2, i: Integer;
  635. s: String;
  636. HPackage: TPasPackage;
  637. begin
  638. pkg:=nil; module:=nil; result:='';
  639. // Find or create package
  640. DotPos := Pos('.', AName);
  641. s := Copy(AName, 1, DotPos - 1);
  642. HPackage := nil;
  643. for i := 0 to FPackages.Count - 1 do
  644. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  645. begin
  646. HPackage := TPasPackage(FPackages[i]);
  647. break;
  648. end;
  649. if not Assigned(HPackage) then
  650. begin
  651. if not CreateNew then
  652. exit;
  653. HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  654. '', 0));
  655. FPackages.Add(HPackage);
  656. end;
  657. // Find or create module
  658. DotPos2 := DotPos;
  659. repeat
  660. Inc(DotPos2);
  661. until AName[DotPos2] = '.';
  662. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  663. Module := nil;
  664. for i := 0 to HPackage.Modules.Count - 1 do
  665. if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then
  666. begin
  667. Module := TPasModule(HPackage.Modules[i]);
  668. break;
  669. end;
  670. if not Assigned(Module) then
  671. begin
  672. if not CreateNew then
  673. exit;
  674. Module := TPasExternalModule.Create(s, HPackage);
  675. Module.InterfaceSection := TInterfaceSection.Create('', Module);
  676. HPackage.Modules.Add(Module);
  677. end;
  678. pkg:=hpackage;
  679. result:=Copy(AName, DotPos2 + 1, length(AName)-dotpos2);
  680. end;
  681. function SearchInList(clslist:TFPList;s:string):TPasElement;
  682. var i : integer;
  683. ClassEl: TPasElement;
  684. begin
  685. result:=nil;
  686. for i:=0 to clslist.count-1 do
  687. begin
  688. ClassEl := TPasElement(clslist[i]);
  689. if CompareText(ClassEl.Name,s) =0 then
  690. exit(Classel);
  691. end;
  692. end;
  693. function ResolveClassType(AName:String):TPasClassType;
  694. var
  695. pkg : TPasPackage;
  696. module : TPasModule;
  697. s : string;
  698. begin
  699. Result:=nil;
  700. s:=ResolvePackageModule(AName,pkg,module,False);
  701. if not assigned(module) then
  702. exit;
  703. result:=TPasClassType(SearchInList(Module.InterfaceSection.Classes,s));
  704. end;
  705. function ResolveAliasType(AName:String):TPasAliasType;
  706. var
  707. pkg : TPasPackage;
  708. module : TPasModule;
  709. s : string;
  710. begin
  711. Result:=nil;
  712. s:=ResolvePackageModule(AName,pkg,module,False);
  713. if not assigned(module) then
  714. exit;
  715. result:=TPasAliasType(SearchInList(Module.InterfaceSection.Types,s));
  716. if not (result is TPasAliasType) then
  717. result:=nil;
  718. end;
  719. procedure ReadClasses;
  720. function CreateClass(const AName: String;InheritanceStr:String): TPasClassType;
  721. var
  722. s: String;
  723. HPackage: TPasPackage;
  724. Module: TPasModule;
  725. begin
  726. s:= ResolvePackageModule(AName,HPackage,Module,True);
  727. // Create node for class
  728. Result := TPasExternalClassType.Create(s, Module.InterfaceSection);
  729. Result.ObjKind := okClass;
  730. Module.InterfaceSection.Declarations.Add(Result);
  731. Module.InterfaceSection.Classes.Add(Result);
  732. // defer processing inheritancestr till all classes are loaded.
  733. if inheritancestr<>'' then
  734. InheritanceInfo.AddObject(Inheritancestr,result);
  735. end;
  736. procedure splitalias(var instr:string;out outstr:string);
  737. var i,j:integer;
  738. begin
  739. if length(instr)=0 then exit;
  740. instr:=trim(instr);
  741. i:=pos('(',instr);
  742. if i>0 then
  743. begin
  744. j:=length(instr)-i;
  745. if instr[length(instr)]=')' then
  746. dec(j);
  747. outstr:=copy(instr,i+1,j);
  748. delete(instr,i,j+2);
  749. end
  750. end;
  751. Function ResolveAndLinkClass(clname:String;IsClass:boolean;cls:TPasClassType):TPasClassType;
  752. begin
  753. result:=TPasClassType(ResolveClassType(clname));
  754. if assigned(result) and not (cls=result) then // save from tobject=implicit tobject
  755. begin
  756. result.addref;
  757. if IsClass then
  758. begin
  759. cls.ancestortype:=result;
  760. // writeln(cls.name, ' has as ancestor ',result.pathname);
  761. end
  762. else
  763. begin
  764. cls.interfaces.add(result);
  765. // writeln(cls.name, ' implements ',result.pathname);
  766. end;
  767. end
  768. else
  769. if cls<>result then
  770. DoLog('Warning : ancestor class %s of class %s could not be resolved',[clname,cls.name]);
  771. end;
  772. function CreateAliasType (alname,clname : string;parentclass:TPasClassType; out cl2 :TPasClassType):TPasAliasType;
  773. // create alias clname = alname
  774. var
  775. pkg : TPasPackage;
  776. module : TPasModule;
  777. s : string;
  778. begin
  779. Result:=nil;
  780. s:=ResolvePackageModule(Alname,pkg,module,True);
  781. if not assigned(module) then
  782. exit;
  783. cl2:=TPasClassType(ResolveClassType(alname));
  784. if assigned( cl2) and not (parentclass=cl2) then
  785. begin
  786. result:=ResolveAliasType(clname);
  787. if assigned(result) then
  788. begin
  789. // writeln('found alias ',clname,' (',s,') ',result.classname);
  790. end
  791. else
  792. begin
  793. // writeln('new alias ',clname,' (',s,') ');
  794. cl2.addref;
  795. Result := TPasAliasType(CreateElement(TPasAliasType,s,module.interfacesection,vispublic,'',0));
  796. module.interfacesection.Declarations.Add(Result);
  797. TPasAliasType(Result).DestType := cl2;
  798. end
  799. end
  800. end;
  801. procedure ProcessInheritanceStrings(inhInfo:TStringList);
  802. var i,j : integer;
  803. cls : TPasClassType;
  804. cls2: TPasClassType;
  805. clname,
  806. alname : string;
  807. inhclass : TStringList;
  808. begin
  809. inhclass:=TStringList.Create;
  810. inhclass.delimiter:=',';
  811. if InhInfo.Count>0 then
  812. for i:=0 to InhInfo.Count-1 do
  813. begin
  814. cls:=TPasClassType(InhInfo.Objects[i]);
  815. inhclass.clear;
  816. inhclass.delimitedtext:=InhInfo[i];
  817. for j:= 0 to inhclass.count-1 do
  818. begin
  819. //writeln('processing',inhclass[j]);
  820. clname:=inhclass[j];
  821. splitalias(clname,alname);
  822. if alname<>'' then // the class//interface we refered to is an alias
  823. begin
  824. // writeln('Found alias pair ',clname,' = ',alname);
  825. if not assigned(CreateAliasType(alname,clname,cls,cls2)) then
  826. DoLog('Warning: creating alias %s for %s failed!',[alname,clname]);
  827. end
  828. else
  829. cls2:=ResolveAndLinkClass(clname,j=0,cls);
  830. end;
  831. end;
  832. inhclass.free;
  833. end;
  834. var
  835. s, Name: String;
  836. CurClass: TPasClassType;
  837. i: Integer;
  838. Member: TPasElement;
  839. begin
  840. inheritanceinfo :=TStringlist.Create;
  841. Try
  842. CurClass := nil;
  843. while True do
  844. begin
  845. ReadLn(f, s);
  846. if Length(s) = 0 then
  847. break;
  848. if s[1] = '#' then
  849. begin
  850. // New class
  851. i := Pos(' ', s);
  852. CurClass := CreateClass(Copy(s, 1, i - 1), copy(s,i+1,length(s)));
  853. end else
  854. begin
  855. i := Pos(' ', s);
  856. if i = 0 then
  857. Name := Copy(s, 3, Length(s))
  858. else
  859. Name := Copy(s, 3, i - 3);
  860. case s[2] of
  861. 'M':
  862. Member := TPasProcedure.Create(Name, CurClass);
  863. 'P':
  864. begin
  865. Member := TPasProperty.Create(Name, CurClass);
  866. if i > 0 then
  867. while i <= Length(s) do
  868. begin
  869. case s[i] of
  870. 'r':
  871. TPasProperty(Member).ReadAccessorName := '<dummy>';
  872. 'w':
  873. TPasProperty(Member).WriteAccessorName := '<dummy>';
  874. 's':
  875. TPasProperty(Member).StoredAccessorName := '<dummy>';
  876. end;
  877. Inc(i);
  878. end;
  879. end;
  880. 'V':
  881. Member := TPasVariable.Create(Name, CurClass);
  882. else
  883. raise Exception.Create('Invalid member type: ' + s[2]);
  884. end;
  885. CurClass.Members.Add(Member);
  886. end;
  887. end;
  888. ProcessInheritanceStrings(Inheritanceinfo);
  889. finally
  890. inheritanceinfo.Free;
  891. end;
  892. end;
  893. var
  894. s: String;
  895. buf : Array[1..ContentBufSize-1] of byte;
  896. begin
  897. if not FileExists(AFileName) then
  898. raise EInOutError.Create('File not found: ' + AFileName);
  899. Assign(f, AFilename);
  900. Reset(f);
  901. SetTextBuf(F,Buf,SizeOf(Buf));
  902. while not EOF(f) do
  903. begin
  904. ReadLn(f, s);
  905. if (Length(s) = 0) or (s[1] = '#') then
  906. continue;
  907. if s = ':link tree' then
  908. ReadLinkTree
  909. else if s = ':classes' then
  910. ReadClasses
  911. else
  912. repeat
  913. ReadLn(f, s);
  914. until EOF(f) or (Length(s) = 0);
  915. end;
  916. Close(f);
  917. end;
  918. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  919. var
  920. ContentFile: Text;
  921. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  922. var
  923. ChildNode: TLinkNode;
  924. begin
  925. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  926. ChildNode := ALinkNode.FirstChild;
  927. while Assigned(ChildNode) do
  928. begin
  929. ProcessLinkNode(ChildNode, AIdent + ' ');
  930. ChildNode := ChildNode.NextSibling;
  931. end;
  932. end;
  933. function CheckImplicitInterfaceLink(const s : String):String;
  934. begin
  935. if uppercase(s)='IUNKNOWN' then
  936. Result:='#rtl.System.IUnknown'
  937. else
  938. Result:=s;
  939. end;
  940. var
  941. LinkNode: TLinkNode;
  942. i, j, k: Integer;
  943. Module: TPasModule;
  944. Alias : TPasAliasType;
  945. ClassDecl: TPasClassType;
  946. Member: TPasElement;
  947. s: String;
  948. Buf : Array[0..ContentBufSize-1] of byte;
  949. begin
  950. Assign(ContentFile, AFilename);
  951. Rewrite(ContentFile);
  952. SetTextBuf(ContentFile,Buf,SizeOf(Buf));
  953. try
  954. WriteLn(ContentFile, '# FPDoc Content File');
  955. WriteLn(ContentFile, ':link tree');
  956. LinkNode := RootLinkNode.FirstChild;
  957. while Assigned(LinkNode) do
  958. begin
  959. if LinkNode.Name = Package.Name then
  960. begin
  961. ProcessLinkNode(LinkNode, '');
  962. end;
  963. LinkNode := LinkNode.NextSibling;
  964. end;
  965. if Assigned(Package) then
  966. begin
  967. WriteLn(ContentFile);
  968. WriteLn(ContentFile, ':classes');
  969. for i := 0 to Package.Modules.Count - 1 do
  970. begin
  971. Module := TPasModule(Package.Modules[i]);
  972. if not assigned(Module.InterfaceSection) then
  973. continue;
  974. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  975. begin
  976. ClassDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  977. Write(ContentFile, CheckImplicitInterfaceLink(ClassDecl.PathName), ' ');
  978. if Assigned(ClassDecl.AncestorType) then
  979. begin
  980. // simple aliases to class types are coded as "alias(classtype)"
  981. Write(ContentFile, CheckImplicitInterfaceLink(ClassDecl.AncestorType.PathName));
  982. if ClassDecl.AncestorType is TPasAliasType then
  983. begin
  984. alias:= TPasAliasType(ClassDecl.AncestorType);
  985. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  986. write(ContentFile,'(',alias.desttype.PathName,')');
  987. end;
  988. end
  989. else if ClassDecl.ObjKind = okClass then
  990. Write(ContentFile, '#rtl.System.TObject')
  991. else if ClassDecl.ObjKind = okInterface then
  992. Write(ContentFile, '#rtl.System.IUnknown');
  993. if ClassDecl.Interfaces.Count>0 then
  994. begin
  995. for k:=0 to ClassDecl.Interfaces.count-1 do
  996. begin
  997. write(contentfile,',',CheckImplicitInterfaceLink(TPasClassType(ClassDecl.Interfaces[k]).PathName));
  998. if TPasElement(ClassDecl.Interfaces[k]) is TPasAliasType then
  999. begin
  1000. alias:= TPasAliasType(ClassDecl.Interfaces[k]);
  1001. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  1002. write(ContentFile,'(',CheckImplicitInterfaceLink(alias.desttype.PathName),')');
  1003. end;
  1004. end;
  1005. end;
  1006. writeln(contentfile);
  1007. for k := 0 to ClassDecl.Members.Count - 1 do
  1008. begin
  1009. Member := TPasElement(ClassDecl.Members[k]);
  1010. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  1011. SetLength(s, 0);
  1012. if Member.ClassType = TPasVariable then
  1013. Write(ContentFile, 'V')
  1014. else if Member.ClassType = TPasProperty then
  1015. begin
  1016. Write(ContentFile, 'P');
  1017. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  1018. s := s + 'r';
  1019. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  1020. s := s + 'w';
  1021. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  1022. s := s + 's';
  1023. end else
  1024. Write(ContentFile, 'M'); // Member must be a method
  1025. Write(ContentFile, Member.Name);
  1026. if Length(s) > 0 then
  1027. WriteLn(ContentFile, ' ', s)
  1028. else
  1029. WriteLn(ContentFile);
  1030. end;
  1031. end;
  1032. end;
  1033. end;
  1034. finally
  1035. Close(ContentFile);
  1036. end;
  1037. end;
  1038. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  1039. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  1040. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  1041. begin
  1042. Result := AClass.Create(AName, AParent);
  1043. Result.Visibility := AVisibility;
  1044. if AClass.InheritsFrom(TPasModule) then
  1045. CurModule := TPasModule(Result);
  1046. Result.SourceFilename := ASourceFilename;
  1047. Result.SourceLinenumber := ASourceLinenumber;
  1048. end;
  1049. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  1050. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  1051. var
  1052. l: TFPList;
  1053. i: Integer;
  1054. begin
  1055. If assigned(AModule.InterfaceSection) and
  1056. Assigned(AModule.InterfaceSection.Declarations) then
  1057. begin
  1058. l:=AModule.InterfaceSection.Declarations;
  1059. for i := 0 to l.Count - 1 do
  1060. begin
  1061. Result := TPasElement(l[i]);
  1062. if CompareText(Result.Name, LocalName) = 0 then
  1063. exit;
  1064. end;
  1065. end;
  1066. Result := nil;
  1067. end;
  1068. var
  1069. i: Integer;
  1070. Module: TPasElement;
  1071. begin
  1072. Result := FindInModule(CurModule, AName);
  1073. if not Assigned(Result) and assigned (CurModule.InterfaceSection) then
  1074. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  1075. begin
  1076. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  1077. if Module.ClassType.InheritsFrom(TPasModule) then
  1078. begin
  1079. Result := FindInModule(TPasModule(Module), AName);
  1080. if Assigned(Result) then
  1081. exit;
  1082. end;
  1083. end;
  1084. end;
  1085. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  1086. function FindInPackage(APackage: TPasPackage): TPasModule;
  1087. var
  1088. i: Integer;
  1089. begin
  1090. for i := 0 to APackage.Modules.Count - 1 do
  1091. begin
  1092. Result := TPasModule(APackage.Modules[i]);
  1093. if CompareText(Result.Name, AName) = 0 then
  1094. exit;
  1095. end;
  1096. Result := nil;
  1097. end;
  1098. var
  1099. i: Integer;
  1100. AInPutLine,OSTarget,CPUTarget : String;
  1101. begin
  1102. Result := FindInPackage(Package);
  1103. if not Assigned(Result) then
  1104. for i := FPackages.Count - 1 downto 0 do
  1105. begin
  1106. if TPasPackage(FPackages[i]) = Package then
  1107. continue;
  1108. Result := FindInPackage(TPasPackage(FPackages[i]));
  1109. if Assigned(Result) then
  1110. exit;
  1111. end;
  1112. if Not Assigned(Result) and Assigned(FOnParseUnit) then
  1113. begin
  1114. FOnParseUnit(Self,AName,AInputLine,OSTarget,CPUTarget);
  1115. If (AInPutLine<>'') then
  1116. Result:=ParseUsedUnit(AName,AInputLine,OSTarget,CPUTarget);
  1117. end;
  1118. end;
  1119. function TFPDocEngine.HintsToStr(Hints: TPasMemberHints): String;
  1120. Var
  1121. H : TPasMemberHint;
  1122. begin
  1123. Result:='';
  1124. For h:=Low(TPasMemberHint) to High(TPasMemberHint) do
  1125. if h in Hints then
  1126. begin
  1127. if (Result<>'') then
  1128. Result:=Result+', ';
  1129. Result:=Result+cPasMemberHint[h]
  1130. end;
  1131. end;
  1132. function TFPDocEngine.ParseUsedUnit(AName, AInputLine, AOSTarget,
  1133. ACPUTarget: String): TPasModule;
  1134. Var
  1135. M : TPasModule;
  1136. begin
  1137. DoLog(SParsingUsedUnit,[AName,AInputLine]);
  1138. M:=CurModule;
  1139. CurModule:=Nil;
  1140. try
  1141. ParseSource(Self,AInputLine,AOSTarget,ACPUTarget,True);
  1142. Result:=CurModule;
  1143. finally
  1144. CurModule:=M;
  1145. end;
  1146. end;
  1147. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  1148. begin
  1149. RootLinkNode.CreateChildren(APathName, ALinkTo);
  1150. end;
  1151. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  1152. var
  1153. LinkNode: TLinkNode;
  1154. begin
  1155. LinkNode := RootLinkNode.FindChild(AName);
  1156. if Assigned(LinkNode) then
  1157. Result := LinkNode.Link
  1158. else
  1159. SetLength(Result, 0);
  1160. end;
  1161. function TFPDocEngine.ResolveLinkInPackages(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1162. Var
  1163. ThisPackage: TLinkNode;
  1164. begin
  1165. { Try all packages }
  1166. Result:='';
  1167. ThisPackage:=RootLinkNode.FirstChild;
  1168. while Assigned(ThisPackage) and (Result='') do
  1169. begin
  1170. Result:=ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest, Strict);
  1171. ThisPackage := ThisPackage.NextSibling;
  1172. end;
  1173. end;
  1174. function TFPDocEngine.ResolveLinkInUsedUnits(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1175. var
  1176. i: Integer;
  1177. UL: TFPList;
  1178. begin
  1179. Result:='';
  1180. UL:=AModule.InterfaceSection.UsesList;
  1181. I:=UL.Count-1;
  1182. While (Result='') and (I>=0) do
  1183. begin
  1184. Result:=ResolveLinkInPackages(AModule,TPasType(UL[i]).Name+'.'+ALinkDest, strict);
  1185. Dec(I);
  1186. end;
  1187. end;
  1188. function TFPDocEngine.ResolveLink(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1189. var
  1190. i: Integer;
  1191. begin
  1192. {
  1193. if Assigned(AModule) then
  1194. system.WriteLn('ResolveLink(', AModule.Name, ' - ', ALinkDest, ')... ')
  1195. else
  1196. system.WriteLn('ResolveLink(Nil - ', ALinkDest, ')... ');
  1197. }
  1198. if (ALinkDest='') then
  1199. Exit('');
  1200. if (ALinkDest[1] = '#') then
  1201. Result := FindAbsoluteLink(ALinkDest)
  1202. else if (AModule=Nil) then
  1203. Result:= FindAbsoluteLink(RootLinkNode.FirstChild.Name+'.'+ALinkDest)
  1204. else
  1205. begin
  1206. if Pos(AModule.Name,ALinkDest) = 1 then
  1207. Result := ResolveLink(AModule, AModule.packagename + '.' + ALinkDest, Strict)
  1208. else
  1209. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest, Strict);
  1210. if (Result='') then
  1211. begin
  1212. Result:=ResolveLinkInPackages(AModule,ALinkDest,Strict);
  1213. if (Result='') then
  1214. Result:=ResolveLinkInUsedUnits(Amodule,AlinkDest,Strict);
  1215. end;
  1216. end;
  1217. // Match on parent : class/enumerated/record/module
  1218. if (Result='') and not strict then
  1219. for i := Length(ALinkDest) downto 1 do
  1220. if ALinkDest[i] = '.' then
  1221. begin
  1222. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1), Strict);
  1223. exit;
  1224. end;
  1225. end;
  1226. procedure ReadXMLFileALT(OUT ADoc:TXMLDocument;const AFileName:ansistring);
  1227. var
  1228. Parser: TDOMParser;
  1229. Src: TXMLInputSource;
  1230. FileStream: TStream;
  1231. begin
  1232. ADoc := nil;
  1233. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  1234. try
  1235. Parser := TDOMParser.Create; // create a parser object
  1236. try
  1237. Src := TXMLInputSource.Create(FileStream); // and the input source
  1238. src.SystemId:=FileNameToUri(AFileName);
  1239. try
  1240. Parser.Options.PreserveWhitespace := True;
  1241. Parser.Parse(Src, ADoc);
  1242. finally
  1243. Src.Free; // cleanup
  1244. end;
  1245. finally
  1246. Parser.Free;
  1247. end;
  1248. finally
  1249. FileStream.Free;
  1250. end;
  1251. end;
  1252. procedure TFPDocEngine.AddDocFile(const AFilename: String;DontTrim:boolean=false);
  1253. Var
  1254. PN : String;
  1255. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  1256. var
  1257. Subnode: TDOMNode;
  1258. begin
  1259. if OwnerDocNode = RootDocNode then
  1260. Result := OwnerDocNode.CreateChildren('#' + Element['name'])
  1261. else
  1262. Result := OwnerDocNode.CreateChildren(Element['name']);
  1263. Result.FNode := Element;
  1264. Result.FLink := Element['link'];
  1265. if (Element['alwaysvisible'] = '1') and (Element.NodeName='element') then
  1266. FAlwaysVisible.Add(LowerCase(PN+'.'+TDocNode(OwnerDocNode).Name+'.'+Element['name']));
  1267. Result.FIsSkipped := Element['skip'] = '1';
  1268. Subnode := Element.FirstChild;
  1269. while Assigned(Subnode) do
  1270. begin
  1271. if Subnode.NodeType = ELEMENT_NODE then
  1272. begin
  1273. if Subnode.NodeName = 'short' then
  1274. Result.FShortDescr := TDOMElement(Subnode)
  1275. else if Subnode.NodeName = 'descr' then
  1276. Result.FDescr := TDOMElement(Subnode)
  1277. else if Subnode.NodeName = 'version' then
  1278. begin
  1279. Result.FVersion := TDOMElement(Subnode)
  1280. end
  1281. else if Subnode.NodeName = 'errors' then
  1282. Result.FErrorsDoc := TDOMElement(Subnode)
  1283. else if Subnode.NodeName = 'seealso' then
  1284. Result.FSeeAlso := TDOMElement(Subnode)
  1285. else if (Subnode.NodeName = 'example') and
  1286. not Assigned(Result.FirstExample) then
  1287. Result.FFirstExample := TDOMElement(Subnode)
  1288. else if (Subnode.NodeName = 'notes') then
  1289. Result.FNotes := TDOMElement(Subnode);
  1290. end;
  1291. Subnode := Subnode.NextSibling;
  1292. end;
  1293. end;
  1294. Procedure ReadTopics(TopicNode : TDocNode);
  1295. Var
  1296. SubNode : TDOMNode;
  1297. begin
  1298. SubNode:=TopicNode.FNode.FirstChilD;
  1299. While Assigned(SubNode) do
  1300. begin
  1301. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  1302. With ReadNode(TopicNode,TDomElement(SubNode)) do
  1303. // We could allow recursion here, but we won't, because it doesn't work on paper.
  1304. FTopicNode:=True;
  1305. SubNode:=Subnode.NextSibling;
  1306. end;
  1307. end;
  1308. var
  1309. Node, Subnode, Subsubnode: TDOMNode;
  1310. Doc: TXMLDocument;
  1311. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  1312. begin
  1313. if DontTrim then
  1314. ReadXMLFileALT(Doc, AFilename)
  1315. else
  1316. ReadXMLFile(Doc, AFilename);
  1317. DescrDocs.Add(Doc);
  1318. DescrDocNames.Add(AFilename);
  1319. Node := Doc.DocumentElement.FirstChild;
  1320. while Assigned(Node) do
  1321. begin
  1322. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  1323. begin
  1324. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  1325. PackageDocNode.IncRefCount;
  1326. PN:=PackageDocNode.Name;
  1327. // Scan all 'module' elements within this package element
  1328. Subnode := Node.FirstChild;
  1329. while Assigned(Subnode) do
  1330. begin
  1331. if (Subnode.NodeType = ELEMENT_NODE) then
  1332. begin
  1333. If (Subnode.NodeName = 'module') then
  1334. begin
  1335. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  1336. // Scan all 'element' elements within this module element
  1337. Subsubnode := Subnode.FirstChild;
  1338. while Assigned(Subsubnode) do
  1339. begin
  1340. if (Subsubnode.NodeType = ELEMENT_NODE) then
  1341. begin
  1342. if (Subsubnode.NodeName = 'element') then
  1343. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  1344. else if (SubSubNode.NodeName='topic') then
  1345. begin
  1346. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  1347. TopicNode.FTopicNode:=True;
  1348. ReadTopics(TopicNode);
  1349. end;
  1350. end;
  1351. Subsubnode := Subsubnode.NextSibling;
  1352. end;
  1353. end
  1354. else if (SubNode.NodeName='topic') then
  1355. begin
  1356. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  1357. TopicNode.FTopicNode:=True;
  1358. ReadTopics(TopicNode);
  1359. end;
  1360. end;
  1361. Subnode := Subnode.NextSibling;
  1362. end;
  1363. end;
  1364. Node := Node.NextSibling;
  1365. end;
  1366. end;
  1367. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  1368. begin
  1369. Result:=Nil;
  1370. If Assigned(AElement) then
  1371. begin
  1372. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  1373. Result := FindDocNode(AElement.GetModule, AElement.Name)
  1374. else
  1375. begin
  1376. Result := RootDocNode.FindChild(AElement.PathName);
  1377. if (Result=Nil) and (AElement is TPasoperator) then
  1378. Result:=RootDocNode.FindChild(TPasOperator(AElement).OldName(True));
  1379. end;
  1380. if (Result=Nil) and
  1381. WarnNoNode and
  1382. (Length(AElement.PathName)>0) and
  1383. (AElement.PathName[1]='#') then
  1384. DoLog(Format('No documentation node found for identifier : %s',[AElement.PathName]));
  1385. end;
  1386. end;
  1387. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  1388. const AName: String): TDocNode;
  1389. var
  1390. CurPackage: TDocNode;
  1391. UnitList: TFPList;
  1392. i: Integer;
  1393. begin
  1394. if Length(AName) = 0 then
  1395. Result := nil
  1396. else
  1397. begin
  1398. if AName[1] = '#' then
  1399. Result := RootDocNode.FindChild(AName)
  1400. else
  1401. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  1402. if (not Assigned(Result)) and Assigned(ARefModule) then
  1403. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  1404. if (not Assigned(Result)) and (AName[1] <> '#') then
  1405. begin
  1406. CurPackage := RootDocNode.FirstChild;
  1407. while Assigned(CurPackage) do
  1408. begin
  1409. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  1410. if Assigned(Result) then
  1411. break;
  1412. CurPackage := CurPackage.NextSibling;
  1413. end;
  1414. if not Assigned(Result) and assigned(CurModule.InterfaceSection) then
  1415. begin
  1416. { Okay, then we have to try all imported units of the current module }
  1417. UnitList := CurModule.InterfaceSection.UsesList;
  1418. for i := UnitList.Count - 1 downto 0 do
  1419. begin
  1420. { Try all packages }
  1421. CurPackage := RootDocNode.FirstChild;
  1422. while Assigned(CurPackage) do
  1423. begin
  1424. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  1425. TPasType(UnitList[i]).Name + '.' + AName);
  1426. if Assigned(Result) then
  1427. break;
  1428. CurPackage := CurPackage.NextSibling;
  1429. end;
  1430. end;
  1431. end;
  1432. end;
  1433. end;
  1434. end;
  1435. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  1436. var
  1437. DocNode,N: TDocNode;
  1438. begin
  1439. DocNode := FindDocNode(AElement);
  1440. if Assigned(DocNode) then
  1441. begin
  1442. N:=FindLinkedNode(DocNode);
  1443. If (N<>Nil) then
  1444. DocNode:=N;
  1445. Result := DocNode.ShortDescr;
  1446. end
  1447. else
  1448. Result := nil;
  1449. end;
  1450. function TFPDocEngine.FindLinkedNode(ANode : TDocNode) : TDocNode;
  1451. begin
  1452. If (ANode.Link='') then
  1453. Result:=Nil
  1454. else
  1455. Result:=FindDocNode(CurModule,ANode.Link);
  1456. end;
  1457. function TFPDocEngine.ShowElement(El: TPasElement): Boolean;
  1458. begin
  1459. Case El.Visibility of
  1460. visStrictPrivate,
  1461. visPrivate :
  1462. Result:=Not HidePrivate;
  1463. visStrictProtected,
  1464. visProtected :
  1465. begin
  1466. Result:=Not HideProtected;
  1467. if not Result then
  1468. Result:=FAlwaysVisible.IndexOf(LowerCase(El.PathName))<>-1;
  1469. end
  1470. Else
  1471. Result:=True
  1472. end;
  1473. end;
  1474. procedure TFPDocEngine.StartDocumenting;
  1475. begin
  1476. FAlwaysVisible.Sorted:=True;
  1477. end;
  1478. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  1479. const AName: String): TDOMElement;
  1480. var
  1481. N,DocNode: TDocNode;
  1482. begin
  1483. DocNode := FindDocNode(ARefModule, AName);
  1484. if Assigned(DocNode) then
  1485. begin
  1486. N:=FindLinkedNode(DocNode);
  1487. If (N<>Nil) then
  1488. DocNode:=N;
  1489. Result := DocNode.ShortDescr;
  1490. end
  1491. else
  1492. Result := nil;
  1493. end;
  1494. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1495. var
  1496. i: Integer;
  1497. fn : String;
  1498. begin
  1499. Result:='';
  1500. for i := 0 to DescrDocs.Count - 1 do
  1501. begin
  1502. Fn:=ExElement['file'];
  1503. if (FN<>'') and (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then
  1504. begin
  1505. Result := ExtractFilePath(DescrDocNames[i]) + FN;
  1506. if (ExtractFileExt(Result)='') then
  1507. Result:=Result+'.pp';
  1508. end;
  1509. end;
  1510. end;
  1511. { Global helpers }
  1512. procedure TranslateDocStrings(const Lang: String);
  1513. Const
  1514. {$ifdef unix}
  1515. DefDir = '/usr/local/share/locale';
  1516. {$else}
  1517. DefDir = 'intl';
  1518. {$endif}
  1519. var
  1520. mo: TMOFile;
  1521. dir : string;
  1522. begin
  1523. dir:=modir;
  1524. If Dir='' then
  1525. Dir:=DefDir;
  1526. Dir:=IncludeTrailingPathDelimiter(Dir);
  1527. {$IFDEF Unix}
  1528. mo := TMOFile.Create(Format(Dir+'%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1529. {$ELSE}
  1530. mo := TMOFile.Create(Format(Dir+'dglobals.%s.mo', [Lang]));
  1531. {$ENDIF}
  1532. try
  1533. TranslateResourceStrings(mo);
  1534. finally
  1535. mo.Free;
  1536. end;
  1537. end;
  1538. Function IsLinkNode(Node : TDomNode) : Boolean;
  1539. begin
  1540. Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');
  1541. end;
  1542. Function IsExampleNode(Example : TDomNode) : Boolean;
  1543. begin
  1544. Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')
  1545. end;
  1546. function IsLinkAbsolute(ALink: String): boolean;
  1547. var
  1548. i: integer;
  1549. begin
  1550. Result := false;
  1551. for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do
  1552. if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin
  1553. Result := true;
  1554. break;
  1555. end;
  1556. end;
  1557. initialization
  1558. LEOL:=Length(LineEnding);
  1559. end.