dglobals.pp 51 KB

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