2
0

dglobals.pp 47 KB

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