dglobals.pp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. {
  2. $Id$
  3. FPDoc - Free Pascal Documentation Tool
  4. Copyright (C) 2000 - 2002 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. * Global declarations
  7. * Link list management
  8. * Document node tree
  9. * Main engine
  10. See the file COPYING, included in this distribution,
  11. for details about the copyright.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. }
  16. unit dGlobals;
  17. {$MODE objfpc}
  18. {$H+}
  19. interface
  20. uses Classes, DOM, PasTree, PParser;
  21. resourcestring
  22. // Output strings
  23. SDocPackageTitle = 'Reference for package ''%s''';
  24. SDocPrograms = 'Programs';
  25. SDocUnits = 'Units';
  26. SDocUnitTitle = 'Reference for unit ''%s''';
  27. SDocInterfaceSection = 'Interface section';
  28. SDocImplementationSection = 'Implementation section';
  29. SDocUsedUnits = 'Used units';
  30. SDocUsedUnitsByUnitXY = 'Used units by unit ''%s''';
  31. SDocConstsTypesVars = 'Constants, types and variables';
  32. SDocResStrings = 'Resource strings';
  33. SDocTypes = 'Types';
  34. SDocConstants = 'Constants';
  35. SDocClasses = 'Classes';
  36. SDocProceduresAndFunctions = 'Procedures and functions';
  37. SDocVariables = 'Variables';
  38. SDocUnitOverview = 'Overview of unit ''%s''';
  39. SDocOverview = 'Overview';
  40. SDocSearch = 'Search';
  41. SDocDeclaration = 'Declaration';
  42. SDocDescription = 'Description';
  43. SDocErrors = 'Errors';
  44. SDocSeeAlso = 'See also';
  45. SDocExample = 'Example';
  46. SDocArguments = 'Arguments';
  47. SDocFunctionResult = 'Function result';
  48. SDocRemark = 'Remark: ';
  49. SDocMethodOverview = 'Method overview';
  50. SDocPropertyOverview = 'Property overview';
  51. SDocPage = 'Page';
  52. SDocMethod = 'Method';
  53. SDocProperty = 'Property';
  54. SDocAccess = 'Access';
  55. SDocInheritance = 'Inheritance';
  56. SDocProperties = 'Properties';
  57. SDocMethods = 'Methods';
  58. SDocEvents = 'Events';
  59. SDocByName = 'by Name';
  60. SDocValue = 'Value';
  61. SDocExplanation = 'Explanation';
  62. SDocValuesForEnum = 'Enumeration values for type %s';
  63. SDocSourcePosition = 'Source position: %s line %d';
  64. // Topics
  65. SDocRelatedTopics = 'Related topics';
  66. SDocUp = 'Up';
  67. SDocNext = 'Next';
  68. SDocPrevious = 'Previous';
  69. type
  70. // Assumes a list of TObject instances and frees them on destruction
  71. TObjectList = class(TList)
  72. public
  73. destructor Destroy; override;
  74. end;
  75. { Link entry tree
  76. TFPDocEngine stores the root of the entry tree in its property
  77. "RootLinkNode". The root has one child node for each package, for which
  78. documentation links are available. The children of a package node
  79. are module nodes; and the children of a module node are the top-level
  80. declarations of this module; the next level in the tree stores e.g. record
  81. members, and so on...
  82. }
  83. TLinkNode = class
  84. private
  85. FFirstChild, FNextSibling: TLinkNode;
  86. FName: String;
  87. FLink: String;
  88. public
  89. constructor Create(const AName, ALink: String);
  90. destructor Destroy; override;
  91. function FindChild(const APathName: String): TLinkNode;
  92. function CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  93. // Properties for tree structure
  94. property FirstChild: TLinkNode read FFirstChild;
  95. property NextSibling: TLinkNode read FNextSibling;
  96. // Link properties
  97. property Name: String read FName;
  98. property Link: String read FLink;
  99. end;
  100. { Documentation entry tree
  101. TFPDocEngine stores the root of the entry tree in its property
  102. "RootDocNode". The root has one child node for each package, for which
  103. documentation is being provided by the user. The children of a package node
  104. are module nodes; and the children of a module node are the top-level
  105. declarations of this module; the next level in the tree stores e.g. record
  106. members, and so on...
  107. }
  108. TDocNode = class
  109. private
  110. FFirstChild, FNextSibling: TDocNode;
  111. FName: String;
  112. FNode: TDOMElement;
  113. FIsSkipped: Boolean;
  114. FShortDescr: TDOMElement;
  115. FDescr: TDOMElement;
  116. FErrorsDoc: TDOMElement;
  117. FSeeAlso: TDOMElement;
  118. FFirstExample: TDOMElement;
  119. FLink: String;
  120. FTopicNode : Boolean;
  121. public
  122. constructor Create(const AName: String; ANode: TDOMElement);
  123. destructor Destroy; override;
  124. function FindChild(const APathName: String): TDocNode;
  125. function CreateChildren(const APathName: String): TDocNode;
  126. // Properties for tree structure
  127. property FirstChild: TDocNode read FFirstChild;
  128. property NextSibling: TDocNode read FNextSibling;
  129. // Basic properties
  130. property Name: String read FName;
  131. property Node: TDOMElement read FNode;
  132. // Data fetched from the XML document
  133. property IsSkipped: Boolean read FIsSkipped;
  134. property ShortDescr: TDOMElement read FShortDescr;
  135. property Descr: TDOMElement read FDescr;
  136. property ErrorsDoc: TDOMElement read FErrorsDoc;
  137. property SeeAlso: TDOMElement read FSeeAlso;
  138. property FirstExample: TDOMElement read FFirstExample;
  139. property Link: String read FLink;
  140. Property TopicNode : Boolean Read FTopicNode;
  141. end;
  142. // The main FPDoc engine
  143. TFPDocEngine = class(TPasTreeContainer)
  144. protected
  145. DescrDocs: TObjectList; // List of XML documents
  146. DescrDocNames: TStringList; // Names of the XML documents
  147. FRootLinkNode: TLinkNode;
  148. FRootDocNode: TDocNode;
  149. FPackages: TList; // List of TFPPackage objects
  150. CurModule: TPasModule;
  151. CurPackageDocNode: TDocNode;
  152. public
  153. constructor Create;
  154. destructor Destroy; override;
  155. procedure SetPackageName(const APackageName: String);
  156. procedure ReadContentFile(const AFilename, ALinkPrefix: String);
  157. procedure WriteContentFile(const AFilename: String);
  158. function CreateElement(AClass: TPTreeElement; const AName: String;
  159. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  160. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  161. override;
  162. function FindElement(const AName: String): TPasElement; override;
  163. function FindModule(const AName: String): TPasModule; override;
  164. // Link tree support
  165. procedure AddLink(const APathName, ALinkTo: String);
  166. function FindAbsoluteLink(const AName: String): String;
  167. function ResolveLink(AModule: TPasModule; const ALinkDest: String): String;
  168. // Documentation file support
  169. procedure AddDocFile(const AFilename: String);
  170. // Documentation retrieval
  171. function FindDocNode(AElement: TPasElement): TDocNode;
  172. function FindDocNode(ARefModule: TPasModule; const AName: String): TDocNode;
  173. function FindShortDescr(AElement: TPasElement): TDOMElement;
  174. function FindShortDescr(ARefModule: TPasModule;
  175. const AName: String): TDOMElement;
  176. function GetExampleFilename(const ExElement: TDOMElement): String;
  177. property RootLinkNode: TLinkNode read FRootLinkNode;
  178. property RootDocNode: TDocNode read FRootDocNode;
  179. property Package: TPasPackage read FPackage;
  180. Output: String;
  181. HasContentFile: Boolean;
  182. HidePrivate: Boolean; // Hide private class members in output?
  183. HideProtected: Boolean; // Hide protected class members in output?
  184. end;
  185. procedure TranslateDocStrings(const Lang: String);
  186. implementation
  187. uses SysUtils, Gettext, XMLRead;
  188. { TObjectList }
  189. destructor TObjectList.Destroy;
  190. var
  191. i: Integer;
  192. begin
  193. for i := 0 to Count - 1 do
  194. TObject(Items[i]).Free;
  195. inherited Destroy;
  196. end;
  197. { TLinkNode }
  198. constructor TLinkNode.Create(const AName, ALink: String);
  199. begin
  200. inherited Create;
  201. FName := AName;
  202. FLink := ALink;
  203. end;
  204. destructor TLinkNode.Destroy;
  205. begin
  206. if Assigned(FirstChild) then
  207. FirstChild.Free;
  208. if Assigned(NextSibling) then
  209. NextSibling.Free;
  210. inherited Destroy;
  211. end;
  212. function TLinkNode.FindChild(const APathName: String): TLinkNode;
  213. var
  214. DotPos: Integer;
  215. ChildName: String;
  216. Child: TLinkNode;
  217. begin
  218. if Length(APathName) = 0 then
  219. Result := Self
  220. else
  221. begin
  222. DotPos := Pos('.', APathName);
  223. if DotPos = 0 then
  224. ChildName := APathName
  225. else
  226. ChildName := Copy(APathName, 1, DotPos - 1);
  227. Child := FirstChild;
  228. while Assigned(Child) do
  229. begin
  230. if CompareText(Child.Name, ChildName) = 0 then
  231. begin
  232. if DotPos = 0 then
  233. Result := Child
  234. else
  235. Result := Child.FindChild(
  236. Copy(APathName, DotPos + 1, Length(APathName)));
  237. exit;
  238. end;
  239. Child := Child.NextSibling;
  240. end;
  241. Result := nil;
  242. end;
  243. end;
  244. function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  245. var
  246. DotPos: Integer;
  247. ChildName: String;
  248. Child, LastChild: TLinkNode;
  249. begin
  250. if Length(APathName) = 0 then
  251. Result := Self
  252. else
  253. begin
  254. DotPos := Pos('.', APathName);
  255. if DotPos = 0 then
  256. ChildName := APathName
  257. else
  258. ChildName := Copy(APathName, 1, DotPos - 1);
  259. Child := FirstChild;
  260. LastChild := nil;
  261. while Assigned(Child) do
  262. begin
  263. if CompareText(Child.Name, ChildName) = 0 then
  264. begin
  265. if DotPos = 0 then
  266. Result := Child
  267. else
  268. Result := Child.CreateChildren(
  269. Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);
  270. exit;
  271. end;
  272. LastChild := Child;
  273. Child := Child.NextSibling;
  274. end;
  275. { No child found, let's create one if we are at the end of the path }
  276. if DotPos > 0 then
  277. // !!!: better throw an exception
  278. WriteLn('Link path does not exist: ', APathName);
  279. Result := TLinkNode.Create(ChildName, ALinkTo);
  280. if Assigned(LastChild) then
  281. LastChild.FNextSibling := Result
  282. else
  283. FFirstChild := Result;
  284. end;
  285. end;
  286. { TDocNode }
  287. constructor TDocNode.Create(const AName: String; ANode: TDOMElement);
  288. begin
  289. inherited Create;
  290. FName := AName;
  291. FNode := ANode;
  292. end;
  293. destructor TDocNode.Destroy;
  294. begin
  295. if Assigned(FirstChild) then
  296. FirstChild.Free;
  297. if Assigned(NextSibling) then
  298. NextSibling.Free;
  299. inherited Destroy;
  300. end;
  301. function TDocNode.FindChild(const APathName: String): TDocNode;
  302. var
  303. DotPos: Integer;
  304. ChildName: String;
  305. Child: TDocNode;
  306. begin
  307. if Length(APathName) = 0 then
  308. Result := Self
  309. else
  310. begin
  311. DotPos := Pos('.', APathName);
  312. if DotPos = 0 then
  313. ChildName := APathName
  314. else
  315. ChildName := Copy(APathName, 1, DotPos - 1);
  316. Child := FirstChild;
  317. while Assigned(Child) do
  318. begin
  319. if CompareText(Child.Name, ChildName) = 0 then
  320. begin
  321. if DotPos = 0 then
  322. Result := Child
  323. else
  324. Result := Child.FindChild(
  325. Copy(APathName, DotPos + 1, Length(APathName)));
  326. exit;
  327. end;
  328. Child := Child.NextSibling;
  329. end;
  330. Result := nil;
  331. end;
  332. end;
  333. function TDocNode.CreateChildren(const APathName: String): TDocNode;
  334. var
  335. DotPos: Integer;
  336. ChildName: String;
  337. Child: TDocNode;
  338. begin
  339. if Length(APathName) = 0 then
  340. Result := Self
  341. else
  342. begin
  343. DotPos := Pos('.', APathName);
  344. if DotPos = 0 then
  345. ChildName := APathName
  346. else
  347. ChildName := Copy(APathName, 1, DotPos - 1);
  348. Child := FirstChild;
  349. while Assigned(Child) do
  350. begin
  351. if CompareText(Child.Name, ChildName) = 0 then
  352. begin
  353. if DotPos = 0 then
  354. Result := Child
  355. else
  356. Result := Child.CreateChildren(
  357. Copy(APathName, DotPos + 1, Length(APathName)));
  358. exit;
  359. end;
  360. Child := Child.NextSibling;
  361. end;
  362. // No child found, let's create one
  363. Result := TDocNode.Create(ChildName, nil);
  364. if Assigned(FirstChild) then
  365. begin
  366. Result.FNextSibling := FirstChild;
  367. FFirstChild := Result;
  368. end else
  369. FFirstChild := Result;
  370. if DotPos > 0 then
  371. Result := Result.CreateChildren(
  372. Copy(APathName, DotPos + 1, Length(APathName)));
  373. end;
  374. end;
  375. { TFPDocEngine }
  376. constructor TFPDocEngine.Create;
  377. begin
  378. inherited Create;
  379. DescrDocs := TObjectList.Create;
  380. DescrDocNames := TStringList.Create;
  381. FRootLinkNode := TLinkNode.Create('', '');
  382. FRootDocNode := TDocNode.Create('', nil);
  383. HidePrivate := True;
  384. FPackages := TList.Create;
  385. end;
  386. destructor TFPDocEngine.Destroy;
  387. var
  388. i: Integer;
  389. begin
  390. for i := 0 to FPackages.Count - 1 do
  391. TPasPackage(FPackages[i]).Release;
  392. FPackages.Free;
  393. FRootDocNode.Free;
  394. FRootLinkNode.Free;
  395. DescrDocNames.Free;
  396. DescrDocs.Free;
  397. inherited Destroy;
  398. end;
  399. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  400. begin
  401. ASSERT(not Assigned(Package));
  402. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  403. '#' + APackageName, nil, '', 0));
  404. FPackages.Add(FPackage);
  405. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  406. end;
  407. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  408. var
  409. f: Text;
  410. procedure ReadLinkTree;
  411. var
  412. s: String;
  413. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  414. CurParent, PrevSibling, NewNode: TLinkNode;
  415. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  416. begin
  417. PrevSpaces := 0;
  418. CurParent := RootLinkNode;
  419. PrevSibling := nil;
  420. StackIndex := 0;
  421. while True do
  422. begin
  423. ReadLn(f, s);
  424. if Length(s) = 0 then
  425. break;
  426. ThisSpaces := 0;
  427. while s[ThisSpaces + 1] = ' ' do
  428. Inc(ThisSpaces);
  429. if ThisSpaces <> PrevSpaces then
  430. begin
  431. if ThisSpaces > PrevSpaces then
  432. begin
  433. { Dive down one level }
  434. ParentStack[StackIndex] := CurParent;
  435. SiblingStack[StackIndex] := PrevSibling;
  436. Inc(StackIndex);
  437. CurParent := PrevSibling;
  438. PrevSibling := nil;
  439. end else
  440. while PrevSpaces > ThisSpaces do
  441. begin
  442. Dec(StackIndex);
  443. CurParent := ParentStack[StackIndex];
  444. PrevSibling := SiblingStack[StackIndex];
  445. Dec(PrevSpaces);
  446. end;
  447. PrevSpaces := ThisSpaces;
  448. end;
  449. i := ThisSpaces + 1;
  450. while s[i] <> ' ' do
  451. Inc(i);
  452. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  453. ALinkPrefix + Copy(s, i + 1, Length(s)));
  454. if Assigned(PrevSibling) then
  455. PrevSibling.FNextSibling := NewNode
  456. else
  457. CurParent.FFirstChild := NewNode;
  458. PrevSibling := NewNode;
  459. end;
  460. end;
  461. procedure ReadClasses;
  462. function CreateClass(const AName: String): TPasClassType;
  463. var
  464. DotPos, DotPos2, i: Integer;
  465. s: String;
  466. Package: TPasPackage;
  467. Module: TPasModule;
  468. begin
  469. // Find or create package
  470. DotPos := Pos('.', AName);
  471. s := Copy(AName, 1, DotPos - 1);
  472. Package := nil;
  473. for i := 0 to FPackages.Count - 1 do
  474. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  475. begin
  476. Package := TPasPackage(FPackages[i]);
  477. break;
  478. end;
  479. if not Assigned(Package) then
  480. begin
  481. Package := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  482. '', 0));
  483. FPackages.Add(Package);
  484. end;
  485. // Find or create module
  486. DotPos2 := DotPos;
  487. repeat
  488. Inc(DotPos2);
  489. until AName[DotPos2] = '.';
  490. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  491. Module := nil;
  492. for i := 0 to Package.Modules.Count - 1 do
  493. if CompareText(TPasModule(Package.Modules[i]).Name, s) = 0 then
  494. begin
  495. Module := TPasModule(Package.Modules[i]);
  496. break;
  497. end;
  498. if not Assigned(Module) then
  499. begin
  500. Module := TPasModule.Create(s, Package);
  501. Module.InterfaceSection := TPasSection.Create('', Module);
  502. Package.Modules.Add(Module);
  503. end;
  504. // Create node for class
  505. Result := TPasClassType.Create(Copy(AName, DotPos2 + 1, Length(AName)),
  506. Module.InterfaceSection);
  507. Result.ObjKind := okClass;
  508. Module.InterfaceSection.Declarations.Add(Result);
  509. Module.InterfaceSection.Classes.Add(Result);
  510. end;
  511. var
  512. s, Name: String;
  513. CurClass: TPasClassType;
  514. i: Integer;
  515. Member: TPasElement;
  516. begin
  517. CurClass := nil;
  518. while True do
  519. begin
  520. ReadLn(f, s);
  521. if Length(s) = 0 then
  522. break;
  523. if s[1] = '#' then
  524. begin
  525. // New class
  526. i := Pos(' ', s);
  527. CurClass := CreateClass(Copy(s, 1, i - 1));
  528. end else
  529. begin
  530. i := Pos(' ', s);
  531. if i = 0 then
  532. Name := Copy(s, 3, Length(s))
  533. else
  534. Name := Copy(s, 3, i - 3);
  535. case s[2] of
  536. 'M':
  537. Member := TPasProcedure.Create(Name, CurClass);
  538. 'P':
  539. begin
  540. Member := TPasProperty.Create(Name, CurClass);
  541. if i > 0 then
  542. while i <= Length(s) do
  543. begin
  544. case s[i] of
  545. 'r':
  546. TPasProperty(Member).ReadAccessorName := '<dummy>';
  547. 'w':
  548. TPasProperty(Member).WriteAccessorName := '<dummy>';
  549. 's':
  550. TPasProperty(Member).StoredAccessorName := '<dummy>';
  551. end;
  552. Inc(i);
  553. end;
  554. end;
  555. 'V':
  556. Member := TPasVariable.Create(Name, CurClass);
  557. else
  558. raise Exception.Create('Invalid member type: ' + s[2]);
  559. end;
  560. CurClass.Members.Add(Member);
  561. end;
  562. end;
  563. end;
  564. var
  565. s: String;
  566. begin
  567. Assign(f, AFilename);
  568. Reset(f);
  569. while not EOF(f) do
  570. begin
  571. ReadLn(f, s);
  572. if (Length(s) = 0) or (s[1] = '#') then
  573. continue;
  574. if s = ':link tree' then
  575. ReadLinkTree
  576. else if s = ':classes' then
  577. ReadClasses
  578. else
  579. repeat
  580. ReadLn(f, s);
  581. until EOF(f) or (Length(s) = 0);
  582. end;
  583. Close(f);
  584. end;
  585. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  586. var
  587. ContentFile: Text;
  588. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  589. var
  590. ChildNode: TLinkNode;
  591. begin
  592. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  593. ChildNode := ALinkNode.FirstChild;
  594. while Assigned(ChildNode) do
  595. begin
  596. ProcessLinkNode(ChildNode, AIdent + ' ');
  597. ChildNode := ChildNode.NextSibling;
  598. end;
  599. end;
  600. var
  601. LinkNode: TLinkNode;
  602. i, j, k: Integer;
  603. Module: TPasModule;
  604. ClassDecl: TPasClassType;
  605. Member: TPasElement;
  606. s: String;
  607. begin
  608. Assign(ContentFile, AFilename);
  609. Rewrite(ContentFile);
  610. try
  611. WriteLn(ContentFile, '# FPDoc Content File');
  612. WriteLn(ContentFile, ':link tree');
  613. LinkNode := RootLinkNode.FirstChild;
  614. while Assigned(LinkNode) do
  615. begin
  616. if LinkNode.Name = Package.Name then
  617. begin
  618. ProcessLinkNode(LinkNode, '');
  619. end;
  620. LinkNode := LinkNode.NextSibling;
  621. end;
  622. if Assigned(Package) then
  623. begin
  624. WriteLn(ContentFile);
  625. WriteLn(ContentFile, ':classes');
  626. for i := 0 to Package.Modules.Count - 1 do
  627. begin
  628. Module := TPasModule(Package.Modules[i]);
  629. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  630. begin
  631. ClassDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  632. Write(ContentFile, ClassDecl.PathName, ' ');
  633. if Assigned(ClassDecl.AncestorType) then
  634. WriteLn(ContentFile, ClassDecl.AncestorType.PathName)
  635. else if ClassDecl.ObjKind = okClass then
  636. WriteLn(ContentFile, '.TObject');
  637. for k := 0 to ClassDecl.Members.Count - 1 do
  638. begin
  639. Member := TPasElement(ClassDecl.Members[k]);
  640. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  641. SetLength(s, 0);
  642. if Member.ClassType = TPasVariable then
  643. Write(ContentFile, 'V')
  644. else if Member.ClassType = TPasProperty then
  645. begin
  646. Write(ContentFile, 'P');
  647. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  648. s := s + 'r';
  649. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  650. s := s + 'w';
  651. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  652. s := s + 's';
  653. end else
  654. Write(ContentFile, 'M'); // Member must be a method
  655. Write(ContentFile, Member.Name);
  656. if Length(s) > 0 then
  657. WriteLn(ContentFile, ' ', s)
  658. else
  659. WriteLn(ContentFile);
  660. end;
  661. end;
  662. end;
  663. end;
  664. finally
  665. Close(ContentFile);
  666. end;
  667. end;
  668. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  669. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  670. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  671. begin
  672. Result := AClass.Create(AName, AParent);
  673. Result.Visibility := AVisibility;
  674. if AClass.InheritsFrom(TPasModule) then
  675. CurModule := TPasModule(Result);
  676. Result.SourceFilename := ASourceFilename;
  677. Result.SourceLinenumber := ASourceLinenumber;
  678. end;
  679. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  680. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  681. var
  682. l: TList;
  683. i: Integer;
  684. begin
  685. l := AModule.InterfaceSection.Declarations;
  686. for i := 0 to l.Count - 1 do
  687. begin
  688. Result := TPasElement(l[i]);
  689. if CompareText(Result.Name, LocalName) = 0 then
  690. exit;
  691. end;
  692. Result := nil;
  693. end;
  694. var
  695. i: Integer;
  696. //ModuleName, LocalName: String;
  697. Module: TPasElement;
  698. begin
  699. {!!!: Don't know if we ever will have to use the following:
  700. i := Pos('.', AName);
  701. if i <> 0 then
  702. begin
  703. WriteLn('Dot found in name: ', AName);
  704. Result := nil;
  705. end else
  706. begin}
  707. Result := FindInModule(CurModule, AName);
  708. if not Assigned(Result) then
  709. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  710. begin
  711. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  712. if Module.ClassType = TPasModule then
  713. begin
  714. Result := FindInModule(TPasModule(Module), AName);
  715. if Assigned(Result) then
  716. exit;
  717. end;
  718. end;
  719. {end;}
  720. end;
  721. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  722. function FindInPackage(APackage: TPasPackage): TPasModule;
  723. var
  724. i: Integer;
  725. begin
  726. for i := 0 to APackage.Modules.Count - 1 do
  727. begin
  728. Result := TPasModule(APackage.Modules[i]);
  729. if CompareText(Result.Name, AName) = 0 then
  730. exit;
  731. end;
  732. Result := nil;
  733. end;
  734. var
  735. i: Integer;
  736. begin
  737. Result := FindInPackage(Package);
  738. if not Assigned(Result) then
  739. for i := FPackages.Count - 1 downto 0 do
  740. begin
  741. if TPasPackage(FPackages[i]) = Package then
  742. continue;
  743. Result := FindInPackage(TPasPackage(FPackages[i]));
  744. if Assigned(Result) then
  745. exit;
  746. end;
  747. end;
  748. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  749. begin
  750. RootLinkNode.CreateChildren(APathName, ALinkTo);
  751. end;
  752. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  753. var
  754. LinkNode: TLinkNode;
  755. begin
  756. LinkNode := RootLinkNode.FindChild(AName);
  757. if Assigned(LinkNode) then
  758. Result := LinkNode.Link
  759. else
  760. SetLength(Result, 0);
  761. end;
  762. function TFPDocEngine.ResolveLink(AModule: TPasModule;
  763. const ALinkDest: String): String;
  764. var
  765. i: Integer;
  766. ThisPackage: TLinkNode;
  767. UnitList: TList;
  768. begin
  769. //WriteLn('ResolveLink(', ALinkDest, ')... ');
  770. if Length(ALinkDest) = 0 then
  771. begin
  772. SetLength(Result, 0);
  773. exit;
  774. end;
  775. if (ALinkDest[1] = '#') or (not assigned(AModule)) then
  776. Result := FindAbsoluteLink(ALinkDest)
  777. else
  778. begin
  779. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest);
  780. if Length(Result) > 0 then
  781. exit;
  782. { Try all packages }
  783. SetLength(Result, 0);
  784. ThisPackage := RootLinkNode.FirstChild;
  785. while Assigned(ThisPackage) do
  786. begin
  787. Result := ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest);
  788. if Length(Result) > 0 then
  789. exit;
  790. ThisPackage := ThisPackage.NextSibling;
  791. end;
  792. if Length(Result) = 0 then
  793. begin
  794. { Okay, then we have to try all imported units of the current module }
  795. UnitList := AModule.InterfaceSection.UsesList;
  796. for i := UnitList.Count - 1 downto 0 do
  797. begin
  798. { Try all packages }
  799. ThisPackage := RootLinkNode.FirstChild;
  800. while Assigned(ThisPackage) do
  801. begin
  802. Result := ResolveLink(AModule, ThisPackage.Name + '.' +
  803. TPasType(UnitList[i]).Name + '.' + ALinkDest);
  804. if Length(Result) > 0 then
  805. exit;
  806. ThisPackage := ThisPackage.NextSibling;
  807. end;
  808. end;
  809. end;
  810. end;
  811. if Length(Result) = 0 then
  812. for i := Length(ALinkDest) downto 1 do
  813. if ALinkDest[i] = '.' then
  814. begin
  815. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1));
  816. exit;
  817. end;
  818. end;
  819. procedure TFPDocEngine.AddDocFile(const AFilename: String);
  820. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  821. var
  822. Subnode: TDOMNode;
  823. begin
  824. if OwnerDocNode = RootDocNode then
  825. Result := OwnerDocNode.CreateChildren('#' + Element['name'])
  826. else
  827. Result := OwnerDocNode.CreateChildren(Element['name']);
  828. Result.FNode := Element;
  829. Result.FLink := Element['link'];
  830. Result.FIsSkipped := Element['skip'] = '1';
  831. Subnode := Element.FirstChild;
  832. while Assigned(Subnode) do
  833. begin
  834. if Subnode.NodeType = ELEMENT_NODE then
  835. begin
  836. if Subnode.NodeName = 'short' then
  837. Result.FShortDescr := TDOMElement(Subnode)
  838. else if Subnode.NodeName = 'descr' then
  839. Result.FDescr := TDOMElement(Subnode)
  840. else if Subnode.NodeName = 'errors' then
  841. Result.FErrorsDoc := TDOMElement(Subnode)
  842. else if Subnode.NodeName = 'seealso' then
  843. Result.FSeeAlso := TDOMElement(Subnode)
  844. else if (Subnode.NodeName = 'example') and
  845. not Assigned(Result.FirstExample) then
  846. Result.FFirstExample := TDOMElement(Subnode);
  847. end;
  848. Subnode := Subnode.NextSibling;
  849. end;
  850. end;
  851. Procedure ReadTopics(TopicNode : TDocNode);
  852. Var
  853. SubNode : TDOMNode;
  854. begin
  855. SubNode:=TopicNode.FNode.FirstChilD;
  856. While Assigned(SubNode) do
  857. begin
  858. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  859. With ReadNode(TopicNode,TDomElement(SubNode)) do
  860. // We could allow recursion here, but we won't, because it doesn't work on paper.
  861. FTopicNode:=True;
  862. SubNode:=Subnode.NextSibling;
  863. end;
  864. end;
  865. var
  866. i: Integer;
  867. Node, Subnode, Subsubnode: TDOMNode;
  868. Element: TDOMElement;
  869. Doc: TXMLDocument;
  870. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  871. begin
  872. ReadXMLFile(Doc, AFilename);
  873. DescrDocs.Add(Doc);
  874. DescrDocNames.Add(AFilename);
  875. Node := Doc.DocumentElement.FirstChild;
  876. while Assigned(Node) do
  877. begin
  878. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  879. begin
  880. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  881. // Scan all 'module' elements within this package element
  882. Subnode := Node.FirstChild;
  883. while Assigned(Subnode) do
  884. begin
  885. if (Subnode.NodeType = ELEMENT_NODE) then
  886. begin
  887. If (Subnode.NodeName = 'module') then
  888. begin
  889. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  890. // Scan all 'element' elements within this module element
  891. Subsubnode := Subnode.FirstChild;
  892. while Assigned(Subsubnode) do
  893. begin
  894. if (Subsubnode.NodeType = ELEMENT_NODE) then
  895. begin
  896. if (Subsubnode.NodeName = 'element') then
  897. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  898. else if (SubSubNode.NodeName='topic') then
  899. begin
  900. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  901. TopicNode.FTopicNode:=True;
  902. ReadTopics(TopicNode);
  903. end;
  904. end;
  905. Subsubnode := Subsubnode.NextSibling;
  906. end;
  907. end
  908. else if (SubNode.NodeName='topic') then
  909. begin
  910. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  911. TopicNode.FTopicNode:=True;
  912. ReadTopics(TopicNode);
  913. end;
  914. end;
  915. Subnode := Subnode.NextSibling;
  916. end;
  917. end;
  918. Node := Node.NextSibling;
  919. end;
  920. end;
  921. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  922. begin
  923. Result:=Nil;
  924. If Assigned(AElement) then
  925. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  926. Result := FindDocNode(AElement.GetModule, AElement.Name)
  927. else
  928. Result := RootDocNode.FindChild(AElement.PathName);
  929. end;
  930. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  931. const AName: String): TDocNode;
  932. var
  933. CurPackage: TDocNode;
  934. UnitList: TList;
  935. i: Integer;
  936. begin
  937. if Length(AName) = 0 then
  938. Result := nil
  939. else
  940. begin
  941. if AName[1] = '#' then
  942. Result := RootDocNode.FindChild(AName)
  943. else
  944. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  945. if (not Assigned(Result)) and Assigned(ARefModule) then
  946. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  947. if (not Assigned(Result)) and (AName[1] <> '#') then
  948. begin
  949. CurPackage := RootDocNode.FirstChild;
  950. while Assigned(CurPackage) do
  951. begin
  952. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  953. if Assigned(Result) then
  954. break;
  955. CurPackage := CurPackage.NextSibling;
  956. end;
  957. if not Assigned(Result) then
  958. begin
  959. { Okay, then we have to try all imported units of the current module }
  960. UnitList := CurModule.InterfaceSection.UsesList;
  961. for i := UnitList.Count - 1 downto 0 do
  962. begin
  963. { Try all packages }
  964. CurPackage := RootDocNode.FirstChild;
  965. while Assigned(CurPackage) do
  966. begin
  967. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  968. TPasType(UnitList[i]).Name + '.' + AName);
  969. if Assigned(Result) then
  970. break;
  971. CurPackage := CurPackage.NextSibling;
  972. end;
  973. end;
  974. end;
  975. end;
  976. end;
  977. end;
  978. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  979. var
  980. DocNode: TDocNode;
  981. begin
  982. DocNode := FindDocNode(AElement);
  983. if Assigned(DocNode) then
  984. Result := DocNode.ShortDescr
  985. else
  986. Result := nil;
  987. end;
  988. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  989. const AName: String): TDOMElement;
  990. var
  991. DocNode: TDocNode;
  992. begin
  993. DocNode := FindDocNode(ARefModule, AName);
  994. if Assigned(DocNode) then
  995. Result := DocNode.ShortDescr
  996. else
  997. Result := nil;
  998. end;
  999. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1000. var
  1001. i: Integer;
  1002. begin
  1003. for i := 0 to DescrDocs.Count - 1 do
  1004. if TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument then
  1005. begin
  1006. Result := ExtractFilePath(DescrDocNames[i]) + ExElement['file'];
  1007. exit;
  1008. end;
  1009. SetLength(Result, 0);
  1010. end;
  1011. { Global helpers }
  1012. procedure TranslateDocStrings(const Lang: String);
  1013. var
  1014. mo: TMOFile;
  1015. begin
  1016. {$IFDEF Unix}
  1017. mo := TMOFile.Create(Format('/usr/local/share/locale/%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1018. {$ELSE}
  1019. mo := TMOFile.Create(Format('intl/dglobals.%s.mo', [Lang]));
  1020. {$ENDIF}
  1021. try
  1022. TranslateResourceStrings(mo);
  1023. finally
  1024. mo.Free;
  1025. end;
  1026. end;
  1027. end.
  1028. {
  1029. $Log$
  1030. Revision 1.3 2004-06-06 10:53:02 michael
  1031. + Added Topic support
  1032. Revision 1.2 2003/11/28 12:51:37 sg
  1033. * Added support for source references
  1034. Revision 1.1 2003/03/17 23:03:20 michael
  1035. + Initial import in CVS
  1036. Revision 1.13 2003/03/13 22:02:13 sg
  1037. * New version with many bugfixes and our own parser (now independent of the
  1038. compiler source)
  1039. Revision 1.12 2002/11/15 19:44:18 sg
  1040. * Cosmetic changes
  1041. Revision 1.11 2002/10/12 17:00:45 michael
  1042. + Changes to be able to disable private/protected nodes in skeleton
  1043. Revision 1.10 2002/05/24 00:13:22 sg
  1044. * much improved new version, including many linking and output fixes
  1045. Revision 1.9 2002/03/25 23:16:24 sg
  1046. * fixed missing storing of documenation data for the DocNode of a module
  1047. (so e.g. the Unit Overview is working again)
  1048. Revision 1.8 2002/03/12 10:58:35 sg
  1049. * reworked linking engine and internal structure
  1050. Revision 1.7 2002/01/20 11:19:55 michael
  1051. + Added link attribute and property to TFPelement
  1052. Revision 1.6 2001/12/17 22:16:02 sg
  1053. * Added TFPDocEngine.HideProtected
  1054. }