dwriter.pp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. * Output string definitions
  6. * Basic writer (output generator) class
  7. See the file COPYING, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. }
  13. unit dWriter;
  14. {$MODE objfpc}
  15. {$H+}
  16. interface
  17. uses Classes, DOM, dGlobals, PasTree, SysUtils;
  18. resourcestring
  19. SErrFileWriting = 'An error occured during writing of file "%s": %s';
  20. SErrInvalidShortDescr = 'Invalid short description';
  21. SErrInvalidDescr = 'Invalid description (illegal XML element: "%s")';
  22. SErrInvalidParaContent = 'Invalid paragraph content';
  23. SErrInvalidElementInList = 'Invalid element in list - only "li" allowed';
  24. SErrInvalidListContent = 'Invalid list content';
  25. SErrInvalidRemarkContent = 'Invalid <remark> content (illegal XML element: "%s")';
  26. SErrListIsEmpty = 'List is empty - need at least one "li" element';
  27. SErrInvalidDefinitionTermContent = 'Invalid content in definition term';
  28. SErrDefinitionEntryMissing = 'Definition entry after definition term is missing';
  29. SErrInvalidBorderValue = 'Invalid "border" value for %s';
  30. SErrInvalidTableContent = 'Invalid table content';
  31. SErrTableRowEmpty = 'Table row is empty (no "td" elements found)';
  32. SErrInvalidContentBeforeSectionTitle = 'Invalid content before section title';
  33. SErrSectionTitleExpected = 'Section title ("title" element) expected';
  34. SErrDescrTagUnknown = 'Warning: Unknown tag "%s" in description';
  35. SErrUnknownEntityReference = 'Warning: Unknown entity reference "&%s;" found';
  36. SErrUnknownLinkID = 'Warning: Target ID of <link> is unknown: "%s"';
  37. SErrUnknownPrintShortID = 'Warning: Target ID of <printshort> is unknown: "%s"';
  38. SErrUnknownLink = 'Could not resolve link to "%s"';
  39. SErralreadyRegistered = 'Class for output format "%s" already registered';
  40. SErrUnknownWriterClass = 'Unknown output format "%s"';
  41. type
  42. // Phony element for pas pages.
  43. TTopicElement = Class(TPaselement)
  44. Constructor Create(const AName: String; AParent: TPasElement); override;
  45. Destructor Destroy; override;
  46. TopicNode : TDocNode;
  47. Previous,
  48. Next : TPasElement;
  49. Subtopics : TList;
  50. end;
  51. { TFPDocWriter }
  52. TFPDocWriter = class
  53. private
  54. FEngine : TFPDocEngine;
  55. FPackage : TPasPackage;
  56. FTopics : TList;
  57. protected
  58. procedure Warning(AContext: TPasElement; const AMsg: String);
  59. procedure Warning(AContext: TPasElement; const AMsg: String;
  60. const Args: array of const);
  61. // function FindShortDescr(const Name: String): TDOMElement;
  62. // Description conversion
  63. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  64. function IsExtShort(Node: TDOMNode): Boolean;
  65. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  66. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  67. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  68. MayBeEmpty: Boolean);
  69. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  70. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  71. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  72. AutoInsertBlock: Boolean);
  73. function ConvertNonSectionBlock(AContext: TPasElement;
  74. Node: TDOMNode): Boolean;
  75. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  76. Node: TDOMNode);
  77. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  78. Function FindTopicElement(Node : TDocNode): TTopicElement;
  79. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  80. procedure DescrBeginBold; virtual; abstract;
  81. procedure DescrEndBold; virtual; abstract;
  82. procedure DescrBeginItalic; virtual; abstract;
  83. procedure DescrEndItalic; virtual; abstract;
  84. procedure DescrBeginEmph; virtual; abstract;
  85. procedure DescrEndEmph; virtual; abstract;
  86. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  87. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  88. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  89. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  90. procedure DescrEndLink; virtual; abstract;
  91. procedure DescrWriteLinebreak; virtual; abstract;
  92. procedure DescrBeginParagraph; virtual; abstract;
  93. procedure DescrEndParagraph; virtual; abstract;
  94. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  95. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  96. procedure DescrEndCode; virtual; abstract;
  97. procedure DescrBeginOrderedList; virtual; abstract;
  98. procedure DescrEndOrderedList; virtual; abstract;
  99. procedure DescrBeginUnorderedList; virtual; abstract;
  100. procedure DescrEndUnorderedList; virtual; abstract;
  101. procedure DescrBeginDefinitionList; virtual; abstract;
  102. procedure DescrEndDefinitionList; virtual; abstract;
  103. procedure DescrBeginListItem; virtual; abstract;
  104. procedure DescrEndListItem; virtual; abstract;
  105. procedure DescrBeginDefinitionTerm; virtual; abstract;
  106. procedure DescrEndDefinitionTerm; virtual; abstract;
  107. procedure DescrBeginDefinitionEntry; virtual; abstract;
  108. procedure DescrEndDefinitionEntry; virtual; abstract;
  109. procedure DescrBeginSectionTitle; virtual; abstract;
  110. procedure DescrBeginSectionBody; virtual; abstract;
  111. procedure DescrEndSection; virtual; abstract;
  112. procedure DescrBeginRemark; virtual; abstract;
  113. procedure DescrEndRemark; virtual; abstract;
  114. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  115. procedure DescrEndTable; virtual; abstract;
  116. procedure DescrBeginTableCaption; virtual; abstract;
  117. procedure DescrEndTableCaption; virtual; abstract;
  118. procedure DescrBeginTableHeadRow; virtual; abstract;
  119. procedure DescrEndTableHeadRow; virtual; abstract;
  120. procedure DescrBeginTableRow; virtual; abstract;
  121. procedure DescrEndTableRow; virtual; abstract;
  122. procedure DescrBeginTableCell; virtual; abstract;
  123. procedure DescrEndTableCell; virtual; abstract;
  124. public
  125. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  126. destructor Destroy; override;
  127. property Engine : TFPDocEngine read FEngine;
  128. Property Package : TPasPackage read FPackage;
  129. Property Topics : TList Read FTopics;
  130. // Should return True if option was succesfully interpreted.
  131. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  132. Class Procedure Usage(List : TStrings); virtual;
  133. procedure WriteDoc; virtual; Abstract;
  134. procedure WriteDescr(Element: TPasElement);
  135. procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
  136. procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
  137. Procedure FPDocError(Msg : String);
  138. Procedure FPDocError(Fmt : String; Args : Array of Const);
  139. Function ShowMember(M : TPasElement) : boolean;
  140. Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  141. end;
  142. TFPDocWriterClass = Class of TFPDocWriter;
  143. EFPDocWriterError = Class(Exception);
  144. // Register backend
  145. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  146. // UnRegister backend
  147. Procedure UnRegisterWriter(Const AName : String);
  148. // Return back end class. Exception if not found.
  149. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  150. // Return index of back end class.
  151. Function FindWriterClass(AName : String) : Integer;
  152. // List of backend in name=descr form.
  153. Procedure EnumWriters(List : TStrings);
  154. implementation
  155. { ---------------------------------------------------------------------
  156. Writer registration
  157. ---------------------------------------------------------------------}
  158. Type
  159. { TWriterRecord }
  160. TWriterRecord = Class(TObject)
  161. Private
  162. FClass : TFPDocWriterClass;
  163. FName : String;
  164. FDescription : String;
  165. Public
  166. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  167. end;
  168. { TWriterRecord }
  169. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  170. ADescr: String);
  171. begin
  172. FClass:=AClass;
  173. FName:=AName;
  174. FDescription:=ADescr;
  175. end;
  176. Var
  177. Writers : TStringList;
  178. Procedure InitWriterList;
  179. begin
  180. Writers:=TStringList.Create;
  181. Writers.Sorted:=True;
  182. end;
  183. Procedure DoneWriterList;
  184. Var
  185. I : Integer;
  186. begin
  187. For I:=Writers.Count-1 downto 0 do
  188. Writers.Objects[i].Free;
  189. FreeAndNil(Writers);
  190. end;
  191. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  192. begin
  193. If Writers.IndexOf(AName)<>-1 then
  194. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  195. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  196. end;
  197. function FindWriterClass(AName : String) : Integer;
  198. begin
  199. Result:=Writers.IndexOf(AName);
  200. end;
  201. function GetWriterClass(AName : String) : TFPDocWriterClass;
  202. Var
  203. Index : Integer;
  204. begin
  205. Index:=FindWriterClass(AName);
  206. If Index=-1 then
  207. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  208. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  209. end;
  210. // UnRegister backend
  211. Procedure UnRegisterWriter(Const AName : String);
  212. Var
  213. Index : Integer;
  214. begin
  215. Index:=Writers.IndexOf(AName);
  216. If Index=-1 then
  217. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  218. Writers.Objects[Index].Free;
  219. Writers.Delete(Index);
  220. end;
  221. Procedure EnumWriters(List : TStrings);
  222. Var
  223. I : Integer;
  224. begin
  225. List.Clear;
  226. For I:=0 to Writers.Count-1 do
  227. With (Writers.Objects[I] as TWriterRecord) do
  228. List.Add(FName+'='+FDescription);
  229. end;
  230. { ---------------------------------------------------------------------
  231. TFPDocWriter
  232. ---------------------------------------------------------------------}
  233. {
  234. fmtIPF:
  235. begin
  236. if Length(Engine.Output) = 0 then
  237. WriteLn(SCmdLineOutputOptionMissing)
  238. else
  239. CreateIPFDocForPackage(Engine.Package, Engine);
  240. end;
  241. }
  242. Constructor TFPDocWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
  243. begin
  244. inherited Create;
  245. FEngine := AEngine;
  246. FPackage := APackage;
  247. FTopics:=Tlist.Create;
  248. end;
  249. destructor TFPDocWriter.Destroy;
  250. Var
  251. i : integer;
  252. begin
  253. For I:=0 to FTopics.Count-1 do
  254. TTopicElement(FTopics[i]).Free;
  255. FTopics.Free;
  256. Inherited;
  257. end;
  258. function TFPDocWriter.InterpretOption(Const Cmd,Arg : String): Boolean;
  259. begin
  260. Result:=False;
  261. end;
  262. Class procedure TFPDocWriter.Usage(List: TStrings);
  263. begin
  264. // Do nothing.
  265. end;
  266. Function TFPDocWriter.FindTopicElement(Node : TDocNode): TTopicElement;
  267. Var
  268. I : Integer;
  269. begin
  270. Result:=Nil;
  271. I:=FTopics.Count-1;
  272. While (I>=0) and (Result=Nil) do
  273. begin
  274. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  275. Result:=TTopicElement(FTopics[i]);
  276. Dec(I);
  277. end;
  278. end;
  279. { ---------------------------------------------------------------------
  280. Generic documentation node conversion
  281. ---------------------------------------------------------------------}
  282. function IsContentNodeType(Node: TDOMNode): Boolean;
  283. begin
  284. Result := (Node.NodeType = ELEMENT_NODE) or (Node.NodeType = TEXT_NODE) or
  285. (Node.NodeType = ENTITY_REFERENCE_NODE);
  286. end;
  287. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  288. begin
  289. if (AContext<>nil) then
  290. WriteLn('[', AContext.PathName, '] ', AMsg)
  291. else
  292. WriteLn('[<no context>] ', AMsg);
  293. end;
  294. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  295. const Args: array of const);
  296. begin
  297. Warning(AContext, Format(AMsg, Args));
  298. end;
  299. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  300. var
  301. Child: TDOMNode;
  302. begin
  303. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  304. Result := True
  305. else
  306. begin
  307. Child := Node.FirstChild;
  308. while Assigned(Child) do
  309. begin
  310. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  311. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  312. begin
  313. Result := False;
  314. exit;
  315. end;
  316. Child := Child.NextSibling;
  317. end;
  318. end;
  319. Result := True;
  320. end;
  321. { Check wether the nodes starting with the node given as argument make up an
  322. 'extshort' production. }
  323. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  324. begin
  325. while Assigned(Node) do
  326. begin
  327. if Node.NodeType = ELEMENT_NODE then
  328. if (Node.NodeName <> 'br') and
  329. (Node.NodeName <> 'link') and
  330. (Node.NodeName <> 'b') and
  331. (Node.NodeName <> 'file') and
  332. (Node.NodeName <> 'i') and
  333. (Node.NodeName <> 'kw') and
  334. (Node.NodeName <> 'printshort') and
  335. (Node.NodeName <> 'var') then
  336. begin
  337. Result := False;
  338. exit;
  339. end;
  340. Node := Node.NextSibling;
  341. end;
  342. Result := True;
  343. end;
  344. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  345. El: TDOMElement): Boolean;
  346. var
  347. Node: TDOMNode;
  348. begin
  349. Result := False;
  350. if not Assigned(El) then
  351. exit;
  352. Node := El.FirstChild;
  353. while Assigned(Node) do
  354. begin
  355. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  356. ConvertLink(AContext, TDOMElement(Node))
  357. else
  358. if not ConvertBaseShort(AContext, Node) then
  359. exit;
  360. Node := Node.NextSibling;
  361. end;
  362. Result := True;
  363. end;
  364. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  365. Node: TDOMNode): Boolean;
  366. function ConvertText: DOMString;
  367. var
  368. s: String;
  369. i: Integer;
  370. begin
  371. if Node.NodeType = TEXT_NODE then
  372. begin
  373. s := Node.NodeValue;
  374. i := 1;
  375. SetLength(Result, 0);
  376. while i <= Length(s) do
  377. if s[i] = #13 then
  378. begin
  379. Result := Result + ' ';
  380. Inc(i);
  381. if s[i] = #10 then
  382. Inc(i);
  383. end else if s[i] = #10 then
  384. begin
  385. Result := Result + ' ';
  386. Inc(i);
  387. end else
  388. begin
  389. Result := Result + s[i];
  390. Inc(i);
  391. end;
  392. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  393. if Node.NodeName = 'fpc' then
  394. Result := 'Free Pascal'
  395. else if Node.NodeName = 'delphi' then
  396. Result := 'Delphi'
  397. else
  398. begin
  399. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  400. Result := Node.NodeName;
  401. end
  402. else if Node.NodeType = ELEMENT_NODE then
  403. SetLength(Result, 0);
  404. end;
  405. function ConvertTextContent: DOMString;
  406. begin
  407. SetLength(Result, 0);
  408. Node := Node.FirstChild;
  409. while Assigned(Node) do
  410. begin
  411. Result := Result + ConvertText;
  412. Node := Node.NextSibling;
  413. end;
  414. end;
  415. var
  416. El, DescrEl: TDOMElement;
  417. FPEl: TPasElement;
  418. begin
  419. Result := True;
  420. if Node.NodeType = ELEMENT_NODE then
  421. if Node.NodeName = 'b' then
  422. begin
  423. DescrBeginBold;
  424. ConvertBaseShortList(AContext, Node, False);
  425. DescrEndBold;
  426. end else
  427. if Node.NodeName = 'i' then
  428. begin
  429. DescrBeginItalic;
  430. ConvertBaseShortList(AContext, Node, False);
  431. DescrEndItalic;
  432. end else
  433. if Node.NodeName = 'em' then
  434. begin
  435. DescrBeginEmph;
  436. ConvertBaseShortList(AContext, Node, False);
  437. DescrEndEmph;
  438. end else
  439. if Node.NodeName = 'file' then
  440. DescrWriteFileEl(ConvertTextContent)
  441. else if Node.NodeName = 'kw' then
  442. DescrWriteKeywordEl(ConvertTextContent)
  443. else if Node.NodeName = 'printshort' then
  444. begin
  445. El := TDOMElement(Node);
  446. DescrEl := Engine.FindShortDescr(AContext.GetModule, El['id']);
  447. if Assigned(DescrEl) then
  448. ConvertShort(AContext, DescrEl)
  449. else
  450. begin
  451. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  452. DescrBeginBold;
  453. DescrWriteText('#ShortDescr:' + El['id']);
  454. DescrEndBold;
  455. end;
  456. end else if Node.NodeName = 'var' then
  457. DescrWriteVarEl(ConvertTextContent)
  458. else
  459. Result := False
  460. else
  461. DescrWriteText(ConvertText);
  462. end;
  463. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  464. Node: TDOMNode; MayBeEmpty: Boolean);
  465. var
  466. Child: TDOMNode;
  467. begin
  468. Child := Node.FirstChild;
  469. while Assigned(Child) do
  470. begin
  471. if not ConvertBaseShort(AContext, Child) then
  472. Warning(AContext, SErrInvalidShortDescr)
  473. else
  474. MayBeEmpty := True;
  475. Child := Child.NextSibling;
  476. end;
  477. if not MayBeEmpty then
  478. Warning(AContext, SErrInvalidShortDescr)
  479. end;
  480. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  481. begin
  482. DescrBeginLink(El['id']);
  483. if not IsDescrNodeEmpty(El) then
  484. ConvertBaseShortList(AContext, El, True)
  485. else
  486. DescrWriteText(El['id']);
  487. DescrEndLink;
  488. end;
  489. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  490. Node: TDOMNode): Boolean;
  491. begin
  492. Result := False;
  493. while Assigned(Node) do
  494. begin
  495. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  496. ConvertLink(AContext, TDOMElement(Node))
  497. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  498. DescrWriteLinebreak
  499. else
  500. if not ConvertBaseShort(AContext, Node) then
  501. exit;
  502. Node := Node.NextSibling;
  503. end;
  504. Result := True;
  505. end;
  506. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  507. AutoInsertBlock: Boolean);
  508. var
  509. Node, Child: TDOMNode;
  510. ParaCreated: Boolean;
  511. begin
  512. if AutoInsertBlock then
  513. if IsExtShort(El.FirstChild) then
  514. DescrBeginParagraph
  515. else
  516. AutoInsertBlock := False;
  517. Node := El.FirstChild;
  518. if not ConvertExtShort(AContext, Node) then
  519. begin
  520. while Assigned(Node) do
  521. begin
  522. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  523. begin
  524. DescrBeginSectionTitle;
  525. Child := Node.FirstChild;
  526. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  527. begin
  528. if not IsDescrNodeEmpty(Child) then
  529. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  530. Child := Child.NextSibling;
  531. end;
  532. if not Assigned(Child) or (Child.NodeName <> 'title') then
  533. Warning(AContext, SErrSectionTitleExpected)
  534. else
  535. ConvertShort(AContext, TDOMElement(Child));
  536. DescrBeginSectionBody;
  537. if IsExtShort(Child) then
  538. begin
  539. DescrBeginParagraph;
  540. ParaCreated := True;
  541. end else
  542. ParaCreated := False;
  543. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  544. if ParaCreated then
  545. DescrEndParagraph;
  546. DescrEndSection;
  547. end else if not ConvertNonSectionBlock(AContext, Node) then
  548. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  549. Node := Node.NextSibling;
  550. end;
  551. end else
  552. if AutoInsertBlock then
  553. DescrEndParagraph;
  554. end;
  555. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  556. Node: TDOMNode);
  557. begin
  558. if not ConvertExtShort(AContext, Node) then
  559. while Assigned(Node) do
  560. begin
  561. if not ConvertNonSectionBlock(AContext, Node) then
  562. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  563. Node := Node.NextSibling;
  564. end;
  565. end;
  566. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  567. Node: TDOMNode): Boolean;
  568. procedure ConvertCells(Node: TDOMNode);
  569. var
  570. Child: TDOMNode;
  571. IsEmpty: Boolean;
  572. begin
  573. Node := Node.FirstChild;
  574. IsEmpty := True;
  575. while Assigned(Node) do
  576. begin
  577. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  578. begin
  579. DescrBeginTableCell;
  580. Child := Node.FirstChild;
  581. if not ConvertExtShort(AContext, Child) then
  582. while Assigned(Child) do
  583. begin
  584. if not ConvertSimpleBlock(AContext, Child) then
  585. Warning(AContext, SErrInvalidTableContent);
  586. Child := Child.NextSibling;
  587. end;
  588. DescrEndTableCell;
  589. IsEmpty := False;
  590. end else
  591. if IsContentNodeType(Node) then
  592. Warning(AContext, SErrInvalidTableContent);
  593. Node := Node.NextSibling;
  594. end;
  595. if IsEmpty then
  596. Warning(AContext, SErrTableRowEmpty);
  597. end;
  598. procedure ConvertTable;
  599. function GetColCount(Node: TDOMNode): Integer;
  600. begin
  601. Result := 0;
  602. Node := Node.FirstChild;
  603. while Assigned(Node) do
  604. begin
  605. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  606. Inc(Result);
  607. Node := Node.NextSibling;
  608. end;
  609. end;
  610. var
  611. s: String;
  612. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  613. ColCount, ThisRowColCount: Integer;
  614. Subnode: TDOMNode;
  615. begin
  616. s := TDOMElement(Node)['border'];
  617. if s = '1' then
  618. HasBorder := True
  619. else
  620. begin
  621. HasBorder := False;
  622. if (Length(s) <> 0) and (s <> '0') then
  623. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  624. end;
  625. // Determine the number of columns
  626. ColCount := 0;
  627. Subnode := Node.FirstChild;
  628. while Assigned(Subnode) do
  629. begin
  630. if Subnode.NodeType = ELEMENT_NODE then
  631. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  632. (Subnode.NodeName = 'tr') then
  633. begin
  634. ThisRowColCount := GetColCount(Subnode);
  635. if ThisRowColCount > ColCount then
  636. ColCount := ThisRowColCount;
  637. end;
  638. Subnode := Subnode.NextSibling;
  639. end;
  640. DescrBeginTable(ColCount, HasBorder);
  641. Node := Node.FirstChild;
  642. CaptionPossible := True;
  643. HeadRowPossible := True;
  644. while Assigned(Node) do
  645. begin
  646. if Node.NodeType = ELEMENT_NODE then
  647. if CaptionPossible and (Node.NodeName = 'caption') then
  648. begin
  649. DescrBeginTableCaption;
  650. if not ConvertExtShort(AContext, Node.FirstChild) then
  651. Warning(AContext, SErrInvalidTableContent);
  652. DescrEndTableCaption;
  653. CaptionPossible := False;
  654. end else if HeadRowPossible and (Node.NodeName = 'th') then
  655. begin
  656. DescrBeginTableHeadRow;
  657. ConvertCells(Node);
  658. DescrEndTableHeadRow;
  659. CaptionPossible := False;
  660. HeadRowPossible := False;
  661. end else if Node.NodeName = 'tr' then
  662. begin
  663. DescrBeginTableRow;
  664. ConvertCells(Node);
  665. DescrEndTableRow;
  666. end else
  667. Warning(AContext, SErrInvalidTableContent)
  668. else if IsContentNodeType(Node) then
  669. Warning(AContext, SErrInvalidTableContent);
  670. Node := Node.NextSibling;
  671. end;
  672. DescrEndTable;
  673. end;
  674. begin
  675. if Node.NodeType <> ELEMENT_NODE then
  676. begin
  677. Result := Node.NodeType = COMMENT_NODE;
  678. exit;
  679. end;
  680. if Node.NodeName = 'remark' then
  681. begin
  682. DescrBeginRemark;
  683. Node := Node.FirstChild;
  684. if not ConvertExtShort(AContext, Node) then
  685. while Assigned(Node) do
  686. begin
  687. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  688. ConvertTable
  689. else
  690. if not ConvertSimpleBlock(AContext, Node) then
  691. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  692. Node := Node.NextSibling;
  693. end;
  694. DescrEndRemark;
  695. Result := True;
  696. end else if Node.NodeName = 'table' then
  697. begin
  698. ConvertTable;
  699. Result := True;
  700. end else
  701. Result := ConvertSimpleBlock(AContext, Node);
  702. end;
  703. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  704. Node: TDOMNode): Boolean;
  705. procedure ConvertListItems;
  706. var
  707. Empty: Boolean;
  708. begin
  709. Node := Node.FirstChild;
  710. Empty := True;
  711. while Assigned(Node) do
  712. begin
  713. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  714. then
  715. Warning(AContext, SErrInvalidListContent)
  716. else if Node.NodeType = ELEMENT_NODE then
  717. if Node.NodeName = 'li' then
  718. begin
  719. DescrBeginListItem;
  720. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  721. DescrEndListItem;
  722. Empty := False;
  723. end else
  724. Warning(AContext, SErrInvalidElementInList);
  725. Node := Node.NextSibling;
  726. end;
  727. if Empty then
  728. Warning(AContext, SErrListIsEmpty);
  729. end;
  730. procedure ConvertDefinitionList;
  731. var
  732. Empty, ExpectDTNext: Boolean;
  733. begin
  734. Node := Node.FirstChild;
  735. Empty := True;
  736. ExpectDTNext := True;
  737. while Assigned(Node) do
  738. begin
  739. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  740. then
  741. Warning(AContext, SErrInvalidListContent)
  742. else if Node.NodeType = ELEMENT_NODE then
  743. if ExpectDTNext and (Node.NodeName = 'dt') then
  744. begin
  745. DescrBeginDefinitionTerm;
  746. if not ConvertShort(AContext, TDOMElement(Node)) then
  747. Warning(AContext, SErrInvalidDefinitionTermContent);
  748. DescrEndDefinitionTerm;
  749. Empty := False;
  750. ExpectDTNext := False;
  751. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  752. begin
  753. DescrBeginDefinitionEntry;
  754. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  755. DescrEndDefinitionEntry;
  756. ExpectDTNext := True;
  757. end else
  758. Warning(AContext, SErrInvalidElementInList);
  759. Node := Node.NextSibling;
  760. end;
  761. if Empty then
  762. Warning(AContext, SErrListIsEmpty)
  763. else if not ExpectDTNext then
  764. Warning(AContext, SErrDefinitionEntryMissing);
  765. end;
  766. procedure ProcessCodeBody(Node: TDOMNode);
  767. var
  768. s: String;
  769. i, j: Integer;
  770. begin
  771. Node := Node.FirstChild;
  772. SetLength(s, 0);
  773. while Assigned(Node) do
  774. begin
  775. if Node.NodeType = TEXT_NODE then
  776. begin
  777. s := s + Node.NodeValue;
  778. j := 1;
  779. for i := 1 to Length(s) do
  780. // In XML, linefeeds are normalized to #10 by the parser!
  781. if s[i] = #10 then
  782. begin
  783. DescrWriteCodeLine(Copy(s, j, i - j));
  784. j := i + 1;
  785. end;
  786. if j > 1 then
  787. s := Copy(s, j, Length(s));
  788. end;
  789. Node := Node.NextSibling;
  790. end;
  791. if Length(s) > 0 then
  792. DescrWriteCodeLine(s);
  793. end;
  794. var
  795. s: String;
  796. HasBorder: Boolean;
  797. begin
  798. if Node.NodeType <> ELEMENT_NODE then
  799. begin
  800. Result := False;
  801. exit;
  802. end;
  803. if Node.NodeName = 'p' then
  804. begin
  805. DescrBeginParagraph;
  806. if not ConvertExtShort(AContext, Node.FirstChild) then
  807. Warning(AContext, SErrInvalidParaContent);
  808. DescrEndParagraph;
  809. Result := True;
  810. end else if Node.NodeName = 'code' then
  811. begin
  812. s := TDOMElement(Node)['border'];
  813. if s = '1' then
  814. HasBorder := True
  815. else
  816. begin
  817. if (Length(s) > 0) and (s <> '0') then
  818. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  819. end;
  820. DescrBeginCode(HasBorder, TDOMElement(Node)['highlighter']);
  821. ProcessCodeBody(Node);
  822. DescrEndCode;
  823. Result := True;
  824. end else if Node.NodeName = 'pre' then
  825. begin
  826. DescrBeginCode(False, 'none');
  827. ProcessCodeBody(Node);
  828. DescrEndCode;
  829. Result := True;
  830. end else if Node.NodeName = 'ul' then
  831. begin
  832. DescrBeginUnorderedList;
  833. ConvertListItems;
  834. DescrEndUnorderedList;
  835. Result := True;
  836. end else if Node.NodeName = 'ol' then
  837. begin
  838. DescrBeginOrderedList;
  839. ConvertListItems;
  840. DescrEndOrderedList;
  841. Result := True;
  842. end else if Node.NodeName = 'dl' then
  843. begin
  844. DescrBeginDefinitionList;
  845. ConvertDefinitionList;
  846. DescrEndDefinitionList;
  847. Result := True;
  848. end else
  849. Result := False;
  850. end;
  851. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  852. begin
  853. Inherited Create(AName,AParent);
  854. SubTopics:=TList.Create;
  855. end;
  856. Destructor TTopicElement.Destroy;
  857. begin
  858. // Actual subtopics are freed by TFPDocWriter Topics list.
  859. SubTopics.Free;
  860. Inherited;
  861. end;
  862. procedure TFPDocWriter.WriteDescr(Element: TPasElement);
  863. begin
  864. WriteDescr(ELement,Engine.FindDocNode(Element));
  865. end;
  866. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  867. begin
  868. if Assigned(DocNode) then
  869. begin
  870. if not IsDescrNodeEmpty(DocNode.Descr) then
  871. WriteDescr(Element, DocNode.Descr)
  872. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  873. WriteDescr(Element, DocNode.ShortDescr);
  874. end;
  875. end;
  876. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  877. begin
  878. if Assigned(DescrNode) then
  879. ConvertDescr(AContext, DescrNode, False);
  880. end;
  881. procedure TFPDocWriter.FPDocError(Msg: String);
  882. begin
  883. Raise EFPDocWriterError.Create(Msg);
  884. end;
  885. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  886. begin
  887. FPDocError(Format(Fmt,Args));
  888. end;
  889. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  890. begin
  891. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  892. If Result then
  893. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  894. end;
  895. Procedure TFPDocWriter.GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  896. Var
  897. I : Integer;
  898. M : TPasElement;
  899. begin
  900. List.Clear;
  901. List.Sorted:=False;
  902. for i := 0 to ClassDecl.Members.Count - 1 do
  903. begin
  904. M:=TPasElement(ClassDecl.Members[i]);
  905. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  906. List.AddObject(M.Name,M);
  907. end;
  908. List.Sorted:=False;
  909. end;
  910. initialization
  911. InitWriterList;
  912. finalization
  913. DoneWriterList;
  914. end.