dglobals.pp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  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. FreeAndNil(FPackages);
  420. FreeAndNil(FRootDocNode);
  421. FreeAndNil(FRootLinkNode);
  422. FreeAndNil(DescrDocNames);
  423. FreeAndNil(DescrDocs);
  424. FreeAndNil(FAlwaysVisible);
  425. inherited Destroy;
  426. end;
  427. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  428. begin
  429. ASSERT(not Assigned(Package));
  430. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  431. '#' + APackageName, nil, '', 0));
  432. FPackages.Add(FPackage);
  433. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  434. If Assigned(CurPackageDocNode) then
  435. CurPackageDocNode.IncRefCount;
  436. end;
  437. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  438. var
  439. f: Text;
  440. inheritanceinfo : TStringlist; // contents list of TPasClass with inheritance info
  441. // like this #PackageName.ModuleName.ClassName
  442. tmpLinkPrefix : string;
  443. procedure ReadLinkTree;
  444. var
  445. s: String;
  446. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  447. CurParent, PrevSibling, NewNode: TLinkNode;
  448. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  449. begin
  450. PrevSpaces := 0;
  451. CurParent := RootLinkNode;
  452. PrevSibling := CurParent.FirstChild;
  453. if assigned(PrevSibling) then
  454. while assigned(PrevSibling.NextSibling) do
  455. PrevSibling := PrevSibling.NextSibling;
  456. StackIndex := 0;
  457. while True do
  458. begin
  459. ReadLn(f, s);
  460. if Length(s) = 0 then
  461. break;
  462. ThisSpaces := 0;
  463. while s[ThisSpaces + 1] = ' ' do
  464. Inc(ThisSpaces);
  465. if ThisSpaces <> PrevSpaces then
  466. begin
  467. if ThisSpaces > PrevSpaces then
  468. begin
  469. { Dive down one level }
  470. ParentStack[StackIndex] := CurParent;
  471. SiblingStack[StackIndex] := PrevSibling;
  472. Inc(StackIndex);
  473. CurParent := PrevSibling;
  474. PrevSibling := nil;
  475. end else
  476. while PrevSpaces > ThisSpaces do
  477. begin
  478. Dec(StackIndex);
  479. CurParent := ParentStack[StackIndex];
  480. PrevSibling := SiblingStack[StackIndex];
  481. Dec(PrevSpaces);
  482. end;
  483. PrevSpaces := ThisSpaces;
  484. end;
  485. i := ThisSpaces + 1;
  486. while s[i] <> ' ' do
  487. Inc(i);
  488. if ALinkPrefix <> '' then
  489. tmpLinkPrefix := ExcludeTrailingPathDelimiter(ALinkPrefix)+'/';
  490. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  491. tmpLinkPrefix + Copy(s, i + 1, Length(s)));
  492. if pos(' ',newnode.link)>0 then
  493. writeln(stderr,'Bad format imported node: name="',newnode.name,'" link="',newnode.link,'"');
  494. if Assigned(PrevSibling) then
  495. PrevSibling.FNextSibling := NewNode
  496. else
  497. CurParent.FFirstChild := NewNode;
  498. PrevSibling := NewNode;
  499. end;
  500. end;
  501. function ResolvePackageModule(AName:String;out pkg:TPasPackage;out module:TPasModule;createnew:boolean):String;
  502. var
  503. DotPos, DotPos2, i: Integer;
  504. s: String;
  505. HPackage: TPasPackage;
  506. begin
  507. pkg:=nil; module:=nil; result:='';
  508. // Find or create package
  509. DotPos := Pos('.', AName);
  510. s := Copy(AName, 1, DotPos - 1);
  511. HPackage := nil;
  512. for i := 0 to FPackages.Count - 1 do
  513. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  514. begin
  515. HPackage := TPasPackage(FPackages[i]);
  516. break;
  517. end;
  518. if not Assigned(HPackage) then
  519. begin
  520. if not CreateNew then
  521. exit;
  522. HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  523. '', 0));
  524. FPackages.Add(HPackage);
  525. end;
  526. // Find or create module
  527. DotPos2 := DotPos;
  528. repeat
  529. Inc(DotPos2);
  530. until AName[DotPos2] = '.';
  531. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  532. Module := nil;
  533. for i := 0 to HPackage.Modules.Count - 1 do
  534. if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then
  535. begin
  536. Module := TPasModule(HPackage.Modules[i]);
  537. break;
  538. end;
  539. if not Assigned(Module) then
  540. begin
  541. if not CreateNew then
  542. exit;
  543. Module := TPasExternalModule.Create(s, HPackage);
  544. HPackage.Modules.Add(Module);
  545. Module.InterfaceSection := TInterfaceSection.Create('', Module);
  546. Module.PackageName:= HPackage.Name;
  547. end;
  548. pkg:=hpackage;
  549. result:=Copy(AName, DotPos2 + 1, length(AName)-dotpos2);
  550. end;
  551. function SearchInList(clslist:TFPList;s:string):TPasElement;
  552. var i : integer;
  553. ClassEl: TPasElement;
  554. begin
  555. result:=nil;
  556. for i:=0 to clslist.count-1 do
  557. begin
  558. ClassEl := TPasElement(clslist[i]);
  559. if CompareText(ClassEl.Name,s) =0 then
  560. exit(Classel);
  561. end;
  562. end;
  563. function ResolveClassType(AName:String):TPasClassType;
  564. var
  565. pkg : TPasPackage;
  566. module : TPasModule;
  567. s : string;
  568. begin
  569. Result:=nil;
  570. s:=ResolvePackageModule(AName,pkg,module,False);
  571. if not assigned(module) then
  572. exit;
  573. result:=TPasClassType(SearchInList(Module.InterfaceSection.Classes,s));
  574. end;
  575. function ResolveAliasType(AName:String):TPasAliasType;
  576. var
  577. pkg : TPasPackage;
  578. module : TPasModule;
  579. s : string;
  580. begin
  581. Result:=nil;
  582. s:=ResolvePackageModule(AName,pkg,module,False);
  583. if not assigned(module) then
  584. exit;
  585. result:=TPasAliasType(SearchInList(Module.InterfaceSection.Types,s));
  586. if not (result is TPasAliasType) then
  587. result:=nil;
  588. end;
  589. procedure ReadClasses;
  590. function CreateClass(const AName: String; const InheritanceStr:String): TPasClassType;
  591. var
  592. s: String;
  593. HPackage: TPasPackage;
  594. Module: TPasModule;
  595. begin
  596. s:= ResolvePackageModule(AName,HPackage,Module,True);
  597. // Create node for class
  598. Result := TPasExternalClassType.Create(s, Module.InterfaceSection);
  599. Result.ObjKind := okClass;
  600. Module.InterfaceSection.Declarations.Add(Result);
  601. Module.InterfaceSection.Classes.Add(Result);
  602. // defer processing inheritancestr till all classes are loaded.
  603. if InheritanceStr<>'' then
  604. InheritanceInfo.AddObject(InheritanceStr,Result);
  605. end;
  606. procedure splitalias(var instr:string;out outstr:string);
  607. var i,j:integer;
  608. begin
  609. if length(instr)=0 then exit;
  610. instr:=trim(instr);
  611. i:=pos('(',instr);
  612. if i>0 then
  613. begin
  614. j:=length(instr)-i;
  615. if instr[length(instr)]=')' then
  616. dec(j);
  617. outstr:=copy(instr,i+1,j);
  618. delete(instr,i,j+2);
  619. end
  620. end;
  621. Function ResolveAndLinkClass(clname:String;IsClass:boolean;cls:TPasClassType):TPasClassType;
  622. begin
  623. result:=TPasClassType(ResolveClassType(clname));
  624. if assigned(result) and not (cls=result) then // save from tobject=implicit tobject
  625. begin
  626. result.addref{$IFDEF CheckPasTreeRefCount}('ReadContentFile.ResolveAndLinkClass'){$ENDIF};
  627. if IsClass then
  628. begin
  629. cls.ancestortype:=result;
  630. // writeln(cls.name, ' has as ancestor ',result.pathname);
  631. end
  632. else
  633. begin
  634. cls.interfaces.add(result);
  635. // writeln(cls.name, ' implements ',result.pathname);
  636. end;
  637. end
  638. else
  639. if (dleXCT in FDocLogLevels) and (cls<>result) then
  640. DoLog('Warning : ancestor class %s of class %s could not be resolved',[clname,cls.name]);
  641. end;
  642. function CreateAliasType (alname,clname : string;parentclass:TPasClassType; out cl2 :TPasClassType):TPasAliasType;
  643. // create alias clname = alname
  644. var
  645. pkg : TPasPackage;
  646. module : TPasModule;
  647. s : string;
  648. begin
  649. Result:=nil;
  650. s:=ResolvePackageModule(Alname,pkg,module,True);
  651. if not assigned(module) then
  652. exit;
  653. cl2:=TPasClassType(ResolveClassType(alname));
  654. if assigned( cl2) and not (parentclass=cl2) then
  655. begin
  656. result:=ResolveAliasType(clname);
  657. if assigned(result) then
  658. begin
  659. // writeln('found alias ',clname,' (',s,') ',result.classname);
  660. end
  661. else
  662. begin
  663. // writeln('new alias ',clname,' (',s,') ');
  664. Result := TPasAliasType(CreateElement(TPasAliasType,s,module.interfacesection,vispublic,'',0));
  665. module.interfacesection.Declarations.Add(Result);
  666. TPasAliasType(Result).DestType := cl2;
  667. cl2.addref{$IFDEF CheckPasTreeRefCount}('ReadContentFile.CreateAliasType'){$ENDIF};
  668. end
  669. end
  670. end;
  671. procedure ProcessInheritanceStrings(inhInfo:TStringList);
  672. var i,j : integer;
  673. cls : TPasClassType;
  674. cls2: TPasClassType;
  675. clname,
  676. alname : string;
  677. inhclass : TStringList;
  678. begin
  679. inhclass:=TStringList.Create;
  680. inhclass.delimiter:=',';
  681. if InhInfo.Count>0 then
  682. for i:=0 to InhInfo.Count-1 do
  683. begin
  684. cls:=TPasClassType(InhInfo.Objects[i]);
  685. inhclass.clear;
  686. inhclass.delimitedtext:=InhInfo[i];
  687. for j:= 0 to inhclass.count-1 do
  688. begin
  689. // writeln('processing',inhclass[j]);
  690. clname:=inhclass[j];
  691. splitalias(clname,alname);
  692. if alname<>'' then // the class//interface we refered to is an alias
  693. begin
  694. // writeln('Found alias pair ',clname,' = ',alname);
  695. if (dleXCT in FDocLogLevels) and not assigned(CreateAliasType(alname,clname,cls,cls2)) then
  696. DoLog('Warning: creating alias %s for %s failed!',[alname,clname]);
  697. end
  698. else
  699. cls2:=ResolveAndLinkClass(clname,j=0,cls);
  700. end;
  701. end;
  702. inhclass.free;
  703. end;
  704. var
  705. s, Name: String;
  706. CurClass: TPasClassType;
  707. i: Integer;
  708. Member: TPasElement;
  709. begin
  710. inheritanceinfo :=TStringlist.Create;
  711. Try
  712. CurClass := nil;
  713. while True do
  714. begin
  715. ReadLn(f, s);
  716. if Length(s) = 0 then
  717. break;
  718. if s[1] = '#' then
  719. begin
  720. // New class
  721. i := Pos(' ', s);
  722. CurClass := CreateClass(Copy(s, 1, i - 1), copy(s,i+1,length(s)));
  723. end else
  724. begin
  725. i := Pos(' ', s);
  726. if i = 0 then
  727. Name := Copy(s, 3, Length(s))
  728. else
  729. Name := Copy(s, 3, i - 3);
  730. case s[2] of
  731. 'M':
  732. Member := TPasProcedure.Create(Name, CurClass);
  733. 'P':
  734. begin
  735. Member := TPasProperty.Create(Name, CurClass);
  736. if i > 0 then
  737. while i <= Length(s) do
  738. begin
  739. case s[i] of
  740. 'r':
  741. TPasProperty(Member).ReadAccessorName := '<dummy>';
  742. 'w':
  743. TPasProperty(Member).WriteAccessorName := '<dummy>';
  744. 's':
  745. TPasProperty(Member).StoredAccessorName := '<dummy>';
  746. end;
  747. Inc(i);
  748. end;
  749. end;
  750. 'V':
  751. Member := TPasVariable.Create(Name, CurClass);
  752. else
  753. raise Exception.Create('Invalid member type: ' + s[2]);
  754. end;
  755. CurClass.Members.Add(Member);
  756. end;
  757. end;
  758. ProcessInheritanceStrings(Inheritanceinfo);
  759. finally
  760. inheritanceinfo.Free;
  761. end;
  762. end;
  763. var
  764. s: String;
  765. buf : TBufType;
  766. begin
  767. buf:=Default(TBufType);
  768. if not FileExists(AFileName) then
  769. raise EInOutError.Create('File not found: ' + AFileName);
  770. Assign(f, AFilename);
  771. Reset(f);
  772. SetTextBuf(F,Buf,SizeOf(Buf));
  773. while not EOF(f) do
  774. begin
  775. ReadLn(f, s);
  776. if (Length(s) = 0) or (s[1] = '#') then
  777. continue;
  778. if s = ':link tree' then
  779. ReadLinkTree
  780. else if s = ':classes' then
  781. ReadClasses
  782. else
  783. repeat
  784. ReadLn(f, s);
  785. until EOF(f) or (Length(s) = 0);
  786. end;
  787. Close(f);
  788. end;
  789. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  790. var
  791. ContentFile: Text;
  792. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  793. var
  794. ChildNode: TLinkNode;
  795. begin
  796. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  797. ChildNode := ALinkNode.FirstChild;
  798. while Assigned(ChildNode) do
  799. begin
  800. ProcessLinkNode(ChildNode, AIdent + ' ');
  801. ChildNode := ChildNode.NextSibling;
  802. end;
  803. end;
  804. function CheckImplicitLink(const s : String):String;
  805. begin
  806. if uppercase(s)='IUNKNOWN' then
  807. Result:='#rtl.System.IUnknown'
  808. else if uppercase(s)='TOBJECT' then
  809. Result:='#rtl.System.TObject'
  810. else
  811. Result:=s;
  812. end;
  813. var
  814. LinkNode: TLinkNode;
  815. i, j, k: Integer;
  816. Module: TPasModule;
  817. Alias : TPasAliasType;
  818. MemberDecl: TPasMembersType;
  819. ClassLikeDecl : TPasClassType;
  820. Member: TPasElement;
  821. s: String;
  822. Buf : TBufType;
  823. begin
  824. Buf:=Default(TBufType);
  825. Assign(ContentFile, AFilename);
  826. Rewrite(ContentFile);
  827. SetTextBuf(ContentFile,Buf,SizeOf(Buf));
  828. try
  829. WriteLn(ContentFile, '# FPDoc Content File');
  830. WriteLn(ContentFile, ':link tree');
  831. LinkNode := RootLinkNode.FirstChild;
  832. while Assigned(LinkNode) do
  833. begin
  834. if LinkNode.Name = Package.Name then
  835. begin
  836. ProcessLinkNode(LinkNode, '');
  837. end;
  838. LinkNode := LinkNode.NextSibling;
  839. end;
  840. if Assigned(Package) then
  841. begin
  842. WriteLn(ContentFile);
  843. WriteLn(ContentFile, ':classes');
  844. for i := 0 to Package.Modules.Count - 1 do
  845. begin
  846. Module := TPasModule(Package.Modules[i]);
  847. if not assigned(Module.InterfaceSection) then
  848. continue;
  849. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  850. begin
  851. MemberDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  852. if MemberDecl is TPasClassType then
  853. ClassLikeDecl:=MemberDecl as TPasClassType
  854. else
  855. ClassLikeDecl:=nil;
  856. Write(ContentFile, CheckImplicitLink(MemberDecl.PathName), ' ');
  857. if Assigned(ClassLikeDecl) then
  858. begin
  859. if Assigned(ClassLikeDecl.AncestorType) then
  860. begin
  861. // simple aliases to class types are coded as "alias(classtype)"
  862. Write(ContentFile, CheckImplicitLink(ClassLikeDecl.AncestorType.PathName));
  863. if ClassLikeDecl.AncestorType is TPasAliasType then
  864. begin
  865. alias:= TPasAliasType(ClassLikeDecl.AncestorType);
  866. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  867. write(ContentFile,'(',alias.desttype.PathName,')');
  868. end;
  869. end
  870. else if ClassLikeDecl.ObjKind = okClass then
  871. Write(ContentFile, '#rtl.System.TObject')
  872. else if ClassLikeDecl.ObjKind = okInterface then
  873. Write(ContentFile, '#rtl.System.IUnknown');
  874. if ClassLikeDecl.Interfaces.Count>0 then
  875. begin
  876. for k:=0 to ClassLikeDecl.Interfaces.count-1 do
  877. begin
  878. write(contentfile,',',CheckImplicitLink(TPasType(ClassLikeDecl.Interfaces[k]).PathName));
  879. if TPasElement(ClassLikeDecl.Interfaces[k]) is TPasAliasType then
  880. begin
  881. alias:= TPasAliasType(ClassLikeDecl.Interfaces[k]);
  882. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  883. write(ContentFile,'(',CheckImplicitLink(alias.desttype.PathName),')');
  884. end;
  885. end;
  886. end;
  887. end;
  888. writeln(contentfile);
  889. for k := 0 to MemberDecl.Members.Count - 1 do
  890. begin
  891. Member := TPasElement(MemberDecl.Members[k]);
  892. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  893. S:='';
  894. if Member.ClassType = TPasVariable then
  895. Write(ContentFile, 'V')
  896. else if Member.ClassType = TPasProperty then
  897. begin
  898. Write(ContentFile, 'P');
  899. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  900. s := s + 'r';
  901. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  902. s := s + 'w';
  903. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  904. s := s + 's';
  905. end else
  906. Write(ContentFile, 'M'); // Member must be a method
  907. Write(ContentFile, Member.Name);
  908. if Length(s) > 0 then
  909. WriteLn(ContentFile, ' ', s)
  910. else
  911. WriteLn(ContentFile);
  912. end;
  913. end;
  914. end;
  915. end;
  916. finally
  917. Close(ContentFile);
  918. end;
  919. end;
  920. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  921. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  922. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  923. begin
  924. Result := AClass.Create(AName, AParent);
  925. Result.Visibility := AVisibility;
  926. if AClass.InheritsFrom(TPasModule) then
  927. CurModule := TPasModule(Result);
  928. Result.SourceFilename := ASourceFilename;
  929. Result.SourceLinenumber := ASourceLinenumber;
  930. end;
  931. function TFPDocEngine.FindElement ( const AName: String; AModule: TPasModule
  932. ) : TPasElement;
  933. var
  934. l: TFPList;
  935. i: Integer;
  936. begin
  937. If Assigned(AModule) and Assigned(AModule.InterfaceSection) and
  938. Assigned(AModule.InterfaceSection.Declarations) then
  939. begin
  940. l:=AModule.InterfaceSection.Declarations;
  941. for i := 0 to l.Count - 1 do
  942. begin
  943. Result := TPasElement(l[i]);
  944. if CompareText(Result.Name, AName) = 0 then
  945. exit;
  946. end;
  947. end;
  948. Result := nil;
  949. end;
  950. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  951. var
  952. i: Integer;
  953. Module: TPasElement;
  954. begin
  955. Result := FindElement( AName, CurModule );
  956. if not Assigned(Result) and assigned (CurModule.InterfaceSection) then
  957. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  958. begin
  959. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  960. if Module.ClassType.InheritsFrom(TPasModule) then
  961. begin
  962. Result := FindElement(AName, TPasModule(Module));
  963. if Assigned(Result) then
  964. exit;
  965. end;
  966. end;
  967. end;
  968. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  969. function FindInPackage(APackage: TPasPackage): TPasModule;
  970. var
  971. i: Integer;
  972. begin
  973. if not Assigned(APackage) then Exit;
  974. for i := 0 to APackage.Modules.Count - 1 do
  975. begin
  976. Result := TPasModule(APackage.Modules[i]);
  977. if CompareText(Result.Name, AName) = 0 then
  978. exit;
  979. end;
  980. Result := nil;
  981. end;
  982. var
  983. i: Integer;
  984. AInPutLine,OSTarget,CPUTarget : String;
  985. begin
  986. Result := FindInPackage(Package);
  987. if not Assigned(Result) and (FPackages.Count > 0) then
  988. for i := FPackages.Count - 1 downto 0 do
  989. begin
  990. if TPasPackage(FPackages[i]) = Package then
  991. continue;
  992. Result := FindInPackage(TPasPackage(FPackages[i]));
  993. if Assigned(Result) then
  994. exit;
  995. end;
  996. if Not Assigned(Result) and Assigned(FOnParseUnit) then
  997. begin
  998. FOnParseUnit(Self,AName,AInputLine,OSTarget,CPUTarget);
  999. If (AInPutLine<>'') then
  1000. Result:=ParseUsedUnit(AName,AInputLine,OSTarget,CPUTarget);
  1001. end;
  1002. end;
  1003. function TFPDocEngine.HintsToStr(Hints: TPasMemberHints): String;
  1004. Var
  1005. H : TPasMemberHint;
  1006. begin
  1007. Result:='';
  1008. For h:=Low(TPasMemberHint) to High(TPasMemberHint) do
  1009. if h in Hints then
  1010. begin
  1011. if (Result<>'') then
  1012. Result:=Result+', ';
  1013. Result:=Result+cPasMemberHint[h]
  1014. end;
  1015. end;
  1016. function TFPDocEngine.ParseUsedUnit(AName, AInputLine, AOSTarget,
  1017. ACPUTarget: String): TPasModule;
  1018. Var
  1019. M : TPasModule;
  1020. begin
  1021. if dleWarnUsedFile in FDocLogLevels then
  1022. DoLog(SParsingUsedUnit,[AName,AInputLine]);
  1023. M:=CurModule;
  1024. CurModule:=Nil;
  1025. try
  1026. ParseSource(Self,AInputLine,AOSTarget,ACPUTarget,[poUseStreams]); //[poSkipDefaultDefs];
  1027. Result:=CurModule;
  1028. finally
  1029. CurModule:=M;
  1030. end;
  1031. end;
  1032. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  1033. begin
  1034. RootLinkNode.CreateChildren(APathName, ALinkTo);
  1035. end;
  1036. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  1037. var
  1038. LinkNode: TLinkNode;
  1039. begin
  1040. LinkNode := RootLinkNode.FindChild(AName);
  1041. if Assigned(LinkNode) then
  1042. Result := LinkNode.Link
  1043. else
  1044. SetLength(Result, 0);
  1045. end;
  1046. function TFPDocEngine.ResolveLinkInPackages(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1047. Var
  1048. ThisPackage: TLinkNode;
  1049. begin
  1050. { Try all packages }
  1051. Result:='';
  1052. ThisPackage:=RootLinkNode.FirstChild;
  1053. while Assigned(ThisPackage) and (Result='') do
  1054. begin
  1055. Result:=ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest, Strict);
  1056. ThisPackage := ThisPackage.NextSibling;
  1057. end;
  1058. end;
  1059. function TFPDocEngine.ResolveLinkInUsedUnits(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1060. var
  1061. i: Integer;
  1062. UL: TFPList;
  1063. begin
  1064. Result:='';
  1065. UL:=AModule.InterfaceSection.UsesList;
  1066. I:=UL.Count-1;
  1067. While (Result='') and (I>=0) do
  1068. begin
  1069. Result:=ResolveLinkInPackages(AModule,TPasType(UL[i]).Name+'.'+ALinkDest, strict);
  1070. Dec(I);
  1071. end;
  1072. end;
  1073. function TFPDocEngine.ResolveLink(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1074. var
  1075. i: Integer;
  1076. begin
  1077. {
  1078. if Assigned(AModule) then
  1079. system.WriteLn('ResolveLink(', AModule.Name, ' - ', ALinkDest, ')... ')
  1080. else
  1081. system.WriteLn('ResolveLink(Nil - ', ALinkDest, ')... ');
  1082. }
  1083. if (ALinkDest='') then
  1084. Exit('');
  1085. if (ALinkDest[1] = '#') then
  1086. // Link has full path
  1087. Result := FindAbsoluteLink(ALinkDest)
  1088. else if (AModule=Nil) then
  1089. // Trying to add package name only
  1090. Result:= FindAbsoluteLink(RootLinkNode.FirstChild.Name+'.'+ALinkDest)
  1091. else
  1092. begin
  1093. if Pos(LowerCase(AModule.Name)+'.',LowerCase(ALinkDest)) = 1 then
  1094. // fix ERROR - Link starts from name of module
  1095. Result := ResolveLink(AModule, AModule.packagename + '.' + ALinkDest, Strict)
  1096. else
  1097. // Link should be a first level inside of module
  1098. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest, Strict);
  1099. if (Result='') then
  1100. begin
  1101. Result:=ResolveLinkInPackages(AModule,ALinkDest,Strict);
  1102. if (Result='') then
  1103. Result:=ResolveLinkInUsedUnits(Amodule,AlinkDest,Strict);
  1104. end;
  1105. end;
  1106. // Match on parent : class/enumerated/record/module
  1107. if (Result='') and not strict then
  1108. begin
  1109. // TODO: I didn't see a calling this code at entire lcl package
  1110. // Writeln('INFO UnStrinct(): ' + ALinkDest);
  1111. for i := Length(ALinkDest) downto 1 do
  1112. if ALinkDest[i] = '.' then
  1113. begin
  1114. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1), Strict);
  1115. //if Result <> '' then Writeln('INFO LinkResolved UnStrinct(): '+Result);
  1116. exit;
  1117. end;
  1118. end;
  1119. end;
  1120. procedure ReadXMLFileALT(OUT ADoc:TXMLDocument;const AFileName:ansistring);
  1121. var
  1122. Parser: TDOMParser;
  1123. Src: TXMLInputSource;
  1124. FileStream: TStream;
  1125. begin
  1126. ADoc := nil;
  1127. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  1128. try
  1129. Parser := TDOMParser.Create; // create a parser object
  1130. try
  1131. Src := TXMLInputSource.Create(FileStream); // and the input source
  1132. src.SystemId:=UTF8Decode(FileNameToUri(AFileName));
  1133. try
  1134. Parser.Options.PreserveWhitespace := True;
  1135. Parser.Parse(Src, ADoc);
  1136. finally
  1137. Src.Free; // cleanup
  1138. end;
  1139. finally
  1140. Parser.Free;
  1141. end;
  1142. finally
  1143. FileStream.Free;
  1144. end;
  1145. end;
  1146. procedure TFPDocEngine.AddDocFile(const AFilename: String;DontTrim:boolean=false);
  1147. Var
  1148. PN : String;
  1149. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  1150. var
  1151. Subnode: TDOMNode;
  1152. begin
  1153. if OwnerDocNode = RootDocNode then
  1154. Result := OwnerDocNode.CreateChildren('#' + UTF8Encode(Element['name']))
  1155. else
  1156. Result := OwnerDocNode.CreateChildren(UTF8Encode(Element['name']));
  1157. Result.FNode := Element;
  1158. Result.FLink := UTF8Encode(Element['link']);
  1159. if (Element['alwaysvisible'] = '1') and (Element.NodeName='element') then
  1160. FAlwaysVisible.Add(LowerCase(PN+'.'+TDocNode(OwnerDocNode).Name+'.'+UTF8Encode(Element['name'])));
  1161. Result.FIsSkipped := Element['skip'] = '1';
  1162. Subnode := Element.FirstChild;
  1163. while Assigned(Subnode) do
  1164. begin
  1165. if Subnode.NodeType = ELEMENT_NODE then
  1166. begin
  1167. if Subnode.NodeName = 'short' then
  1168. Result.FShortDescr := TDOMElement(Subnode)
  1169. else if Subnode.NodeName = 'descr' then
  1170. Result.FDescr := TDOMElement(Subnode)
  1171. else if Subnode.NodeName = 'version' then
  1172. begin
  1173. Result.FVersion := TDOMElement(Subnode)
  1174. end
  1175. else if Subnode.NodeName = 'errors' then
  1176. Result.FErrorsDoc := TDOMElement(Subnode)
  1177. else if Subnode.NodeName = 'seealso' then
  1178. Result.FSeeAlso := TDOMElement(Subnode)
  1179. else if (Subnode.NodeName = 'example') and
  1180. not Assigned(Result.FirstExample) then
  1181. Result.FFirstExample := TDOMElement(Subnode)
  1182. else if (Subnode.NodeName = 'notes') then
  1183. Result.FNotes := TDOMElement(Subnode);
  1184. end;
  1185. Subnode := Subnode.NextSibling;
  1186. end;
  1187. end;
  1188. Procedure ReadTopics(TopicNode : TDocNode);
  1189. Var
  1190. SubNode : TDOMNode;
  1191. begin
  1192. SubNode:=TopicNode.FNode.FirstChilD;
  1193. While Assigned(SubNode) do
  1194. begin
  1195. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  1196. With ReadNode(TopicNode,TDomElement(SubNode)) do
  1197. // We could allow recursion here, but we won't, because it doesn't work on paper.
  1198. FTopicNode:=True;
  1199. SubNode:=Subnode.NextSibling;
  1200. end;
  1201. end;
  1202. var
  1203. Node, Subnode, Subsubnode: TDOMNode;
  1204. Doc: TXMLDocument;
  1205. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  1206. begin
  1207. if DontTrim then
  1208. ReadXMLFileALT(Doc, AFilename)
  1209. else
  1210. ReadXMLFile(Doc, AFilename);
  1211. DescrDocs.Add(Doc);
  1212. DescrDocNames.Add(AFilename);
  1213. Node := Doc.DocumentElement.FirstChild;
  1214. while Assigned(Node) do
  1215. begin
  1216. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  1217. begin
  1218. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  1219. PackageDocNode.IncRefCount;
  1220. PN:=PackageDocNode.Name;
  1221. // Scan all 'module' elements within this package element
  1222. Subnode := Node.FirstChild;
  1223. while Assigned(Subnode) do
  1224. begin
  1225. if (Subnode.NodeType = ELEMENT_NODE) then
  1226. begin
  1227. If (Subnode.NodeName = 'module') then
  1228. begin
  1229. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  1230. // Scan all 'element' elements within this module element
  1231. Subsubnode := Subnode.FirstChild;
  1232. while Assigned(Subsubnode) do
  1233. begin
  1234. if (Subsubnode.NodeType = ELEMENT_NODE) then
  1235. begin
  1236. if (Subsubnode.NodeName = 'element') then
  1237. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  1238. else if (SubSubNode.NodeName='topic') then
  1239. begin
  1240. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  1241. TopicNode.FTopicNode:=True;
  1242. ReadTopics(TopicNode);
  1243. end;
  1244. end;
  1245. Subsubnode := Subsubnode.NextSibling;
  1246. end;
  1247. end
  1248. else if (SubNode.NodeName='topic') then
  1249. begin
  1250. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  1251. TopicNode.FTopicNode:=True;
  1252. ReadTopics(TopicNode);
  1253. end;
  1254. end;
  1255. Subnode := Subnode.NextSibling;
  1256. end;
  1257. end;
  1258. Node := Node.NextSibling;
  1259. end;
  1260. end;
  1261. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  1262. begin
  1263. Result:=Nil;
  1264. If not Assigned(AElement) then
  1265. exit;
  1266. if aElement.CustomData is TDocNode then
  1267. Exit(TDocNode(aElement.CustomData));
  1268. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  1269. Result := FindDocNode(AElement.GetModule, AElement.Name)
  1270. else
  1271. begin
  1272. Result := RootDocNode.FindChild(AElement.PathName);
  1273. if (Result=Nil) and (AElement is TPasoperator) then
  1274. Result:=RootDocNode.FindChild(TPasOperator(AElement).OldName(True));
  1275. end;
  1276. if (Result<>Nil) then
  1277. begin
  1278. if aElement.CustomData=Nil then
  1279. aElement.CustomData:=Result;
  1280. end
  1281. else if (dleWarnNoNode in FDocLogLevels) and
  1282. (Length(AElement.PathName)>0) and
  1283. (AElement.PathName[1]='#') then
  1284. DoLog(Format('No documentation node found for identifier : %s',[AElement.PathName]));
  1285. end;
  1286. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  1287. const AName: String): TDocNode;
  1288. var
  1289. CurPackage: TDocNode;
  1290. UnitList: TFPList;
  1291. i: Integer;
  1292. begin
  1293. if Length(AName) = 0 then
  1294. Result := nil
  1295. else
  1296. begin
  1297. if AName[1] = '#' then
  1298. Result := RootDocNode.FindChild(AName)
  1299. else
  1300. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  1301. if (not Assigned(Result)) and Assigned(ARefModule) then
  1302. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  1303. if (not Assigned(Result)) and (AName[1] <> '#') then
  1304. begin
  1305. CurPackage := RootDocNode.FirstChild;
  1306. while Assigned(CurPackage) do
  1307. begin
  1308. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  1309. if Assigned(Result) then
  1310. break;
  1311. CurPackage := CurPackage.NextSibling;
  1312. end;
  1313. if not Assigned(Result) and assigned(CurModule.InterfaceSection) then
  1314. begin
  1315. { Okay, then we have to try all imported units of the current module }
  1316. UnitList := CurModule.InterfaceSection.UsesList;
  1317. for i := UnitList.Count - 1 downto 0 do
  1318. begin
  1319. { Try all packages }
  1320. CurPackage := RootDocNode.FirstChild;
  1321. while Assigned(CurPackage) do
  1322. begin
  1323. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  1324. TPasType(UnitList[i]).Name + '.' + AName);
  1325. if Assigned(Result) then
  1326. break;
  1327. CurPackage := CurPackage.NextSibling;
  1328. end;
  1329. end;
  1330. end;
  1331. end;
  1332. end;
  1333. end;
  1334. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  1335. var
  1336. DocNode,N: TDocNode;
  1337. begin
  1338. DocNode := FindDocNode(AElement);
  1339. if Assigned(DocNode) then
  1340. begin
  1341. N:=FindLinkedNode(DocNode);
  1342. If (N<>Nil) then
  1343. DocNode:=N;
  1344. Result := DocNode.ShortDescr;
  1345. end
  1346. else
  1347. Result := nil;
  1348. end;
  1349. function TFPDocEngine.FindLinkedNode(ANode : TDocNode) : TDocNode;
  1350. begin
  1351. If (ANode.Link='') then
  1352. Result:=Nil
  1353. else
  1354. Result:=FindDocNode(CurModule,ANode.Link);
  1355. end;
  1356. function TFPDocEngine.ShowElement(El: TPasElement): Boolean;
  1357. begin
  1358. Case El.Visibility of
  1359. visStrictPrivate,
  1360. visPrivate :
  1361. Result:=Not HidePrivate;
  1362. visStrictProtected,
  1363. visProtected :
  1364. begin
  1365. Result:=Not HideProtected;
  1366. if not Result then
  1367. Result:=FAlwaysVisible.IndexOf(LowerCase(El.PathName))<>-1;
  1368. end
  1369. Else
  1370. Result:=True
  1371. end;
  1372. end;
  1373. procedure TFPDocEngine.StartDocumenting;
  1374. begin
  1375. FAlwaysVisible.Sorted:=True;
  1376. end;
  1377. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  1378. const AName: String): TDOMElement;
  1379. var
  1380. N,DocNode: TDocNode;
  1381. begin
  1382. DocNode := FindDocNode(ARefModule, AName);
  1383. if Assigned(DocNode) then
  1384. begin
  1385. N:=FindLinkedNode(DocNode);
  1386. If (N<>Nil) then
  1387. DocNode:=N;
  1388. Result := DocNode.ShortDescr;
  1389. end
  1390. else
  1391. Result := nil;
  1392. end;
  1393. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1394. var
  1395. i: Integer;
  1396. fn : String;
  1397. begin
  1398. Result:='';
  1399. Fn:=UTF8Encode(ExElement['file']);
  1400. if FN='' then
  1401. exit;
  1402. if ExamplesPath<>'' then
  1403. Result:=IncludeTrailingPathDelimiter(ExamplesPath)+FN
  1404. else
  1405. begin
  1406. I:=0;
  1407. While (Result='') and (I<DescrDocs.Count) do
  1408. begin
  1409. if (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then
  1410. Result := ExtractFilePath(DescrDocNames[i]) + FN;
  1411. Inc(I);
  1412. end;
  1413. end;
  1414. if (ExtractFileExt(Result)='') then
  1415. Result:=Result+'.pp';
  1416. end;
  1417. { Global helpers }
  1418. procedure TranslateDocStrings(const Lang: String);
  1419. Const
  1420. {$ifdef unix}
  1421. DefDir = '/usr/local/share/locale';
  1422. {$else}
  1423. DefDir = 'intl';
  1424. {$endif}
  1425. var
  1426. mo: TMOFile;
  1427. dir : string;
  1428. begin
  1429. dir:=modir;
  1430. If Dir='' then
  1431. Dir:=DefDir;
  1432. Dir:=IncludeTrailingPathDelimiter(Dir);
  1433. {$IFDEF Unix}
  1434. mo := TMOFile.Create(Format(Dir+'%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1435. {$ELSE}
  1436. mo := TMOFile.Create(Format(Dir+'dglobals.%s.mo', [Lang]));
  1437. {$ENDIF}
  1438. try
  1439. TranslateResourceStrings(mo);
  1440. finally
  1441. mo.Free;
  1442. end;
  1443. end;
  1444. Function IsLinkNode(Node : TDomNode) : Boolean;
  1445. begin
  1446. Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');
  1447. end;
  1448. Function IsExampleNode(Example : TDomNode) : Boolean;
  1449. begin
  1450. Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')
  1451. end;
  1452. function IsLinkAbsolute(ALink: String): boolean;
  1453. var
  1454. i: integer;
  1455. begin
  1456. Result := false;
  1457. for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do
  1458. if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin
  1459. Result := true;
  1460. break;
  1461. end;
  1462. end;
  1463. {$IFDEF EXCEPTION_STACK}
  1464. function DumpExceptionCallStack(E: Exception):String;
  1465. var
  1466. I: Integer;
  1467. Frames: PPointer;
  1468. begin
  1469. Result := 'Program exception! ' + LineEnding +
  1470. 'Stacktrace:' + LineEnding + LineEnding;
  1471. if E <> nil then begin
  1472. Result := Result + 'Exception class: ' + E.ClassName + LineEnding +
  1473. 'Message: ' + E.Message + LineEnding;
  1474. end;
  1475. Result := Result + BackTraceStrFunc(ExceptAddr);
  1476. Frames := ExceptFrames;
  1477. for I := 0 to ExceptFrameCount - 1 do
  1478. Result := Result + LineEnding + BackTraceStrFunc(Frames[I]);
  1479. end;
  1480. {$ENDIF}
  1481. initialization
  1482. LEOL:=Length(LineEnding);
  1483. end.