dglobals.pp 49 KB

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