xmlread.pp 103 KB

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