dglobals.pp 29 KB

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