dwriter.pp 29 KB

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