dglobals.pp 54 KB

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