xmlread.pp 104 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836
  1. {
  2. This file is part of the Free Component Library
  3. XML reading routines.
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. Modified in 2006 by Sergei Gorelkin, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit XMLRead;
  13. {$ifdef fpc}
  14. {$MODE objfpc}{$H+}
  15. {$endif}
  16. interface
  17. uses
  18. SysUtils, Classes, DOM;
  19. type
  20. TErrorSeverity = (esWarning, esError, esFatal);
  21. EXMLReadError = class(Exception)
  22. private
  23. FSeverity: TErrorSeverity;
  24. FErrorMessage: string;
  25. FLine: Integer;
  26. FLinePos: Integer;
  27. public
  28. property Severity: TErrorSeverity read FSeverity;
  29. property ErrorMessage: string read FErrorMessage;
  30. property Line: Integer read FLine;
  31. property LinePos: Integer read FLinePos;
  32. end;
  33. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  34. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text); overload;
  35. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream); overload;
  36. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String); overload;
  37. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String); overload;
  38. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text); overload;
  39. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream); overload;
  40. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream; const ABaseURI: String); overload;
  41. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  42. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text); overload;
  43. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream); overload;
  44. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String); overload;
  45. type
  46. TDOMParseOptions = class(TObject)
  47. private
  48. FValidate: Boolean;
  49. FPreserveWhitespace: Boolean;
  50. FExpandEntities: Boolean;
  51. FIgnoreComments: Boolean;
  52. FCDSectionsAsText: Boolean;
  53. FResolveExternals: Boolean;
  54. FNamespaces: Boolean;
  55. FDisallowDoctype: Boolean;
  56. FMaxChars: Cardinal;
  57. public
  58. property Validate: Boolean read FValidate write FValidate;
  59. property PreserveWhitespace: Boolean read FPreserveWhitespace write FPreserveWhitespace;
  60. property ExpandEntities: Boolean read FExpandEntities write FExpandEntities;
  61. property IgnoreComments: Boolean read FIgnoreComments write FIgnoreComments;
  62. property CDSectionsAsText: Boolean read FCDSectionsAsText write FCDSectionsAsText;
  63. property ResolveExternals: Boolean read FResolveExternals write FResolveExternals;
  64. property Namespaces: Boolean read FNamespaces write FNamespaces;
  65. property DisallowDoctype: Boolean read FDisallowDoctype write FDisallowDoctype;
  66. property MaxChars: Cardinal read FMaxChars write FMaxChars;
  67. end;
  68. // NOTE: DOM 3 LS ACTION_TYPE enumeration starts at 1
  69. TXMLContextAction = (
  70. xaAppendAsChildren = 1,
  71. xaReplaceChildren,
  72. xaInsertBefore,
  73. xaInsertAfter,
  74. xaReplace);
  75. TXMLErrorEvent = procedure(Error: EXMLReadError) of object;
  76. TXMLInputSource = class(TObject)
  77. private
  78. FStream: TStream;
  79. FStringData: string;
  80. FBaseURI: WideString;
  81. FSystemID: WideString;
  82. FPublicID: WideString;
  83. // FEncoding: string;
  84. public
  85. constructor Create(AStream: TStream); overload;
  86. constructor Create(const AStringData: string); overload;
  87. property Stream: TStream read FStream;
  88. property StringData: string read FStringData;
  89. property BaseURI: WideString read FBaseURI write FBaseURI;
  90. property SystemID: WideString read FSystemID write FSystemID;
  91. property PublicID: WideString read FPublicID write FPublicID;
  92. // property Encoding: string read FEncoding write FEncoding;
  93. end;
  94. TDOMParser = class(TObject)
  95. private
  96. FOptions: TDOMParseOptions;
  97. FOnError: TXMLErrorEvent;
  98. public
  99. constructor Create;
  100. destructor Destroy; override;
  101. procedure Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  102. procedure ParseUri(const URI: WideString; out ADoc: TXMLDocument);
  103. function ParseWithContext(Src: TXMLInputSource; Context: TDOMNode;
  104. Action: TXMLContextAction): TDOMNode;
  105. property Options: TDOMParseOptions read FOptions;
  106. property OnError: TXMLErrorEvent read FOnError write FOnError;
  107. end;
  108. TDecoder = record
  109. Context: Pointer;
  110. Decode: function(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  111. Cleanup: procedure(Context: Pointer); stdcall;
  112. end;
  113. TGetDecoderProc = function(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
  114. procedure RegisterDecoder(Proc: TGetDecoderProc);
  115. // =======================================================
  116. implementation
  117. uses
  118. UriParser, xmlutils;
  119. const
  120. PubidChars: TSetOfChar = [' ', #13, #10, 'a'..'z', 'A'..'Z', '0'..'9',
  121. '-', '''', '(', ')', '+', ',', '.', '/', ':', '=', '?', ';', '!', '*',
  122. '#', '@', '$', '_', '%'];
  123. type
  124. TDOMNotationEx = class(TDOMNotation);
  125. TDOMDocumentTypeEx = class(TDOMDocumentType);
  126. TDOMElementDef = class;
  127. TDTDSubsetType = (dsNone, dsInternal, dsExternal);
  128. // This may be augmented with ByteOffset, UTF8Offset, etc.
  129. TLocation = record
  130. Line: Integer;
  131. LinePos: Integer;
  132. end;
  133. TDOMEntityEx = class(TDOMEntity)
  134. protected
  135. FExternallyDeclared: Boolean;
  136. FResolved: Boolean;
  137. FOnStack: Boolean;
  138. FBetweenDecls: Boolean;
  139. FReplacementText: DOMString;
  140. FURI: DOMString;
  141. FStartLocation: TLocation;
  142. FCharCount: Cardinal;
  143. end;
  144. PWideCharBuf = ^TWideCharBuf;
  145. TWideCharBuf = record
  146. Buffer: PWideChar;
  147. Length: Integer;
  148. MaxLength: Integer;
  149. end;
  150. TXMLReader = class;
  151. TXMLCharSource = class(TObject)
  152. private
  153. FBuf: PWideChar;
  154. FBufEnd: PWideChar;
  155. FReader: TXMLReader;
  156. FParent: TXMLCharSource;
  157. FEntity: TObject; // weak reference
  158. FLineNo: Integer;
  159. LFPos: PWideChar;
  160. FXML11Rules: Boolean;
  161. FSystemID: WideString;
  162. FPublicID: WideString;
  163. FCharCount: Cardinal;
  164. function GetSystemID: WideString;
  165. function GetPublicID: WideString;
  166. protected
  167. function Reload: Boolean; virtual;
  168. public
  169. DTDSubsetType: TDTDSubsetType;
  170. constructor Create(const AData: WideString);
  171. procedure NextChar;
  172. procedure NewLine; virtual;
  173. function SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  174. wsflag: PBoolean = nil): WideChar; virtual;
  175. procedure Initialize; virtual;
  176. function SetEncoding(const AEncoding: string): Boolean; virtual;
  177. function Matches(const arg: WideString): Boolean;
  178. property SystemID: WideString read GetSystemID write FSystemID;
  179. property PublicID: WideString read GetPublicID write FPublicID;
  180. end;
  181. TXMLDecodingSource = class(TXMLCharSource)
  182. private
  183. FCharBuf: PChar;
  184. FCharBufEnd: PChar;
  185. FBufStart: PWideChar;
  186. FDecoder: TDecoder;
  187. FHasBOM: Boolean;
  188. FFixedUCS2: string;
  189. FBufSize: Integer;
  190. procedure DecodingError(const Msg: string);
  191. protected
  192. function Reload: Boolean; override;
  193. procedure FetchData; virtual;
  194. public
  195. procedure AfterConstruction; override;
  196. destructor Destroy; override;
  197. function SetEncoding(const AEncoding: string): Boolean; override;
  198. procedure NewLine; override;
  199. function SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  200. wsflag: PBoolean = nil): WideChar; override;
  201. procedure Initialize; override;
  202. end;
  203. TXMLStreamInputSource = class(TXMLDecodingSource)
  204. private
  205. FAllocated: PChar;
  206. FStream: TStream;
  207. FCapacity: Integer;
  208. FOwnStream: Boolean;
  209. FEof: Boolean;
  210. public
  211. constructor Create(AStream: TStream; AOwnStream: Boolean);
  212. destructor Destroy; override;
  213. procedure FetchData; override;
  214. end;
  215. TXMLFileInputSource = class(TXMLDecodingSource)
  216. private
  217. FFile: ^Text;
  218. FString: string;
  219. public
  220. constructor Create(var AFile: Text);
  221. procedure FetchData; override;
  222. end;
  223. PForwardRef = ^TForwardRef;
  224. TForwardRef = record
  225. Value: WideString;
  226. Loc: TLocation;
  227. end;
  228. TCPType = (ctName, ctChoice, ctSeq);
  229. TCPQuant = (cqOnce, cqZeroOrOnce, cqZeroOrMore, cqOnceOrMore);
  230. TContentParticle = class(TObject)
  231. private
  232. FParent: TContentParticle;
  233. FChildren: TFPList;
  234. FIndex: Integer;
  235. function GetChildCount: Integer;
  236. function GetChild(Index: Integer): TContentParticle;
  237. public
  238. CPType: TCPType;
  239. CPQuant: TCPQuant;
  240. Def: TDOMElementDef;
  241. destructor Destroy; override;
  242. function Add: TContentParticle;
  243. function IsRequired: Boolean;
  244. function FindFirst(aDef: TDOMElementDef): TContentParticle;
  245. function FindNext(aDef: TDOMElementDef; ChildIdx: Integer): TContentParticle;
  246. function MoreRequired(ChildIdx: Integer): Boolean;
  247. property ChildCount: Integer read GetChildCount;
  248. property Children[Index: Integer]: TContentParticle read GetChild;
  249. end;
  250. TElementValidator = object
  251. FElement: TDOMElement;
  252. FElementDef: TDOMElementDef;
  253. FCurCP: TContentParticle;
  254. FFailed: Boolean;
  255. function IsElementAllowed(Def: TDOMElementDef): Boolean;
  256. function Incomplete: Boolean;
  257. end;
  258. TXMLReadState = (rsProlog, rsDTD, rsRoot, rsEpilog);
  259. TElementContentType = (
  260. ctUndeclared,
  261. ctAny,
  262. ctEmpty,
  263. ctMixed,
  264. ctChildren
  265. );
  266. TCheckNameFlags = set of (cnOptional, cnToken);
  267. TBinding = class
  268. public
  269. uri: WideString;
  270. next: TBinding;
  271. prevPrefixBinding: TObject;
  272. Prefix: PHashItem;
  273. end;
  274. TPrefixedAttr = record
  275. Attr: TDOMAttr;
  276. PrefixLen: Integer; // to avoid recalculation
  277. end;
  278. TXMLReader = class
  279. private
  280. FSource: TXMLCharSource;
  281. FCtrl: TDOMParser;
  282. FXML11: Boolean;
  283. FState: TXMLReadState;
  284. FRecognizePE: Boolean;
  285. FHavePERefs: Boolean;
  286. FInsideDecl: Boolean;
  287. FDocNotValid: Boolean;
  288. FValue: TWideCharBuf;
  289. FEntityValue: TWideCharBuf;
  290. FName: TWideCharBuf;
  291. FTokenStart: TLocation;
  292. FStandalone: Boolean; // property of Doc ?
  293. FNamePages: PByteArray;
  294. FDocType: TDOMDocumentTypeEx; // a shortcut
  295. FPEMap: TDOMNamedNodeMap;
  296. FIDRefs: TFPList;
  297. FNotationRefs: TFPList;
  298. FCurrContentType: TElementContentType;
  299. FSaViolation: Boolean;
  300. FDTDStartPos: PWideChar;
  301. FIntSubset: TWideCharBuf;
  302. FAttrTag: Cardinal;
  303. FPrefixes: THashTable;
  304. FBindings: TFPList;
  305. FDefaultPrefix: THashItem;
  306. FWorkAtts: array of TPrefixedAttr;
  307. FBindingStack: array of TBinding;
  308. FFreeBindings: TBinding;
  309. FNsAttHash: TDblHashArray;
  310. FStdPrefix_xml: PHashItem;
  311. FStdPrefix_xmlns: PHashItem;
  312. FColonPos: Integer;
  313. FValidate: Boolean; // parsing options, copy of FCtrl.Options
  314. FPreserveWhitespace: Boolean;
  315. FExpandEntities: Boolean;
  316. FIgnoreComments: Boolean;
  317. FCDSectionsAsText: Boolean;
  318. FResolveExternals: Boolean;
  319. FNamespaces: Boolean;
  320. FDisallowDoctype: Boolean;
  321. FMaxChars: Cardinal;
  322. procedure SkipQuote(out Delim: WideChar; required: Boolean = True);
  323. procedure Initialize(ASource: TXMLCharSource);
  324. function DoParseAttValue(Delim: WideChar): Boolean;
  325. function ContextPush(AEntity: TDOMEntityEx): Boolean;
  326. function ContextPop: Boolean;
  327. procedure XML11_BuildTables;
  328. procedure ParseQuantity(CP: TContentParticle);
  329. procedure StoreLocation(out Loc: TLocation);
  330. function ValidateAttrSyntax(AttrDef: TDOMAttrDef; const aValue: WideString): Boolean;
  331. procedure ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
  332. procedure AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer);
  333. procedure ClearRefs(aList: TFPList);
  334. procedure ValidateIdRefs;
  335. procedure StandaloneError(LineOffs: Integer = 0);
  336. procedure CallErrorHandler(E: EXMLReadError);
  337. function FindOrCreateElDef: TDOMElementDef;
  338. function SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean;
  339. procedure CheckMaxChars;
  340. protected
  341. FCursor: TDOMNode_WithChildren;
  342. FNesting: Integer;
  343. FValidator: array of TElementValidator;
  344. procedure DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer=0);
  345. procedure DoErrorPos(Severity: TErrorSeverity; const descr: string;
  346. const ErrPos: TLocation);
  347. procedure FatalError(const descr: String; LineOffs: Integer=0); overload;
  348. procedure FatalError(const descr: string; const args: array of const; LineOffs: Integer=0); overload;
  349. procedure FatalError(Expected: WideChar); overload;
  350. function SkipWhitespace(PercentAloneIsOk: Boolean = False): Boolean;
  351. function SkipS(required: Boolean = False): Boolean;
  352. procedure ExpectWhitespace;
  353. procedure ExpectString(const s: String);
  354. procedure ExpectChar(wc: WideChar);
  355. function CheckForChar(c: WideChar): Boolean;
  356. procedure RaiseNameNotFound;
  357. function CheckName(aFlags: TCheckNameFlags = []): Boolean;
  358. procedure CheckNCName;
  359. function ExpectName: WideString; // [5]
  360. procedure SkipQuotedLiteral(out Literal: WideString; required: Boolean = True);
  361. procedure ExpectAttValue; // [10]
  362. procedure ParseComment; // [15]
  363. procedure ParsePI; // [16]
  364. procedure ParseCDSect; // [18]
  365. procedure ParseXmlOrTextDecl(TextDecl: Boolean);
  366. procedure ExpectEq;
  367. procedure ParseDoctypeDecl; // [28]
  368. procedure ParseMarkupDecl; // [29]
  369. procedure ParseElement; // [39]
  370. procedure ParseEndTag; // [42]
  371. procedure DoEndElement(ErrOffset: Integer);
  372. procedure ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef);
  373. procedure ParseContent; // [43]
  374. function ResolvePredefined: Boolean;
  375. procedure IncludeEntity(InAttr: Boolean);
  376. procedure StartPE;
  377. function ParseRef(var ToFill: TWideCharBuf): Boolean; // [67]
  378. function ParseExternalID(out SysID, PubID: WideString; // [75]
  379. SysIdOptional: Boolean): Boolean;
  380. procedure BadPENesting(S: TErrorSeverity = esError);
  381. procedure ParseEntityDecl;
  382. function ParseEntityDeclValue(Delim: WideChar): Boolean;
  383. procedure ParseAttlistDecl;
  384. procedure ExpectChoiceOrSeq(CP: TContentParticle);
  385. procedure ParseElementDecl;
  386. procedure ParseNotationDecl;
  387. function ResolveEntity(const AbsSysID, PublicID, BaseURI: WideString; out Source: TXMLCharSource): Boolean;
  388. procedure ProcessDefaultAttributes(Element: TDOMElement; Map: TDOMNamedNodeMap);
  389. procedure ProcessNamespaceAtts(Element: TDOMElement);
  390. procedure AddBinding(Attr: TDOMAttr; Prefix: PHashItem; var Chain: TBinding);
  391. procedure EndNamespaceScope(var Chain: TBinding);
  392. procedure PushVC(aElement: TDOMElement; aElDef: TDOMElementDef);
  393. procedure PopVC;
  394. procedure UpdateConstraints;
  395. procedure ValidateDTD;
  396. procedure ValidateRoot;
  397. procedure ValidationError(const Msg: string; const args: array of const; LineOffs: Integer = -1);
  398. procedure DoAttrText(ch: PWideChar; Count: Integer);
  399. procedure DTDReloadHook;
  400. procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
  401. // Some SAX-alike stuff (at a very early stage)
  402. procedure DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean=False);
  403. procedure DoComment(ch: PWideChar; Count: Integer);
  404. procedure DoCDSect(ch: PWideChar; Count: Integer);
  405. procedure DoNotationDecl(const aName, aPubID, aSysID: WideString);
  406. public
  407. doc: TDOMDocument;
  408. constructor Create; overload;
  409. constructor Create(AParser: TDOMParser); overload;
  410. destructor Destroy; override;
  411. procedure ProcessXML(ASource: TXMLCharSource); // [1]
  412. procedure ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
  413. procedure ProcessDTD(ASource: TXMLCharSource); // ([29])
  414. end;
  415. // Attribute/Element declarations
  416. TDOMElementDef = class(TDOMElement)
  417. public
  418. FExternallyDeclared: Boolean;
  419. ContentType: TElementContentType;
  420. IDAttr: TDOMAttrDef;
  421. NotationAttr: TDOMAttrDef;
  422. RootCP: TContentParticle;
  423. destructor Destroy; override;
  424. end;
  425. const
  426. NullLocation: TLocation = (Line: 0; LinePos: 0);
  427. { Decoders }
  428. var
  429. Decoders: array of TGetDecoderProc;
  430. procedure RegisterDecoder(Proc: TGetDecoderProc);
  431. var
  432. L: Integer;
  433. begin
  434. L := Length(Decoders);
  435. SetLength(Decoders, L+1);
  436. Decoders[L] := Proc;
  437. end;
  438. function FindDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean;
  439. var
  440. I: Integer;
  441. begin
  442. Result := False;
  443. for I := 0 to High(Decoders) do
  444. if Decoders[I](AEncoding, Decoder) then
  445. begin
  446. Result := True;
  447. Exit;
  448. end;
  449. end;
  450. function Decode_UCS2(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  451. var
  452. cnt: Cardinal;
  453. begin
  454. cnt := OutCnt; // num of widechars
  455. if cnt > InCnt div sizeof(WideChar) then
  456. cnt := InCnt div sizeof(WideChar);
  457. Move(InBuf^, OutBuf^, cnt * sizeof(WideChar));
  458. Dec(InCnt, cnt*sizeof(WideChar));
  459. Dec(OutCnt, cnt);
  460. Result := cnt;
  461. end;
  462. function Decode_UCS2_Swapped(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  463. var
  464. I: Integer;
  465. cnt: Cardinal;
  466. InPtr: PChar;
  467. begin
  468. cnt := OutCnt; // num of widechars
  469. if cnt > InCnt div sizeof(WideChar) then
  470. cnt := InCnt div sizeof(WideChar);
  471. InPtr := InBuf;
  472. for I := 0 to cnt-1 do
  473. begin
  474. OutBuf[I] := WideChar((ord(InPtr^) shl 8) or ord(InPtr[1]));
  475. Inc(InPtr, 2);
  476. end;
  477. Dec(InCnt, cnt*sizeof(WideChar));
  478. Dec(OutCnt, cnt);
  479. Result := cnt;
  480. end;
  481. function Decode_88591(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  482. var
  483. I: Integer;
  484. cnt: Cardinal;
  485. begin
  486. cnt := OutCnt; // num of widechars
  487. if cnt > InCnt then
  488. cnt := InCnt;
  489. for I := 0 to cnt-1 do
  490. OutBuf[I] := WideChar(ord(InBuf[I]));
  491. Dec(InCnt, cnt);
  492. Dec(OutCnt, cnt);
  493. Result := cnt;
  494. end;
  495. function Decode_UTF8(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
  496. const
  497. MaxCode: array[1..4] of Cardinal = ($7F, $7FF, $FFFF, $1FFFFF);
  498. var
  499. i, j, bc: Cardinal;
  500. Value: Cardinal;
  501. begin
  502. result := 0;
  503. i := OutCnt;
  504. while (i > 0) and (InCnt > 0) do
  505. begin
  506. bc := 1;
  507. Value := ord(InBuf^);
  508. if Value < $80 then
  509. OutBuf^ := WideChar(Value)
  510. else
  511. begin
  512. if Value < $C2 then
  513. begin
  514. Result := -1;
  515. Break;
  516. end;
  517. Inc(bc);
  518. if Value > $DF then
  519. begin
  520. Inc(bc);
  521. if Value > $EF then
  522. begin
  523. Inc(bc);
  524. if Value > $F7 then // never encountered in the tests.
  525. begin
  526. Result := -1;
  527. Break;
  528. end;
  529. end;
  530. end;
  531. if InCnt < bc then
  532. Break;
  533. j := 1;
  534. while j < bc do
  535. begin
  536. if InBuf[j] in [#$80..#$BF] then
  537. Value := (Value shl 6) or (Cardinal(InBuf[j]) and $3F)
  538. else
  539. begin
  540. Result := -1;
  541. Break;
  542. end;
  543. Inc(j);
  544. end;
  545. Value := Value and MaxCode[bc];
  546. // RFC2279 check
  547. if Value <= MaxCode[bc-1] then
  548. begin
  549. Result := -1;
  550. Break;
  551. end;
  552. case Value of
  553. 0..$D7FF, $E000..$FFFF: OutBuf^ := WideChar(Value);
  554. $10000..$10FFFF:
  555. begin
  556. if i < 2 then Break;
  557. OutBuf^ := WideChar($D7C0 + (Value shr 10));
  558. OutBuf[1] := WideChar($DC00 xor (Value and $3FF));
  559. Inc(OutBuf); // once here
  560. Dec(i);
  561. end
  562. else
  563. begin
  564. Result := -1;
  565. Break;
  566. end;
  567. end;
  568. end;
  569. Inc(OutBuf);
  570. Inc(InBuf, bc);
  571. Dec(InCnt, bc);
  572. Dec(i);
  573. end;
  574. if Result >= 0 then
  575. Result := OutCnt-i;
  576. OutCnt := i;
  577. end;
  578. function Is_8859_1(const AEncoding: string): Boolean;
  579. begin
  580. Result := SameText(AEncoding, 'ISO-8859-1') or
  581. SameText(AEncoding, 'ISO_8859-1') or
  582. SameText(AEncoding, 'latin1') or
  583. SameText(AEncoding, 'iso-ir-100') or
  584. SameText(AEncoding, 'l1') or
  585. SameText(AEncoding, 'IBM819') or
  586. SameText(AEncoding, 'CP819') or
  587. SameText(AEncoding, 'csISOLatin1') or
  588. // This one is not in character-sets.txt, but used in most FPC documentation...
  589. SameText(AEncoding, 'ISO8859-1');
  590. end;
  591. procedure BufAllocate(var ABuffer: TWideCharBuf; ALength: Integer);
  592. begin
  593. ABuffer.MaxLength := ALength;
  594. ABuffer.Length := 0;
  595. ABuffer.Buffer := AllocMem(ABuffer.MaxLength*SizeOf(WideChar));
  596. end;
  597. procedure BufAppend(var ABuffer: TWideCharBuf; wc: WideChar);
  598. begin
  599. if ABuffer.Length >= ABuffer.MaxLength then
  600. begin
  601. ReallocMem(ABuffer.Buffer, ABuffer.MaxLength * 2 * SizeOf(WideChar));
  602. FillChar(ABuffer.Buffer[ABuffer.MaxLength], ABuffer.MaxLength * SizeOf(WideChar),0);
  603. ABuffer.MaxLength := ABuffer.MaxLength * 2;
  604. end;
  605. ABuffer.Buffer[ABuffer.Length] := wc;
  606. Inc(ABuffer.Length);
  607. end;
  608. procedure BufAppendChunk(var ABuf: TWideCharBuf; pstart, pend: PWideChar);
  609. var
  610. Len: Integer;
  611. begin
  612. Len := PEnd - PStart;
  613. if Len <= 0 then
  614. Exit;
  615. if Len >= ABuf.MaxLength - ABuf.Length then
  616. begin
  617. ABuf.MaxLength := (Len + ABuf.Length)*2;
  618. // note: memory clean isn't necessary here.
  619. // To avoid garbage, control Length field.
  620. ReallocMem(ABuf.Buffer, ABuf.MaxLength * sizeof(WideChar));
  621. end;
  622. Move(pstart^, ABuf.Buffer[ABuf.Length], Len * sizeof(WideChar));
  623. Inc(ABuf.Length, Len);
  624. end;
  625. function BufEquals(const ABuf: TWideCharBuf; const Arg: WideString): Boolean;
  626. begin
  627. Result := (ABuf.Length = Length(Arg)) and
  628. CompareMem(ABuf.Buffer, Pointer(Arg), ABuf.Length*sizeof(WideChar));
  629. end;
  630. { TXMLInputSource }
  631. constructor TXMLInputSource.Create(AStream: TStream);
  632. begin
  633. inherited Create;
  634. FStream := AStream;
  635. end;
  636. constructor TXMLInputSource.Create(const AStringData: string);
  637. begin
  638. inherited Create;
  639. FStringData := AStringData;
  640. end;
  641. { TDOMParser }
  642. constructor TDOMParser.Create;
  643. begin
  644. FOptions := TDOMParseOptions.Create;
  645. end;
  646. destructor TDOMParser.Destroy;
  647. begin
  648. FOptions.Free;
  649. inherited Destroy;
  650. end;
  651. procedure TDOMParser.Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  652. var
  653. InputSrc: TXMLCharSource;
  654. begin
  655. with TXMLReader.Create(Self) do
  656. try
  657. ConvertSource(Src, InputSrc); // handles 'no-input-specified' case
  658. ProcessXML(InputSrc)
  659. finally
  660. ADoc := TXMLDocument(doc);
  661. Free;
  662. end;
  663. end;
  664. procedure TDOMParser.ParseUri(const URI: WideString; out ADoc: TXMLDocument);
  665. var
  666. Src: TXMLCharSource;
  667. begin
  668. ADoc := nil;
  669. with TXMLReader.Create(Self) do
  670. try
  671. if ResolveEntity(URI, '', '', Src) then
  672. ProcessXML(Src)
  673. else
  674. DoErrorPos(esFatal, 'The specified URI could not be resolved', NullLocation);
  675. finally
  676. ADoc := TXMLDocument(doc);
  677. Free;
  678. end;
  679. end;
  680. function TDOMParser.ParseWithContext(Src: TXMLInputSource;
  681. Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
  682. var
  683. InputSrc: TXMLCharSource;
  684. Frag: TDOMDocumentFragment;
  685. node: TDOMNode;
  686. begin
  687. if Action in [xaInsertBefore, xaInsertAfter, xaReplace] then
  688. node := Context.ParentNode
  689. else
  690. node := Context;
  691. // TODO: replacing document isn't yet supported
  692. if (Action = xaReplaceChildren) and (node.NodeType = DOCUMENT_NODE) then
  693. raise EDOMNotSupported.Create('DOMParser.ParseWithContext');
  694. if not (node.NodeType in [ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE]) then
  695. raise EDOMHierarchyRequest.Create('DOMParser.ParseWithContext');
  696. with TXMLReader.Create(Self) do
  697. try
  698. ConvertSource(Src, InputSrc); // handles 'no-input-specified' case
  699. Frag := Context.OwnerDocument.CreateDocumentFragment;
  700. try
  701. ProcessFragment(InputSrc, Frag);
  702. Result := Frag.FirstChild;
  703. case Action of
  704. xaAppendAsChildren: Context.AppendChild(Frag);
  705. xaReplaceChildren: begin
  706. Context.TextContent := ''; // removes children
  707. Context.ReplaceChild(Frag, Context.FirstChild);
  708. end;
  709. xaInsertBefore: node.InsertBefore(Frag, Context);
  710. xaInsertAfter: node.InsertBefore(Frag, Context.NextSibling);
  711. xaReplace: node.ReplaceChild(Frag, Context);
  712. end;
  713. finally
  714. Frag.Free;
  715. end;
  716. finally
  717. Free;
  718. end;
  719. end;
  720. { TXMLCharSource }
  721. constructor TXMLCharSource.Create(const AData: WideString);
  722. begin
  723. inherited Create;
  724. FLineNo := 1;
  725. FBuf := PWideChar(AData);
  726. FBufEnd := FBuf + Length(AData);
  727. LFPos := FBuf-1;
  728. FCharCount := Length(AData);
  729. end;
  730. procedure TXMLCharSource.Initialize;
  731. begin
  732. end;
  733. function TXMLCharSource.SetEncoding(const AEncoding: string): Boolean;
  734. begin
  735. Result := True; // always succeed
  736. end;
  737. function TXMLCharSource.GetPublicID: WideString;
  738. begin
  739. if FPublicID <> '' then
  740. Result := FPublicID
  741. else if Assigned(FParent) then
  742. Result := FParent.PublicID
  743. else
  744. Result := '';
  745. end;
  746. function TXMLCharSource.GetSystemID: WideString;
  747. begin
  748. if FSystemID <> '' then
  749. Result := FSystemID
  750. else if Assigned(FParent) then
  751. Result := FParent.SystemID
  752. else
  753. Result := '';
  754. end;
  755. function TXMLCharSource.Reload: Boolean;
  756. begin
  757. Result := False;
  758. end;
  759. procedure TXMLCharSource.NewLine;
  760. begin
  761. Inc(FLineNo);
  762. LFPos := FBuf;
  763. end;
  764. function TXMLCharSource.SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  765. wsflag: PBoolean): WideChar;
  766. var
  767. old: PWideChar;
  768. nonws: Boolean;
  769. begin
  770. old := FBuf;
  771. nonws := False;
  772. repeat
  773. if FBuf^ = #10 then
  774. NewLine;
  775. if (FBuf^ < #255) and (Char(ord(FBuf^)) in Delim) then
  776. Break;
  777. if (FBuf^ > #32) or not (Char(ord(FBuf^)) in [#32, #9, #10, #13]) then
  778. nonws := True;
  779. Inc(FBuf);
  780. until False;
  781. Result := FBuf^;
  782. BufAppendChunk(ToFill, old, FBuf);
  783. if Assigned(wsflag) then
  784. wsflag^ := wsflag^ or nonws;
  785. end;
  786. function TXMLCharSource.Matches(const arg: WideString): Boolean;
  787. begin
  788. Result := False;
  789. if (FBufEnd >= FBuf + Length(arg)) or Reload then
  790. Result := CompareMem(Pointer(arg), FBuf, Length(arg)*sizeof(WideChar));
  791. if Result then
  792. Inc(FBuf, Length(arg));
  793. end;
  794. { TXMLDecodingSource }
  795. procedure TXMLDecodingSource.AfterConstruction;
  796. begin
  797. inherited AfterConstruction;
  798. FBufStart := AllocMem(4096);
  799. FBuf := FBufStart;
  800. FBufEnd := FBuf;
  801. LFPos := FBuf-1;
  802. end;
  803. destructor TXMLDecodingSource.Destroy;
  804. begin
  805. FreeMem(FBufStart);
  806. if Assigned(FDecoder.Cleanup) then
  807. FDecoder.Cleanup(FDecoder.Context);
  808. inherited Destroy;
  809. end;
  810. procedure TXMLDecodingSource.FetchData;
  811. begin
  812. end;
  813. procedure TXMLDecodingSource.DecodingError(const Msg: string);
  814. begin
  815. // count line endings to obtain correct error location
  816. while FBuf < FBufEnd do
  817. begin
  818. if (FBuf^ = #10) or (FBuf^ = #13) or (FXML11Rules and ((FBuf^ = #$85) or (FBuf^ = #$2028))) then
  819. begin
  820. if (FBuf^ = #13) and (FBuf < FBufEnd-1) and
  821. ((FBuf[1] = #10) or (FXML11Rules and (FBuf[1] = #$85))) then
  822. Inc(FBuf);
  823. LFPos := FBuf;
  824. Inc(FLineNo);
  825. end;
  826. Inc(FBuf);
  827. end;
  828. FReader.FatalError(Msg);
  829. end;
  830. function TXMLDecodingSource.Reload: Boolean;
  831. var
  832. Remainder: PtrInt;
  833. r, inLeft: Cardinal;
  834. rslt: Integer;
  835. begin
  836. if DTDSubsetType = dsInternal then
  837. FReader.DTDReloadHook;
  838. Remainder := FBufEnd - FBuf;
  839. if Remainder > 0 then
  840. Move(FBuf^, FBufStart^, Remainder * sizeof(WideChar));
  841. Dec(LFPos, FBuf-FBufStart);
  842. FBuf := FBufStart;
  843. FBufEnd := FBufStart + Remainder;
  844. repeat
  845. inLeft := FCharBufEnd - FCharBuf;
  846. if inLeft < 4 then // may contain an incomplete char
  847. begin
  848. FetchData;
  849. inLeft := FCharBufEnd - FCharBuf;
  850. if inLeft <= 0 then
  851. Break;
  852. end;
  853. r := FBufStart + FBufSize - FBufEnd;
  854. if r = 0 then
  855. Break;
  856. rslt := FDecoder.Decode(FDecoder.Context, FCharBuf, inLeft, FBufEnd, r);
  857. { Sanity checks: r and inLeft must not increase. }
  858. if inLeft + FCharBuf <= FCharBufEnd then
  859. FCharBuf := FCharBufEnd - inLeft
  860. else
  861. DecodingError('Decoder error: input byte count out of bounds');
  862. if r + FBufEnd <= FBufStart + FBufSize then
  863. FBufEnd := FBufStart + FBufSize - r
  864. else
  865. DecodingError('Decoder error: output char count out of bounds');
  866. if rslt = 0 then
  867. Break
  868. else if rslt < 0 then
  869. DecodingError('Invalid character in input stream')
  870. else
  871. begin
  872. Inc(FCharCount, rslt);
  873. FReader.CheckMaxChars;
  874. end;
  875. until False;
  876. FBufEnd^ := #0;
  877. Result := FBuf < FBufEnd;
  878. end;
  879. const
  880. XmlSign: array [0..4] of WideChar = ('<', '?', 'x', 'm', 'l');
  881. procedure TXMLDecodingSource.Initialize;
  882. begin
  883. inherited;
  884. FLineNo := 1;
  885. FXml11Rules := FReader.FXML11;
  886. FDecoder.Decode := @Decode_UTF8;
  887. FFixedUCS2 := '';
  888. if FCharBufEnd-FCharBuf > 1 then
  889. begin
  890. if (FCharBuf[0] = #$FE) and (FCharBuf[1] = #$FF) then
  891. begin
  892. FFixedUCS2 := 'UTF-16BE';
  893. FDecoder.Decode := {$IFNDEF ENDIAN_BIG} @Decode_UCS2_Swapped {$ELSE} @Decode_UCS2 {$ENDIF};
  894. end
  895. else if (FCharBuf[0] = #$FF) and (FCharBuf[1] = #$FE) then
  896. begin
  897. FFixedUCS2 := 'UTF-16LE';
  898. FDecoder.Decode := {$IFDEF ENDIAN_BIG} @Decode_UCS2_Swapped {$ELSE} @Decode_UCS2 {$ENDIF};
  899. end;
  900. end;
  901. FBufSize := 6; // possible BOM and '<?xml'
  902. Reload;
  903. if FBuf^ = #$FEFF then
  904. begin
  905. FHasBOM := True;
  906. Inc(FBuf);
  907. end;
  908. LFPos := FBuf-1;
  909. if CompareMem(FBuf, @XmlSign[0], sizeof(XmlSign)) then
  910. begin
  911. FBufSize := 3; // don't decode past XML declaration
  912. Inc(FBuf, Length(XmlSign));
  913. FReader.ParseXmlOrTextDecl(FParent <> nil);
  914. end;
  915. FBufSize := 2047;
  916. end;
  917. function TXMLDecodingSource.SetEncoding(const AEncoding: string): Boolean;
  918. var
  919. NewDecoder: TDecoder;
  920. begin
  921. Result := True;
  922. if (FFixedUCS2 = '') and SameText(AEncoding, 'UTF-8') then
  923. Exit;
  924. if FFixedUCS2 <> '' then
  925. begin
  926. Result := SameText(AEncoding, FFixedUCS2) or
  927. SameText(AEncoding, 'UTF-16') or
  928. SameText(AEncoding, 'unicode');
  929. Exit;
  930. end;
  931. // TODO: must fail when a byte-based stream is labeled as word-based.
  932. // see rmt-e2e-61, it now fails but for a completely different reason.
  933. FillChar(NewDecoder, sizeof(TDecoder), 0);
  934. if Is_8859_1(AEncoding) then
  935. FDecoder.Decode := @Decode_88591
  936. else if FindDecoder(AEncoding, NewDecoder) then
  937. FDecoder := NewDecoder
  938. else
  939. Result := False;
  940. end;
  941. procedure TXMLDecodingSource.NewLine;
  942. begin
  943. case FBuf^ of
  944. #10: begin
  945. Inc(FLineNo);
  946. LFPos := FBuf;
  947. end;
  948. #13: begin
  949. Inc(FLineNo);
  950. LFPos := FBuf;
  951. // Reload trashes the buffer, it should be consumed beforehand
  952. if (FBufEnd >= FBuf+2) or Reload then
  953. begin
  954. if (FBuf[1] = #10) or (FXML11Rules and (FBuf[1] = #$85)) then
  955. begin
  956. Inc(FBuf);
  957. Inc(LFPos);
  958. end;
  959. FBuf^ := #10;
  960. end;
  961. end;
  962. #$85, #$2028: if FXML11Rules then
  963. begin
  964. FBuf^ := #10;
  965. Inc(FLineNo);
  966. LFPos := FBuf;
  967. end;
  968. end;
  969. end;
  970. { TXMLStreamInputSource }
  971. const
  972. Slack = 16;
  973. constructor TXMLStreamInputSource.Create(AStream: TStream; AOwnStream: Boolean);
  974. begin
  975. FStream := AStream;
  976. FCapacity := 4096;
  977. GetMem(FAllocated, FCapacity+Slack);
  978. FCharBuf := FAllocated+(Slack-4);
  979. FCharBufEnd := FCharBuf;
  980. FOwnStream := AOwnStream;
  981. FetchData;
  982. end;
  983. destructor TXMLStreamInputSource.Destroy;
  984. begin
  985. FreeMem(FAllocated);
  986. if FOwnStream then
  987. FStream.Free;
  988. inherited Destroy;
  989. end;
  990. procedure TXMLStreamInputSource.FetchData;
  991. var
  992. Remainder, BytesRead: Integer;
  993. OldBuf: PChar;
  994. begin
  995. Assert(FCharBufEnd - FCharBuf < Slack-4);
  996. if FEof then
  997. Exit;
  998. OldBuf := FCharBuf;
  999. Remainder := FCharBufEnd - FCharBuf;
  1000. if Remainder < 0 then
  1001. Remainder := 0;
  1002. FCharBuf := FAllocated+Slack-4-Remainder;
  1003. if Remainder > 0 then
  1004. Move(OldBuf^, FCharBuf^, Remainder);
  1005. BytesRead := FStream.Read(FAllocated[Slack-4], FCapacity);
  1006. if BytesRead < FCapacity then
  1007. FEof := True;
  1008. FCharBufEnd := FAllocated + (Slack-4) + BytesRead;
  1009. PWideChar(FCharBufEnd)^ := #0;
  1010. end;
  1011. { TXMLFileInputSource }
  1012. constructor TXMLFileInputSource.Create(var AFile: Text);
  1013. begin
  1014. FFile := @AFile;
  1015. SystemID := FilenameToURI(TTextRec(AFile).Name);
  1016. FetchData;
  1017. end;
  1018. procedure TXMLFileInputSource.FetchData;
  1019. begin
  1020. if not Eof(FFile^) then
  1021. begin
  1022. ReadLn(FFile^, FString);
  1023. FString := FString + #10; // bad solution...
  1024. FCharBuf := PChar(FString);
  1025. FCharBufEnd := FCharBuf + Length(FString);
  1026. end;
  1027. end;
  1028. { helper that closes handle upon destruction }
  1029. type
  1030. THandleOwnerStream = class(THandleStream)
  1031. public
  1032. destructor Destroy; override;
  1033. end;
  1034. destructor THandleOwnerStream.Destroy;
  1035. begin
  1036. if Handle >= 0 then FileClose(Handle);
  1037. inherited Destroy;
  1038. end;
  1039. { TXMLReader }
  1040. procedure TXMLReader.ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
  1041. begin
  1042. SrcOut := nil;
  1043. if Assigned(SrcIn) then
  1044. begin
  1045. if Assigned(SrcIn.FStream) then
  1046. SrcOut := TXMLStreamInputSource.Create(SrcIn.FStream, False)
  1047. else if SrcIn.FStringData <> '' then
  1048. SrcOut := TXMLStreamInputSource.Create(TStringStream.Create(SrcIn.FStringData), True)
  1049. else if (SrcIn.SystemID <> '') then
  1050. ResolveEntity(SrcIn.SystemID, SrcIn.PublicID, SrcIn.BaseURI, SrcOut);
  1051. end;
  1052. if (SrcOut = nil) and (FSource = nil) then
  1053. DoErrorPos(esFatal, 'No input source specified', NullLocation);
  1054. end;
  1055. procedure TXMLReader.StoreLocation(out Loc: TLocation);
  1056. begin
  1057. Loc.Line := FSource.FLineNo;
  1058. Loc.LinePos := FSource.FBuf-FSource.LFPos;
  1059. end;
  1060. function TXMLReader.ResolveEntity(const AbsSysID, PublicID, BaseURI: WideString; out Source: TXMLCharSource): Boolean;
  1061. var
  1062. Filename: string;
  1063. Stream: TStream;
  1064. fd: THandle;
  1065. begin
  1066. Source := nil;
  1067. Result := False;
  1068. { TODO: alternative resolvers
  1069. These may be 'internal' resolvers or a handler set by application.
  1070. Internal resolvers should probably produce a TStream
  1071. ( so that internal classes need not be exported ).
  1072. External resolver will produce TXMLInputSource that should be converted.
  1073. External resolver must NOT be called for root entity.
  1074. External resolver can return nil, in which case we do the default }
  1075. if URIToFilename(AbsSysID, Filename) then
  1076. begin
  1077. fd := FileOpen(Filename, fmOpenRead + fmShareDenyWrite);
  1078. if fd <> THandle(-1) then
  1079. begin
  1080. Stream := THandleOwnerStream.Create(fd);
  1081. Source := TXMLStreamInputSource.Create(Stream, True);
  1082. Source.SystemID := AbsSysID; // <- Revisit: Really need absolute sysID?
  1083. Source.PublicID := PublicID;
  1084. end;
  1085. end;
  1086. Result := Assigned(Source);
  1087. end;
  1088. procedure TXMLReader.Initialize(ASource: TXMLCharSource);
  1089. begin
  1090. ASource.FParent := FSource;
  1091. FSource := ASource;
  1092. FSource.FReader := Self;
  1093. FSource.Initialize;
  1094. end;
  1095. procedure TXMLReader.FatalError(Expected: WideChar);
  1096. begin
  1097. // FIX: don't output what is found - anything may be found, including exploits...
  1098. FatalError('Expected "%1s"', [string(Expected)]);
  1099. end;
  1100. procedure TXMLReader.FatalError(const descr: String; LineOffs: Integer);
  1101. begin
  1102. DoError(esFatal, descr, LineOffs);
  1103. end;
  1104. procedure TXMLReader.FatalError(const descr: string; const args: array of const; LineOffs: Integer);
  1105. begin
  1106. DoError(esFatal, Format(descr, args), LineOffs);
  1107. end;
  1108. procedure TXMLReader.ValidationError(const Msg: string; const Args: array of const; LineOffs: Integer);
  1109. begin
  1110. FDocNotValid := True;
  1111. if FValidate then
  1112. DoError(esError, Format(Msg, Args), LineOffs);
  1113. end;
  1114. procedure TXMLReader.DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer);
  1115. var
  1116. Loc: TLocation;
  1117. begin
  1118. StoreLocation(Loc);
  1119. if LineOffs >= 0 then
  1120. begin
  1121. Dec(Loc.LinePos, LineOffs);
  1122. DoErrorPos(Severity, descr, Loc);
  1123. end
  1124. else
  1125. DoErrorPos(Severity, descr, FTokenStart);
  1126. end;
  1127. procedure TXMLReader.DoErrorPos(Severity: TErrorSeverity; const descr: string; const ErrPos: TLocation);
  1128. var
  1129. E: EXMLReadError;
  1130. begin
  1131. if Assigned(FSource) then
  1132. E := EXMLReadError.CreateFmt('In ''%s'' (line %d pos %d): %s', [FSource.SystemID, ErrPos.Line, ErrPos.LinePos, descr])
  1133. else
  1134. E := EXMLReadError.Create(descr);
  1135. E.FSeverity := Severity;
  1136. E.FErrorMessage := descr;
  1137. E.FLine := ErrPos.Line;
  1138. E.FLinePos := ErrPos.LinePos;
  1139. CallErrorHandler(E);
  1140. // No 'finally'! If user handler raises exception, control should not get here
  1141. // and the exception will be freed in CallErrorHandler (below)
  1142. E.Free;
  1143. end;
  1144. procedure TXMLReader.CheckMaxChars;
  1145. var
  1146. src: TXMLCharSource;
  1147. total: Cardinal;
  1148. begin
  1149. if FMaxChars = 0 then
  1150. Exit;
  1151. src := FSource;
  1152. total := 0;
  1153. repeat
  1154. Inc(total, src.FCharCount);
  1155. if total > FMaxChars then
  1156. FatalError('Exceeded character count limit');
  1157. src := src.FParent;
  1158. until src = nil;
  1159. end;
  1160. procedure TXMLReader.CallErrorHandler(E: EXMLReadError);
  1161. begin
  1162. try
  1163. if Assigned(FCtrl) and Assigned(FCtrl.FOnError) then
  1164. FCtrl.FOnError(E);
  1165. if E.Severity = esFatal then
  1166. raise E;
  1167. except
  1168. if ExceptObject <> E then
  1169. E.Free;
  1170. raise;
  1171. end;
  1172. end;
  1173. function TXMLReader.SkipWhitespace(PercentAloneIsOk: Boolean): Boolean;
  1174. begin
  1175. Result := False;
  1176. repeat
  1177. Result := SkipS or Result;
  1178. if FSource.FBuf^ = #0 then
  1179. begin
  1180. Result := True; // report whitespace upon exiting the PE
  1181. if not ContextPop then
  1182. Break;
  1183. end
  1184. else if FSource.FBuf^ = '%' then
  1185. begin
  1186. if not FRecognizePE then
  1187. Break;
  1188. // This is the only case where look-ahead is needed
  1189. if FSource.FBuf > FSource.FBufEnd-2 then
  1190. FSource.Reload;
  1191. if (not PercentAloneIsOk) or (Byte(FSource.FBuf[1]) in NamingBitmap[FNamePages^[$100+hi(Word(FSource.FBuf[1]))]]) or
  1192. (FXML11 and (FSource.FBuf[1] >= #$D800) and (FSource.FBuf[1] <= #$DB7F)) then
  1193. begin
  1194. Inc(FSource.FBuf); // skip '%'
  1195. CheckName;
  1196. ExpectChar(';');
  1197. StartPE;
  1198. Result := True; // report whitespace upon entering the PE
  1199. end
  1200. else Break;
  1201. end
  1202. else
  1203. Break;
  1204. until False;
  1205. end;
  1206. procedure TXMLReader.ExpectWhitespace;
  1207. begin
  1208. if not SkipWhitespace then
  1209. FatalError('Expected whitespace');
  1210. end;
  1211. function TXMLReader.SkipS(Required: Boolean): Boolean;
  1212. var
  1213. p: PWideChar;
  1214. begin
  1215. Result := False;
  1216. repeat
  1217. p := FSource.FBuf;
  1218. repeat
  1219. if (p^ = #10) or (p^ = #13) or (FXML11 and ((p^ = #$85) or (p^ = #$2028))) then
  1220. begin
  1221. FSource.FBuf := p;
  1222. FSource.NewLine;
  1223. p := FSource.FBuf;
  1224. end
  1225. else if (p^ <> #32) and (p^ <> #9) then
  1226. Break;
  1227. Inc(p);
  1228. Result := True;
  1229. until False;
  1230. FSource.FBuf := p;
  1231. until (p^ <> #0) or (not FSource.Reload);
  1232. if (not Result) and Required then
  1233. FatalError('Expected whitespace');
  1234. end;
  1235. procedure TXMLReader.ExpectString(const s: String);
  1236. var
  1237. I: Integer;
  1238. begin
  1239. for I := 1 to Length(s) do
  1240. begin
  1241. if FSource.FBuf^ <> WideChar(ord(s[i])) then
  1242. FatalError('Expected "%s"', [s], i-1);
  1243. FSource.NextChar;
  1244. end;
  1245. end;
  1246. function TXMLReader.CheckForChar(c: WideChar): Boolean;
  1247. begin
  1248. Result := (FSource.FBuf^ = c);
  1249. if Result then
  1250. begin
  1251. Inc(FSource.FBuf);
  1252. if FSource.FBuf >= FSource.FBufEnd then
  1253. FSource.Reload;
  1254. end;
  1255. end;
  1256. procedure TXMLReader.SkipQuote(out Delim: WideChar; required: Boolean);
  1257. begin
  1258. Delim := #0;
  1259. if (FSource.FBuf^ = '''') or (FSource.FBuf^ = '"') then
  1260. begin
  1261. Delim := FSource.FBuf^;
  1262. FSource.NextChar; // skip quote
  1263. end
  1264. else if required then
  1265. FatalError('Expected single or double quote');
  1266. end;
  1267. const
  1268. PrefixDefault: array[0..4] of WideChar = ('x','m','l','n','s');
  1269. constructor TXMLReader.Create;
  1270. var
  1271. b: TBinding;
  1272. begin
  1273. inherited Create;
  1274. BufAllocate(FName, 128);
  1275. BufAllocate(FValue, 512);
  1276. FIDRefs := TFPList.Create;
  1277. FNotationRefs := TFPList.Create;
  1278. FPrefixes := THashTable.Create(16, False);
  1279. FBindings := TFPList.Create;
  1280. FNsAttHash := TDblHashArray.Create;
  1281. SetLength(FWorkAtts, 16);
  1282. SetLength(FBindingStack, 16);
  1283. FStdPrefix_xml := FPrefixes.FindOrAdd(@PrefixDefault, 3);
  1284. FStdPrefix_xmlns := FPrefixes.FindOrAdd(@PrefixDefault, 5);
  1285. { implicit binding for the 'xml' prefix }
  1286. b := TBinding.Create;
  1287. FBindings.Add(b);
  1288. FStdPrefix_xml^.Data := b;
  1289. b.uri := stduri_xml;
  1290. b.Prefix := FStdPrefix_xml;
  1291. // Set char rules to XML 1.0
  1292. FNamePages := @NamePages;
  1293. SetLength(FValidator, 16);
  1294. end;
  1295. constructor TXMLReader.Create(AParser: TDOMParser);
  1296. begin
  1297. Create;
  1298. FCtrl := AParser;
  1299. FValidate := FCtrl.Options.Validate;
  1300. FPreserveWhitespace := FCtrl.Options.PreserveWhitespace;
  1301. FExpandEntities := FCtrl.Options.ExpandEntities;
  1302. FCDSectionsAsText := FCtrl.Options.CDSectionsAsText;
  1303. FIgnoreComments := FCtrl.Options.IgnoreComments;
  1304. FResolveExternals := FCtrl.Options.ResolveExternals;
  1305. FNamespaces := FCtrl.Options.Namespaces;
  1306. FDisallowDoctype := FCtrl.Options.DisallowDoctype;
  1307. FMaxChars := FCtrl.Options.MaxChars;
  1308. end;
  1309. destructor TXMLReader.Destroy;
  1310. var
  1311. I: Integer;
  1312. begin
  1313. if Assigned(FEntityValue.Buffer) then
  1314. FreeMem(FEntityValue.Buffer);
  1315. FreeMem(FName.Buffer);
  1316. FreeMem(FValue.Buffer);
  1317. if Assigned(FSource) then
  1318. while ContextPop do; // clean input stack
  1319. FSource.Free;
  1320. FPEMap.Free;
  1321. ClearRefs(FNotationRefs);
  1322. ClearRefs(FIDRefs);
  1323. FNsAttHash.Free;
  1324. for I := FBindings.Count-1 downto 0 do
  1325. TObject(FBindings.List^[I]).Free;
  1326. FPrefixes.Free;
  1327. FBindings.Free;
  1328. FNotationRefs.Free;
  1329. FIDRefs.Free;
  1330. inherited Destroy;
  1331. end;
  1332. procedure TXMLReader.XML11_BuildTables;
  1333. begin
  1334. FNamePages := Xml11NamePages;
  1335. FXML11 := True;
  1336. FSource.FXml11Rules := True;
  1337. end;
  1338. procedure TXMLReader.ProcessXML(ASource: TXMLCharSource);
  1339. begin
  1340. doc := TXMLDocument.Create;
  1341. doc.documentURI := ASource.SystemID; // TODO: to be changed to URI or BaseURI
  1342. FCursor := doc;
  1343. FState := rsProlog;
  1344. FNesting := 0;
  1345. Initialize(ASource);
  1346. ParseContent;
  1347. if FState < rsRoot then
  1348. FatalError('Root element is missing');
  1349. if FValidate and Assigned(FDocType) then
  1350. ValidateIdRefs;
  1351. end;
  1352. procedure TXMLReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
  1353. begin
  1354. doc := AOwner.OwnerDocument;
  1355. FCursor := AOwner as TDOMNode_WithChildren;
  1356. FState := rsRoot;
  1357. Initialize(ASource);
  1358. FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1');
  1359. ParseContent;
  1360. end;
  1361. function TXMLReader.CheckName(aFlags: TCheckNameFlags): Boolean;
  1362. var
  1363. p: PWideChar;
  1364. NameStartFlag: Boolean;
  1365. begin
  1366. p := FSource.FBuf;
  1367. FName.Length := 0;
  1368. FColonPos := -1;
  1369. NameStartFlag := not (cnToken in aFlags);
  1370. repeat
  1371. if NameStartFlag then
  1372. begin
  1373. if (Byte(p^) in NamingBitmap[FNamePages^[hi(Word(p^))]]) or
  1374. ((p^ = ':') and (not FNamespaces)) then
  1375. Inc(p)
  1376. else if FXML11 and ((p^ >= #$D800) and (p^ <= #$DB7F) and
  1377. (p[1] >= #$DC00) and (p[1] <= #$DFFF)) then
  1378. Inc(p, 2)
  1379. else
  1380. begin
  1381. // here we come either when first char of name is bad (it may be a colon),
  1382. // or when a colon is not followed by a valid NameStartChar
  1383. FSource.FBuf := p;
  1384. Result := False;
  1385. Break;
  1386. end;
  1387. NameStartFlag := False;
  1388. end;
  1389. if FXML11 then
  1390. repeat
  1391. if Byte(p^) in NamingBitmap[FNamePages^[$100+hi(Word(p^))]] then
  1392. Inc(p)
  1393. else if ((p^ >= #$D800) and (p^ <= #$DB7F) and
  1394. (p[1] >= #$DC00) and (p[1] <= #$DFFF)) then
  1395. Inc(p,2)
  1396. else
  1397. Break;
  1398. until False
  1399. else
  1400. while Byte(p^) in NamingBitmap[FNamePages^[$100+hi(Word(p^))]] do
  1401. Inc(p);
  1402. if p^ = ':' then
  1403. begin
  1404. if (cnToken in aFlags) or not FNamespaces then // colon has no specific meaning
  1405. begin
  1406. Inc(p);
  1407. if p^ <> #0 then Continue;
  1408. end
  1409. else if FColonPos = -1 then // this is the first colon, remember it
  1410. begin
  1411. FColonPos := p-FSource.FBuf+FName.Length;
  1412. NameStartFlag := True;
  1413. Inc(p);
  1414. if p^ <> #0 then Continue;
  1415. end;
  1416. end;
  1417. BufAppendChunk(FName, FSource.FBuf, p);
  1418. Result := (FName.Length > 0);
  1419. FSource.FBuf := p;
  1420. if (p^ <> #0) or not FSource.Reload then
  1421. Break;
  1422. p := FSource.FBuf;
  1423. until False;
  1424. if not (Result or (cnOptional in aFlags)) then
  1425. RaiseNameNotFound;
  1426. end;
  1427. procedure TXMLReader.CheckNCName;
  1428. begin
  1429. if FNamespaces and (FColonPos <> -1) then
  1430. FatalError('Names of entities, notations and processing instructions may not contain colons', FName.Length);
  1431. end;
  1432. procedure TXMLReader.RaiseNameNotFound;
  1433. begin
  1434. if FColonPos <> -1 then
  1435. FatalError('Bad QName syntax, local part is missing')
  1436. else
  1437. // Coming at no cost, this allows more user-friendly error messages
  1438. with FSource do
  1439. if (FBuf^ = #32) or (FBuf^ = #10) or (FBuf^ = #9) or (FBuf^ = #13) then
  1440. FatalError('Whitespace is not allowed here')
  1441. else
  1442. FatalError('Name starts with invalid character');
  1443. end;
  1444. function TXMLReader.ExpectName: WideString;
  1445. begin
  1446. CheckName;
  1447. SetString(Result, FName.Buffer, FName.Length);
  1448. end;
  1449. function TXMLReader.ResolvePredefined: Boolean;
  1450. var
  1451. wc: WideChar;
  1452. begin
  1453. Result := False;
  1454. with FName do
  1455. begin
  1456. if (Length = 2) and (Buffer[1] = 't') then
  1457. begin
  1458. if Buffer[0] = 'l' then
  1459. wc := '<'
  1460. else if Buffer[0] = 'g' then
  1461. wc := '>'
  1462. else Exit;
  1463. end
  1464. else if Buffer[0] = 'a' then
  1465. begin
  1466. if (Length = 3) and (Buffer[1] = 'm') and (Buffer[2] = 'p') then
  1467. wc := '&'
  1468. else if (Length = 4) and (Buffer[1] = 'p') and (Buffer[2] = 'o') and
  1469. (Buffer[3] = 's') then
  1470. wc := ''''
  1471. else Exit;
  1472. end
  1473. else if (Length = 4) and (Buffer[0] = 'q') and (Buffer[1] = 'u') and
  1474. (Buffer[2] = 'o') and (Buffer[3] ='t') then
  1475. wc := '"'
  1476. else
  1477. Exit;
  1478. end; // with
  1479. BufAppend(FValue, wc);
  1480. Result := True;
  1481. end;
  1482. function TXMLReader.ParseRef(var ToFill: TWideCharBuf): Boolean; // [67]
  1483. var
  1484. Value: Integer;
  1485. begin
  1486. FSource.NextChar; // skip '&'
  1487. Result := CheckForChar('#');
  1488. if Result then
  1489. begin
  1490. Value := 0;
  1491. if CheckForChar('x') then
  1492. repeat
  1493. case FSource.FBuf^ of
  1494. '0'..'9': Value := Value * 16 + Ord(FSource.FBuf^) - Ord('0');
  1495. 'a'..'f': Value := Value * 16 + Ord(FSource.FBuf^) - (Ord('a') - 10);
  1496. 'A'..'F': Value := Value * 16 + Ord(FSource.FBuf^) - (Ord('A') - 10);
  1497. else
  1498. Break;
  1499. end;
  1500. FSource.NextChar;
  1501. until Value > $10FFFF
  1502. else
  1503. repeat
  1504. case FSource.FBuf^ of
  1505. '0'..'9': Value := Value * 10 + Ord(FSource.FBuf^) - Ord('0');
  1506. else
  1507. Break;
  1508. end;
  1509. FSource.NextChar;
  1510. until Value > $10FFFF;
  1511. case Value of
  1512. $01..$08, $0B..$0C, $0E..$1F:
  1513. if FXML11 then
  1514. BufAppend(ToFill, WideChar(Value))
  1515. else
  1516. FatalError('Invalid character reference');
  1517. $09, $0A, $0D, $20..$D7FF, $E000..$FFFD:
  1518. BufAppend(ToFill, WideChar(Value));
  1519. $10000..$10FFFF:
  1520. begin
  1521. BufAppend(ToFill, WideChar($D7C0 + (Value shr 10)));
  1522. BufAppend(ToFill, WideChar($DC00 xor (Value and $3FF)));
  1523. end;
  1524. else
  1525. FatalError('Invalid character reference');
  1526. end;
  1527. end
  1528. else CheckName;
  1529. ExpectChar(';');
  1530. end;
  1531. const
  1532. AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13];
  1533. EntityValueDelims: TSetOfChar = [#0, '%', '&', '''', '"'];
  1534. SQ_Delim: TSetOfChar = [#0, ''''];
  1535. DQ_Delim: TSetOfChar = [#0, '"'];
  1536. GT_Delim: TSetOfChar = [#0, '>'];
  1537. function TXMLReader.DoParseAttValue(Delim: WideChar): Boolean;
  1538. var
  1539. wc: WideChar;
  1540. begin
  1541. FValue.Length := 0;
  1542. repeat
  1543. wc := FSource.SkipUntil(FValue, AttrDelims);
  1544. if wc = '<' then
  1545. FatalError('Character ''<'' is not allowed in attribute value')
  1546. else if wc = '&' then
  1547. begin
  1548. if ParseRef(FValue) or ResolvePredefined then
  1549. Continue;
  1550. // have to insert entity or reference
  1551. if FValue.Length > 0 then
  1552. begin
  1553. DoAttrText(FValue.Buffer, FValue.Length);
  1554. FValue.Length := 0;
  1555. end;
  1556. IncludeEntity(True);
  1557. end
  1558. else if wc <> #0 then
  1559. begin
  1560. FSource.NextChar;
  1561. if wc = Delim then
  1562. Break;
  1563. if (wc = #10) or (wc = #9) or (wc = #13) then
  1564. wc := #32;
  1565. BufAppend(FValue, wc);
  1566. end;
  1567. until wc = #0;
  1568. // When processing the included entity, Delim = #0, so getting here isn't a error
  1569. if FValue.Length > 0 then
  1570. DoAttrText(FValue.Buffer, FValue.Length);
  1571. FValue.Length := 0;
  1572. Result := wc <> #0;
  1573. end;
  1574. function TXMLReader.ContextPush(AEntity: TDOMEntityEx): Boolean;
  1575. var
  1576. Src: TXMLCharSource;
  1577. begin
  1578. if (AEntity.SystemID <> '') and not AEntity.FResolved then
  1579. begin
  1580. Result := ResolveEntity(AEntity.FURI, AEntity.PublicID, '', Src);
  1581. if not Result then
  1582. begin
  1583. // TODO: a detailed message like SysErrorMessage(GetLastError) would be great here
  1584. ValidationError('Unable to resolve external entity ''%s''', [AEntity.NodeName]);
  1585. Exit;
  1586. end;
  1587. end
  1588. else
  1589. begin
  1590. Src := TXMLCharSource.Create(AEntity.FReplacementText);
  1591. // needed in case of prefetched external PE
  1592. Src.SystemID := AEntity.FURI;
  1593. Src.FLineNo := AEntity.FStartLocation.Line;
  1594. Src.LFPos := Src.FBuf - AEntity.FStartLocation.LinePos;
  1595. end;
  1596. AEntity.FOnStack := True;
  1597. Src.FEntity := AEntity;
  1598. Initialize(Src);
  1599. Result := True;
  1600. end;
  1601. function TXMLReader.ContextPop: Boolean;
  1602. var
  1603. Src: TXMLCharSource;
  1604. Error: Boolean;
  1605. begin
  1606. Result := Assigned(FSource.FParent) and (FSource.DTDSubsetType = dsNone);
  1607. if Result then
  1608. begin
  1609. Src := FSource.FParent;
  1610. Error := False;
  1611. if Assigned(FSource.FEntity) then
  1612. begin
  1613. TDOMEntityEx(FSource.FEntity).FOnStack := False;
  1614. TDOMEntityEx(FSource.FEntity).FCharCount := FSource.FCharCount;
  1615. // [28a] PE that was started between MarkupDecls may not end inside MarkupDecl
  1616. Error := TDOMEntityEx(FSource.FEntity).FBetweenDecls and FInsideDecl;
  1617. end;
  1618. FSource.Free;
  1619. FSource := Src;
  1620. // correct position of this error is after PE reference
  1621. if Error then
  1622. BadPENesting(esFatal);
  1623. end;
  1624. end;
  1625. procedure TXMLReader.IncludeEntity(InAttr: Boolean);
  1626. var
  1627. AEntity: TDOMEntityEx;
  1628. RefName: WideString;
  1629. Child: TDOMNode;
  1630. SaveCursor: TDOMNode_WithChildren;
  1631. cnt: Cardinal;
  1632. begin
  1633. AEntity := nil;
  1634. SetString(RefName, FName.Buffer, FName.Length);
  1635. cnt := FName.Length+2;
  1636. if Assigned(FDocType) then
  1637. AEntity := FDocType.Entities.GetNamedItem(RefName) as TDOMEntityEx;
  1638. if AEntity = nil then
  1639. begin
  1640. if FStandalone or (FDocType = nil) or not (FHavePERefs or (FDocType.SystemID <> '')) then
  1641. FatalError('Reference to undefined entity ''%s''', [RefName], cnt)
  1642. else
  1643. ValidationError('Undefined entity ''%s'' referenced', [RefName], cnt);
  1644. FCursor.AppendChild(doc.CreateEntityReference(RefName));
  1645. Exit;
  1646. end;
  1647. if InAttr and (AEntity.SystemID <> '') then
  1648. FatalError('External entity reference is not allowed in attribute value', cnt);
  1649. if FStandalone and AEntity.FExternallyDeclared then
  1650. FatalError('Standalone constraint violation', cnt);
  1651. if AEntity.NotationName <> '' then
  1652. FatalError('Reference to unparsed entity ''%s''', [RefName], cnt);
  1653. if not AEntity.FResolved then
  1654. begin
  1655. if AEntity.FOnStack then
  1656. FatalError('Entity ''%s'' recursively references itself', [RefName]);
  1657. if ContextPush(AEntity) then
  1658. begin
  1659. SaveCursor := FCursor;
  1660. FCursor := AEntity; // build child node tree for the entity
  1661. try
  1662. AEntity.SetReadOnly(False);
  1663. if InAttr then
  1664. DoParseAttValue(#0)
  1665. else
  1666. ParseContent;
  1667. AEntity.FResolved := True;
  1668. finally
  1669. AEntity.SetReadOnly(True);
  1670. ContextPop;
  1671. FCursor := SaveCursor;
  1672. FValue.Length := 0;
  1673. end;
  1674. end;
  1675. end;
  1676. // charcount of the entity included is known at this point
  1677. Inc(FSource.FCharCount, AEntity.FCharCount - cnt);
  1678. CheckMaxChars;
  1679. if (not FExpandEntities) or (not AEntity.FResolved) then
  1680. begin
  1681. // This will clone Entity children
  1682. FCursor.AppendChild(doc.CreateEntityReference(RefName));
  1683. Exit;
  1684. end;
  1685. Child := AEntity.FirstChild; // clone the entity node tree
  1686. while Assigned(Child) do
  1687. begin
  1688. FCursor.AppendChild(Child.CloneNode(True));
  1689. Child := Child.NextSibling;
  1690. end;
  1691. end;
  1692. procedure TXMLReader.StartPE;
  1693. var
  1694. PEName: WideString;
  1695. PEnt: TDOMEntityEx;
  1696. begin
  1697. SetString(PEName, FName.Buffer, FName.Length);
  1698. PEnt := nil;
  1699. if Assigned(FPEMap) then
  1700. PEnt := FPEMap.GetNamedItem(PEName) as TDOMEntityEx;
  1701. if PEnt = nil then // TODO -cVC: Referencing undefined PE
  1702. begin // (These are classified as 'optional errors'...)
  1703. // ValidationError('Undefined parameter entity referenced: %s', [PEName]);
  1704. Exit;
  1705. end;
  1706. if PEnt.FOnStack then
  1707. FatalError('Entity ''%%%s'' recursively references itself', [PEnt.NodeName]);
  1708. { cache an external PE so it's only fetched once }
  1709. if (PEnt.SystemID <> '') and not PEnt.FResolved then
  1710. begin
  1711. if ContextPush(PEnt) then
  1712. try
  1713. FValue.Length := 0;
  1714. FSource.SkipUntil(FValue, [#0]);
  1715. SetString(PEnt.FReplacementText, FValue.Buffer, FValue.Length);
  1716. PEnt.FCharCount := FValue.Length;
  1717. PEnt.FStartLocation.Line := 1;
  1718. PEnt.FStartLocation.LinePos := 1;
  1719. finally
  1720. ContextPop;
  1721. PEnt.FResolved := True;
  1722. FValue.Length := 0;
  1723. end;
  1724. end;
  1725. Inc(FSource.FCharCount, PEnt.FCharCount);
  1726. CheckMaxChars;
  1727. PEnt.FBetweenDecls := not FInsideDecl;
  1728. ContextPush(PEnt);
  1729. FHavePERefs := True;
  1730. end;
  1731. procedure TXMLReader.ExpectAttValue; // [10]
  1732. var
  1733. Delim: WideChar;
  1734. begin
  1735. SkipQuote(Delim);
  1736. StoreLocation(FTokenStart);
  1737. if not DoParseAttValue(Delim) then
  1738. FatalError('Literal has no closing quote',-1);
  1739. end;
  1740. procedure TXMLReader.SkipQuotedLiteral(out Literal: WideString; required: Boolean);
  1741. var
  1742. Delim: WideChar;
  1743. begin
  1744. SkipQuote(Delim, required);
  1745. if Delim <> #0 then
  1746. begin
  1747. StoreLocation(FTokenStart);
  1748. FValue.Length := 0;
  1749. if Delim = '''' then
  1750. Delim := FSource.SkipUntil(FValue, SQ_Delim)
  1751. else
  1752. Delim := FSource.SkipUntil(FValue, DQ_Delim);
  1753. if Delim = #0 then
  1754. FatalError('Literal has no closing quote', -1);
  1755. FSource.NextChar;
  1756. SetString(Literal, FValue.Buffer, FValue.Length);
  1757. end;
  1758. end;
  1759. function TXMLReader.SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean;
  1760. var
  1761. wc: WideChar;
  1762. begin
  1763. Result := False;
  1764. FValue.Length := 0;
  1765. StoreLocation(FTokenStart);
  1766. repeat
  1767. wc := FSource.SkipUntil(FValue, Delim);
  1768. if wc <> #0 then
  1769. begin
  1770. FSource.NextChar;
  1771. if (FValue.Length > ord(c2 <> #0)) then
  1772. begin
  1773. if (FValue.Buffer[FValue.Length-1] = c1) and
  1774. ((c2 = #0) or ((c2 <> #0) and (FValue.Buffer[FValue.Length-2] = c2))) then
  1775. begin
  1776. Dec(FValue.Length, ord(c2 <> #0) + 1);
  1777. Result := True;
  1778. Exit;
  1779. end;
  1780. end;
  1781. BufAppend(FValue, wc);
  1782. end;
  1783. until wc = #0;
  1784. end;
  1785. procedure TXMLReader.ParseComment; // [15]
  1786. begin
  1787. ExpectString('--');
  1788. if SkipUntilSeq([#0, '-'], '-') then
  1789. begin
  1790. ExpectChar('>');
  1791. DoComment(FValue.Buffer, FValue.Length);
  1792. end
  1793. else
  1794. FatalError('Unterminated comment', -1);
  1795. end;
  1796. procedure TXMLReader.ParsePI; // [16]
  1797. var
  1798. Name, Value: WideString;
  1799. PINode: TDOMProcessingInstruction;
  1800. begin
  1801. FSource.NextChar; // skip '?'
  1802. Name := ExpectName;
  1803. CheckNCName;
  1804. with FName do
  1805. if (Length = 3) and
  1806. ((Buffer[0] = 'X') or (Buffer[0] = 'x')) and
  1807. ((Buffer[1] = 'M') or (Buffer[1] = 'm')) and
  1808. ((Buffer[2] = 'L') or (Buffer[2] = 'l')) then
  1809. begin
  1810. if Name <> 'xml' then
  1811. FatalError('''xml'' is a reserved word; it must be lowercase', FName.Length)
  1812. else
  1813. FatalError('XML declaration is not allowed here', FName.Length);
  1814. end;
  1815. if FSource.FBuf^ <> '?' then
  1816. SkipS(True);
  1817. if SkipUntilSeq(GT_Delim, '?') then
  1818. begin
  1819. SetString(Value, FValue.Buffer, FValue.Length);
  1820. // SAX: ContentHandler.ProcessingInstruction(Name, Value);
  1821. if FCurrContentType = ctEmpty then
  1822. ValidationError('Processing instructions are not allowed within EMPTY elements', []);
  1823. PINode := Doc.CreateProcessingInstruction(Name, Value);
  1824. if Assigned(FCursor) then
  1825. FCursor.AppendChild(PINode)
  1826. else // to comply with certain tests, insert PI from DTD before DTD
  1827. Doc.InsertBefore(PINode, FDocType);
  1828. end
  1829. else
  1830. FatalError('Unterminated processing instruction', -1);
  1831. end;
  1832. procedure TXMLReader.ParseXmlOrTextDecl(TextDecl: Boolean);
  1833. var
  1834. TmpStr: WideString;
  1835. IsXML11: Boolean;
  1836. begin
  1837. SkipS(True);
  1838. // VersionInfo: optional in TextDecl, required in XmlDecl
  1839. if (not TextDecl) or (FSource.FBuf^ = 'v') then
  1840. begin
  1841. ExpectString('version'); // [24]
  1842. ExpectEq;
  1843. SkipQuotedLiteral(TmpStr);
  1844. IsXML11 := False;
  1845. if TmpStr = '1.1' then // Checking for bad chars is implied
  1846. IsXML11 := True
  1847. else if TmpStr <> '1.0' then
  1848. { should be no whitespace in these literals, but that isn't checked now }
  1849. FatalError('Illegal version number', -1);
  1850. if not TextDecl then
  1851. begin
  1852. if doc.InheritsFrom(TXMLDocument) then
  1853. TXMLDocument(doc).XMLVersion := TmpStr;
  1854. if IsXML11 then
  1855. XML11_BuildTables;
  1856. end
  1857. else // parsing external entity
  1858. if IsXML11 and not FXML11 then
  1859. FatalError('XML 1.0 document cannot invoke XML 1.1 entities', -1);
  1860. if FSource.FBuf^ <> '?' then
  1861. SkipS(True);
  1862. end;
  1863. // EncodingDecl: required in TextDecl, optional in XmlDecl
  1864. if TextDecl or (FSource.FBuf^ = 'e') then // [80]
  1865. begin
  1866. ExpectString('encoding');
  1867. ExpectEq;
  1868. SkipQuotedLiteral(TmpStr);
  1869. if not IsValidXmlEncoding(TmpStr) then
  1870. FatalError('Illegal encoding name', -1);
  1871. if not FSource.SetEncoding(TmpStr) then // <-- Wide2Ansi conversion here
  1872. FatalError('Encoding ''%s'' is not supported', [TmpStr], -1);
  1873. // getting here means that specified encoding is supported
  1874. // TODO: maybe assign the 'preferred' encoding name?
  1875. if not TextDecl and doc.InheritsFrom(TXMLDocument) then
  1876. TXMLDocument(doc).Encoding := TmpStr;
  1877. if FSource.FBuf^ <> '?' then
  1878. SkipS(True);
  1879. end;
  1880. // SDDecl: forbidden in TextDecl, optional in XmlDecl
  1881. if (not TextDecl) and (FSource.FBuf^ = 's') then
  1882. begin
  1883. ExpectString('standalone');
  1884. ExpectEq;
  1885. SkipQuotedLiteral(TmpStr);
  1886. if TmpStr = 'yes' then
  1887. FStandalone := True
  1888. else if TmpStr <> 'no' then
  1889. FatalError('Only "yes" or "no" are permitted as values of "standalone"', -1);
  1890. SkipS;
  1891. end;
  1892. ExpectString('?>');
  1893. end;
  1894. procedure TXMLReader.DTDReloadHook;
  1895. var
  1896. p: PWideChar;
  1897. begin
  1898. { FSource converts CR, NEL and LSEP linebreaks to LF, and CR-NEL sequences to CR-LF.
  1899. We must further remove the CR chars and have only LF's left. }
  1900. p := FDTDStartPos;
  1901. while p < FSource.FBuf do
  1902. begin
  1903. while (p < FSource.FBuf) and (p^ <> #13) do
  1904. Inc(p);
  1905. BufAppendChunk(FIntSubset, FDTDStartPos, p);
  1906. if p^ = #13 then
  1907. Inc(p);
  1908. FDTDStartPos := p;
  1909. end;
  1910. FDTDStartPos := TXMLDecodingSource(FSource).FBufStart;
  1911. end;
  1912. procedure TXMLReader.ParseDoctypeDecl; // [28]
  1913. var
  1914. Src: TXMLCharSource;
  1915. DoctypeURI: WideString;
  1916. begin
  1917. if FState >= rsDTD then
  1918. FatalError('Markup declaration is not allowed here');
  1919. if FDisallowDoctype then
  1920. FatalError('Document type is prohibited by parser settings');
  1921. ExpectString('DOCTYPE');
  1922. SkipS(True);
  1923. FDocType := TDOMDocumentTypeEx(TDOMDocumentType.Create(doc));
  1924. FState := rsDTD;
  1925. try
  1926. FDocType.FName := ExpectName;
  1927. SkipS(True);
  1928. ParseExternalID(FDocType.FSystemID, FDocType.FPublicID, False);
  1929. SkipS;
  1930. finally
  1931. // DONE: append node after its name has been set; always append to avoid leak
  1932. Doc.AppendChild(FDocType);
  1933. FCursor := nil;
  1934. end;
  1935. if CheckForChar('[') then
  1936. begin
  1937. BufAllocate(FIntSubset, 256);
  1938. FSource.DTDSubsetType := dsInternal;
  1939. try
  1940. FDTDStartPos := FSource.FBuf;
  1941. ParseMarkupDecl;
  1942. DTDReloadHook; // fetch last chunk
  1943. SetString(FDocType.FInternalSubset, FIntSubset.Buffer, FIntSubset.Length);
  1944. finally
  1945. FreeMem(FIntSubset.Buffer);
  1946. FSource.DTDSubsetType := dsNone;
  1947. end;
  1948. ExpectChar(']');
  1949. SkipS;
  1950. end;
  1951. ExpectChar('>');
  1952. if (FDocType.SystemID <> '') then
  1953. begin
  1954. ResolveRelativeURI(FSource.SystemID, FDocType.SystemID, DoctypeURI);
  1955. if ResolveEntity(DocTypeURI, FDocType.PublicID, '', Src) then
  1956. begin
  1957. Initialize(Src);
  1958. try
  1959. Src.DTDSubsetType := dsExternal;
  1960. ParseMarkupDecl;
  1961. finally
  1962. Src.DTDSubsetType := dsNone;
  1963. ContextPop;
  1964. end;
  1965. end
  1966. else
  1967. ValidationError('Unable to resolve external DTD subset', []);
  1968. end;
  1969. FCursor := Doc;
  1970. ValidateDTD;
  1971. FDocType.SetReadOnly(True);
  1972. end;
  1973. procedure TXMLReader.ExpectEq; // [25]
  1974. begin
  1975. if FSource.FBuf^ <> '=' then
  1976. SkipS;
  1977. if FSource.FBuf^ <> '=' then
  1978. FatalError('Expected "="');
  1979. FSource.NextChar;
  1980. SkipS;
  1981. end;
  1982. { DTD stuff }
  1983. procedure TXMLReader.BadPENesting(S: TErrorSeverity);
  1984. begin
  1985. if (S = esFatal) or FValidate then
  1986. DoError(S, 'Parameter entities must be properly nested');
  1987. end;
  1988. procedure TXMLReader.StandaloneError(LineOffs: Integer);
  1989. begin
  1990. ValidationError('Standalone constriant violation', [], LineOffs);
  1991. end;
  1992. procedure TXMLReader.ParseQuantity(CP: TContentParticle);
  1993. begin
  1994. case FSource.FBuf^ of
  1995. '?': CP.CPQuant := cqZeroOrOnce;
  1996. '*': CP.CPQuant := cqZeroOrMore;
  1997. '+': CP.CPQuant := cqOnceOrMore;
  1998. else
  1999. Exit;
  2000. end;
  2001. FSource.NextChar;
  2002. end;
  2003. function TXMLReader.FindOrCreateElDef: TDOMElementDef;
  2004. var
  2005. p: PHashItem;
  2006. begin
  2007. CheckName;
  2008. p := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
  2009. Result := TDOMElementDef(p^.Data);
  2010. if Result = nil then
  2011. begin
  2012. Result := TDOMElementDef.Create(doc);
  2013. Result.FNSI.QName := p;
  2014. p^.Data := Result;
  2015. end;
  2016. end;
  2017. procedure TXMLReader.ExpectChoiceOrSeq(CP: TContentParticle); // [49], [50]
  2018. var
  2019. Delim: WideChar;
  2020. CurrentEntity: TObject;
  2021. CurrentCP: TContentParticle;
  2022. begin
  2023. Delim := #0;
  2024. repeat
  2025. CurrentCP := CP.Add;
  2026. SkipWhitespace;
  2027. if CheckForChar('(') then
  2028. begin
  2029. CurrentEntity := FSource.FEntity;
  2030. ExpectChoiceOrSeq(CurrentCP);
  2031. if CurrentEntity <> FSource.FEntity then
  2032. BadPENesting;
  2033. FSource.NextChar;
  2034. end
  2035. else
  2036. CurrentCP.Def := FindOrCreateElDef;
  2037. ParseQuantity(CurrentCP);
  2038. SkipWhitespace;
  2039. if FSource.FBuf^ = ')' then
  2040. Break;
  2041. if Delim = #0 then
  2042. begin
  2043. if (FSource.FBuf^ = '|') or (FSource.FBuf^ = ',') then
  2044. Delim := FSource.FBuf^
  2045. else
  2046. FatalError('Expected pipe or comma delimiter');
  2047. end
  2048. else
  2049. if FSource.FBuf^ <> Delim then
  2050. FatalError(Delim);
  2051. FSource.NextChar; // skip delimiter
  2052. until False;
  2053. if Delim = '|' then
  2054. CP.CPType := ctChoice
  2055. else
  2056. CP.CPType := ctSeq; // '(foo)' is a sequence!
  2057. end;
  2058. procedure TXMLReader.ParseElementDecl; // [45]
  2059. var
  2060. ElDef: TDOMElementDef;
  2061. CurrentEntity: TObject;
  2062. I: Integer;
  2063. CP: TContentParticle;
  2064. Typ: TElementContentType;
  2065. ExtDecl: Boolean;
  2066. begin
  2067. CP := nil;
  2068. Typ := ctUndeclared; // satisfy compiler
  2069. ExpectWhitespace;
  2070. ElDef := FindOrCreateElDef;
  2071. if ElDef.ContentType <> ctUndeclared then
  2072. ValidationError('Duplicate declaration of element ''%s''', [ElDef.TagName], FName.Length);
  2073. ExtDecl := FSource.DTDSubsetType <> dsInternal;
  2074. ExpectWhitespace;
  2075. if FSource.Matches('EMPTY') then
  2076. Typ := ctEmpty
  2077. else if FSource.Matches('ANY') then
  2078. Typ := ctAny
  2079. else if CheckForChar('(') then
  2080. begin
  2081. CP := TContentParticle.Create;
  2082. try
  2083. CurrentEntity := FSource.FEntity;
  2084. SkipWhitespace;
  2085. if FSource.Matches('#PCDATA') then // Mixed section [51]
  2086. begin
  2087. SkipWhitespace;
  2088. Typ := ctMixed;
  2089. while FSource.FBuf^ <> ')' do
  2090. begin
  2091. ExpectChar('|');
  2092. SkipWhitespace;
  2093. with CP.Add do
  2094. begin
  2095. Def := FindOrCreateElDef;
  2096. for I := CP.ChildCount-2 downto 0 do
  2097. if Def = CP.Children[I].Def then
  2098. ValidationError('Duplicate token in mixed section', [], FName.Length);
  2099. end;
  2100. SkipWhitespace;
  2101. end;
  2102. if CurrentEntity <> FSource.FEntity then
  2103. BadPENesting;
  2104. FSource.NextChar;
  2105. if (not CheckForChar('*')) and (CP.ChildCount > 0) then
  2106. FatalError(WideChar('*'));
  2107. end
  2108. else // Children section [47]
  2109. begin
  2110. Typ := ctChildren;
  2111. ExpectChoiceOrSeq(CP);
  2112. if CurrentEntity <> FSource.FEntity then
  2113. BadPENesting;
  2114. FSource.NextChar;
  2115. ParseQuantity(CP);
  2116. end;
  2117. except
  2118. CP.Free;
  2119. raise;
  2120. end;
  2121. end
  2122. else
  2123. FatalError('Invalid content specification');
  2124. // SAX: DeclHandler.ElementDecl(name, model);
  2125. if ElDef.ContentType = ctUndeclared then
  2126. begin
  2127. ElDef.FExternallyDeclared := ExtDecl;
  2128. ElDef.ContentType := Typ;
  2129. ElDef.RootCP := CP;
  2130. end
  2131. else
  2132. CP.Free;
  2133. end;
  2134. procedure TXMLReader.ParseNotationDecl; // [82]
  2135. var
  2136. Name, SysID, PubID: WideString;
  2137. begin
  2138. ExpectWhitespace;
  2139. Name := ExpectName;
  2140. CheckNCName;
  2141. ExpectWhitespace;
  2142. if not ParseExternalID(SysID, PubID, True) then
  2143. FatalError('Expected external or public ID');
  2144. DoNotationDecl(Name, PubID, SysID);
  2145. end;
  2146. const
  2147. AttrDataTypeNames: array[TAttrDataType] of WideString = (
  2148. 'CDATA',
  2149. 'ID',
  2150. 'IDREF',
  2151. 'IDREFS',
  2152. 'ENTITY',
  2153. 'ENTITIES',
  2154. 'NMTOKEN',
  2155. 'NMTOKENS',
  2156. 'NOTATION'
  2157. );
  2158. procedure TXMLReader.ParseAttlistDecl; // [52]
  2159. var
  2160. ElDef: TDOMElementDef;
  2161. AttDef: TDOMAttrDef;
  2162. dt: TAttrDataType;
  2163. Found, DiscardIt: Boolean;
  2164. Offsets: array [Boolean] of Integer;
  2165. begin
  2166. ExpectWhitespace;
  2167. ElDef := FindOrCreateElDef;
  2168. SkipWhitespace;
  2169. while FSource.FBuf^ <> '>' do
  2170. begin
  2171. CheckName;
  2172. ExpectWhitespace;
  2173. AttDef := doc.CreateAttributeDef(FName.Buffer, FName.Length);
  2174. try
  2175. AttDef.ExternallyDeclared := FSource.DTDSubsetType <> dsInternal;
  2176. // In case of duplicate declaration of the same attribute, we must discard it,
  2177. // not modifying ElDef, and suppressing certain validation errors.
  2178. DiscardIt := Assigned(ElDef.GetAttributeNode(AttDef.Name));
  2179. if not DiscardIt then
  2180. ElDef.SetAttributeNode(AttDef);
  2181. if CheckForChar('(') then // [59]
  2182. begin
  2183. AttDef.DataType := dtNmToken;
  2184. repeat
  2185. SkipWhitespace;
  2186. CheckName([cnToken]);
  2187. if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
  2188. ValidationError('Duplicate token in enumerated attibute declaration', [], FName.Length);
  2189. SkipWhitespace;
  2190. until not CheckForChar('|');
  2191. ExpectChar(')');
  2192. ExpectWhitespace;
  2193. end
  2194. else
  2195. begin
  2196. StoreLocation(FTokenStart);
  2197. // search topside-up so that e.g. NMTOKENS is matched before NMTOKEN
  2198. for dt := dtNotation downto dtCData do
  2199. begin
  2200. Found := FSource.Matches(AttrDataTypeNames[dt]);
  2201. if Found then
  2202. Break;
  2203. end;
  2204. if Found and SkipWhitespace then
  2205. begin
  2206. AttDef.DataType := dt;
  2207. if (dt = dtId) and not DiscardIt then
  2208. begin
  2209. if Assigned(ElDef.IDAttr) then
  2210. ValidationError('Only one attribute of type ID is allowed per element',[])
  2211. else
  2212. ElDef.IDAttr := AttDef;
  2213. end
  2214. else if dt = dtNotation then // no test cases for these ?!
  2215. begin
  2216. if not DiscardIt then
  2217. begin
  2218. if Assigned(ElDef.NotationAttr) then
  2219. ValidationError('Only one attribute of type NOTATION is allowed per element',[])
  2220. else
  2221. ElDef.NotationAttr := AttDef;
  2222. if ElDef.ContentType = ctEmpty then
  2223. ValidationError('NOTATION attributes are not allowed on EMPTY elements',[]);
  2224. end;
  2225. ExpectChar('(');
  2226. repeat
  2227. SkipWhitespace;
  2228. StoreLocation(FTokenStart);
  2229. CheckName;
  2230. CheckNCName;
  2231. if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
  2232. ValidationError('Duplicate token in NOTATION attribute declaration',[], FName.Length);
  2233. if not DiscardIt then
  2234. AddForwardRef(FNotationRefs, FName.Buffer, FName.Length);
  2235. SkipWhitespace;
  2236. until not CheckForChar('|');
  2237. ExpectChar(')');
  2238. ExpectWhitespace;
  2239. end;
  2240. end
  2241. else
  2242. begin
  2243. // don't report 'expected whitespace' if token does not match completely
  2244. Offsets[False] := 0;
  2245. Offsets[True] := Length(AttrDataTypeNames[dt]);
  2246. if Found and (FSource.FBuf^ < 'A') then
  2247. ExpectWhitespace
  2248. else
  2249. FatalError('Illegal attribute type for ''%s''', [AttDef.Name], Offsets[Found]);
  2250. end;
  2251. end;
  2252. StoreLocation(FTokenStart);
  2253. if FSource.Matches('#REQUIRED') then
  2254. AttDef.Default := adRequired
  2255. else if FSource.Matches('#IMPLIED') then
  2256. AttDef.Default := adImplied
  2257. else if FSource.Matches('#FIXED') then
  2258. begin
  2259. AttDef.Default := adFixed;
  2260. ExpectWhitespace;
  2261. end
  2262. else
  2263. AttDef.Default := adDefault;
  2264. if AttDef.Default in [adDefault, adFixed] then
  2265. begin
  2266. if AttDef.DataType = dtId then
  2267. ValidationError('An attribute of type ID cannot have a default value',[]);
  2268. FCursor := AttDef;
  2269. // See comments to valid-sa-094: PE expansion should be disabled in AttDef.
  2270. // ExpectAttValue() does not recognize PEs anyway, so setting FRecognizePEs isn't needed
  2271. // Saving/restoring FCursor is also redundant because it is always nil here.
  2272. ExpectAttValue;
  2273. FCursor := nil;
  2274. if not ValidateAttrSyntax(AttDef, AttDef.NodeValue) then
  2275. ValidationError('Default value for attribute ''%s'' has wrong syntax', [AttDef.Name]);
  2276. end;
  2277. // SAX: DeclHandler.AttributeDecl(...)
  2278. if DiscardIt then
  2279. AttDef.Free;
  2280. except
  2281. AttDef.Free;
  2282. raise;
  2283. end;
  2284. SkipWhitespace;
  2285. end;
  2286. end;
  2287. function TXMLReader.ParseEntityDeclValue(Delim: WideChar): Boolean; // [9]
  2288. var
  2289. CurrentEntity: TObject;
  2290. wc: WideChar;
  2291. begin
  2292. CurrentEntity := FSource.FEntity;
  2293. if FEntityValue.Buffer = nil then
  2294. BufAllocate(FEntityValue, 256);
  2295. FEntityValue.Length := 0;
  2296. repeat
  2297. wc := FSource.SkipUntil(FEntityValue, EntityValueDelims);
  2298. if wc = '%' then
  2299. begin
  2300. FSource.NextChar;
  2301. CheckName;
  2302. ExpectChar(';');
  2303. if FSource.DTDSubsetType = dsInternal then
  2304. FatalError('PE reference not allowed here in internal subset', FName.Length+2);
  2305. StartPE;
  2306. end
  2307. else if wc = '&' then
  2308. begin
  2309. // expand CharRefs, bypass (but check for well-formedness) EntityRefs
  2310. if not ParseRef(FEntityValue) then
  2311. begin
  2312. BufAppend(FEntityValue, '&');
  2313. BufAppendChunk(FEntityValue, FName.Buffer, FName.Buffer + FName.Length);
  2314. BufAppend(FEntityValue, ';');
  2315. end;
  2316. end
  2317. else if wc <> #0 then
  2318. begin
  2319. FSource.NextChar;
  2320. // terminating delimiter must be in the same context as the starting one
  2321. if (wc = Delim) and (CurrentEntity = FSource.FEntity) then
  2322. Break;
  2323. BufAppend(FEntityValue, wc);
  2324. end
  2325. else if (FSource.FEntity = CurrentEntity) or not ContextPop then // #0
  2326. Break;
  2327. until False;
  2328. Result := (wc <> #0);
  2329. end;
  2330. procedure TXMLReader.ParseEntityDecl; // [70]
  2331. var
  2332. NDataAllowed: Boolean;
  2333. Delim: WideChar;
  2334. Entity: TDOMEntityEx;
  2335. Map: TDOMNamedNodeMap;
  2336. begin
  2337. if not SkipWhitespace(True) then
  2338. FatalError('Expected whitespace');
  2339. NDataAllowed := True;
  2340. Map := FDocType.Entities;
  2341. if CheckForChar('%') then // [72]
  2342. begin
  2343. ExpectWhitespace;
  2344. NDataAllowed := False;
  2345. if FPEMap = nil then
  2346. FPEMap := TDOMNamedNodeMap.Create(FDocType, ENTITY_NODE);
  2347. Map := FPEMap;
  2348. end;
  2349. Entity := TDOMEntityEx.Create(Doc);
  2350. Entity.SetReadOnly(True);
  2351. try
  2352. Entity.FExternallyDeclared := FSource.DTDSubsetType <> dsInternal;
  2353. Entity.FName := ExpectName;
  2354. CheckNCName;
  2355. ExpectWhitespace;
  2356. if (FSource.FBuf^ = '"') or (FSource.FBuf^ = '''') then
  2357. begin
  2358. NDataAllowed := False;
  2359. Delim := FSource.FBuf^;
  2360. FSource.NextChar;
  2361. StoreLocation(Entity.FStartLocation);
  2362. if not ParseEntityDeclValue(Delim) then
  2363. DoErrorPos(esFatal, 'Literal has no closing quote', Entity.FStartLocation);
  2364. SetString(Entity.FReplacementText, FEntityValue.Buffer, FEntityValue.Length);
  2365. Entity.FCharCount := FEntityValue.Length;
  2366. end
  2367. else
  2368. begin
  2369. if not ParseExternalID(Entity.FSystemID, Entity.FPublicID, False) then
  2370. FatalError('Expected entity value or external ID');
  2371. { need to resolve entity's SystemID relative to the current source,
  2372. which may differ from the source at the point of inclusion }
  2373. ResolveRelativeURI(FSource.SystemID, Entity.SystemID, Entity.FURI);
  2374. end;
  2375. if NDataAllowed then // [76]
  2376. begin
  2377. if FSource.FBuf^ <> '>' then
  2378. ExpectWhitespace;
  2379. if FSource.Matches('NDATA') then
  2380. begin
  2381. ExpectWhitespace;
  2382. StoreLocation(FTokenStart);
  2383. Entity.FNotationName := ExpectName;
  2384. AddForwardRef(FNotationRefs, FName.Buffer, FName.Length);
  2385. // SAX: DTDHandler.UnparsedEntityDecl(...);
  2386. end;
  2387. end;
  2388. except
  2389. Entity.Free;
  2390. raise;
  2391. end;
  2392. // Repeated declarations of same entity are legal but must be ignored
  2393. if Map.GetNamedItem(Entity.NodeName) = nil then
  2394. Map.SetNamedItem(Entity)
  2395. else
  2396. Entity.Free;
  2397. end;
  2398. procedure TXMLReader.ParseMarkupDecl; // [29]
  2399. var
  2400. IncludeLevel: Integer;
  2401. IgnoreLevel: Integer;
  2402. CurrentEntity: TObject;
  2403. IncludeLoc: TLocation;
  2404. IgnoreLoc: TLocation;
  2405. wc: WideChar;
  2406. CondType: (ctUnknown, ctInclude, ctIgnore);
  2407. begin
  2408. IncludeLevel := 0;
  2409. IgnoreLevel := 0;
  2410. repeat
  2411. FRecognizePE := True; // PERef between declarations should always be recognized
  2412. SkipWhitespace;
  2413. FRecognizePE := False;
  2414. if (FSource.FBuf^ = ']') and (IncludeLevel > 0) then
  2415. begin
  2416. ExpectString(']]>');
  2417. Dec(IncludeLevel);
  2418. Continue;
  2419. end;
  2420. if not CheckForChar('<') then
  2421. Break;
  2422. CurrentEntity := FSource.FEntity;
  2423. if FSource.FBuf^ = '?' then
  2424. ParsePI
  2425. else
  2426. begin
  2427. ExpectChar('!');
  2428. if FSource.FBuf^ = '-' then
  2429. ParseComment
  2430. else if CheckForChar('[') then
  2431. begin
  2432. if FSource.DTDSubsetType = dsInternal then
  2433. FatalError('Conditional sections are not allowed in internal subset', 1);
  2434. FRecognizePE := True;
  2435. SkipWhitespace;
  2436. CondType := ctUnknown; // satisfy compiler
  2437. if FSource.Matches('INCLUDE') then
  2438. CondType := ctInclude
  2439. else if FSource.Matches('IGNORE') then
  2440. CondType := ctIgnore
  2441. else
  2442. FatalError('Expected "INCLUDE" or "IGNORE"');
  2443. SkipWhitespace;
  2444. if CurrentEntity <> FSource.FEntity then
  2445. BadPENesting;
  2446. ExpectChar('[');
  2447. if CondType = ctInclude then
  2448. begin
  2449. if IncludeLevel = 0 then
  2450. StoreLocation(IncludeLoc);
  2451. Inc(IncludeLevel);
  2452. end
  2453. else if CondType = ctIgnore then
  2454. begin
  2455. StoreLocation(IgnoreLoc);
  2456. IgnoreLevel := 1;
  2457. repeat
  2458. FValue.Length := 0;
  2459. wc := FSource.SkipUntil(FValue, [#0, '<', ']']);
  2460. if FSource.Matches('<![') then
  2461. Inc(IgnoreLevel)
  2462. else if FSource.Matches(']]>') then
  2463. Dec(IgnoreLevel)
  2464. else if wc <> #0 then
  2465. FSource.NextChar
  2466. else // PE's aren't recognized in ignore section, cannot ContextPop()
  2467. DoErrorPos(esFatal, 'IGNORE section is not closed', IgnoreLoc);
  2468. until IgnoreLevel=0;
  2469. end;
  2470. end
  2471. else
  2472. begin
  2473. FRecognizePE := FSource.DTDSubsetType <> dsInternal;
  2474. FInsideDecl := True;
  2475. if FSource.Matches('ELEMENT') then
  2476. ParseElementDecl
  2477. else if FSource.Matches('ENTITY') then
  2478. ParseEntityDecl
  2479. else if FSource.Matches('ATTLIST') then
  2480. ParseAttlistDecl
  2481. else if FSource.Matches('NOTATION') then
  2482. ParseNotationDecl
  2483. else
  2484. FatalError('Illegal markup declaration');
  2485. SkipWhitespace;
  2486. FRecognizePE := False;
  2487. if CurrentEntity <> FSource.FEntity then
  2488. BadPENesting;
  2489. ExpectChar('>');
  2490. FInsideDecl := False;
  2491. end;
  2492. end;
  2493. until False;
  2494. FRecognizePE := False;
  2495. if IncludeLevel > 0 then
  2496. DoErrorPos(esFatal, 'INCLUDE section is not closed', IncludeLoc);
  2497. if (FSource.DTDSubsetType = dsInternal) and (FSource.FBuf^ = ']') then
  2498. Exit;
  2499. if FSource.FBuf^ <> #0 then
  2500. FatalError('Illegal character in DTD');
  2501. end;
  2502. procedure TXMLReader.ProcessDTD(ASource: TXMLCharSource);
  2503. begin
  2504. doc := TXMLDocument.Create;
  2505. FDocType := TDOMDocumentTypeEx.Create(doc);
  2506. // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
  2507. // DONE: It's ok to have FCursor=nil now
  2508. doc.AppendChild(FDocType);
  2509. Initialize(ASource);
  2510. ParseMarkupDecl;
  2511. end;
  2512. procedure TXMLReader.ParseCDSect; // [18]
  2513. begin
  2514. ExpectString('[CDATA[');
  2515. if FState <> rsRoot then
  2516. FatalError('Illegal at document level');
  2517. if SkipUntilSeq(GT_Delim, ']', ']') then
  2518. DoCDSect(FValue.Buffer, FValue.Length)
  2519. else
  2520. FatalError('Unterminated CDATA section', -1);
  2521. end;
  2522. // The code below does the bulk of the parsing, and must be as fast as possible.
  2523. // To minimize CPU cache effects, methods from different classes are kept together
  2524. function TXMLDecodingSource.SkipUntil(var ToFill: TWideCharBuf; const Delim: TSetOfChar;
  2525. wsflag: PBoolean): WideChar;
  2526. var
  2527. old: PWideChar;
  2528. nonws: Boolean;
  2529. wc: WideChar;
  2530. begin
  2531. nonws := False;
  2532. repeat
  2533. old := FBuf;
  2534. repeat
  2535. wc := FBuf^;
  2536. if (wc = #10) or (wc = #13) or (FXML11Rules and ((wc = #$85) or
  2537. (wc = #$2028))) then
  2538. begin
  2539. // strictly this is needed only for 2-byte lineendings
  2540. BufAppendChunk(ToFill, old, FBuf);
  2541. NewLine;
  2542. old := FBuf;
  2543. wc := FBuf^
  2544. end
  2545. else if ((wc < #32) and (not ((wc = #0) and (FBuf >= FBufEnd))) and
  2546. (wc <> #9)) or (wc > #$FFFD) or
  2547. (FXML11Rules and (wc >= #$7F) and (wc <= #$9F)) then
  2548. FReader.FatalError('Invalid character');
  2549. if (wc < #255) and (Char(ord(wc)) in Delim) then
  2550. Break;
  2551. // the checks above filter away everything below #32 that isn't a whitespace
  2552. if wc > #32 then
  2553. nonws := True;
  2554. Inc(FBuf);
  2555. until False;
  2556. Result := wc;
  2557. BufAppendChunk(ToFill, old, FBuf);
  2558. until (Result <> #0) or (not Reload);
  2559. if Assigned(wsflag) then
  2560. wsflag^ := wsflag^ or nonws;
  2561. end;
  2562. procedure TXMLReader.ParseContent;
  2563. var
  2564. nonWs: Boolean;
  2565. wc: WideChar;
  2566. StartNesting: Integer;
  2567. begin
  2568. StartNesting := FNesting;
  2569. with FSource do
  2570. repeat
  2571. if FBuf^ = '<' then
  2572. begin
  2573. Inc(FBuf);
  2574. if FBufEnd < FBuf + 2 then
  2575. Reload;
  2576. if FBuf^ = '/' then
  2577. begin
  2578. if FNesting <= StartNesting then
  2579. FatalError('End-tag is not allowed here');
  2580. Inc(FBuf);
  2581. ParseEndTag;
  2582. end
  2583. else if CheckName([cnOptional]) then
  2584. ParseElement
  2585. else if FBuf^ = '!' then
  2586. begin
  2587. Inc(FBuf);
  2588. if FBuf^ = '[' then
  2589. ParseCDSect
  2590. else if FBuf^ = '-' then
  2591. ParseComment
  2592. else
  2593. ParseDoctypeDecl;
  2594. end
  2595. else if FBuf^ = '?' then
  2596. ParsePI
  2597. else
  2598. RaiseNameNotFound;
  2599. end
  2600. else
  2601. begin
  2602. FValue.Length := 0;
  2603. nonWs := False;
  2604. StoreLocation(FTokenStart);
  2605. repeat
  2606. wc := SkipUntil(FValue, [#0, '<', '&', '>'], @nonWs);
  2607. if (wc = '<') or (wc = #0) then
  2608. Break
  2609. else if wc = '>' then
  2610. begin
  2611. with FValue do if (Length > 1) and (Buffer[Length-1] = ']') and
  2612. (Buffer[Length-2] = ']') then
  2613. FatalError('Literal '']]>'' is not allowed in text', 2);
  2614. BufAppend(FValue, wc);
  2615. NextChar;
  2616. end
  2617. else if wc = '&' then
  2618. begin
  2619. if FState <> rsRoot then
  2620. FatalError('Illegal at document level');
  2621. if FCurrContentType = ctEmpty then
  2622. ValidationError('References are illegal in EMPTY elements', []);
  2623. if ParseRef(FValue) or ResolvePredefined then
  2624. nonWs := True // CharRef to whitespace is not considered whitespace
  2625. else
  2626. begin
  2627. if (nonWs or FPreserveWhitespace) and (FValue.Length > 0) then
  2628. begin
  2629. // 'Reference illegal at root' is checked above, no need to check here
  2630. DoText(FValue.Buffer, FValue.Length, not nonWs);
  2631. FValue.Length := 0;
  2632. end;
  2633. IncludeEntity(False);
  2634. end;
  2635. end;
  2636. until False;
  2637. if FState = rsRoot then
  2638. begin
  2639. if (nonWs or FPreserveWhitespace) and (FValue.Length > 0) then
  2640. begin
  2641. DoText(FValue.Buffer, FValue.Length, not nonWs);
  2642. FValue.Length := 0;
  2643. end;
  2644. end
  2645. else if nonWs then
  2646. FatalError('Illegal at document level', -1);
  2647. end;
  2648. until FBuf^ = #0;
  2649. if FNesting > StartNesting then
  2650. FatalError('End-tag is missing for ''%s''', [FValidator[FNesting].FElement.NSI.QName^.Key]);
  2651. end;
  2652. procedure TXMLCharSource.NextChar;
  2653. begin
  2654. Inc(FBuf);
  2655. if FBuf >= FBufEnd then
  2656. Reload;
  2657. end;
  2658. procedure TXMLReader.ExpectChar(wc: WideChar);
  2659. begin
  2660. if FSource.FBuf^ = wc then
  2661. FSource.NextChar
  2662. else
  2663. FatalError(wc);
  2664. end;
  2665. // Element name already in FNameBuffer
  2666. procedure TXMLReader.ParseElement; // [39] [40] [44]
  2667. var
  2668. NewElem: TDOMElement;
  2669. ElDef: TDOMElementDef;
  2670. IsEmpty: Boolean;
  2671. ElName: PHashItem;
  2672. begin
  2673. if FState > rsRoot then
  2674. FatalError('Only one top-level element allowed', FName.Length)
  2675. else if FState < rsRoot then
  2676. begin
  2677. if FValidate then
  2678. ValidateRoot;
  2679. FState := rsRoot;
  2680. end;
  2681. NewElem := doc.CreateElementBuf(FName.Buffer, FName.Length);
  2682. FCursor.AppendChild(NewElem);
  2683. // we're about to process a new set of attributes
  2684. Inc(FAttrTag);
  2685. // Remember the hash entry, we'll need it often
  2686. ElName := NewElem.NSI.QName;
  2687. // Find declaration for this element
  2688. ElDef := TDOMElementDef(ElName^.Data);
  2689. if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then
  2690. ValidationError('Using undeclared element ''%s''',[ElName^.Key], FName.Length);
  2691. // Check if new element is allowed in current context
  2692. if FValidate and not FValidator[FNesting].IsElementAllowed(ElDef) then
  2693. ValidationError('Element ''%s'' is not allowed in this context',[ElName^.Key], FName.Length);
  2694. IsEmpty := False;
  2695. while (FSource.FBuf^ <> '>') and (FSource.FBuf^ <> '/') do
  2696. begin
  2697. SkipS(True);
  2698. if (FSource.FBuf^ = '>') or (FSource.FBuf^ = '/') then
  2699. Break;
  2700. ParseAttribute(NewElem, ElDef);
  2701. end;
  2702. if FSource.FBuf^ = '/' then
  2703. begin
  2704. IsEmpty := True;
  2705. FSource.NextChar;
  2706. end;
  2707. ExpectChar('>');
  2708. if Assigned(ElDef) and Assigned(ElDef.FAttributes) then
  2709. ProcessDefaultAttributes(NewElem, ElDef.FAttributes);
  2710. PushVC(NewElem, ElDef); // this increases FNesting
  2711. if FNamespaces then
  2712. ProcessNamespaceAtts(NewElem);
  2713. if not IsEmpty then
  2714. begin
  2715. FCursor := NewElem;
  2716. if not FPreserveWhitespace then // critical for testsuite compliance
  2717. SkipS;
  2718. end
  2719. else
  2720. DoEndElement(0);
  2721. end;
  2722. procedure TXMLReader.DoEndElement(ErrOffset: Integer);
  2723. var
  2724. NewElem: TDOMElement;
  2725. begin
  2726. NewElem := FValidator[FNesting].FElement;
  2727. TDOMNode(FCursor) := NewElem.ParentNode;
  2728. if FCursor = doc then
  2729. FState := rsEpilog;
  2730. if FValidate and FValidator[FNesting].Incomplete then
  2731. ValidationError('Element ''%s'' is missing required sub-elements', [NewElem.NSI.QName^.Key], ErrOffset);
  2732. if FNamespaces then
  2733. EndNamespaceScope(FBindingStack[FNesting]);
  2734. PopVC;
  2735. end;
  2736. procedure TXMLReader.ParseEndTag; // [42]
  2737. var
  2738. ErrOffset: Integer;
  2739. ElName: PHashItem;
  2740. begin
  2741. ElName := FValidator[FNesting].FElement.NSI.QName;
  2742. CheckName;
  2743. if not BufEquals(FName, ElName^.Key) then
  2744. FatalError('Unmatching element end tag (expected "</%s>")', [ElName^.Key], FName.Length);
  2745. if FSource.FBuf^ = '>' then // this handles majority of cases
  2746. begin
  2747. ErrOffset := FName.Length+1;
  2748. FSource.NextChar;
  2749. end
  2750. else // but if closing '>' is preceded by whitespace,
  2751. begin // skipping it is likely to lose position info.
  2752. StoreLocation(FTokenStart);
  2753. Dec(FTokenStart.LinePos, FName.Length);
  2754. ErrOffset := -1;
  2755. SkipS;
  2756. ExpectChar('>');
  2757. end;
  2758. DoEndElement(ErrOffset);
  2759. end;
  2760. procedure TXMLReader.ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef);
  2761. var
  2762. attr: TDOMAttr;
  2763. AttDef: TDOMAttrDef;
  2764. OldAttr: TDOMNode;
  2765. procedure CheckValue;
  2766. var
  2767. AttValue, OldValue: WideString;
  2768. begin
  2769. if FStandalone and AttDef.ExternallyDeclared then
  2770. begin
  2771. OldValue := Attr.Value;
  2772. Attr.DataType := AttDef.DataType;
  2773. AttValue := Attr.Value;
  2774. if AttValue <> OldValue then
  2775. StandaloneError(-1);
  2776. end
  2777. else
  2778. begin
  2779. Attr.DataType := AttDef.DataType;
  2780. AttValue := Attr.Value;
  2781. end;
  2782. // TODO: what about normalization of AttDef.Value? (Currently it IS normalized)
  2783. if (AttDef.Default = adFixed) and (AttDef.Value <> AttValue) then
  2784. ValidationError('Value of attribute ''%s'' does not match its #FIXED default',[AttDef.Name], -1);
  2785. if not ValidateAttrSyntax(AttDef, AttValue) then
  2786. ValidationError('Attribute ''%s'' type mismatch', [AttDef.Name], -1);
  2787. ValidateAttrValue(Attr, AttValue);
  2788. end;
  2789. begin
  2790. CheckName;
  2791. attr := doc.CreateAttributeBuf(FName.Buffer, FName.Length);
  2792. if Assigned(ElDef) then
  2793. begin
  2794. AttDef := TDOMAttrDef(ElDef.GetAttributeNode(attr.NSI.QName^.Key));
  2795. if AttDef = nil then
  2796. ValidationError('Using undeclared attribute ''%s'' on element ''%s''',[attr.NSI.QName^.Key, Elem.NSI.QName^.Key], FName.Length)
  2797. else
  2798. AttDef.Tag := FAttrTag; // indicates that this one is specified
  2799. end
  2800. else
  2801. AttDef := nil;
  2802. // !!cannot use TDOMElement.SetAttributeNode because it will free old attribute
  2803. OldAttr := Elem.Attributes.SetNamedItem(Attr);
  2804. if Assigned(OldAttr) then
  2805. begin
  2806. OldAttr.Free;
  2807. FatalError('Duplicate attribute', FName.Length);
  2808. end;
  2809. ExpectEq;
  2810. FCursor := attr;
  2811. ExpectAttValue;
  2812. if Assigned(AttDef) and ((AttDef.DataType <> dtCdata) or (AttDef.Default = adFixed)) then
  2813. CheckValue;
  2814. end;
  2815. procedure TXMLReader.AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer);
  2816. var
  2817. w: PForwardRef;
  2818. begin
  2819. New(w);
  2820. SetString(w^.Value, Buf, Length);
  2821. w^.Loc := FTokenStart;
  2822. aList.Add(w);
  2823. end;
  2824. procedure TXMLReader.ClearRefs(aList: TFPList);
  2825. var
  2826. I: Integer;
  2827. begin
  2828. for I := 0 to aList.Count-1 do
  2829. Dispose(PForwardRef(aList.List^[I]));
  2830. aList.Clear;
  2831. end;
  2832. procedure TXMLReader.ValidateIdRefs;
  2833. var
  2834. I: Integer;
  2835. begin
  2836. for I := 0 to FIDRefs.Count-1 do
  2837. with PForwardRef(FIDRefs.List^[I])^ do
  2838. if Doc.GetElementById(Value) = nil then
  2839. DoErrorPos(esError, Format('The ID ''%s'' does not match any element', [Value]), Loc);
  2840. ClearRefs(FIDRefs);
  2841. end;
  2842. procedure TXMLReader.ProcessDefaultAttributes(Element: TDOMElement; Map: TDOMNamedNodeMap);
  2843. var
  2844. I: Integer;
  2845. AttDef: TDOMAttrDef;
  2846. Attr: TDOMAttr;
  2847. begin
  2848. for I := 0 to Map.Length-1 do
  2849. begin
  2850. AttDef := Map[I] as TDOMAttrDef;
  2851. if AttDef.Tag <> FAttrTag then // this one wasn't specified
  2852. begin
  2853. case AttDef.Default of
  2854. adDefault, adFixed: begin
  2855. if FStandalone and AttDef.ExternallyDeclared then
  2856. StandaloneError;
  2857. Attr := TDOMAttr(AttDef.CloneNode(True));
  2858. Element.SetAttributeNode(Attr);
  2859. ValidateAttrValue(Attr, Attr.Value);
  2860. end;
  2861. adRequired: ValidationError('Required attribute ''%s'' of element ''%s'' is missing',[AttDef.Name, Element.TagName], 0)
  2862. end;
  2863. end;
  2864. end;
  2865. end;
  2866. procedure TXMLReader.AddBinding(Attr: TDOMAttr; Prefix: PHashItem; var Chain: TBinding);
  2867. var
  2868. nsUri: DOMString;
  2869. b: TBinding;
  2870. begin
  2871. nsUri := Attr.NodeValue;
  2872. { 'xml' is allowed to be bound to the correct namespace }
  2873. if ((nsUri = stduri_xml) <> (Prefix = FStdPrefix_xml)) or
  2874. (Prefix = FStdPrefix_xmlns) or
  2875. (nsUri = stduri_xmlns) then
  2876. begin
  2877. if (Prefix = FStdPrefix_xml) or (Prefix = FStdPrefix_xmlns) then
  2878. FatalError('Illegal usage of reserved prefix ''%s''', [Prefix^.Key])
  2879. else
  2880. FatalError('Illegal usage of reserved namespace URI ''%s''', [nsUri]);
  2881. end;
  2882. { try reusing an existing binding }
  2883. b := FFreeBindings;
  2884. if Assigned(b) then
  2885. FFreeBindings := b.Next
  2886. else { no free bindings, create a new one }
  2887. begin
  2888. b := TBinding.Create;
  2889. FBindings.Add(b);
  2890. end;
  2891. b.uri := nsUri;
  2892. b.prefix := Prefix;
  2893. b.PrevPrefixBinding := Prefix^.Data;
  2894. if nsUri = '' then
  2895. begin
  2896. if (FXML11 or (Prefix = @FDefaultPrefix)) then // prefix being unbound
  2897. Prefix^.Data := nil
  2898. else
  2899. FatalError('Illegal undefining of namespace'); { position - ? }
  2900. end
  2901. else
  2902. Prefix^.Data := b;
  2903. b.Next := Chain;
  2904. Chain := b;
  2905. end;
  2906. procedure TXMLReader.EndNamespaceScope(var Chain: TBinding);
  2907. var
  2908. b: TBinding;
  2909. begin
  2910. while Assigned(Chain) do
  2911. begin
  2912. b := Chain;
  2913. Chain := b.next;
  2914. b.next := FFreeBindings;
  2915. FFreeBindings := b;
  2916. b.Prefix^.Data := b.prevPrefixBinding;
  2917. end;
  2918. end;
  2919. procedure TXMLReader.ProcessNamespaceAtts(Element: TDOMElement);
  2920. var
  2921. I, J: Integer;
  2922. Map: TDOMNamedNodeMap;
  2923. Prefix, AttrName: PHashItem;
  2924. Attr: TDOMAttr;
  2925. PrefixCount: Integer;
  2926. b: TBinding;
  2927. begin
  2928. if FNesting = Length(FBindingStack) then
  2929. SetLength(FBindingStack, FNesting * 2);
  2930. PrefixCount := 0;
  2931. if Element.HasAttributes then
  2932. begin
  2933. Map := Element.Attributes;
  2934. if Map.Length > LongWord(Length(FWorkAtts)) then
  2935. SetLength(FWorkAtts, Map.Length+10);
  2936. { Pass 1, identify prefixed attrs and assign prefixes }
  2937. for I := 0 to Map.Length-1 do
  2938. begin
  2939. Attr := TDOMAttr(Map[I]);
  2940. AttrName := Attr.NSI.QName;
  2941. if Pos(WideString('xmlns'), AttrName^.Key) = 1 then
  2942. begin
  2943. { this is a namespace declaration }
  2944. if Length(AttrName^.Key) = 5 then
  2945. begin
  2946. // TODO: check all consequences of having zero PrefixLength
  2947. Attr.SetNSI(stduri_xmlns, 0);
  2948. AddBinding(Attr, @FDefaultPrefix, FBindingStack[FNesting]);
  2949. end
  2950. else if AttrName^.Key[6] = ':' then
  2951. begin
  2952. Prefix := FPrefixes.FindOrAdd(@AttrName^.Key[7], Length(AttrName^.Key)-6);
  2953. Attr.SetNSI(stduri_xmlns, 6);
  2954. AddBinding(Attr, Prefix, FBindingStack[FNesting]);
  2955. end;
  2956. end
  2957. else
  2958. begin
  2959. J := Pos(WideChar(':'), AttrName^.Key);
  2960. if J > 1 then
  2961. begin
  2962. FWorkAtts[PrefixCount].Attr := Attr;
  2963. FWorkAtts[PrefixCount].PrefixLen := J;
  2964. Inc(PrefixCount);
  2965. end;
  2966. end;
  2967. end;
  2968. end;
  2969. { Pass 2, now all bindings are known, handle remaining prefixed attributes }
  2970. if PrefixCount > 0 then
  2971. begin
  2972. FNsAttHash.Init(PrefixCount);
  2973. for I := 0 to PrefixCount-1 do
  2974. begin
  2975. AttrName := FWorkAtts[I].Attr.NSI.QName;
  2976. Prefix := FPrefixes.FindOrAdd(PWideChar(AttrName^.Key), FWorkAtts[I].PrefixLen-1);
  2977. b := TBinding(Prefix^.Data);
  2978. if b = nil then
  2979. FatalError('Unbound prefix "%s"', [Prefix^.Key]);
  2980. { detect duplicates }
  2981. J := FWorkAtts[I].PrefixLen+1;
  2982. if FNsAttHash.Locate(@b.uri, @AttrName^.Key[J], Length(AttrName^.Key) - J) then
  2983. FatalError('Duplicate prefixed attribute');
  2984. // convert Attr into namespaced one (by hack for the time being)
  2985. FWorkAtts[I].Attr.SetNSI(b.uri, J-1);
  2986. end;
  2987. end;
  2988. { Finally, expand the element name }
  2989. J := Pos(WideChar(':'), Element.NSI.QName^.Key);
  2990. if J > 1 then
  2991. begin
  2992. Prefix := FPrefixes.FindOrAdd(PWideChar(Element.NSI.QName^.Key), J-1);
  2993. if Prefix^.Data = nil then
  2994. FatalError('Unbound prefix "%s"', [Prefix^.Key]);
  2995. b := TBinding(Prefix^.Data);
  2996. end
  2997. else if Assigned(FDefaultPrefix.Data) then
  2998. b := TBinding(FDefaultPrefix.Data)
  2999. else
  3000. Exit;
  3001. // convert Element into namespaced one (by hack for the time being)
  3002. Element.SetNSI(b.uri, J);
  3003. end;
  3004. function TXMLReader.ParseExternalID(out SysID, PubID: WideString; // [75]
  3005. SysIdOptional: Boolean): Boolean;
  3006. var
  3007. I: Integer;
  3008. wc: WideChar;
  3009. begin
  3010. if FSource.Matches('SYSTEM') then
  3011. begin
  3012. ExpectWhitespace;
  3013. SkipQuotedLiteral(SysID);
  3014. Result := True;
  3015. end
  3016. else if FSource.Matches('PUBLIC') then
  3017. begin
  3018. ExpectWhitespace;
  3019. SkipQuotedLiteral(PubID);
  3020. for I := 1 to Length(PubID) do
  3021. begin
  3022. wc := PubID[I];
  3023. if (wc > #255) or not (Char(ord(wc)) in PubidChars) then
  3024. FatalError('Illegal Public ID literal', -1);
  3025. if (wc = #10) or (wc = #13) then
  3026. PubID[I] := #32;
  3027. end;
  3028. NormalizeSpaces(PubID);
  3029. if SysIdOptional then
  3030. SkipWhitespace
  3031. else
  3032. ExpectWhitespace;
  3033. SkipQuotedLiteral(SysID, not SysIdOptional);
  3034. Result := True;
  3035. end else
  3036. Result := False;
  3037. end;
  3038. function TXMLReader.ValidateAttrSyntax(AttrDef: TDOMAttrDef; const aValue: WideString): Boolean;
  3039. begin
  3040. case AttrDef.DataType of
  3041. dtId, dtIdRef, dtEntity: Result := IsXmlName(aValue, FXML11) and
  3042. ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
  3043. dtIdRefs, dtEntities: Result := IsXmlNames(aValue, FXML11) and
  3044. ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
  3045. dtNmToken: Result := IsXmlNmToken(aValue, FXML11) and AttrDef.HasEnumToken(aValue);
  3046. dtNmTokens: Result := IsXmlNmTokens(aValue, FXML11);
  3047. // IsXmlName() not necessary - enum is never empty and contains valid names
  3048. dtNotation: Result := AttrDef.HasEnumToken(aValue);
  3049. else
  3050. Result := True;
  3051. end;
  3052. end;
  3053. procedure TXMLReader.ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
  3054. var
  3055. L, StartPos, EndPos: Integer;
  3056. Entity: TDOMEntity;
  3057. begin
  3058. L := Length(aValue);
  3059. case Attr.DataType of
  3060. dtId: if not Doc.AddID(Attr) then
  3061. ValidationError('The ID ''%s'' is not unique', [aValue], -1);
  3062. dtIdRef, dtIdRefs: begin
  3063. StartPos := 1;
  3064. while StartPos <= L do
  3065. begin
  3066. EndPos := StartPos;
  3067. while (EndPos <= L) and (aValue[EndPos] <> #32) do
  3068. Inc(EndPos);
  3069. AddForwardRef(FIDRefs, @aValue[StartPos], EndPos-StartPos);
  3070. StartPos := EndPos + 1;
  3071. end;
  3072. end;
  3073. dtEntity, dtEntities: begin
  3074. StartPos := 1;
  3075. while StartPos <= L do
  3076. begin
  3077. EndPos := StartPos;
  3078. while (EndPos <= L) and (aValue[EndPos] <> #32) do
  3079. Inc(EndPos);
  3080. Entity := TDOMEntity(FDocType.Entities.GetNamedItem(Copy(aValue, StartPos, EndPos-StartPos)));
  3081. if (Entity = nil) or (Entity.NotationName = '') then
  3082. ValidationError('Attribute ''%s'' type mismatch', [Attr.Name], -1);
  3083. StartPos := EndPos + 1;
  3084. end;
  3085. end;
  3086. end;
  3087. end;
  3088. procedure TXMLReader.ValidateRoot;
  3089. begin
  3090. if Assigned(FDocType) then
  3091. begin
  3092. if not BufEquals(FName, FDocType.Name) then
  3093. ValidationError('Root element name does not match DTD', [], FName.Length);
  3094. end
  3095. else
  3096. ValidationError('Missing DTD', [], FName.Length);
  3097. end;
  3098. procedure TXMLReader.ValidateDTD;
  3099. var
  3100. I: Integer;
  3101. begin
  3102. if FValidate then
  3103. for I := 0 to FNotationRefs.Count-1 do
  3104. with PForwardRef(FNotationRefs[I])^ do
  3105. if FDocType.Notations.GetNamedItem(Value) = nil then
  3106. DoErrorPos(esError, Format('Notation ''%s'' is not declared', [Value]), Loc);
  3107. ClearRefs(FNotationRefs);
  3108. end;
  3109. procedure TXMLReader.DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean);
  3110. var
  3111. TextNode: TDOMText;
  3112. begin
  3113. // Validating filter part
  3114. case FCurrContentType of
  3115. ctChildren:
  3116. if not Whitespace then
  3117. ValidationError('Character data is not allowed in element-only content',[])
  3118. else
  3119. if FSaViolation then
  3120. StandaloneError(-1);
  3121. ctEmpty:
  3122. ValidationError('Character data is not allowed in EMPTY elements', []);
  3123. end;
  3124. // Document builder part
  3125. TextNode := Doc.CreateTextNodeBuf(ch, Count, Whitespace and (FCurrContentType = ctChildren));
  3126. FCursor.AppendChild(TextNode);
  3127. end;
  3128. procedure TXMLReader.DoAttrText(ch: PWideChar; Count: Integer);
  3129. begin
  3130. FCursor.AppendChild(Doc.CreateTextNodeBuf(ch, Count, False));
  3131. end;
  3132. procedure TXMLReader.DoComment(ch: PWideChar; Count: Integer);
  3133. var
  3134. Node: TDOMComment;
  3135. begin
  3136. // validation filter part
  3137. if FCurrContentType = ctEmpty then
  3138. ValidationError('Comments are not allowed within EMPTY elements', []);
  3139. // DOM builder part
  3140. if (not FIgnoreComments) and Assigned(FCursor) then
  3141. begin
  3142. Node := Doc.CreateCommentBuf(ch, Count);
  3143. FCursor.AppendChild(Node);
  3144. end;
  3145. end;
  3146. procedure TXMLReader.DoCDSect(ch: PWideChar; Count: Integer);
  3147. var
  3148. s: WideString;
  3149. begin
  3150. if FCurrContentType = ctChildren then
  3151. ValidationError('CDATA sections are not allowed in element-only content',[]);
  3152. if not FCDSectionsAsText then
  3153. begin
  3154. SetString(s, ch, Count);
  3155. // SAX: LexicalHandler.StartCDATA;
  3156. // SAX: ContentHandler.Characters(...);
  3157. FCursor.AppendChild(doc.CreateCDATASection(s));
  3158. // SAX: LexicalHandler.EndCDATA;
  3159. end
  3160. else
  3161. FCursor.AppendChild(doc.CreateTextNodeBuf(ch, Count, False));
  3162. end;
  3163. procedure TXMLReader.DoNotationDecl(const aName, aPubID, aSysID: WideString);
  3164. var
  3165. Notation: TDOMNotationEx;
  3166. begin
  3167. if FDocType.Notations.GetNamedItem(aName) = nil then
  3168. begin
  3169. Notation := TDOMNotationEx(TDOMNotation.Create(doc));
  3170. Notation.FName := aName;
  3171. Notation.FPublicID := aPubID;
  3172. Notation.FSystemID := aSysID;
  3173. FDocType.Notations.SetNamedItem(Notation);
  3174. end
  3175. else
  3176. ValidationError('Duplicate notation declaration: ''%s''', [aName]);
  3177. end;
  3178. procedure TXMLReader.PushVC(aElement: TDOMElement; aElDef: TDOMElementDef);
  3179. begin
  3180. Inc(FNesting);
  3181. if FNesting >= Length(FValidator) then
  3182. SetLength(FValidator, FNesting * 2);
  3183. FValidator[FNesting].FElement := aElement;
  3184. FValidator[FNesting].FElementDef := aElDef;
  3185. FValidator[FNesting].FCurCP := nil;
  3186. FValidator[FNesting].FFailed := False;
  3187. UpdateConstraints;
  3188. end;
  3189. procedure TXMLReader.PopVC;
  3190. begin
  3191. if FNesting > 0 then Dec(FNesting);
  3192. UpdateConstraints;
  3193. end;
  3194. procedure TXMLReader.UpdateConstraints;
  3195. begin
  3196. if FValidate and Assigned(FValidator[FNesting].FElementDef) then
  3197. begin
  3198. FCurrContentType := FValidator[FNesting].FElementDef.ContentType;
  3199. FSaViolation := FStandalone and (FValidator[FNesting].FElementDef.FExternallyDeclared);
  3200. end
  3201. else
  3202. begin
  3203. FCurrContentType := ctAny;
  3204. FSaViolation := False;
  3205. end;
  3206. end;
  3207. { TElementValidator }
  3208. function TElementValidator.IsElementAllowed(Def: TDOMElementDef): Boolean;
  3209. var
  3210. I: Integer;
  3211. Next: TContentParticle;
  3212. begin
  3213. Result := True;
  3214. // if element is not declared, non-validity has been already reported, no need to report again...
  3215. if Assigned(Def) and Assigned(FElementDef) then
  3216. begin
  3217. case FElementDef.ContentType of
  3218. ctMixed: begin
  3219. for I := 0 to FElementDef.RootCP.ChildCount-1 do
  3220. begin
  3221. if Def = FElementDef.RootCP.Children[I].Def then
  3222. Exit;
  3223. end;
  3224. Result := False;
  3225. end;
  3226. ctEmpty: Result := False;
  3227. ctChildren: begin
  3228. if FCurCP = nil then
  3229. Next := FElementDef.RootCP.FindFirst(Def)
  3230. else
  3231. Next := FCurCP.FindNext(Def, 0); { second arg ignored here }
  3232. Result := Assigned(Next);
  3233. if Result then
  3234. FCurCP := Next
  3235. else
  3236. FFailed := True; // used to prevent extra error at the end of element
  3237. end;
  3238. // ctAny, ctUndeclared: returns True by default
  3239. end;
  3240. end;
  3241. end;
  3242. function TElementValidator.Incomplete: Boolean;
  3243. begin
  3244. if Assigned(FElementDef) and (FElementDef.ContentType = ctChildren) and (not FFailed) then
  3245. begin
  3246. if FCurCP <> nil then
  3247. Result := FCurCP.MoreRequired(0) { arg ignored here }
  3248. else
  3249. Result := FElementDef.RootCP.IsRequired;
  3250. end
  3251. else
  3252. Result := False;
  3253. end;
  3254. { TContentParticle }
  3255. function TContentParticle.Add: TContentParticle;
  3256. begin
  3257. if FChildren = nil then
  3258. FChildren := TFPList.Create;
  3259. Result := TContentParticle.Create;
  3260. Result.FParent := Self;
  3261. Result.FIndex := FChildren.Add(Result);
  3262. end;
  3263. destructor TContentParticle.Destroy;
  3264. var
  3265. I: Integer;
  3266. begin
  3267. if Assigned(FChildren) then
  3268. for I := FChildren.Count-1 downto 0 do
  3269. TObject(FChildren[I]).Free;
  3270. FChildren.Free;
  3271. inherited Destroy;
  3272. end;
  3273. function TContentParticle.GetChild(Index: Integer): TContentParticle;
  3274. begin
  3275. Result := TContentParticle(FChildren[Index]);
  3276. end;
  3277. function TContentParticle.GetChildCount: Integer;
  3278. begin
  3279. if Assigned(FChildren) then
  3280. Result := FChildren.Count
  3281. else
  3282. Result := 0;
  3283. end;
  3284. function TContentParticle.IsRequired: Boolean;
  3285. var
  3286. I: Integer;
  3287. begin
  3288. Result := (CPQuant = cqOnce) or (CPQuant = cqOnceOrMore);
  3289. // do not return True if all children are optional
  3290. if (CPType <> ctName) and Result then
  3291. begin
  3292. for I := 0 to ChildCount-1 do
  3293. begin
  3294. Result := Children[I].IsRequired;
  3295. if Result then Exit;
  3296. end;
  3297. end;
  3298. end;
  3299. function TContentParticle.MoreRequired(ChildIdx: Integer): Boolean;
  3300. var
  3301. I: Integer;
  3302. begin
  3303. Result := False;
  3304. if CPType = ctSeq then
  3305. begin
  3306. for I := ChildIdx + 1 to ChildCount-1 do
  3307. begin
  3308. Result := Children[I].IsRequired;
  3309. if Result then Exit;
  3310. end;
  3311. end;
  3312. if Assigned(FParent) then
  3313. Result := FParent.MoreRequired(FIndex);
  3314. end;
  3315. function TContentParticle.FindFirst(aDef: TDOMElementDef): TContentParticle;
  3316. var
  3317. I: Integer;
  3318. begin
  3319. Result := nil;
  3320. case CPType of
  3321. ctSeq:
  3322. for I := 0 to ChildCount-1 do with Children[I] do
  3323. begin
  3324. Result := FindFirst(aDef);
  3325. if Assigned(Result) or IsRequired then
  3326. Exit;
  3327. end;
  3328. ctChoice:
  3329. for I := 0 to ChildCount-1 do with Children[I] do
  3330. begin
  3331. Result := FindFirst(aDef);
  3332. if Assigned(Result) then
  3333. Exit;
  3334. end;
  3335. else // ctName
  3336. if aDef = Self.Def then
  3337. Result := Self
  3338. end;
  3339. end;
  3340. function TContentParticle.FindNext(aDef: TDOMElementDef;
  3341. ChildIdx: Integer): TContentParticle;
  3342. var
  3343. I: Integer;
  3344. begin
  3345. Result := nil;
  3346. if CPType = ctSeq then // search sequence to its end
  3347. begin
  3348. for I := ChildIdx + 1 to ChildCount-1 do with Children[I] do
  3349. begin
  3350. Result := FindFirst(aDef);
  3351. if (Result <> nil) or IsRequired then
  3352. Exit;
  3353. end;
  3354. end;
  3355. if (CPQuant = cqZeroOrMore) or (CPQuant = cqOnceOrMore) then
  3356. Result := FindFirst(aDef);
  3357. if (Result = nil) and Assigned(FParent) then
  3358. Result := FParent.FindNext(aDef, FIndex);
  3359. end;
  3360. { TDOMElementDef }
  3361. destructor TDOMElementDef.Destroy;
  3362. begin
  3363. RootCP.Free;
  3364. inherited Destroy;
  3365. end;
  3366. { plain calls }
  3367. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text);
  3368. var
  3369. Reader: TXMLReader;
  3370. Src: TXMLCharSource;
  3371. begin
  3372. ADoc := nil;
  3373. Src := TXMLFileInputSource.Create(f);
  3374. Reader := TXMLReader.Create;
  3375. try
  3376. Reader.ProcessXML(Src);
  3377. finally
  3378. ADoc := TXMLDocument(Reader.Doc);
  3379. Reader.Free;
  3380. end;
  3381. end;
  3382. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String);
  3383. var
  3384. Reader: TXMLReader;
  3385. Src: TXMLCharSource;
  3386. begin
  3387. ADoc := nil;
  3388. Reader := TXMLReader.Create;
  3389. try
  3390. Src := TXMLStreamInputSource.Create(f, False);
  3391. Src.SystemID := ABaseURI;
  3392. Reader.ProcessXML(Src);
  3393. finally
  3394. ADoc := TXMLDocument(Reader.doc);
  3395. Reader.Free;
  3396. end;
  3397. end;
  3398. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream);
  3399. begin
  3400. ReadXMLFile(ADoc, f, 'stream:');
  3401. end;
  3402. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String);
  3403. var
  3404. FileStream: TStream;
  3405. begin
  3406. ADoc := nil;
  3407. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3408. try
  3409. ReadXMLFile(ADoc, FileStream, FilenameToURI(AFilename));
  3410. finally
  3411. FileStream.Free;
  3412. end;
  3413. end;
  3414. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text);
  3415. var
  3416. Reader: TXMLReader;
  3417. Src: TXMLCharSource;
  3418. begin
  3419. Reader := TXMLReader.Create;
  3420. try
  3421. Src := TXMLFileInputSource.Create(f);
  3422. Reader.ProcessFragment(Src, AParentNode);
  3423. finally
  3424. Reader.Free;
  3425. end;
  3426. end;
  3427. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream; const ABaseURI: String);
  3428. var
  3429. Reader: TXMLReader;
  3430. Src: TXMLCharSource;
  3431. begin
  3432. Reader := TXMLReader.Create;
  3433. try
  3434. Src := TXMLStreamInputSource.Create(f, False);
  3435. Src.SystemID := ABaseURI;
  3436. Reader.ProcessFragment(Src, AParentNode);
  3437. finally
  3438. Reader.Free;
  3439. end;
  3440. end;
  3441. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream);
  3442. begin
  3443. ReadXMLFragment(AParentNode, f, 'stream:');
  3444. end;
  3445. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  3446. var
  3447. Stream: TStream;
  3448. begin
  3449. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3450. try
  3451. ReadXMLFragment(AParentNode, Stream, FilenameToURI(AFilename));
  3452. finally
  3453. Stream.Free;
  3454. end;
  3455. end;
  3456. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text);
  3457. var
  3458. Reader: TXMLReader;
  3459. Src: TXMLCharSource;
  3460. begin
  3461. ADoc := nil;
  3462. Reader := TXMLReader.Create;
  3463. try
  3464. Src := TXMLFileInputSource.Create(f);
  3465. Reader.ProcessDTD(Src);
  3466. finally
  3467. ADoc := TXMLDocument(Reader.doc);
  3468. Reader.Free;
  3469. end;
  3470. end;
  3471. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream; const ABaseURI: String);
  3472. var
  3473. Reader: TXMLReader;
  3474. Src: TXMLCharSource;
  3475. begin
  3476. ADoc := nil;
  3477. Reader := TXMLReader.Create;
  3478. try
  3479. Src := TXMLStreamInputSource.Create(f, False);
  3480. Src.SystemID := ABaseURI;
  3481. Reader.ProcessDTD(Src);
  3482. finally
  3483. ADoc := TXMLDocument(Reader.doc);
  3484. Reader.Free;
  3485. end;
  3486. end;
  3487. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: TStream);
  3488. begin
  3489. ReadDTDFile(ADoc, f, 'stream:');
  3490. end;
  3491. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String);
  3492. var
  3493. Stream: TStream;
  3494. begin
  3495. ADoc := nil;
  3496. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  3497. try
  3498. ReadDTDFile(ADoc, Stream, FilenameToURI(AFilename));
  3499. finally
  3500. Stream.Free;
  3501. end;
  3502. end;
  3503. end.