dglobals.pp 55 KB

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