dglobals.pp 53 KB

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