dglobals.pp 48 KB

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