xmlreader.pp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. {
  2. This file is part of the Free Component Library
  3. TXMLReader - base class for streamed XML reading.
  4. Copyright (c) 2011 by Sergei Gorelkin, [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit XmlReader;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, xmlutils;
  16. type
  17. TXMLReadState = (
  18. rsInitial,
  19. rsInteractive,
  20. rsError,
  21. rsEndOfFile,
  22. rsClosed
  23. );
  24. { TODO: move EXmlReadError here from xmlread unit,
  25. it must have location information available }
  26. EXmlError = class(Exception) end;
  27. TXMLReader = class(TObject)
  28. protected
  29. FReadState: TXMLReadState;
  30. FReadStringBuf: TWideCharBuf;
  31. protected
  32. function GetEOF: Boolean; virtual;
  33. function GetDepth: Integer; virtual; abstract;
  34. function GetNodeType: TXMLNodeType; virtual; abstract;
  35. function GetValue: XMLString; virtual; abstract;
  36. function GetName: XMLString; virtual; abstract;
  37. function GetLocalName: XMLString; virtual; abstract;
  38. function GetPrefix: XMLString; virtual; abstract;
  39. function GetNamespaceUri: XMLString; virtual; abstract;
  40. function GetBaseUri: XMLString; virtual; abstract;
  41. function GetHasValue: Boolean; virtual; abstract;
  42. function GetAttributeCount: Integer; virtual; abstract;
  43. function GetIsDefault: Boolean; virtual; abstract;
  44. public
  45. destructor Destroy; override;
  46. function Read: Boolean; virtual; abstract;
  47. procedure Close; virtual; abstract;
  48. function MoveToFirstAttribute: Boolean; virtual; abstract;
  49. function MoveToNextAttribute: Boolean; virtual; abstract;
  50. function MoveToElement: Boolean; virtual; abstract;
  51. function ReadAttributeValue: Boolean; virtual; abstract;
  52. function MoveToContent: TXMLNodeType; virtual;
  53. procedure ResolveEntity; virtual; abstract;
  54. function ReadElementString: XMLString; overload;
  55. function ReadElementString(const aName: XMLString): XMLString; overload;
  56. function ReadElementString(const aLocalName, aNamespace: XMLString): XMLString; overload;
  57. procedure ReadEndElement; virtual;
  58. procedure ReadStartElement; overload;
  59. procedure ReadStartElement(const aName: XMLString); overload;
  60. procedure ReadStartElement(const aLocalName, aNamespace: XMLString); overload;
  61. function ReadString: XMLString; virtual;
  62. procedure Skip; virtual;
  63. function LookupNamespace(const APrefix: XMLString): XMLString; virtual; abstract;
  64. function GetAttribute(i: Integer): XMLString; virtual; abstract;
  65. function GetAttribute(const Name: XMLString): XMLString; virtual; abstract;
  66. function GetAttribute(const localName, nsUri: XMLString): XMLString; virtual; abstract;
  67. property nodeType: TXMLNodeType read GetNodeType;
  68. property ReadState: TXMLReadState read FReadState;
  69. property Depth: Integer read GetDepth;
  70. property EOF: Boolean read GetEOF;
  71. property Name: XMLString read GetName;
  72. property LocalName: XMLString read GetLocalName;
  73. property Prefix: XMLString read GetPrefix;
  74. property namespaceUri: XMLString read GetNamespaceUri;
  75. property Value: XMLString read GetValue;
  76. property HasValue: Boolean read GetHasValue;
  77. property AttributeCount: Integer read GetAttributeCount;
  78. property BaseUri: XMLString read GetBaseUri;
  79. property IsDefault: Boolean read GetIsDefault;
  80. end;
  81. implementation
  82. const
  83. ContentNodeTypes = [ntText, ntCDATA, ntElement, ntEndElement,
  84. ntEntityReference, ntEndEntity];
  85. { TXMLReader }
  86. destructor TXMLReader.Destroy;
  87. begin
  88. if Assigned(FReadStringBuf.Buffer) then
  89. FreeMem(FReadStringBuf.Buffer);
  90. inherited Destroy;
  91. end;
  92. function TXMLReader.GetEOF: Boolean;
  93. begin
  94. result := (FReadState=rsEndOfFile);
  95. end;
  96. function TXMLReader.MoveToContent: TXMLNodeType;
  97. begin
  98. if ReadState > rsInteractive then
  99. begin
  100. result := ntNone;
  101. exit;
  102. end;
  103. if nodeType = ntAttribute then
  104. MoveToElement;
  105. repeat
  106. result := nodeType;
  107. if result in ContentNodeTypes then
  108. exit;
  109. until not Read;
  110. result := ntNone;
  111. end;
  112. function TXMLReader.ReadElementString: XMLString;
  113. begin
  114. ReadStartElement;
  115. result := ReadString;
  116. if NodeType <> ntEndElement then
  117. raise EXmlError.Create('Expecting end of element');
  118. Read;
  119. end;
  120. function TXMLReader.ReadElementString(const aName: XMLString): XMLString;
  121. begin
  122. ReadStartElement(aName);
  123. result := ReadString;
  124. if NodeType <> ntEndElement then
  125. raise EXmlError.Create('Expecting end of element');
  126. Read;
  127. end;
  128. function TXMLReader.ReadElementString(const aLocalName, aNamespace: XMLString): XMLString;
  129. begin
  130. ReadStartElement(aLocalName, aNamespace);
  131. result := ReadString;
  132. if NodeType <> ntEndElement then
  133. raise EXmlError.Create('Expecting end of element');
  134. Read;
  135. end;
  136. procedure TXMLReader.ReadEndElement;
  137. begin
  138. if MoveToContent <> ntEndElement then
  139. raise EXmlError.Create('Expecting end of element');
  140. Read;
  141. end;
  142. procedure TXMLReader.ReadStartElement;
  143. begin
  144. if MoveToContent <> ntElement then
  145. raise EXmlError.Create('Invalid node type');
  146. Read;
  147. end;
  148. procedure TXMLReader.ReadStartElement(const aName: XMLString);
  149. begin
  150. if MoveToContent <> ntElement then
  151. raise EXmlError.Create('Invalid node type') ;
  152. if Name <> aName then
  153. raise EXmlError.CreateFmt('Element ''%s'' was not found',[aName]);
  154. Read;
  155. end;
  156. procedure TXMLReader.ReadStartElement(const aLocalName, aNamespace: XMLString);
  157. begin
  158. if MoveToContent <> ntElement then
  159. raise EXmlError.Create('Invalid node type');
  160. if (localName <> aLocalName) or (NamespaceURI <> aNamespace) then
  161. raise EXmlError.CreateFmt('Element ''%s'' with namespace ''%s'' was not found',
  162. [aLocalName, aNamespace]);
  163. Read;
  164. end;
  165. function TXMLReader.ReadString: XMLString;
  166. begin
  167. result := '';
  168. MoveToElement;
  169. if FReadStringBuf.Buffer = nil then
  170. BufAllocate(FReadStringBuf, 512);
  171. FReadStringBuf.Length := 0;
  172. if NodeType = ntElement then
  173. repeat
  174. Read;
  175. if NodeType in [ntText, ntCDATA, ntWhitespace, ntSignificantWhitespace] then
  176. BufAppendString(FReadStringBuf, Value)
  177. else
  178. Break;
  179. until False
  180. else
  181. while NodeType in [ntText,ntCDATA,ntWhitespace,ntSignificantWhitespace] do
  182. begin
  183. BufAppendString(FReadStringBuf, Value);
  184. Read;
  185. end;
  186. SetString(result, FReadStringBuf.Buffer, FReadStringBuf.Length);
  187. FReadStringBuf.Length := 0;
  188. end;
  189. procedure TXMLReader.Skip;
  190. var
  191. i: Integer;
  192. begin
  193. if ReadState <> rsInteractive then
  194. exit;
  195. MoveToElement;
  196. if (NodeType <> ntElement) then
  197. begin
  198. Read;
  199. exit;
  200. end;
  201. i := Depth;
  202. while Read and (i < Depth) do {loop};
  203. if NodeType = ntEndElement then
  204. Read;
  205. end;
  206. end.