dwriter.pp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  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> in unit "%s" 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. TopicNode : TDocNode;
  45. Previous,
  46. Next : TPasElement;
  47. Subtopics : TList;
  48. Constructor Create(const AName: String; AParent: TPasElement); override;
  49. Destructor Destroy; override;
  50. end;
  51. TWriterLogEvent = Procedure(Sender : TObject; Const Msg : String) of object;
  52. TWriterNoteEvent = Procedure(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean) of object;
  53. { TFPDocWriter }
  54. TFPDocWriter = class
  55. private
  56. FEmitNotes: Boolean;
  57. FEngine : TFPDocEngine;
  58. FPackage : TPasPackage;
  59. FTopics : TList;
  60. FImgExt : String;
  61. FBeforeEmitNote : TWriterNoteEvent;
  62. procedure ConvertURL(AContext: TPasElement; El: TDOMElement);
  63. protected
  64. Procedure DoLog(Const Msg : String);
  65. Procedure DoLog(Const Fmt : String; Args : Array of const);
  66. procedure Warning(AContext: TPasElement; const AMsg: String);
  67. procedure Warning(AContext: TPasElement; const AMsg: String;
  68. const Args: array of const);
  69. // function FindShortDescr(const Name: String): TDOMElement;
  70. // Description conversion
  71. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  72. function IsExtShort(Node: TDOMNode): Boolean;
  73. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  74. function ConvertNotes(AContext: TPasElement; El: TDOMElement): Boolean; virtual;
  75. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  76. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  77. MayBeEmpty: Boolean);
  78. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  79. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  80. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  81. AutoInsertBlock: Boolean);
  82. function ConvertNonSectionBlock(AContext: TPasElement;
  83. Node: TDOMNode): Boolean;
  84. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  85. Node: TDOMNode);
  86. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  87. Function FindTopicElement(Node : TDocNode): TTopicElement;
  88. Procedure ConvertImage(El : TDomElement);
  89. Procedure DescrEmitNotesHeader(AContext : TPasElement); virtual;
  90. Procedure DescrEmitNotesFooter(AContext : TPasElement); virtual;
  91. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  92. procedure DescrBeginBold; virtual; abstract;
  93. procedure DescrEndBold; virtual; abstract;
  94. procedure DescrBeginItalic; virtual; abstract;
  95. procedure DescrEndItalic; virtual; abstract;
  96. procedure DescrBeginEmph; virtual; abstract;
  97. procedure DescrEndEmph; virtual; abstract;
  98. procedure DescrWriteImageEl(const AFileName, ACaption,ALinkName : DOMString); virtual;
  99. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  100. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  101. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  102. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  103. procedure DescrEndLink; virtual; abstract;
  104. procedure DescrBeginURL(const AURL: DOMString); virtual; abstract;
  105. procedure DescrEndURL; virtual; abstract;
  106. procedure DescrWriteLinebreak; virtual; abstract;
  107. procedure DescrBeginParagraph; virtual; abstract;
  108. procedure DescrEndParagraph; virtual; abstract;
  109. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  110. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  111. procedure DescrEndCode; virtual; abstract;
  112. procedure DescrBeginOrderedList; virtual; abstract;
  113. procedure DescrEndOrderedList; virtual; abstract;
  114. procedure DescrBeginUnorderedList; virtual; abstract;
  115. procedure DescrEndUnorderedList; virtual; abstract;
  116. procedure DescrBeginDefinitionList; virtual; abstract;
  117. procedure DescrEndDefinitionList; virtual; abstract;
  118. procedure DescrBeginListItem; virtual; abstract;
  119. procedure DescrEndListItem; virtual; abstract;
  120. procedure DescrBeginDefinitionTerm; virtual; abstract;
  121. procedure DescrEndDefinitionTerm; virtual; abstract;
  122. procedure DescrBeginDefinitionEntry; virtual; abstract;
  123. procedure DescrEndDefinitionEntry; virtual; abstract;
  124. procedure DescrBeginSectionTitle; virtual; abstract;
  125. procedure DescrBeginSectionBody; virtual; abstract;
  126. procedure DescrEndSection; virtual; abstract;
  127. procedure DescrBeginRemark; virtual; abstract;
  128. procedure DescrEndRemark; virtual; abstract;
  129. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  130. procedure DescrEndTable; virtual; abstract;
  131. procedure DescrBeginTableCaption; virtual; abstract;
  132. procedure DescrEndTableCaption; virtual; abstract;
  133. procedure DescrBeginTableHeadRow; virtual; abstract;
  134. procedure DescrEndTableHeadRow; virtual; abstract;
  135. procedure DescrBeginTableRow; virtual; abstract;
  136. procedure DescrEndTableRow; virtual; abstract;
  137. procedure DescrBeginTableCell; virtual; abstract;
  138. procedure DescrEndTableCell; virtual; abstract;
  139. public
  140. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  141. destructor Destroy; override;
  142. property Engine : TFPDocEngine read FEngine;
  143. Property Package : TPasPackage read FPackage;
  144. Property Topics : TList Read FTopics;
  145. Property ImageExtension : String Read FImgExt Write FImgExt;
  146. // Should return True if option was succesfully interpreted.
  147. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  148. Class Function FileNameExtension : String; virtual;
  149. Class Procedure Usage(List : TStrings); virtual;
  150. Class procedure SplitImport(var AFilename, ALinkPrefix: String); virtual;
  151. procedure WriteDoc; virtual; Abstract;
  152. Function WriteDescr(Element: TPasElement) : TDocNode;
  153. procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
  154. procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
  155. Procedure FPDocError(Msg : String);
  156. Procedure FPDocError(Fmt : String; Args : Array of Const);
  157. Function ShowMember(M : TPasElement) : boolean;
  158. Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  159. Property EmitNotes : Boolean Read FEmitNotes Write FEmitNotes;
  160. Property BeforeEmitNote : TWriterNoteEvent Read FBeforeEmitNote Write FBeforeEmitNote;
  161. end;
  162. TFPDocWriterClass = Class of TFPDocWriter;
  163. EFPDocWriterError = Class(Exception);
  164. // Register backend
  165. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  166. // UnRegister backend
  167. Procedure UnRegisterWriter(Const AName : String);
  168. // Return back end class. Exception if not found.
  169. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  170. // Return index of back end class.
  171. Function FindWriterClass(AName : String) : Integer;
  172. // List of backend in name=descr form.
  173. Procedure EnumWriters(List : TStrings);
  174. implementation
  175. { ---------------------------------------------------------------------
  176. Writer registration
  177. ---------------------------------------------------------------------}
  178. Type
  179. { TWriterRecord }
  180. TWriterRecord = Class(TObject)
  181. Private
  182. FClass : TFPDocWriterClass;
  183. FName : String;
  184. FDescription : String;
  185. Public
  186. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  187. end;
  188. { TWriterRecord }
  189. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  190. ADescr: String);
  191. begin
  192. FClass:=AClass;
  193. FName:=AName;
  194. FDescription:=ADescr;
  195. end;
  196. Var
  197. Writers : TStringList;
  198. Procedure InitWriterList;
  199. begin
  200. Writers:=TStringList.Create;
  201. Writers.Sorted:=True;
  202. end;
  203. Procedure DoneWriterList;
  204. Var
  205. I : Integer;
  206. begin
  207. For I:=Writers.Count-1 downto 0 do
  208. Writers.Objects[i].Free;
  209. FreeAndNil(Writers);
  210. end;
  211. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  212. begin
  213. If Writers.IndexOf(AName)<>-1 then
  214. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  215. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  216. end;
  217. function FindWriterClass(AName : String) : Integer;
  218. begin
  219. Result:=Writers.IndexOf(AName);
  220. end;
  221. function GetWriterClass(AName : String) : TFPDocWriterClass;
  222. Var
  223. Index : Integer;
  224. begin
  225. Index:=FindWriterClass(AName);
  226. If Index=-1 then
  227. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  228. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  229. end;
  230. // UnRegister backend
  231. Procedure UnRegisterWriter(Const AName : String);
  232. Var
  233. Index : Integer;
  234. begin
  235. Index:=Writers.IndexOf(AName);
  236. If Index=-1 then
  237. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  238. Writers.Objects[Index].Free;
  239. Writers.Delete(Index);
  240. end;
  241. Procedure EnumWriters(List : TStrings);
  242. Var
  243. I : Integer;
  244. begin
  245. List.Clear;
  246. For I:=0 to Writers.Count-1 do
  247. With (Writers.Objects[I] as TWriterRecord) do
  248. List.Add(FName+'='+FDescription);
  249. end;
  250. function IsWhitespaceNode(Node: TDOMText): Boolean;
  251. var
  252. I,L: Integer;
  253. S: DOMString;
  254. P : PWideChar;
  255. begin
  256. S := Node.Data;
  257. Result := True;
  258. I:=0;
  259. L:=Length(S);
  260. P:=PWideChar(S);
  261. While Result and (I<L) do
  262. begin
  263. Result:=P^ in [#32,#10,#9,#13];
  264. Inc(P);
  265. Inc(I);
  266. end;
  267. end;
  268. { ---------------------------------------------------------------------
  269. TFPDocWriter
  270. ---------------------------------------------------------------------}
  271. {
  272. fmtIPF:
  273. begin
  274. if Length(Engine.Output) = 0 then
  275. WriteLn(SCmdLineOutputOptionMissing)
  276. else
  277. CreateIPFDocForPackage(Engine.Package, Engine);
  278. end;
  279. }
  280. Constructor TFPDocWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
  281. begin
  282. inherited Create;
  283. FEngine := AEngine;
  284. FPackage := APackage;
  285. FTopics:=Tlist.Create;
  286. FImgExt:='.png';
  287. end;
  288. destructor TFPDocWriter.Destroy;
  289. Var
  290. i : integer;
  291. begin
  292. For I:=0 to FTopics.Count-1 do
  293. TTopicElement(FTopics[i]).Free;
  294. FTopics.Free;
  295. Inherited;
  296. end;
  297. function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
  298. begin
  299. Result:=False;
  300. end;
  301. class function TFPDocWriter.FileNameExtension: String;
  302. begin
  303. //Override in linear writers with the expected extension.
  304. Result := ''; //Output must not contain an extension.
  305. end;
  306. class procedure TFPDocWriter.Usage(List: TStrings);
  307. begin
  308. // Do nothing.
  309. end;
  310. class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
  311. var
  312. i: integer;
  313. begin
  314. //override in HTML and CHM writer
  315. i := Pos(',', AFilename);
  316. if i > 0 then
  317. begin //split CSV into filename and prefix
  318. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  319. SetLength(AFilename, i-1);
  320. end;
  321. end;
  322. Function TFPDocWriter.FindTopicElement(Node : TDocNode): TTopicElement;
  323. Var
  324. I : Integer;
  325. begin
  326. Result:=Nil;
  327. I:=FTopics.Count-1;
  328. While (I>=0) and (Result=Nil) do
  329. begin
  330. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  331. Result:=TTopicElement(FTopics[i]);
  332. Dec(I);
  333. end;
  334. end;
  335. procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
  336. ALinkName: DOMString);
  337. begin
  338. DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
  339. end;
  340. { ---------------------------------------------------------------------
  341. Generic documentation node conversion
  342. ---------------------------------------------------------------------}
  343. function IsContentNodeType(Node: TDOMNode): Boolean;
  344. begin
  345. Result := (Node.NodeType = ELEMENT_NODE) or
  346. ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
  347. (Node.NodeType = ENTITY_REFERENCE_NODE);
  348. end;
  349. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  350. begin
  351. if (AContext<>nil) then
  352. DoLog('[%s] %s',[AContext.PathName,AMsg])
  353. else
  354. DoLog('[<no context>] %s', [AMsg]);
  355. end;
  356. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  357. const Args: array of const);
  358. begin
  359. Warning(AContext, Format(AMsg, Args));
  360. end;
  361. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  362. var
  363. Child: TDOMNode;
  364. begin
  365. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  366. Result := True
  367. else
  368. begin
  369. Child := Node.FirstChild;
  370. while Assigned(Child) do
  371. begin
  372. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  373. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  374. begin
  375. Result := False;
  376. exit;
  377. end;
  378. Child := Child.NextSibling;
  379. end;
  380. end;
  381. Result := True;
  382. end;
  383. { Check wether the nodes starting with the node given as argument make up an
  384. 'extshort' production. }
  385. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  386. begin
  387. while Assigned(Node) do
  388. begin
  389. if Node.NodeType = ELEMENT_NODE then
  390. if (Node.NodeName <> 'br') and
  391. (Node.NodeName <> 'link') and
  392. (Node.NodeName <> 'url') and
  393. (Node.NodeName <> 'b') and
  394. (Node.NodeName <> 'file') and
  395. (Node.NodeName <> 'i') and
  396. (Node.NodeName <> 'kw') and
  397. (Node.NodeName <> 'printshort') and
  398. (Node.NodeName <> 'var') then
  399. begin
  400. Result := False;
  401. exit;
  402. end;
  403. Node := Node.NextSibling;
  404. end;
  405. Result := True;
  406. end;
  407. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  408. El: TDOMElement): Boolean;
  409. var
  410. Node: TDOMNode;
  411. begin
  412. Result := False;
  413. if not Assigned(El) then
  414. exit;
  415. Node := El.FirstChild;
  416. while Assigned(Node) do
  417. begin
  418. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  419. ConvertLink(AContext, TDOMElement(Node))
  420. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  421. ConvertURL(AContext, TDOMElement(Node))
  422. else
  423. if not ConvertBaseShort(AContext, Node) then
  424. exit;
  425. Node := Node.NextSibling;
  426. end;
  427. Result := True;
  428. end;
  429. function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
  430. ): Boolean;
  431. Var
  432. L : TFPList;
  433. N : TDomNode;
  434. I : Integer;
  435. B : Boolean;
  436. begin
  437. Result:=Assigned(El) and EmitNotes;
  438. If Not Result then
  439. exit;
  440. L:=TFPList.Create;
  441. try
  442. N:=El.FirstChild;
  443. While Assigned(N) do
  444. begin
  445. If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
  446. begin
  447. B:=True;
  448. if Assigned(FBeforeEmitNote) then
  449. FBeforeEmitNote(Self,TDomElement(N),B);
  450. If B then
  451. L.Add(N);
  452. end;
  453. N:=N.NextSibling;
  454. end;
  455. Result:=L.Count>0;
  456. If Not Result then
  457. exit;
  458. DescrEmitNotesHeader(AContext);
  459. DescrBeginUnorderedList;
  460. For i:=0 to L.Count-1 do
  461. begin
  462. DescrBeginListItem;
  463. ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
  464. DescrEndListItem;
  465. end;
  466. DescrEndUnorderedList;
  467. DescrEmitNotesFooter(AContext);
  468. finally
  469. L.Free;
  470. end;
  471. end;
  472. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  473. Node: TDOMNode): Boolean;
  474. function ConvertText: DOMString;
  475. var
  476. s: DOMString;
  477. i: Integer;
  478. begin
  479. if Node.NodeType = TEXT_NODE then
  480. begin
  481. s := Node.NodeValue;
  482. i := 1;
  483. SetLength(Result, 0);
  484. while i <= Length(s) do
  485. if s[i] = #13 then
  486. begin
  487. Result := Result + ' ';
  488. Inc(i);
  489. if s[i] = #10 then
  490. Inc(i);
  491. end else if s[i] = #10 then
  492. begin
  493. Result := Result + ' ';
  494. Inc(i);
  495. end else
  496. begin
  497. Result := Result + s[i];
  498. Inc(i);
  499. end;
  500. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  501. if Node.NodeName = 'fpc' then
  502. Result := 'Free Pascal'
  503. else if Node.NodeName = 'delphi' then
  504. Result := 'Delphi'
  505. else
  506. begin
  507. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  508. Result := Node.NodeName;
  509. end
  510. else if Node.NodeType = ELEMENT_NODE then
  511. SetLength(Result, 0);
  512. end;
  513. function ConvertTextContent: DOMString;
  514. begin
  515. SetLength(Result, 0);
  516. Node := Node.FirstChild;
  517. while Assigned(Node) do
  518. begin
  519. Result := Result + ConvertText;
  520. Node := Node.NextSibling;
  521. end;
  522. end;
  523. var
  524. El, DescrEl: TDOMElement;
  525. FPEl: TPasElement;
  526. begin
  527. Result := True;
  528. if Node.NodeType = ELEMENT_NODE then
  529. if Node.NodeName = 'b' then
  530. begin
  531. DescrBeginBold;
  532. ConvertBaseShortList(AContext, Node, False);
  533. DescrEndBold;
  534. end else
  535. if Node.NodeName = 'i' then
  536. begin
  537. DescrBeginItalic;
  538. ConvertBaseShortList(AContext, Node, False);
  539. DescrEndItalic;
  540. end else
  541. if Node.NodeName = 'em' then
  542. begin
  543. DescrBeginEmph;
  544. ConvertBaseShortList(AContext, Node, False);
  545. DescrEndEmph;
  546. end else
  547. if Node.NodeName = 'file' then
  548. DescrWriteFileEl(ConvertTextContent)
  549. else if Node.NodeName = 'kw' then
  550. DescrWriteKeywordEl(ConvertTextContent)
  551. else if Node.NodeName = 'printshort' then
  552. begin
  553. El := TDOMElement(Node);
  554. DescrEl := Engine.FindShortDescr(AContext.GetModule, El['id']);
  555. if Assigned(DescrEl) then
  556. ConvertShort(AContext, DescrEl)
  557. else
  558. begin
  559. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  560. DescrBeginBold;
  561. DescrWriteText('#ShortDescr:' + El['id']);
  562. DescrEndBold;
  563. end;
  564. end else if Node.NodeName = 'var' then
  565. DescrWriteVarEl(ConvertTextContent)
  566. else
  567. Result := False
  568. else
  569. DescrWriteText(ConvertText);
  570. end;
  571. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  572. Node: TDOMNode; MayBeEmpty: Boolean);
  573. var
  574. Child: TDOMNode;
  575. begin
  576. Child := Node.FirstChild;
  577. while Assigned(Child) do
  578. begin
  579. if not ConvertBaseShort(AContext, Child) then
  580. Warning(AContext, SErrInvalidShortDescr)
  581. else
  582. MayBeEmpty := True;
  583. Child := Child.NextSibling;
  584. end;
  585. if not MayBeEmpty then
  586. Warning(AContext, SErrInvalidShortDescr)
  587. end;
  588. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  589. begin
  590. DescrBeginLink(El['id']);
  591. if not IsDescrNodeEmpty(El) then
  592. ConvertBaseShortList(AContext, El, True)
  593. else
  594. DescrWriteText(El['id']);
  595. DescrEndLink;
  596. end;
  597. procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
  598. begin
  599. DescrBeginURL(El['href']);
  600. if not IsDescrNodeEmpty(El) then
  601. ConvertBaseShortList(AContext, El, True)
  602. else
  603. DescrWriteText(El['href']);
  604. DescrEndURL;
  605. end;
  606. procedure TFPDocWriter.DoLog(const Msg: String);
  607. begin
  608. If Assigned(FEngine.OnLog) then
  609. FEngine.OnLog(Self,Msg);
  610. end;
  611. procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
  612. begin
  613. DoLog(Format(Fmt,Args));
  614. end;
  615. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  616. Node: TDOMNode): Boolean;
  617. begin
  618. Result := False;
  619. while Assigned(Node) do
  620. begin
  621. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  622. ConvertLink(AContext, TDOMElement(Node))
  623. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  624. ConvertURL(AContext, TDOMElement(Node))
  625. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  626. DescrWriteLinebreak
  627. else
  628. if not ConvertBaseShort(AContext, Node) then
  629. exit;
  630. Node := Node.NextSibling;
  631. end;
  632. Result := True;
  633. end;
  634. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  635. AutoInsertBlock: Boolean);
  636. var
  637. Node, Child: TDOMNode;
  638. ParaCreated: Boolean;
  639. begin
  640. if AutoInsertBlock then
  641. if IsExtShort(El.FirstChild) then
  642. DescrBeginParagraph
  643. else
  644. AutoInsertBlock := False;
  645. Node := El.FirstChild;
  646. if not ConvertExtShort(AContext, Node) then
  647. begin
  648. while Assigned(Node) do
  649. begin
  650. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  651. begin
  652. DescrBeginSectionTitle;
  653. Child := Node.FirstChild;
  654. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  655. begin
  656. if not IsDescrNodeEmpty(Child) then
  657. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  658. Child := Child.NextSibling;
  659. end;
  660. if not Assigned(Child) or (Child.NodeName <> 'title') then
  661. Warning(AContext, SErrSectionTitleExpected)
  662. else
  663. ConvertShort(AContext, TDOMElement(Child));
  664. DescrBeginSectionBody;
  665. if IsExtShort(Child) then
  666. begin
  667. DescrBeginParagraph;
  668. ParaCreated := True;
  669. end else
  670. ParaCreated := False;
  671. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  672. if ParaCreated then
  673. DescrEndParagraph;
  674. DescrEndSection;
  675. end else if not ConvertNonSectionBlock(AContext, Node) then
  676. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  677. Node := Node.NextSibling;
  678. end;
  679. end else
  680. if AutoInsertBlock then
  681. DescrEndParagraph;
  682. end;
  683. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  684. Node: TDOMNode);
  685. begin
  686. if not ConvertExtShort(AContext, Node) then
  687. while Assigned(Node) do
  688. begin
  689. if not ConvertNonSectionBlock(AContext, Node) then
  690. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  691. Node := Node.NextSibling;
  692. end;
  693. end;
  694. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  695. Node: TDOMNode): Boolean;
  696. procedure ConvertCells(Node: TDOMNode);
  697. var
  698. Child: TDOMNode;
  699. IsEmpty: Boolean;
  700. begin
  701. Node := Node.FirstChild;
  702. IsEmpty := True;
  703. while Assigned(Node) do
  704. begin
  705. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  706. begin
  707. DescrBeginTableCell;
  708. Child := Node.FirstChild;
  709. if not ConvertExtShort(AContext, Child) then
  710. while Assigned(Child) do
  711. begin
  712. if not ConvertSimpleBlock(AContext, Child) then
  713. Warning(AContext, SErrInvalidTableContent);
  714. Child := Child.NextSibling;
  715. end;
  716. DescrEndTableCell;
  717. IsEmpty := False;
  718. end else
  719. if IsContentNodeType(Node) then
  720. Warning(AContext, SErrInvalidTableContent);
  721. Node := Node.NextSibling;
  722. end;
  723. if IsEmpty then
  724. Warning(AContext, SErrTableRowEmpty);
  725. end;
  726. procedure ConvertTable;
  727. function GetColCount(Node: TDOMNode): Integer;
  728. begin
  729. Result := 0;
  730. Node := Node.FirstChild;
  731. while Assigned(Node) do
  732. begin
  733. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  734. Inc(Result);
  735. Node := Node.NextSibling;
  736. end;
  737. end;
  738. var
  739. s: String;
  740. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  741. ColCount, ThisRowColCount: Integer;
  742. Subnode: TDOMNode;
  743. begin
  744. s := TDOMElement(Node)['border'];
  745. if s = '1' then
  746. HasBorder := True
  747. else
  748. begin
  749. HasBorder := False;
  750. if (Length(s) <> 0) and (s <> '0') then
  751. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  752. end;
  753. // Determine the number of columns
  754. ColCount := 0;
  755. Subnode := Node.FirstChild;
  756. while Assigned(Subnode) do
  757. begin
  758. if Subnode.NodeType = ELEMENT_NODE then
  759. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  760. (Subnode.NodeName = 'tr') then
  761. begin
  762. ThisRowColCount := GetColCount(Subnode);
  763. if ThisRowColCount > ColCount then
  764. ColCount := ThisRowColCount;
  765. end;
  766. Subnode := Subnode.NextSibling;
  767. end;
  768. DescrBeginTable(ColCount, HasBorder);
  769. Node := Node.FirstChild;
  770. CaptionPossible := True;
  771. HeadRowPossible := True;
  772. while Assigned(Node) do
  773. begin
  774. if Node.NodeType = ELEMENT_NODE then
  775. if CaptionPossible and (Node.NodeName = 'caption') then
  776. begin
  777. DescrBeginTableCaption;
  778. if not ConvertExtShort(AContext, Node.FirstChild) then
  779. Warning(AContext, SErrInvalidTableContent);
  780. DescrEndTableCaption;
  781. CaptionPossible := False;
  782. end else if HeadRowPossible and (Node.NodeName = 'th') then
  783. begin
  784. DescrBeginTableHeadRow;
  785. ConvertCells(Node);
  786. DescrEndTableHeadRow;
  787. CaptionPossible := False;
  788. HeadRowPossible := False;
  789. end else if Node.NodeName = 'tr' then
  790. begin
  791. DescrBeginTableRow;
  792. ConvertCells(Node);
  793. DescrEndTableRow;
  794. end else
  795. Warning(AContext, SErrInvalidTableContent)
  796. else if IsContentNodeType(Node) then
  797. Warning(AContext, SErrInvalidTableContent);
  798. Node := Node.NextSibling;
  799. end;
  800. DescrEndTable;
  801. end;
  802. begin
  803. if Node.NodeType <> ELEMENT_NODE then
  804. begin
  805. if Node.NodeType = TEXT_NODE then
  806. Result := IsWhitespaceNode(TDOMText(Node))
  807. else
  808. Result := Node.NodeType = COMMENT_NODE;
  809. exit;
  810. end;
  811. if Node.NodeName = 'remark' then
  812. begin
  813. DescrBeginRemark;
  814. Node := Node.FirstChild;
  815. if not ConvertExtShort(AContext, Node) then
  816. while Assigned(Node) do
  817. begin
  818. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  819. ConvertTable
  820. else
  821. if not ConvertSimpleBlock(AContext, Node) then
  822. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  823. Node := Node.NextSibling;
  824. end;
  825. DescrEndRemark;
  826. Result := True;
  827. end else if Node.NodeName = 'table' then
  828. begin
  829. ConvertTable;
  830. Result := True;
  831. end else
  832. Result := ConvertSimpleBlock(AContext, Node);
  833. end;
  834. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  835. Node: TDOMNode): Boolean;
  836. procedure ConvertListItems;
  837. var
  838. Empty: Boolean;
  839. begin
  840. Node := Node.FirstChild;
  841. Empty := True;
  842. while Assigned(Node) do
  843. begin
  844. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  845. then
  846. Warning(AContext, SErrInvalidListContent)
  847. else if Node.NodeType = ELEMENT_NODE then
  848. if Node.NodeName = 'li' then
  849. begin
  850. DescrBeginListItem;
  851. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  852. DescrEndListItem;
  853. Empty := False;
  854. end else
  855. Warning(AContext, SErrInvalidElementInList);
  856. Node := Node.NextSibling;
  857. end;
  858. if Empty then
  859. Warning(AContext, SErrListIsEmpty);
  860. end;
  861. procedure ConvertDefinitionList;
  862. var
  863. Empty, ExpectDTNext: Boolean;
  864. begin
  865. Node := Node.FirstChild;
  866. Empty := True;
  867. ExpectDTNext := True;
  868. while Assigned(Node) do
  869. begin
  870. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  871. then
  872. Warning(AContext, SErrInvalidListContent)
  873. else if Node.NodeType = ELEMENT_NODE then
  874. if ExpectDTNext and (Node.NodeName = 'dt') then
  875. begin
  876. DescrBeginDefinitionTerm;
  877. if not ConvertShort(AContext, TDOMElement(Node)) then
  878. Warning(AContext, SErrInvalidDefinitionTermContent);
  879. DescrEndDefinitionTerm;
  880. Empty := False;
  881. ExpectDTNext := False;
  882. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  883. begin
  884. DescrBeginDefinitionEntry;
  885. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  886. DescrEndDefinitionEntry;
  887. ExpectDTNext := True;
  888. end else
  889. Warning(AContext, SErrInvalidElementInList);
  890. Node := Node.NextSibling;
  891. end;
  892. if Empty then
  893. Warning(AContext, SErrListIsEmpty)
  894. else if not ExpectDTNext then
  895. Warning(AContext, SErrDefinitionEntryMissing);
  896. end;
  897. procedure ProcessCodeBody(Node: TDOMNode);
  898. var
  899. s: String;
  900. i, j: Integer;
  901. begin
  902. Node := Node.FirstChild;
  903. SetLength(s, 0);
  904. while Assigned(Node) do
  905. begin
  906. if Node.NodeType = TEXT_NODE then
  907. begin
  908. s := s + Node.NodeValue;
  909. j := 1;
  910. for i := 1 to Length(s) do
  911. // In XML, linefeeds are normalized to #10 by the parser!
  912. if s[i] = #10 then
  913. begin
  914. DescrWriteCodeLine(Copy(s, j, i - j));
  915. j := i + 1;
  916. end;
  917. if j > 1 then
  918. s := Copy(s, j, Length(s));
  919. end;
  920. Node := Node.NextSibling;
  921. end;
  922. if Length(s) > 0 then
  923. DescrWriteCodeLine(s);
  924. end;
  925. var
  926. s: String;
  927. HasBorder: Boolean;
  928. begin
  929. if Node.NodeType <> ELEMENT_NODE then
  930. begin
  931. Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
  932. exit;
  933. end;
  934. if Node.NodeName = 'p' then
  935. begin
  936. DescrBeginParagraph;
  937. if not ConvertExtShort(AContext, Node.FirstChild) then
  938. Warning(AContext, SErrInvalidParaContent);
  939. DescrEndParagraph;
  940. Result := True;
  941. end else if Node.NodeName = 'code' then
  942. begin
  943. s := TDOMElement(Node)['border'];
  944. if s = '1' then
  945. HasBorder := True
  946. else
  947. begin
  948. if (Length(s) > 0) and (s <> '0') then
  949. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  950. end;
  951. DescrBeginCode(HasBorder, TDOMElement(Node)['highlighter']);
  952. ProcessCodeBody(Node);
  953. DescrEndCode;
  954. Result := True;
  955. end else if Node.NodeName = 'pre' then
  956. begin
  957. DescrBeginCode(False, 'none');
  958. ProcessCodeBody(Node);
  959. DescrEndCode;
  960. Result := True;
  961. end else if Node.NodeName = 'ul' then
  962. begin
  963. DescrBeginUnorderedList;
  964. ConvertListItems;
  965. DescrEndUnorderedList;
  966. Result := True;
  967. end else if Node.NodeName = 'ol' then
  968. begin
  969. DescrBeginOrderedList;
  970. ConvertListItems;
  971. DescrEndOrderedList;
  972. Result := True;
  973. end else if Node.NodeName = 'dl' then
  974. begin
  975. DescrBeginDefinitionList;
  976. ConvertDefinitionList;
  977. DescrEndDefinitionList;
  978. Result := True;
  979. end else if Node.NodeName = 'img' then
  980. begin
  981. begin
  982. ConvertImage(Node as TDomElement);
  983. Result:=True;
  984. end;
  985. end else
  986. Result := False;
  987. end;
  988. Procedure TFPDocWriter.ConvertImage(El : TDomElement);
  989. Var
  990. FN,Cap,LinkName : DOMString;
  991. begin
  992. FN:=El['file'];
  993. Cap:=El['caption'];
  994. LinkName:=El['name'];
  995. FN:=ChangeFileExt(FN,ImageExtension);
  996. DescrWriteImageEl(FN,Cap,LinkName);
  997. end;
  998. procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
  999. begin
  1000. DescrWriteLinebreak;
  1001. DescrBeginBold;
  1002. DescrWriteText(SDocNotes);
  1003. DescrEndBold;
  1004. DescrWriteLinebreak;
  1005. end;
  1006. procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
  1007. begin
  1008. DescrWriteLinebreak;
  1009. end;
  1010. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  1011. begin
  1012. Inherited Create(AName,AParent);
  1013. SubTopics:=TList.Create;
  1014. end;
  1015. Destructor TTopicElement.Destroy;
  1016. begin
  1017. // Actual subtopics are freed by TFPDocWriter Topics list.
  1018. SubTopics.Free;
  1019. Inherited;
  1020. end;
  1021. Function TFPDocWriter.WriteDescr(Element: TPasElement) : TDocNode;
  1022. begin
  1023. Result:=Engine.FindDocNode(Element);
  1024. WriteDescr(ELement,Result);
  1025. end;
  1026. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  1027. begin
  1028. if Assigned(DocNode) then
  1029. begin
  1030. if not IsDescrNodeEmpty(DocNode.Descr) then
  1031. WriteDescr(Element, DocNode.Descr)
  1032. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  1033. WriteDescr(Element, DocNode.ShortDescr);
  1034. end;
  1035. end;
  1036. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  1037. begin
  1038. if Assigned(DescrNode) then
  1039. ConvertDescr(AContext, DescrNode, False);
  1040. end;
  1041. procedure TFPDocWriter.FPDocError(Msg: String);
  1042. begin
  1043. Raise EFPDocWriterError.Create(Msg);
  1044. end;
  1045. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  1046. begin
  1047. FPDocError(Format(Fmt,Args));
  1048. end;
  1049. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  1050. begin
  1051. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  1052. If Result then
  1053. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  1054. end;
  1055. Procedure TFPDocWriter.GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  1056. Var
  1057. I : Integer;
  1058. M : TPasElement;
  1059. begin
  1060. List.Clear;
  1061. List.Sorted:=False;
  1062. for i := 0 to ClassDecl.Members.Count - 1 do
  1063. begin
  1064. M:=TPasElement(ClassDecl.Members[i]);
  1065. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  1066. List.AddObject(M.Name,M);
  1067. end;
  1068. List.Sorted:=False;
  1069. end;
  1070. initialization
  1071. InitWriterList;
  1072. finalization
  1073. DoneWriterList;
  1074. end.