2
0

dwriter.pp 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. 2005-2012 by
  6. various FPC contributors
  7. * Output string definitions
  8. * Basic writer (output generator) class
  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. unit dWriter;
  16. {$MODE objfpc}
  17. {$H+}
  18. {$WARN 5024 off : Parameter "$1" not used}
  19. interface
  20. uses Classes, DOM, contnrs, dGlobals, PasTree, SysUtils, fpdocclasstree;
  21. type
  22. // Phony element for pas pages.
  23. TTopicElement = Class(TPaselement)
  24. TopicNode : TDocNode;
  25. Previous,
  26. Next : TPasElement;
  27. Subtopics : TList;
  28. Constructor Create(const AName: String; AParent: TPasElement); override;
  29. Destructor Destroy; override;
  30. end;
  31. { TFileAllocator }
  32. TFileAllocator = class
  33. private
  34. FSubPageNames: Boolean;
  35. protected
  36. function GetFilePostfix(ASubindex: Integer):String;
  37. public
  38. procedure Create(); overload;
  39. procedure AllocFilename(AElement: TPasElement; ASubindex: Integer); virtual;
  40. function GetFilename(AElement: TPasElement;
  41. ASubindex: Integer): String; virtual; abstract;
  42. function GetRelativePathToTop(AElement: TPasElement): String; virtual;
  43. function GetCSSFilename(ARelativeTo: TPasElement): DOMString; virtual;
  44. property SubPageNames: Boolean read FSubPageNames write FSubPageNames;
  45. end;
  46. TLongNameFileAllocator = class(TFileAllocator)
  47. private
  48. FExtension: String;
  49. public
  50. constructor Create(const AExtension: String);
  51. function GetFilename(AElement: TPasElement; ASubindex: Integer): String; override;
  52. function GetRelativePathToTop(AElement: TPasElement): String; override;
  53. property Extension: String read FExtension;
  54. end;
  55. TWriterLogEvent = Procedure(Sender : TObject; Const Msg : String) of object;
  56. TWriterNoteEvent = Procedure(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean) of object;
  57. { TFPDocWriter }
  58. TFPDocWriter = class
  59. private
  60. FEmitNotes: Boolean;
  61. FEngine : TFPDocEngine;
  62. FPackage : TPasPackage;
  63. FContext : TPasElement;
  64. FTopics : TList;
  65. FImgExt : String;
  66. FBeforeEmitNote : TWriterNoteEvent;
  67. procedure ConvertURL(AContext: TPasElement; El: TDOMElement);
  68. procedure CreateClassTree;
  69. protected
  70. TreeClass: TClassTreeBuilder; // Global class tree
  71. TreeInterface: TClassTreeBuilder; // Global interface tree
  72. procedure AddElementsFromList(L: TStrings; List: TFPList; UsePathName : Boolean = False);
  73. Procedure DoLog(Const Msg : String);
  74. Procedure DoLog(Const Fmt : String; Args : Array of const);
  75. Procedure OutputResults(); virtual;
  76. procedure Warning(AContext: TPasElement; const AMsg: String);
  77. procedure Warning(AContext: TPasElement; const AMsg: String;
  78. const Args: array of const);
  79. // function FindShortDescr(const Name: String): TDOMElement;
  80. // Description conversion
  81. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  82. function IsExtShort(Node: TDOMNode): Boolean;
  83. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  84. function ConvertNotes(AContext: TPasElement; El: TDOMElement): Boolean; virtual;
  85. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  86. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  87. MayBeEmpty: Boolean);
  88. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  89. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  90. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  91. AutoInsertBlock: Boolean);
  92. function ConvertNonSectionBlock(AContext: TPasElement;
  93. Node: TDOMNode): Boolean;
  94. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  95. Node: TDOMNode);
  96. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  97. Function FindTopicElement(Node : TDocNode): TTopicElement;
  98. Procedure ConvertImage(El : TDomElement);
  99. Procedure DescrEmitNotesHeader(AContext : TPasElement); virtual;
  100. Procedure DescrEmitNotesFooter(AContext : TPasElement); virtual;
  101. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  102. procedure DescrBeginBold; virtual; abstract;
  103. procedure DescrEndBold; virtual; abstract;
  104. procedure DescrBeginItalic; virtual; abstract;
  105. procedure DescrEndItalic; virtual; abstract;
  106. procedure DescrBeginUnderline; virtual; abstract;
  107. procedure DescrEndUnderline; virtual; abstract;
  108. procedure DescrBeginEmph; virtual; abstract;
  109. procedure DescrEndEmph; virtual; abstract;
  110. procedure DescrWriteImageEl(const AFileName, ACaption,ALinkName : DOMString); virtual;
  111. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  112. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  113. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  114. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  115. procedure DescrEndLink; virtual; abstract;
  116. procedure DescrBeginURL(const AURL: DOMString); virtual; abstract;
  117. procedure DescrEndURL; virtual; abstract;
  118. procedure DescrWriteLinebreak; virtual; abstract;
  119. procedure DescrBeginParagraph; virtual; abstract;
  120. procedure DescrEndParagraph; virtual; abstract;
  121. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  122. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  123. procedure DescrEndCode; virtual; abstract;
  124. procedure DescrBeginOrderedList; virtual; abstract;
  125. procedure DescrEndOrderedList; virtual; abstract;
  126. procedure DescrBeginUnorderedList; virtual; abstract;
  127. procedure DescrEndUnorderedList; virtual; abstract;
  128. procedure DescrBeginDefinitionList; virtual; abstract;
  129. procedure DescrEndDefinitionList; virtual; abstract;
  130. procedure DescrBeginListItem; virtual; abstract;
  131. procedure DescrEndListItem; virtual; abstract;
  132. procedure DescrBeginDefinitionTerm; virtual; abstract;
  133. procedure DescrEndDefinitionTerm; virtual; abstract;
  134. procedure DescrBeginDefinitionEntry; virtual; abstract;
  135. procedure DescrEndDefinitionEntry; virtual; abstract;
  136. procedure DescrBeginSectionTitle; virtual; abstract;
  137. procedure DescrBeginSectionBody; virtual; abstract;
  138. procedure DescrEndSection; virtual; abstract;
  139. procedure DescrBeginRemark; virtual; abstract;
  140. procedure DescrEndRemark; virtual; abstract;
  141. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  142. procedure DescrEndTable; virtual; abstract;
  143. procedure DescrBeginTableCaption; virtual; abstract;
  144. procedure DescrEndTableCaption; virtual; abstract;
  145. procedure DescrBeginTableHeadRow; virtual; abstract;
  146. procedure DescrEndTableHeadRow; virtual; abstract;
  147. procedure DescrBeginTableRow; virtual; abstract;
  148. procedure DescrEndTableRow; virtual; abstract;
  149. procedure DescrBeginTableCell; virtual; abstract;
  150. procedure DescrEndTableCell; virtual; abstract;
  151. procedure PrepareDocumentation; virtual;
  152. // Descendents must override this.
  153. procedure DoWriteDocumentation; virtual; Abstract;
  154. Property CurrentContext : TPasElement Read FContext ;
  155. public
  156. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  157. destructor Destroy; override;
  158. procedure AddModuleIdentifiers(AModule: TPasModule; L: TStrings);
  159. property Engine : TFPDocEngine read FEngine;
  160. Property Package : TPasPackage read FPackage;
  161. Property Topics : TList Read FTopics;
  162. Property ImageExtension : String Read FImgExt Write FImgExt;
  163. // Should return True if option was succesfully interpreted.
  164. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  165. Class Function FileNameExtension : String; virtual;
  166. Class Procedure Usage(List : TStrings); virtual;
  167. Class procedure SplitImport(var AFilename, ALinkPrefix: String); virtual;
  168. // Here we start the generation of documentation
  169. procedure WriteDocumentation;
  170. Function WriteDescr(Element: TPasElement) : TDocNode;
  171. procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
  172. procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
  173. Procedure FPDocError(Msg : String);
  174. Procedure FPDocError(Fmt : String; Args : Array of Const);
  175. Function ShowMember(M : TPasElement) : boolean;
  176. Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  177. Property EmitNotes : Boolean Read FEmitNotes Write FEmitNotes;
  178. Property BeforeEmitNote : TWriterNoteEvent Read FBeforeEmitNote Write FBeforeEmitNote;
  179. end;
  180. const
  181. // The Multi-Page doc writer identifies each page by it's index.
  182. IdentifierIndex = 0;
  183. // Subpage indices for modules
  184. ResstrSubindex = 1;
  185. ConstsSubindex = 2;
  186. TypesSubindex = 3;
  187. ClassesSubindex = 4;
  188. ProcsSubindex = 5;
  189. VarsSubindex = 6;
  190. // Maybe needed later for topic overview ??
  191. TopicsSubIndex = 7;
  192. IndexSubIndex = 8;
  193. ClassHierarchySubIndex = 9;
  194. InterfaceHierarchySubIndex = 10;
  195. // Subpage indices for classes
  196. PropertiesByInheritanceSubindex = 11;
  197. PropertiesByNameSubindex = 12;
  198. MethodsByInheritanceSubindex = 13;
  199. MethodsByNameSubindex = 14;
  200. EventsByInheritanceSubindex = 15;
  201. EventsByNameSubindex = 16;
  202. Type
  203. { TMultiFileDocWriter }
  204. { TPageInfo }
  205. TPageInfo = class
  206. Public
  207. Element: TPasElement;
  208. SubpageIndex: Integer;
  209. Constructor Create(aElement : TPasElement; aIndex : Integer);
  210. end;
  211. { TLinkData }
  212. TLinkData = Class(TObject)
  213. FPathName,
  214. FLink,
  215. FModuleName : String;
  216. Constructor Create(Const APathName,ALink,AModuleName : string);
  217. end;
  218. TMultiFileDocWriter = Class(TFPDocWriter)
  219. Private
  220. FSubPageNames: Boolean;
  221. FBaseDirectory: String;
  222. FCurDirectory: String;
  223. FModule: TPasModule;
  224. FPageInfos: TFPObjectList; // list of TPageInfo objects
  225. FLinkUnresolvedCnt: Integer;
  226. FOutputPageNames: TStringList;
  227. function GetOutputPageNames: TStrings;
  228. function GetPageCount: Integer;
  229. function LinkFix(ALink:String):String;
  230. Protected
  231. FAllocator: TFileAllocator;
  232. Procedure LinkUnresolvedInc();
  233. // General resolving routine
  234. function ResolveLinkID(const Name: String): DOMString;
  235. // Simplified resolving routine. Excluded last path after dot
  236. function ResolveLinkIDUnStrict(const Name: String): DOMString;
  237. function ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
  238. function ResolveLinkWithinPackage(AElement: TPasElement; ASubpageIndex: Integer): String;
  239. procedure PrepareDocumentation; override;
  240. function CreateAllocator() : TFileAllocator; virtual; abstract;
  241. Procedure OutputResults(); override;
  242. // aFileName is the filename allocated by the Allocator, nothing prefixed.
  243. procedure WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); virtual; abstract;
  244. procedure AllocatePages; virtual;
  245. // Default page allocation mechanism.
  246. function AddPage(AElement: TPasElement; ASubpageIndex: Integer): TPageInfo; virtual;
  247. procedure AddPages(AElement: TPasElement; ASubpageIndex: Integer; AList: TFPList); virtual;
  248. procedure AddTopicPages(AElement: TPasElement); virtual;
  249. procedure AllocateClassMemberPages(AModule: TPasModule; LinkList: TObjectList); virtual;
  250. procedure AllocateModulePages(AModule: TPasModule; LinkList: TObjectList); virtual;
  251. procedure AllocatePackagePages; virtual;
  252. // Prefix every filename generated with the result of this.
  253. function GetFileBaseDir(aOutput: String): String; virtual;
  254. function ModuleHasClasses(AModule: TPasModule): Boolean;
  255. // Allocate pages etc.
  256. Procedure DoWriteDocumentation; override;
  257. Function MustGeneratePage(aFileName : String) : Boolean; virtual;
  258. Property PageInfos : TFPObjectList Read FPageInfos;
  259. Property SubPageNames: Boolean Read FSubPageNames;
  260. Public
  261. constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
  262. Destructor Destroy; override;
  263. class procedure Usage(List: TStrings); override;
  264. function InterpretOption(const Cmd, Arg: String): boolean; override;
  265. property PageCount: Integer read GetPageCount;
  266. Property Allocator : TFileAllocator Read FAllocator;
  267. Property Module: TPasModule Read FModule Write FModule;
  268. Property CurDirectory: String Read FCurDirectory Write FCurDirectory; // relative to curdir of process
  269. property BaseDirectory: String read FBaseDirectory Write FBaseDirectory; // relative path to package base directory
  270. Property OutputPageNames : TStrings Read GetOutputPageNames;
  271. end;
  272. TFPDocWriterClass = Class of TFPDocWriter;
  273. EFPDocWriterError = Class(Exception);
  274. // Member Filter Callback type
  275. TMemberFilter = function(AMember: TPasElement): Boolean;
  276. // Filter Callbacks
  277. function PropertyFilter(AMember: TPasElement): Boolean;
  278. function MethodFilter(AMember: TPasElement): Boolean;
  279. function EventFilter(AMember: TPasElement): Boolean;
  280. // Register backend
  281. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  282. // UnRegister backend
  283. Procedure UnRegisterWriter(Const AName : String);
  284. // Return back end class. Exception if not found.
  285. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  286. // Return index of back end class.
  287. Function FindWriterClass(AName : String) : Integer;
  288. // List of backend in name=descr form.
  289. Procedure EnumWriters(List : TStrings);
  290. // Sort elements on name
  291. function SortPasElements(Item1, Item2: Pointer): Integer;
  292. implementation
  293. uses strutils, fpdocstrs;
  294. function SortPasElements(Item1, Item2: Pointer): Integer;
  295. begin
  296. Result:=CompareText(TPasElement(Item1).Name,TPasElement(Item2).Name)
  297. end;
  298. { ---------------------------------------------------------------------
  299. Filter callbacks
  300. ---------------------------------------------------------------------}
  301. function PropertyFilter(AMember: TPasElement): Boolean;
  302. begin
  303. Result := (AMember.ClassType = TPasProperty) and
  304. (Copy(AMember.Name, 1, 2) <> 'On');
  305. end;
  306. function MethodFilter(AMember: TPasElement): Boolean;
  307. begin
  308. Result := AMember.InheritsFrom(TPasProcedureBase);
  309. // Writeln(aMember.Name,' (',aMember.ClassName,') is Method ',Result);
  310. end;
  311. function EventFilter(AMember: TPasElement): Boolean;
  312. begin
  313. Result := (AMember.ClassType = TPasProperty) and
  314. (Copy(AMember.Name, 1, 2) = 'On');
  315. end;
  316. { ---------------------------------------------------------------------
  317. Writer registration
  318. ---------------------------------------------------------------------}
  319. Type
  320. { TWriterRecord }
  321. TWriterRecord = Class(TObject)
  322. Private
  323. FClass : TFPDocWriterClass;
  324. FName : String;
  325. FDescription : String;
  326. Public
  327. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  328. end;
  329. { TPageInfo }
  330. constructor TPageInfo.Create(aElement: TPasElement; aIndex: Integer);
  331. begin
  332. Element:=aELement;
  333. SubpageIndex:=aIndex;
  334. end;
  335. { TLinkData }
  336. constructor TLinkData.Create(Const APathName, ALink, AModuleName: string);
  337. begin
  338. FPathName:=APathName;
  339. FLink:=ALink;
  340. FModuleName:=AModuleName;
  341. end;
  342. { TMultiFileDocWriter }
  343. constructor TMultiFileDocWriter.Create(APackage: TPasPackage;
  344. AEngine: TFPDocEngine);
  345. begin
  346. inherited Create(APackage, AEngine);
  347. FPageInfos:=TFPObjectList.Create;
  348. FSubPageNames:= False;
  349. FLinkUnresolvedCnt:=0;
  350. end;
  351. destructor TMultiFileDocWriter.Destroy;
  352. begin
  353. FreeAndNil(FPageInfos);
  354. FreeAndNil(FAllocator);
  355. inherited Destroy;
  356. end;
  357. function TMultiFileDocWriter.GetPageCount: Integer;
  358. begin
  359. Result := PageInfos.Count;
  360. end;
  361. function TMultiFileDocWriter.GetOutputPageNames: TStrings;
  362. begin
  363. If (FoutputPageNames=Nil) then
  364. begin
  365. FOutputPageNames:=TStringList.Create;
  366. FOutputPageNames.Sorted:=True;
  367. end;
  368. Result:=FOutputPageNames;
  369. end;
  370. procedure TMultiFileDocWriter.OutputResults();
  371. begin
  372. DoLog('Unresolved links: %d', [FLinkUnresolvedCnt]);
  373. inherited OutputResults();
  374. end;
  375. procedure TMultiFileDocWriter.LinkUnresolvedInc();
  376. begin
  377. Inc(FLinkUnresolvedCnt);
  378. end;
  379. function TMultiFileDocWriter.ResolveLinkID(const Name: String): DOMString;
  380. var
  381. res: String;
  382. begin
  383. res:=Engine.ResolveLink(Module,Name, True);
  384. // engine can return backslashes on Windows
  385. res:= LinkFix(res);
  386. Result:=UTF8Decode(res);
  387. end;
  388. function TMultiFileDocWriter.ResolveLinkIDUnStrict(const Name: String
  389. ): DOMString;
  390. var
  391. idDot, idLast: Integer;
  392. res: String;
  393. begin
  394. res:=Engine.ResolveLink(Module,Name, True);
  395. if res = '' then
  396. begin
  397. // do simplify on one level from end.
  398. // TOCO: I want to move that code to last check of Engine.ResolveLink() for not Strict
  399. IdDot:= Pos('.', Name);
  400. IdLast:= 0;
  401. // search last dot
  402. while idDot > 0 do
  403. begin
  404. IdLast:= idDot;
  405. IdDot:= Pos('.', Name, IdLast+1);
  406. end;
  407. if idLast > 0 then
  408. // have cut last element
  409. res:= Engine.ResolveLink(Module, Copy(Name, 1, IdLast-1), True);
  410. end;
  411. res:= LinkFix(res);
  412. Result:=UTF8Decode(res);
  413. end;
  414. function TMultiFileDocWriter.LinkFix(ALink: String): String;
  415. var
  416. res, s:String;
  417. begin
  418. res:= ALink;
  419. if Length(res) > 0 then
  420. begin
  421. // If the link is in the same directory as current dir, then remove the directory part.
  422. s:=Copy(res, 1, Length(CurDirectory) + 1);
  423. if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
  424. res := Copy(res, Length(CurDirectory) + 2, Length(res))
  425. else if not IsLinkAbsolute(res) then
  426. res := BaseDirectory + res;
  427. end;
  428. Result:= res;
  429. end;
  430. { Used for:
  431. - <link> elements in descriptions
  432. - "see also" entries
  433. - AppendHyperlink (for unresolved parse tree element links)
  434. }
  435. function TMultiFileDocWriter.ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
  436. begin
  437. Result:=ResolveLinkID(Name);
  438. If (Result='') and (AUnitName<>'') and (length(Name)>0) and (Name[1]<>'#') then
  439. Result:=ResolveLinkID(AUnitName+'.'+Name);
  440. end;
  441. function TMultiFileDocWriter.ResolveLinkWithinPackage(AElement: TPasElement;
  442. ASubpageIndex: Integer): String;
  443. var
  444. ParentEl: TPasElement;
  445. s : String;
  446. begin
  447. ParentEl := AElement;
  448. while Assigned(ParentEl) and not (ParentEl.ClassType = TPasPackage) do
  449. ParentEl := ParentEl.Parent;
  450. if Assigned(ParentEl) and (TPasPackage(ParentEl) = Engine.Package) then
  451. begin
  452. Result := Allocator.GetFilename(AElement, ASubpageIndex);
  453. // engine/allocator can return backslashes on Windows
  454. s:=Copy(Result, 1, Length(CurDirectory) + 1);
  455. if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
  456. Result := Copy(Result, Length(CurDirectory) + 2, Length(Result))
  457. else
  458. Result := BaseDirectory + Result;
  459. end else
  460. SetLength(Result, 0);
  461. end;
  462. procedure TMultiFileDocWriter.PrepareDocumentation;
  463. begin
  464. inherited PrepareDocumentation;
  465. FAllocator:= CreateAllocator();
  466. FAllocator.SubPageNames:= SubPageNames;
  467. end;
  468. function TMultiFileDocWriter.AddPage(AElement: TPasElement;
  469. ASubpageIndex: Integer): TPageInfo;
  470. begin
  471. Result:= TPageInfo.Create(aElement,aSubPageIndex);
  472. PageInfos.Add(Result);
  473. Allocator.AllocFilename(AElement, ASubpageIndex);
  474. if ASubpageIndex = 0 then
  475. Engine.AddLink(AElement.PathName,Allocator.GetFilename(AElement, ASubpageIndex));
  476. end;
  477. procedure TMultiFileDocWriter.AddTopicPages(AElement: TPasElement);
  478. var
  479. PreviousTopic,
  480. TopicElement : TTopicElement;
  481. DocNode,
  482. TopicNode : TDocNode;
  483. begin
  484. DocNode:=Engine.FindDocNode(AElement);
  485. If not Assigned(DocNode) then
  486. exit;
  487. TopicNode:=DocNode.FirstChild;
  488. PreviousTopic:=Nil;
  489. While Assigned(TopicNode) do
  490. begin
  491. If TopicNode.TopicNode then
  492. begin
  493. TopicElement:=TTopicElement.Create(TopicNode.Name,AElement);
  494. Topics.Add(TopicElement);
  495. TopicElement.TopicNode:=TopicNode;
  496. TopicElement.Previous:=PreviousTopic;
  497. If Assigned(PreviousTopic) then
  498. PreviousTopic.Next:=TopicElement;
  499. PreviousTopic:=TopicElement;
  500. if AElement is TTopicElement then
  501. TTopicElement(AElement).SubTopics.Add(TopicElement);
  502. AddPage(TopicElement,IdentifierIndex);
  503. if AElement is TTopicElement then
  504. TTopicElement(AElement).SubTopics.Add(TopicElement)
  505. else // Only one level of recursion.
  506. AddTopicPages(TopicElement);
  507. end;
  508. TopicNode:=TopicNode.NextSibling;
  509. end;
  510. end;
  511. function TMultiFileDocWriter.ModuleHasClasses(AModule: TPasModule): Boolean;
  512. begin
  513. result:=assigned(AModule)
  514. and assigned(AModule.InterfaceSection)
  515. and assigned(AModule.InterfaceSection.Classes)
  516. and (AModule.InterfaceSection.Classes.Count>0);
  517. end;
  518. procedure TMultiFileDocWriter.AddPages(AElement: TPasElement; ASubpageIndex: Integer;
  519. AList: TFPList);
  520. var
  521. i,j: Integer;
  522. R : TPasRecordtype;
  523. FPEl : TPasElement;
  524. DocNode: TDocNode;
  525. begin
  526. if AList.Count > 0 then
  527. begin
  528. AddPage(AElement, ASubpageIndex);
  529. for i := 0 to AList.Count - 1 do
  530. begin
  531. AddPage(TPasElement(AList[i]), 0);
  532. if (TObject(AList[i]) is TPasRecordType) then
  533. begin
  534. R:=TObject(AList[I]) as TPasRecordType;
  535. For J:=0 to R.Members.Count-1 do
  536. begin
  537. FPEl:=TPasElement(R.Members[J]);
  538. if ((FPEL is TPasProperty) or (FPEL is TPasProcedureBase))
  539. and Engine.ShowElement(FPEl) then
  540. begin
  541. DocNode := Engine.FindDocNode(FPEl);
  542. if Assigned(DocNode) then
  543. AddPage(FPEl, 0);
  544. end;
  545. end;
  546. end;
  547. end;
  548. end;
  549. end;
  550. procedure TMultiFileDocWriter.AllocateClassMemberPages(AModule: TPasModule;
  551. LinkList: TObjectList);
  552. var
  553. i, j, k: Integer;
  554. ClassEl: TPasClassType;
  555. FPEl, AncestorMemberEl: TPasElement;
  556. DocNode: TDocNode;
  557. ALink : DOMString;
  558. DidAutolink: Boolean;
  559. begin
  560. for i := 0 to AModule.InterfaceSection.Classes.Count - 1 do
  561. begin
  562. ClassEl := TPasClassType(AModule.InterfaceSection.Classes[i]);
  563. AddPage(ClassEl, 0);
  564. // !!!: Only add when there are items
  565. AddPage(ClassEl, PropertiesByInheritanceSubindex);
  566. AddPage(ClassEl, PropertiesByNameSubindex);
  567. AddPage(ClassEl, MethodsByInheritanceSubindex);
  568. AddPage(ClassEl, MethodsByNameSubindex);
  569. AddPage(ClassEl, EventsByInheritanceSubindex);
  570. AddPage(ClassEl, EventsByNameSubindex);
  571. for j := 0 to ClassEl.Members.Count - 1 do
  572. begin
  573. FPEl := TPasElement(ClassEl.Members[j]);
  574. if Not Engine.ShowElement(FPEl) then
  575. continue;
  576. DocNode := Engine.FindDocNode(FPEl);
  577. if Assigned(DocNode) then
  578. begin
  579. if Assigned(DocNode.Node) then
  580. ALink:=DocNode.Node['link']
  581. else
  582. ALink:='';
  583. If (ALink<>'') then
  584. LinkList.Add(TLinkData.Create(FPEl.PathName,UTF8Encode(ALink),AModule.name))
  585. else
  586. AddPage(FPEl, 0);
  587. end
  588. else
  589. begin
  590. DidAutolink := False;
  591. if Assigned(ClassEl.AncestorType) and
  592. (ClassEl.AncestorType.ClassType.inheritsfrom(TPasClassType)) then
  593. begin
  594. for k := 0 to TPasClassType(ClassEl.AncestorType).Members.Count - 1 do
  595. begin
  596. AncestorMemberEl :=
  597. TPasElement(TPasClassType(ClassEl.AncestorType).Members[k]);
  598. if AncestorMemberEl.Name = FPEl.Name then
  599. begin
  600. DocNode := Engine.FindDocNode(AncestorMemberEl);
  601. if Assigned(DocNode) then
  602. begin
  603. DidAutolink := True;
  604. Engine.AddLink(FPEl.PathName,
  605. Engine.FindAbsoluteLink(AncestorMemberEl.PathName));
  606. break;
  607. end;
  608. end;
  609. end;
  610. end;
  611. if not DidAutolink then
  612. AddPage(FPEl, 0);
  613. end;
  614. end;
  615. end;
  616. end;
  617. procedure TMultiFileDocWriter.AllocateModulePages(AModule: TPasModule; LinkList : TObjectList);
  618. var
  619. i: Integer;
  620. s: String;
  621. begin
  622. if not assigned(Amodule.Interfacesection) then
  623. exit;
  624. AddPage(AModule, 0);
  625. AddPage(AModule,IndexSubIndex);
  626. AddTopicPages(AModule);
  627. with AModule do
  628. begin
  629. if InterfaceSection.ResStrings.Count > 0 then
  630. begin
  631. AddPage(AModule, ResstrSubindex);
  632. s := Allocator.GetFilename(AModule, ResstrSubindex);
  633. for i := 0 to InterfaceSection.ResStrings.Count - 1 do
  634. with TPasResString(InterfaceSection.ResStrings[i]) do
  635. Engine.AddLink(PathName, s + '#' + LowerCase(Name));
  636. end;
  637. AddPages(AModule, ConstsSubindex, InterfaceSection.Consts);
  638. AddPages(AModule, TypesSubindex, InterfaceSection.Types);
  639. if InterfaceSection.Classes.Count > 0 then
  640. begin
  641. AddPage(AModule, ClassesSubindex);
  642. AllocateClassMemberPages(AModule,LinkList);
  643. end;
  644. AddPages(AModule, ProcsSubindex, InterfaceSection.Functions);
  645. AddPages(AModule, VarsSubindex, InterfaceSection.Variables);
  646. end;
  647. end;
  648. procedure TMultiFileDocWriter.AllocatePackagePages;
  649. Var
  650. I : Integer;
  651. H : Boolean;
  652. begin
  653. if Length(Package.Name) <= 1 then
  654. exit;
  655. AddPage(Package, 0);
  656. AddPage(Package,IndexSubIndex);
  657. I:=0;
  658. H:=False;
  659. While (I<Package.Modules.Count) and Not H do
  660. begin
  661. H:=ModuleHasClasses(TPasModule(Package.Modules[i]));
  662. Inc(I);
  663. end;
  664. if H then
  665. AddPage(Package,ClassHierarchySubIndex);
  666. AddTopicPages(Package);
  667. end;
  668. procedure TMultiFileDocWriter.AllocatePages;
  669. Var
  670. L : TObjectList;
  671. ML : TFPList;
  672. I : Integer;
  673. LD : TLinkData;
  674. aFullLink : String;
  675. begin
  676. // Allocate page for the package itself, if a name is given (i.e. <> '#')
  677. AllocatePackagePages;
  678. ML:=Nil;
  679. L:=TObjectList.Create;
  680. try
  681. ML:=TFPList.Create;
  682. ML.AddList(Package.Modules);
  683. ML.Sort(@SortPasElements);
  684. for i := 0 to ML.Count - 1 do
  685. AllocateModulePages(TPasModule(ML[i]),L);
  686. // Resolve links
  687. For I:=0 to L.Count-1 do
  688. begin
  689. LD:=TLinkData(L[i]);
  690. aFullLink:=ResolveLinkIDInUnit(LD.FLink,LD.FModuleName);
  691. Engine.AddLink(LD.FPathName,UTF8Encode(aFullLink));
  692. end;
  693. finally
  694. L.Free;
  695. ML.Free;
  696. end;
  697. end;
  698. function TMultiFileDocWriter.GetFileBaseDir(aOutput: String) : String;
  699. begin
  700. Result:=aOutput;
  701. if Result<>'' then
  702. Result:=IncludeTrailingPathDelimiter(Result);
  703. end;
  704. procedure TMultiFileDocWriter.DoWriteDocumentation;
  705. procedure CreatePath(const AFilename: String);
  706. var
  707. EndIndex: Integer;
  708. Path: String;
  709. begin
  710. EndIndex := Length(AFilename);
  711. if EndIndex = 0 then
  712. exit;
  713. while not (AFilename[EndIndex] in AllowDirectorySeparators) do
  714. begin
  715. Dec(EndIndex);
  716. if EndIndex = 0 then
  717. exit;
  718. end;
  719. Path := Copy(AFilename, 1, EndIndex - 1);
  720. if not DirectoryExists(Path) then
  721. begin
  722. CreatePath(Path);
  723. MkDir(Path);
  724. end;
  725. end;
  726. var
  727. i: Integer;
  728. FileName : String;
  729. FinalFilename: String;
  730. begin
  731. AllocatePages;
  732. DoLog(SWritingPages, [PageCount]);
  733. if Engine.Output <> '' then
  734. Engine.Output := IncludeTrailingBackSlash(Engine.Output);
  735. for i := 0 to PageInfos.Count - 1 do
  736. with TPageInfo(PageInfos[i]) do
  737. begin
  738. FileName:= Allocator.GetFilename(Element, SubpageIndex);
  739. if MustGeneratePage(FileName) then
  740. begin
  741. FinalFilename := GetFileBaseDir(Engine.Output) + FileName;
  742. CreatePath(FinalFilename);
  743. WriteDocPage(FileName,ELement,SubPageIndex);
  744. end;
  745. end;
  746. end;
  747. function TMultiFileDocWriter.MustGeneratePage(aFileName: String): Boolean;
  748. begin
  749. Result:=Not Assigned(FOutputPageNames);
  750. if Not Result then
  751. Result:=FOutputPageNames.IndexOf(aFileName)<>-1;
  752. end;
  753. class procedure TMultiFileDocWriter.Usage(List: TStrings);
  754. begin
  755. List.AddStrings(['--use-subpagenames', SUsageSubNames]);
  756. List.AddStrings(['--only-pages=LIST', SUsageOnlyPages]);
  757. end;
  758. function TMultiFileDocWriter.InterpretOption(const Cmd, Arg: String): boolean;
  759. Var
  760. I : Integer;
  761. FN : String;
  762. begin
  763. // Writeln('Cmd : ',Cmd);
  764. Result := True;
  765. if Cmd = '--use-subpagenames' then
  766. FSubPageNames:= True
  767. else
  768. if Cmd = '--only-pages' then
  769. begin
  770. Result:=Arg<>'';
  771. if Result then
  772. begin
  773. if Arg[1]='@' then
  774. begin
  775. FN:=Copy(Arg,2,Length(Arg)-1);
  776. OutputPageNames.LoadFromFile(FN);
  777. end
  778. else
  779. begin
  780. For I:=1 to WordCount(Arg,[',']) do
  781. OutputPageNames.Add(ExtractWord(I,Arg,[',']));
  782. end;
  783. Writeln('OutputPagenames ',OutputPagenames.CommaText);
  784. end
  785. end
  786. else
  787. Result:=inherited InterPretOption(Cmd, Arg);
  788. end;
  789. { TWriterRecord }
  790. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  791. ADescr: String);
  792. begin
  793. FClass:=AClass;
  794. FName:=AName;
  795. FDescription:=ADescr;
  796. end;
  797. Var
  798. Writers : TStringList;
  799. Procedure InitWriterList;
  800. begin
  801. Writers:=TStringList.Create;
  802. Writers.Sorted:=True;
  803. end;
  804. Procedure DoneWriterList;
  805. Var
  806. I : Integer;
  807. begin
  808. For I:=Writers.Count-1 downto 0 do
  809. Writers.Objects[i].Free;
  810. FreeAndNil(Writers);
  811. end;
  812. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  813. begin
  814. If Writers.IndexOf(AName)<>-1 then
  815. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  816. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  817. end;
  818. function FindWriterClass(AName : String) : Integer;
  819. begin
  820. Result:=Writers.IndexOf(AName);
  821. end;
  822. function GetWriterClass(AName : String) : TFPDocWriterClass;
  823. Var
  824. Index : Integer;
  825. begin
  826. Index:=FindWriterClass(AName);
  827. If Index=-1 then
  828. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  829. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  830. end;
  831. // UnRegister backend
  832. Procedure UnRegisterWriter(Const AName : String);
  833. Var
  834. Index : Integer;
  835. begin
  836. Index:=Writers.IndexOf(AName);
  837. If Index=-1 then
  838. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  839. Writers.Objects[Index].Free;
  840. Writers.Delete(Index);
  841. end;
  842. Procedure EnumWriters(List : TStrings);
  843. Var
  844. I : Integer;
  845. begin
  846. List.Clear;
  847. For I:=0 to Writers.Count-1 do
  848. With (Writers.Objects[I] as TWriterRecord) do
  849. List.Add(FName+'='+FDescription);
  850. end;
  851. function IsWhitespaceNode(Node: TDOMText): Boolean;
  852. var
  853. I,L: Integer;
  854. S: DOMString;
  855. P : PWideChar;
  856. begin
  857. S := Node.Data;
  858. Result := True;
  859. I:=0;
  860. L:=Length(S);
  861. P:=PWideChar(S);
  862. While Result and (I<L) do
  863. begin
  864. Result:=P^ in [#32,#10,#9,#13];
  865. Inc(P);
  866. Inc(I);
  867. end;
  868. end;
  869. { ---------------------------------------------------------------------
  870. TFileAllocator
  871. ---------------------------------------------------------------------}
  872. function TFileAllocator.GetFilePostfix(ASubindex: Integer): String;
  873. begin
  874. if FSubPageNames then
  875. case ASubindex of
  876. IdentifierIndex: Result:='';
  877. ResstrSubindex: Result:='reestr';
  878. ConstsSubindex: Result:='consts';
  879. TypesSubindex: Result:='types';
  880. ClassesSubindex: Result:='classes';
  881. ProcsSubindex: Result:='procs';
  882. VarsSubindex: Result:='vars';
  883. TopicsSubIndex: Result:='topics';
  884. IndexSubIndex: Result:='indexes';
  885. ClassHierarchySubIndex: Result:='class-tree';
  886. InterfaceHierarchySubIndex: Result:='interface-tree';
  887. PropertiesByInheritanceSubindex: Result:='props';
  888. PropertiesByNameSubindex: Result:='props-n';
  889. MethodsByInheritanceSubindex: Result:='methods';
  890. MethodsByNameSubindex: Result:='methods-n';
  891. EventsByInheritanceSubindex: Result:='events';
  892. EventsByNameSubindex: Result:='events-n';
  893. end
  894. else
  895. Result:= IntToStr(ASubindex);
  896. end;
  897. procedure TFileAllocator.Create();
  898. begin
  899. FSubPageNames:= False;
  900. end;
  901. procedure TFileAllocator.AllocFilename(AElement: TPasElement;
  902. ASubindex: Integer);
  903. begin
  904. end;
  905. function TFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  906. begin
  907. Result:='';
  908. end;
  909. function TFileAllocator.GetCSSFilename(ARelativeTo: TPasElement): DOMString;
  910. begin
  911. Result := Utf8Decode(GetRelativePathToTop(ARelativeTo)) + 'fpdoc.css';
  912. end;
  913. { ---------------------------------------------------------------------
  914. TLongNameFileAllocator
  915. ---------------------------------------------------------------------}
  916. constructor TLongNameFileAllocator.Create(const AExtension: String);
  917. begin
  918. inherited Create;
  919. FExtension := AExtension;
  920. end;
  921. function TLongNameFileAllocator.GetFilename(AElement: TPasElement; ASubindex: Integer): String;
  922. var
  923. n,s: String;
  924. i: Integer;
  925. MElement: TPasElement;
  926. begin
  927. Result:='';
  928. if AElement.ClassType = TPasPackage then
  929. Result := 'index'
  930. else if AElement.ClassType = TPasModule then
  931. Result := LowerCase(AElement.Name) + PathDelim + 'index'
  932. else
  933. begin
  934. if AElement is TPasOperator then
  935. begin
  936. if Assigned(AElement.Parent) then
  937. result:=LowerCase(AElement.Parent.PathName);
  938. With TPasOperator(aElement) do
  939. Result:= Result + 'op-'+OperatorTypeToOperatorName(OperatorType);
  940. s := '';
  941. N:=LowerCase(aElement.Name); // Should not contain any weird chars.
  942. Delete(N,1,Pos('(',N));
  943. i := 1;
  944. Repeat
  945. I:=Pos(',',N);
  946. if I=0 then
  947. I:=Pos(')',N);
  948. if I>1 then
  949. begin
  950. if (S<>'') then
  951. S:=S+'-';
  952. S:=S+Copy(N,1,I-1);
  953. end;
  954. Delete(N,1,I);
  955. until I=0;
  956. // First char is maybe :
  957. if (N<>'') and (N[1]=':') then
  958. Delete(N,1,1);
  959. Result:=Result + '-'+ s + '-' + N;
  960. end else
  961. Result := LowerCase(AElement.PathName);
  962. // cut off Package Name
  963. MElement:= AElement.GetModule;
  964. if Assigned(MElement) then
  965. AElement:= MElement;
  966. Result := Copy(Result, Length(AElement.Parent.Name) + 2, MaxInt);
  967. // to skip dots in unit name
  968. i := Length(AElement.Name);
  969. while (i <= Length(Result)) and (Result[i] <> '.') do
  970. Inc(i);
  971. if (i <= Length(Result)) and (i > 0) then
  972. Result[i] := PathDelim;
  973. end;
  974. if ASubindex > 0 then
  975. Result := Result + '-' + GetFilePostfix(ASubindex);
  976. Result := Result + Extension;
  977. end;
  978. function TLongNameFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  979. begin
  980. if (AElement.ClassType=TPasPackage) then
  981. Result := ''
  982. else if (AElement.ClassType=TTopicElement) then
  983. begin
  984. If (AElement.Parent.ClassType=TTopicElement) then
  985. Result:='../'+GetRelativePathToTop(AElement.Parent)
  986. else if (AElement.Parent.ClassType=TPasPackage) then
  987. Result:=''
  988. else if (AElement.Parent.ClassType=TPasModule) then
  989. Result:='../';
  990. end
  991. else
  992. Result := '../';
  993. end;
  994. { ---------------------------------------------------------------------
  995. TFPDocWriter
  996. ---------------------------------------------------------------------}
  997. {
  998. fmtIPF:
  999. begin
  1000. if Length(Engine.Output) = 0 then
  1001. WriteLn(SCmdLineOutputOptionMissing)
  1002. else
  1003. CreateIPFDocForPackage(Engine.Package, Engine);
  1004. end;
  1005. }
  1006. constructor TFPDocWriter.Create ( APackage: TPasPackage; AEngine: TFPDocEngine
  1007. ) ;
  1008. begin
  1009. inherited Create;
  1010. FEngine := AEngine;
  1011. FPackage := APackage;
  1012. FTopics:=Tlist.Create;
  1013. FImgExt:='.png';
  1014. TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okWithFields);
  1015. TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, [okInterface]);
  1016. CreateClassTree;
  1017. end;
  1018. destructor TFPDocWriter.Destroy;
  1019. Var
  1020. i : integer;
  1021. begin
  1022. For I:=0 to FTopics.Count-1 do
  1023. TTopicElement(FTopics[i]).Free;
  1024. FTopics.Free;
  1025. TreeClass.free;
  1026. TreeInterface.Free;
  1027. Inherited;
  1028. end;
  1029. procedure TFPDocWriter.AddModuleIdentifiers(AModule : TPasModule; L : TStrings);
  1030. begin
  1031. if assigned(AModule.InterfaceSection) Then
  1032. begin
  1033. AddElementsFromList(L,AModule.InterfaceSection.Consts);
  1034. AddElementsFromList(L,AModule.InterfaceSection.Types);
  1035. AddElementsFromList(L,AModule.InterfaceSection.Functions);
  1036. AddElementsFromList(L,AModule.InterfaceSection.Classes);
  1037. AddElementsFromList(L,AModule.InterfaceSection.Variables);
  1038. AddElementsFromList(L,AModule.InterfaceSection.ResStrings);
  1039. end;
  1040. end;
  1041. function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
  1042. begin
  1043. Result:=False;
  1044. end;
  1045. class function TFPDocWriter.FileNameExtension: String;
  1046. begin
  1047. //Override in linear writers with the expected extension.
  1048. Result := ''; //Output must not contain an extension.
  1049. end;
  1050. class procedure TFPDocWriter.Usage(List: TStrings);
  1051. begin
  1052. // Do nothing.
  1053. end;
  1054. class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
  1055. var
  1056. i: integer;
  1057. begin
  1058. //override in HTML and CHM writer
  1059. i := Pos(',', AFilename);
  1060. if i > 0 then
  1061. begin //split CSV into filename and prefix
  1062. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  1063. SetLength(AFilename, i-1);
  1064. end;
  1065. end;
  1066. procedure TFPDocWriter.WriteDocumentation;
  1067. begin
  1068. PrepareDocumentation();
  1069. DoWriteDocumentation();
  1070. OutputResults();
  1071. end;
  1072. function TFPDocWriter.FindTopicElement ( Node: TDocNode ) : TTopicElement;
  1073. Var
  1074. I : Integer;
  1075. begin
  1076. Result:=Nil;
  1077. I:=FTopics.Count-1;
  1078. While (I>=0) and (Result=Nil) do
  1079. begin
  1080. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  1081. Result:=TTopicElement(FTopics[i]);
  1082. Dec(I);
  1083. end;
  1084. end;
  1085. procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
  1086. ALinkName: DOMString);
  1087. begin
  1088. DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
  1089. end;
  1090. procedure TFPDocWriter.PrepareDocumentation;
  1091. begin
  1092. // Ancestors can call AllocatePages();CreateAllocator(); into base class
  1093. end;
  1094. { ---------------------------------------------------------------------
  1095. Generic documentation node conversion
  1096. ---------------------------------------------------------------------}
  1097. function IsContentNodeType(Node: TDOMNode): Boolean;
  1098. begin
  1099. Result := (Node.NodeType = ELEMENT_NODE) or
  1100. ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
  1101. (Node.NodeType = ENTITY_REFERENCE_NODE);
  1102. end;
  1103. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  1104. begin
  1105. if (AContext<>nil) then
  1106. DoLog('[%s] %s',[AContext.PathName,AMsg])
  1107. else
  1108. DoLog('[<no context>] %s', [AMsg]);
  1109. end;
  1110. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  1111. const Args: array of const);
  1112. begin
  1113. Warning(AContext, Format(AMsg, Args));
  1114. end;
  1115. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  1116. var
  1117. Child: TDOMNode;
  1118. begin
  1119. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  1120. Result := True
  1121. else
  1122. begin
  1123. Child := Node.FirstChild;
  1124. while Assigned(Child) do
  1125. begin
  1126. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  1127. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  1128. begin
  1129. Result := False;
  1130. exit;
  1131. end;
  1132. Child := Child.NextSibling;
  1133. end;
  1134. end;
  1135. Result := True;
  1136. end;
  1137. { Check wether the nodes starting with the node given as argument make up an
  1138. 'extshort' production. }
  1139. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  1140. begin
  1141. while Assigned(Node) do
  1142. begin
  1143. if Node.NodeType = ELEMENT_NODE then
  1144. if (Node.NodeName <> 'br') and
  1145. (Node.NodeName <> 'link') and
  1146. (Node.NodeName <> 'url') and
  1147. (Node.NodeName <> 'b') and
  1148. (Node.NodeName <> 'file') and
  1149. (Node.NodeName <> 'i') and
  1150. (Node.NodeName <> 'kw') and
  1151. (Node.NodeName <> 'printshort') and
  1152. (Node.NodeName <> 'var') then
  1153. begin
  1154. Result := False;
  1155. exit;
  1156. end;
  1157. Node := Node.NextSibling;
  1158. end;
  1159. Result := True;
  1160. end;
  1161. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  1162. El: TDOMElement): Boolean;
  1163. var
  1164. Node: TDOMNode;
  1165. begin
  1166. Result := False;
  1167. if not Assigned(El) then
  1168. exit;
  1169. FContext:=AContext;
  1170. try
  1171. Node := El.FirstChild;
  1172. while Assigned(Node) do
  1173. begin
  1174. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1175. ConvertLink(AContext, TDOMElement(Node))
  1176. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1177. ConvertURL(AContext, TDOMElement(Node))
  1178. else
  1179. if not ConvertBaseShort(AContext, Node) then
  1180. exit;
  1181. Node := Node.NextSibling;
  1182. end;
  1183. Result := True;
  1184. finally
  1185. FContext:=Nil;
  1186. end;
  1187. end;
  1188. function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
  1189. ): Boolean;
  1190. Var
  1191. L : TFPList;
  1192. N : TDomNode;
  1193. I : Integer;
  1194. B : Boolean;
  1195. begin
  1196. Result:=Assigned(El) and EmitNotes;
  1197. If Not Result then
  1198. exit;
  1199. L:=TFPList.Create;
  1200. try
  1201. N:=El.FirstChild;
  1202. While Assigned(N) do
  1203. begin
  1204. If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
  1205. begin
  1206. B:=True;
  1207. if Assigned(FBeforeEmitNote) then
  1208. FBeforeEmitNote(Self,TDomElement(N),B);
  1209. If B then
  1210. L.Add(N);
  1211. end;
  1212. N:=N.NextSibling;
  1213. end;
  1214. Result:=L.Count>0;
  1215. If Not Result then
  1216. exit;
  1217. DescrEmitNotesHeader(AContext);
  1218. DescrBeginUnorderedList;
  1219. For i:=0 to L.Count-1 do
  1220. begin
  1221. DescrBeginListItem;
  1222. ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
  1223. DescrEndListItem;
  1224. end;
  1225. DescrEndUnorderedList;
  1226. DescrEmitNotesFooter(AContext);
  1227. finally
  1228. L.Free;
  1229. end;
  1230. end;
  1231. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  1232. Node: TDOMNode): Boolean;
  1233. function ConvertText: DOMString;
  1234. var
  1235. s: DOMString;
  1236. i: Integer;
  1237. begin
  1238. if Node.NodeType = TEXT_NODE then
  1239. begin
  1240. s := Node.NodeValue;
  1241. i := 1;
  1242. Result:='';
  1243. while i <= Length(s) do
  1244. if s[i] = #13 then
  1245. begin
  1246. Result := Result + ' ';
  1247. Inc(i);
  1248. if s[i] = #10 then
  1249. Inc(i);
  1250. end else if s[i] = #10 then
  1251. begin
  1252. Result := Result + ' ';
  1253. Inc(i);
  1254. end else
  1255. begin
  1256. Result := Result + s[i];
  1257. Inc(i);
  1258. end;
  1259. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  1260. if Node.NodeName = 'fpc' then
  1261. Result := 'Free Pascal'
  1262. else if Node.NodeName = 'delphi' then
  1263. Result := 'Delphi'
  1264. else
  1265. begin
  1266. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  1267. Result := Node.NodeName;
  1268. end
  1269. else if Node.NodeType = ELEMENT_NODE then
  1270. SetLength(Result, 0);
  1271. end;
  1272. function ConvertTextContent: DOMString;
  1273. begin
  1274. Result:='';
  1275. Node := Node.FirstChild;
  1276. while Assigned(Node) do
  1277. begin
  1278. Result := Result + ConvertText;
  1279. Node := Node.NextSibling;
  1280. end;
  1281. end;
  1282. var
  1283. El, DescrEl: TDOMElement;
  1284. hlp : TPasElement;
  1285. begin
  1286. Result := True;
  1287. if Node.NodeType = ELEMENT_NODE then
  1288. if Node.NodeName = 'b' then
  1289. begin
  1290. DescrBeginBold;
  1291. ConvertBaseShortList(AContext, Node, False);
  1292. DescrEndBold;
  1293. end else
  1294. if Node.NodeName = 'i' then
  1295. begin
  1296. DescrBeginItalic;
  1297. ConvertBaseShortList(AContext, Node, False);
  1298. DescrEndItalic;
  1299. end else
  1300. if Node.NodeName = 'em' then
  1301. begin
  1302. DescrBeginEmph;
  1303. ConvertBaseShortList(AContext, Node, False);
  1304. DescrEndEmph;
  1305. end else
  1306. if Node.NodeName = 'u' then
  1307. begin
  1308. DescrBeginUnderline;
  1309. ConvertBaseShortList(AContext, Node, False);
  1310. DescrEndUnderline;
  1311. end else
  1312. if Node.NodeName = 'file' then
  1313. DescrWriteFileEl(ConvertTextContent)
  1314. else if Node.NodeName = 'kw' then
  1315. DescrWriteKeywordEl(ConvertTextContent)
  1316. else if Node.NodeName = 'printshort' then
  1317. begin
  1318. El := TDOMElement(Node);
  1319. hlp:=AContext;
  1320. while assigned(hlp) and not (hlp is TPasModule) do
  1321. hlp:=hlp.parent;
  1322. if not (hlp is TPasModule) then
  1323. hlp:=nil;
  1324. DescrEl := Engine.FindShortDescr(TPasModule(hlp), UTF8Encode(El['id']));
  1325. if Assigned(DescrEl) then
  1326. ConvertShort(AContext, DescrEl)
  1327. else
  1328. begin
  1329. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  1330. DescrBeginBold;
  1331. DescrWriteText('#ShortDescr:' + El['id']);
  1332. DescrEndBold;
  1333. end;
  1334. end else if Node.NodeName = 'var' then
  1335. DescrWriteVarEl(ConvertTextContent)
  1336. else
  1337. Result := False
  1338. else
  1339. DescrWriteText(ConvertText);
  1340. end;
  1341. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  1342. Node: TDOMNode; MayBeEmpty: Boolean);
  1343. var
  1344. Child: TDOMNode;
  1345. begin
  1346. Child := Node.FirstChild;
  1347. while Assigned(Child) do
  1348. begin
  1349. if not ConvertBaseShort(AContext, Child) then
  1350. Warning(AContext, SErrInvalidShortDescr)
  1351. else
  1352. MayBeEmpty := True;
  1353. Child := Child.NextSibling;
  1354. end;
  1355. if not MayBeEmpty then
  1356. Warning(AContext, SErrInvalidShortDescr)
  1357. end;
  1358. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  1359. begin
  1360. DescrBeginLink(El['id']);
  1361. if not IsDescrNodeEmpty(El) then
  1362. ConvertBaseShortList(AContext, El, True)
  1363. else
  1364. DescrWriteText(El['id']);
  1365. DescrEndLink;
  1366. end;
  1367. procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
  1368. begin
  1369. DescrBeginURL(El['href']);
  1370. if not IsDescrNodeEmpty(El) then
  1371. ConvertBaseShortList(AContext, El, True)
  1372. else
  1373. DescrWriteText(El['href']);
  1374. DescrEndURL;
  1375. end;
  1376. procedure TFPDocWriter.AddElementsFromList ( L: TStrings; List: TFPList;
  1377. UsePathName: Boolean ) ;
  1378. Var
  1379. I : Integer;
  1380. El : TPasElement;
  1381. N : TDocNode;
  1382. begin
  1383. For I:=0 to List.Count-1 do
  1384. begin
  1385. El:=TPasElement(List[I]);
  1386. N:=Engine.FindDocNode(El);
  1387. if (N=Nil) or (not N.IsSkipped) then
  1388. begin
  1389. if UsePathName then
  1390. L.AddObject(El.PathName,El)
  1391. else
  1392. L.AddObject(El.Name,El);
  1393. If el is TPasEnumType then
  1394. AddElementsFromList(L,TPasEnumType(el).Values);
  1395. end;
  1396. end;
  1397. end;
  1398. procedure TFPDocWriter.CreateClassTree;
  1399. var
  1400. L: TStringList;
  1401. M: TPasModule;
  1402. I:Integer;
  1403. begin
  1404. L:=TStringList.Create;
  1405. try
  1406. For I:=0 to Package.Modules.Count-1 do
  1407. begin
  1408. M:=TPasModule(Package.Modules[i]);
  1409. if Not (M is TPasExternalModule) and assigned(M.InterfaceSection) then
  1410. Self.AddElementsFromList(L,M.InterfaceSection.Classes,True)
  1411. end;
  1412. // You can see this tree by using --format=xml option
  1413. TreeClass.BuildTree(L);
  1414. TreeInterface.BuildTree(L);
  1415. Finally
  1416. L.Free;
  1417. end;
  1418. end;
  1419. procedure TFPDocWriter.DoLog(const Msg: String);
  1420. begin
  1421. If Assigned(FEngine.OnLog) then
  1422. FEngine.OnLog(Self,Msg);
  1423. end;
  1424. procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
  1425. begin
  1426. DoLog(Format(Fmt,Args));
  1427. end;
  1428. procedure TFPDocWriter.OutputResults();
  1429. begin
  1430. DoLog('Package: %s - Documentation process finished.', [FPackage.Name]);
  1431. end;
  1432. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  1433. Node: TDOMNode): Boolean;
  1434. begin
  1435. Result := False;
  1436. while Assigned(Node) do
  1437. begin
  1438. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1439. ConvertLink(AContext, TDOMElement(Node))
  1440. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1441. ConvertURL(AContext, TDOMElement(Node))
  1442. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  1443. DescrWriteLinebreak
  1444. else
  1445. if not ConvertBaseShort(AContext, Node) then
  1446. exit;
  1447. Node := Node.NextSibling;
  1448. end;
  1449. Result := True;
  1450. end;
  1451. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  1452. AutoInsertBlock: Boolean);
  1453. var
  1454. Node, Child: TDOMNode;
  1455. ParaCreated: Boolean;
  1456. begin
  1457. FContext:=AContext;
  1458. try
  1459. if AutoInsertBlock then
  1460. if IsExtShort(El.FirstChild) then
  1461. DescrBeginParagraph
  1462. else
  1463. AutoInsertBlock := False;
  1464. Node := El.FirstChild;
  1465. if not ConvertExtShort(AContext, Node) then
  1466. begin
  1467. while Assigned(Node) do
  1468. begin
  1469. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  1470. begin
  1471. DescrBeginSectionTitle;
  1472. Child := Node.FirstChild;
  1473. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  1474. begin
  1475. if not IsDescrNodeEmpty(Child) then
  1476. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  1477. Child := Child.NextSibling;
  1478. end;
  1479. if not Assigned(Child) or (Child.NodeName <> 'title') then
  1480. Warning(AContext, SErrSectionTitleExpected)
  1481. else
  1482. ConvertShort(AContext, TDOMElement(Child));
  1483. DescrBeginSectionBody;
  1484. if IsExtShort(Child) then
  1485. begin
  1486. DescrBeginParagraph;
  1487. ParaCreated := True;
  1488. end else
  1489. ParaCreated := False;
  1490. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  1491. if ParaCreated then
  1492. DescrEndParagraph;
  1493. DescrEndSection;
  1494. end else if not ConvertNonSectionBlock(AContext, Node) then
  1495. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1496. Node := Node.NextSibling;
  1497. end;
  1498. end else
  1499. if AutoInsertBlock then
  1500. DescrEndParagraph;
  1501. finally
  1502. FContext:=Nil;
  1503. end;
  1504. end;
  1505. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  1506. Node: TDOMNode);
  1507. begin
  1508. if not ConvertExtShort(AContext, Node) then
  1509. while Assigned(Node) do
  1510. begin
  1511. if not ConvertNonSectionBlock(AContext, Node) then
  1512. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1513. Node := Node.NextSibling;
  1514. end;
  1515. end;
  1516. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  1517. Node: TDOMNode): Boolean;
  1518. procedure ConvertCells(Node: TDOMNode);
  1519. var
  1520. Child: TDOMNode;
  1521. IsEmpty: Boolean;
  1522. begin
  1523. Node := Node.FirstChild;
  1524. IsEmpty := True;
  1525. while Assigned(Node) do
  1526. begin
  1527. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1528. begin
  1529. DescrBeginTableCell;
  1530. Child := Node.FirstChild;
  1531. if not ConvertExtShort(AContext, Child) then
  1532. while Assigned(Child) do
  1533. begin
  1534. if not ConvertSimpleBlock(AContext, Child) then
  1535. Warning(AContext, SErrInvalidTableContent);
  1536. Child := Child.NextSibling;
  1537. end;
  1538. DescrEndTableCell;
  1539. IsEmpty := False;
  1540. end else
  1541. if IsContentNodeType(Node) then
  1542. Warning(AContext, SErrInvalidTableContent);
  1543. Node := Node.NextSibling;
  1544. end;
  1545. if IsEmpty then
  1546. Warning(AContext, SErrTableRowEmpty);
  1547. end;
  1548. procedure ConvertTable;
  1549. function GetColCount(Node: TDOMNode): Integer;
  1550. begin
  1551. Result := 0;
  1552. Node := Node.FirstChild;
  1553. while Assigned(Node) do
  1554. begin
  1555. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1556. Inc(Result);
  1557. Node := Node.NextSibling;
  1558. end;
  1559. end;
  1560. var
  1561. s: DOMString;
  1562. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  1563. ColCount, ThisRowColCount: Integer;
  1564. Subnode: TDOMNode;
  1565. begin
  1566. s := TDOMElement(Node)['border'];
  1567. if s = '1' then
  1568. HasBorder := True
  1569. else
  1570. begin
  1571. HasBorder := False;
  1572. if (Length(s) <> 0) and (s <> '0') then
  1573. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  1574. end;
  1575. // Determine the number of columns
  1576. ColCount := 0;
  1577. Subnode := Node.FirstChild;
  1578. while Assigned(Subnode) do
  1579. begin
  1580. if Subnode.NodeType = ELEMENT_NODE then
  1581. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  1582. (Subnode.NodeName = 'tr') then
  1583. begin
  1584. ThisRowColCount := GetColCount(Subnode);
  1585. if ThisRowColCount > ColCount then
  1586. ColCount := ThisRowColCount;
  1587. end;
  1588. Subnode := Subnode.NextSibling;
  1589. end;
  1590. DescrBeginTable(ColCount, HasBorder);
  1591. Node := Node.FirstChild;
  1592. CaptionPossible := True;
  1593. HeadRowPossible := True;
  1594. while Assigned(Node) do
  1595. begin
  1596. if Node.NodeType = ELEMENT_NODE then
  1597. if CaptionPossible and (Node.NodeName = 'caption') then
  1598. begin
  1599. DescrBeginTableCaption;
  1600. if not ConvertExtShort(AContext, Node.FirstChild) then
  1601. Warning(AContext, SErrInvalidTableContent);
  1602. DescrEndTableCaption;
  1603. CaptionPossible := False;
  1604. end else if HeadRowPossible and (Node.NodeName = 'th') then
  1605. begin
  1606. DescrBeginTableHeadRow;
  1607. ConvertCells(Node);
  1608. DescrEndTableHeadRow;
  1609. CaptionPossible := False;
  1610. HeadRowPossible := False;
  1611. end else if Node.NodeName = 'tr' then
  1612. begin
  1613. DescrBeginTableRow;
  1614. ConvertCells(Node);
  1615. DescrEndTableRow;
  1616. end else
  1617. Warning(AContext, SErrInvalidTableContent)
  1618. else if IsContentNodeType(Node) then
  1619. Warning(AContext, SErrInvalidTableContent);
  1620. Node := Node.NextSibling;
  1621. end;
  1622. DescrEndTable;
  1623. end;
  1624. begin
  1625. if Node.NodeType <> ELEMENT_NODE then
  1626. begin
  1627. if Node.NodeType = TEXT_NODE then
  1628. Result := IsWhitespaceNode(TDOMText(Node))
  1629. else
  1630. Result := Node.NodeType = COMMENT_NODE;
  1631. exit;
  1632. end;
  1633. if Node.NodeName = 'remark' then
  1634. begin
  1635. DescrBeginRemark;
  1636. Node := Node.FirstChild;
  1637. if not ConvertExtShort(AContext, Node) then
  1638. while Assigned(Node) do
  1639. begin
  1640. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  1641. ConvertTable
  1642. else
  1643. if not ConvertSimpleBlock(AContext, Node) then
  1644. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  1645. Node := Node.NextSibling;
  1646. end;
  1647. DescrEndRemark;
  1648. Result := True;
  1649. end else if Node.NodeName = 'table' then
  1650. begin
  1651. ConvertTable;
  1652. Result := True;
  1653. end else
  1654. Result := ConvertSimpleBlock(AContext, Node);
  1655. end;
  1656. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  1657. Node: TDOMNode): Boolean;
  1658. procedure ConvertListItems;
  1659. var
  1660. Empty: Boolean;
  1661. begin
  1662. Node := Node.FirstChild;
  1663. Empty := True;
  1664. while Assigned(Node) do
  1665. begin
  1666. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1667. then
  1668. Warning(AContext, SErrInvalidListContent)
  1669. else if Node.NodeType = ELEMENT_NODE then
  1670. if Node.NodeName = 'li' then
  1671. begin
  1672. DescrBeginListItem;
  1673. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1674. DescrEndListItem;
  1675. Empty := False;
  1676. end else
  1677. Warning(AContext, SErrInvalidElementInList);
  1678. Node := Node.NextSibling;
  1679. end;
  1680. if Empty then
  1681. Warning(AContext, SErrListIsEmpty);
  1682. end;
  1683. procedure ConvertDefinitionList;
  1684. var
  1685. Empty, ExpectDTNext: Boolean;
  1686. begin
  1687. Node := Node.FirstChild;
  1688. Empty := True;
  1689. ExpectDTNext := True;
  1690. while Assigned(Node) do
  1691. begin
  1692. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1693. then
  1694. Warning(AContext, SErrInvalidListContent)
  1695. else if Node.NodeType = ELEMENT_NODE then
  1696. if ExpectDTNext and (Node.NodeName = 'dt') then
  1697. begin
  1698. DescrBeginDefinitionTerm;
  1699. if not ConvertShort(AContext, TDOMElement(Node)) then
  1700. Warning(AContext, SErrInvalidDefinitionTermContent);
  1701. DescrEndDefinitionTerm;
  1702. Empty := False;
  1703. ExpectDTNext := False;
  1704. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  1705. begin
  1706. DescrBeginDefinitionEntry;
  1707. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1708. DescrEndDefinitionEntry;
  1709. ExpectDTNext := True;
  1710. end else
  1711. Warning(AContext, SErrInvalidElementInList);
  1712. Node := Node.NextSibling;
  1713. end;
  1714. if Empty then
  1715. Warning(AContext, SErrListIsEmpty)
  1716. else if not ExpectDTNext then
  1717. Warning(AContext, SErrDefinitionEntryMissing);
  1718. end;
  1719. procedure ProcessCodeBody(Node: TDOMNode);
  1720. var
  1721. s: String;
  1722. i, j: Integer;
  1723. begin
  1724. Node := Node.FirstChild;
  1725. S:='';
  1726. while Assigned(Node) do
  1727. begin
  1728. if Node.NodeType = TEXT_NODE then
  1729. begin
  1730. s := s + UTF8Encode(Node.NodeValue);
  1731. j := 1;
  1732. for i := 1 to Length(s) do
  1733. // In XML, linefeeds are normalized to #10 by the parser!
  1734. if s[i] = #10 then
  1735. begin
  1736. DescrWriteCodeLine(Copy(s, j, i - j));
  1737. j := i + 1;
  1738. end;
  1739. if j > 1 then
  1740. s := Copy(s, j, Length(s));
  1741. end;
  1742. Node := Node.NextSibling;
  1743. end;
  1744. if Length(s) > 0 then
  1745. DescrWriteCodeLine(s);
  1746. end;
  1747. var
  1748. s: DOMString;
  1749. HasBorder: Boolean;
  1750. begin
  1751. if Node.NodeType <> ELEMENT_NODE then
  1752. begin
  1753. Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
  1754. exit;
  1755. end;
  1756. if Node.NodeName = 'p' then
  1757. begin
  1758. DescrBeginParagraph;
  1759. if not ConvertExtShort(AContext, Node.FirstChild) then
  1760. Warning(AContext, SErrInvalidParaContent);
  1761. DescrEndParagraph;
  1762. Result := True;
  1763. end else if Node.NodeName = 'code' then
  1764. begin
  1765. s := TDOMElement(Node)['border'];
  1766. if s = '1' then
  1767. HasBorder := True
  1768. else
  1769. begin
  1770. if (Length(s) > 0) and (s <> '0') then
  1771. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  1772. end;
  1773. DescrBeginCode(HasBorder, UTF8Encode(TDOMElement(Node)['highlighter']));
  1774. ProcessCodeBody(Node);
  1775. DescrEndCode;
  1776. Result := True;
  1777. end else if Node.NodeName = 'pre' then
  1778. begin
  1779. DescrBeginCode(False, 'none');
  1780. ProcessCodeBody(Node);
  1781. DescrEndCode;
  1782. Result := True;
  1783. end else if Node.NodeName = 'ul' then
  1784. begin
  1785. DescrBeginUnorderedList;
  1786. ConvertListItems;
  1787. DescrEndUnorderedList;
  1788. Result := True;
  1789. end else if Node.NodeName = 'ol' then
  1790. begin
  1791. DescrBeginOrderedList;
  1792. ConvertListItems;
  1793. DescrEndOrderedList;
  1794. Result := True;
  1795. end else if Node.NodeName = 'dl' then
  1796. begin
  1797. DescrBeginDefinitionList;
  1798. ConvertDefinitionList;
  1799. DescrEndDefinitionList;
  1800. Result := True;
  1801. end else if Node.NodeName = 'img' then
  1802. begin
  1803. begin
  1804. ConvertImage(Node as TDomElement);
  1805. Result:=True;
  1806. end;
  1807. end else
  1808. Result := False;
  1809. end;
  1810. procedure TFPDocWriter.ConvertImage ( El: TDomElement ) ;
  1811. Var
  1812. FN,Cap,LinkName : DOMString;
  1813. begin
  1814. FN:=El['file'];
  1815. Cap:=El['caption'];
  1816. LinkName:=El['name'];
  1817. FN:=UTF8decode(ChangeFileExt(UTF8Encode(FN),ImageExtension));
  1818. DescrWriteImageEl(FN,Cap,LinkName);
  1819. end;
  1820. procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
  1821. begin
  1822. DescrWriteLinebreak;
  1823. DescrBeginBold;
  1824. DescrWriteText(UTF8Decode(SDocNotes));
  1825. DescrEndBold;
  1826. DescrWriteLinebreak;
  1827. end;
  1828. procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
  1829. begin
  1830. DescrWriteLinebreak;
  1831. end;
  1832. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  1833. begin
  1834. Inherited Create(AName,AParent);
  1835. SubTopics:=TList.Create;
  1836. end;
  1837. Destructor TTopicElement.Destroy;
  1838. begin
  1839. // Actual subtopics are freed by TFPDocWriter Topics list.
  1840. SubTopics.Free;
  1841. Inherited;
  1842. end;
  1843. function TFPDocWriter.WriteDescr ( Element: TPasElement ) : TDocNode;
  1844. begin
  1845. Result:=Engine.FindDocNode(Element);
  1846. WriteDescr(ELement,Result);
  1847. end;
  1848. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  1849. begin
  1850. if Assigned(DocNode) then
  1851. begin
  1852. if not IsDescrNodeEmpty(DocNode.Descr) then
  1853. WriteDescr(Element, DocNode.Descr)
  1854. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  1855. WriteDescr(Element, DocNode.ShortDescr);
  1856. end;
  1857. end;
  1858. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  1859. begin
  1860. if Assigned(DescrNode) then
  1861. ConvertDescr(AContext, DescrNode, False);
  1862. end;
  1863. procedure TFPDocWriter.FPDocError(Msg: String);
  1864. begin
  1865. Raise EFPDocWriterError.Create(Msg);
  1866. end;
  1867. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  1868. begin
  1869. FPDocError(Format(Fmt,Args));
  1870. end;
  1871. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  1872. begin
  1873. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  1874. If Result then
  1875. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  1876. end;
  1877. procedure TFPDocWriter.GetMethodList ( ClassDecl: TPasClassType;
  1878. List: TStringList ) ;
  1879. Var
  1880. I : Integer;
  1881. M : TPasElement;
  1882. begin
  1883. List.Clear;
  1884. List.Sorted:=False;
  1885. for i := 0 to ClassDecl.Members.Count - 1 do
  1886. begin
  1887. M:=TPasElement(ClassDecl.Members[i]);
  1888. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  1889. List.AddObject(M.Name,M);
  1890. end;
  1891. List.Sorted:=False;
  1892. end;
  1893. initialization
  1894. InitWriterList;
  1895. finalization
  1896. DoneWriterList;
  1897. end.