DTDReader.cs 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
  1. //
  2. // System.Xml.DTDReader
  3. //
  4. // Author:
  5. // Atsushi Enomoto ([email protected])
  6. //
  7. // (C)2003 Atsushi Enomoto
  8. //
  9. // This code is almost imported from existing XmlTextReader.cs
  10. //
  11. using System;
  12. using System.Collections;
  13. using System.Collections.Specialized;
  14. using System.Globalization;
  15. using System.IO;
  16. using System.Text;
  17. using Mono.Xml;
  18. using System.Xml.Schema;
  19. using Mono.Xml.Native;
  20. namespace System.Xml
  21. {
  22. internal class DTDReader : IXmlLineInfo
  23. {
  24. private XmlParserInput currentInput;
  25. private Stack parserInputStack;
  26. private string entityReferenceName;
  27. private char [] nameBuffer;
  28. private int nameLength;
  29. private int nameCapacity;
  30. private const int initialNameCapacity = 256;
  31. private StringBuilder valueBuffer;
  32. private int currentLinkedNodeLineNumber;
  33. private int currentLinkedNodeLinePosition;
  34. // Parameter entity placeholder
  35. private int dtdIncludeSect;
  36. private bool normalization;
  37. private bool processingInternalSubset;
  38. string cachedPublicId;
  39. string cachedSystemId;
  40. DTDObjectModel DTD;
  41. #if DTD_HANDLE_EVENTS
  42. public event ValidationEventHandler ValidationEventHandler;
  43. #endif
  44. // .ctor()
  45. public DTDReader (DTDObjectModel dtd,
  46. int startLineNumber,
  47. int startLinePosition)
  48. {
  49. this.DTD = dtd;
  50. currentLinkedNodeLineNumber = startLineNumber;
  51. currentLinkedNodeLinePosition = startLinePosition;
  52. Init ();
  53. }
  54. // Properties
  55. public string BaseURI {
  56. get { return currentInput.BaseURI; }
  57. }
  58. public bool Normalization {
  59. get { return normalization; }
  60. set { normalization = value; }
  61. }
  62. // A buffer for ReadContent for ReadOuterXml
  63. private StringBuilder CurrentTag {
  64. get {
  65. return currentInput.CurrentMarkup;
  66. }
  67. }
  68. public int LineNumber {
  69. get { return currentInput.LineNumber; }
  70. }
  71. public int LinePosition {
  72. get { return currentInput.LinePosition; }
  73. }
  74. public bool HasLineInfo ()
  75. {
  76. return true;
  77. }
  78. // Methods
  79. private void Init ()
  80. {
  81. parserInputStack = new Stack ();
  82. entityReferenceName = String.Empty;
  83. nameBuffer = new char [initialNameCapacity];
  84. nameLength = 0;
  85. nameCapacity = initialNameCapacity;
  86. valueBuffer = new StringBuilder (512);
  87. }
  88. internal DTDObjectModel GenerateDTDObjectModel ()
  89. {
  90. // now compile DTD
  91. int originalParserDepth = parserInputStack.Count;
  92. bool more;
  93. if (DTD.InternalSubset != null && DTD.InternalSubset.Length > 0) {
  94. this.processingInternalSubset = true;
  95. XmlParserInput original = currentInput;
  96. currentInput = new XmlParserInput (
  97. new StringReader (DTD.InternalSubset),
  98. DTD.BaseURI,
  99. currentLinkedNodeLineNumber,
  100. currentLinkedNodeLinePosition);
  101. currentInput.InitialState = false;
  102. do {
  103. more = ProcessDTDSubset ();
  104. if (PeekChar () == -1 && parserInputStack.Count > 0)
  105. PopParserInput ();
  106. } while (more || parserInputStack.Count > originalParserDepth);
  107. if (dtdIncludeSect != 0)
  108. throw new XmlException (this as IXmlLineInfo,"INCLUDE section is not ended correctly.");
  109. currentInput = original;
  110. this.processingInternalSubset = false;
  111. }
  112. if (DTD.SystemId != null && DTD.SystemId != String.Empty && DTD.Resolver != null) {
  113. PushParserInput (DTD.SystemId);
  114. do {
  115. more = ProcessDTDSubset ();
  116. if (PeekChar () == -1 && parserInputStack.Count > 1)
  117. PopParserInput ();
  118. } while (more || parserInputStack.Count > originalParserDepth + 1);
  119. if (dtdIncludeSect != 0)
  120. throw new XmlException (this as IXmlLineInfo,"INCLUDE section is not ended correctly.");
  121. PopParserInput ();
  122. }
  123. StringCollection sc = new StringCollection ();
  124. // Entity recursion check.
  125. foreach (DTDEntityDeclaration ent in DTD.EntityDecls.Values) {
  126. if (ent.NotationName != null) {
  127. ent.ScanEntityValue (sc);
  128. sc.Clear ();
  129. }
  130. }
  131. return DTD;
  132. }
  133. // Read any one of following:
  134. // elementdecl, AttlistDecl, EntityDecl, NotationDecl,
  135. // PI, Comment, Parameter Entity, or doctype termination char(']')
  136. //
  137. // Returns true if it may have any more contents, or false if not.
  138. private bool ProcessDTDSubset ()
  139. {
  140. SkipWhitespace ();
  141. switch(ReadChar ())
  142. {
  143. case -1:
  144. return false;
  145. case '%':
  146. // It affects on entity references' well-formedness
  147. if (this.processingInternalSubset)
  148. DTD.InternalSubsetHasPEReference = true;
  149. string peName = ReadName ();
  150. Expect (';');
  151. currentInput.InsertParameterEntityBuffer (" ");
  152. currentInput.InsertParameterEntityBuffer (GetPEValue (peName));
  153. currentInput.InsertParameterEntityBuffer (" ");
  154. int currentLine = currentInput.LineNumber;
  155. int currentColumn = currentInput.LinePosition;
  156. while (currentInput.HasPEBuffer)
  157. ProcessDTDSubset ();
  158. if (currentInput.LineNumber != currentLine ||
  159. currentInput.LinePosition != currentColumn)
  160. throw new XmlException (this as IXmlLineInfo,
  161. "Incorrectly nested parameter entity.");
  162. break;
  163. case '<':
  164. int c = ReadChar ();
  165. switch(c)
  166. {
  167. case '?':
  168. // Only read, no store.
  169. ReadProcessingInstruction ();
  170. break;
  171. case '!':
  172. CompileDeclaration ();
  173. break;
  174. case -1:
  175. throw new XmlException (this as IXmlLineInfo, "Unexpected end of stream.");
  176. default:
  177. throw new XmlException (this as IXmlLineInfo, "Syntax Error after '<' character: " + (char) c);
  178. }
  179. break;
  180. case ']':
  181. if (dtdIncludeSect == 0)
  182. throw new XmlException (this as IXmlLineInfo, "Unbalanced end of INCLUDE/IGNORE section.");
  183. // End of inclusion
  184. Expect ("]>");
  185. dtdIncludeSect--;
  186. SkipWhitespace ();
  187. break;
  188. default:
  189. throw new XmlException (this as IXmlLineInfo,String.Format ("Syntax Error inside doctypedecl markup : {0}({1})", PeekChar (), (char) PeekChar ()));
  190. }
  191. currentInput.InitialState = false;
  192. return true;
  193. }
  194. private void CompileDeclaration ()
  195. {
  196. switch(ReadChar ())
  197. {
  198. case '-':
  199. Expect ('-');
  200. // Only read, no store.
  201. ReadComment ();
  202. break;
  203. case 'E':
  204. switch(ReadChar ())
  205. {
  206. case 'N':
  207. Expect ("TITY");
  208. if (!SkipWhitespace ())
  209. throw new XmlException (this as IXmlLineInfo,
  210. "Whitespace is required after '<!ENTITY' in DTD entity declaration.");
  211. LOOPBACK:
  212. if (PeekChar () == '%') {
  213. ReadChar ();
  214. if (!SkipWhitespace ()) {
  215. ExpandPERef ();
  216. goto LOOPBACK;
  217. } else {
  218. TryExpandPERef ();
  219. SkipWhitespace ();
  220. if (XmlChar.IsNameChar (PeekChar ()))
  221. ReadParameterEntityDecl ();
  222. else
  223. throw new XmlException (this as IXmlLineInfo,"expected name character");
  224. }
  225. break;
  226. }
  227. DTDEntityDeclaration ent = ReadEntityDecl ();
  228. if (DTD.EntityDecls [ent.Name] == null)
  229. DTD.EntityDecls.Add (ent.Name, ent);
  230. break;
  231. case 'L':
  232. Expect ("EMENT");
  233. DTDElementDeclaration el = ReadElementDecl ();
  234. DTD.ElementDecls.Add (el.Name, el);
  235. break;
  236. default:
  237. throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!E' (ELEMENT or ENTITY must be found)");
  238. }
  239. break;
  240. case 'A':
  241. Expect ("TTLIST");
  242. DTDAttListDeclaration atl = ReadAttListDecl ();
  243. DTD.AttListDecls.Add (atl.Name, atl);
  244. break;
  245. case 'N':
  246. Expect ("OTATION");
  247. DTDNotationDeclaration not = ReadNotationDecl ();
  248. DTD.NotationDecls.Add (not.Name, not);
  249. break;
  250. case '[':
  251. // conditional sections
  252. SkipWhitespace ();
  253. TryExpandPERef ();
  254. ExpectAfterWhitespace ('I');
  255. switch (ReadChar ()) {
  256. case 'N':
  257. Expect ("CLUDE");
  258. ExpectAfterWhitespace ('[');
  259. dtdIncludeSect++;
  260. break;
  261. case 'G':
  262. Expect ("NORE");
  263. ReadIgnoreSect ();
  264. break;
  265. }
  266. break;
  267. default:
  268. throw new XmlException (this as IXmlLineInfo,"Syntax Error after '<!' characters.");
  269. }
  270. }
  271. private void ReadIgnoreSect ()
  272. {
  273. ExpectAfterWhitespace ('[');
  274. int dtdIgnoreSect = 1;
  275. while (dtdIgnoreSect > 0) {
  276. switch (ReadChar ()) {
  277. case -1:
  278. throw new XmlException (this as IXmlLineInfo,"Unexpected IGNORE section end.");
  279. case '<':
  280. if (PeekChar () != '!')
  281. break;
  282. ReadChar ();
  283. if (PeekChar () != '[')
  284. break;
  285. ReadChar ();
  286. dtdIgnoreSect++;
  287. break;
  288. case ']':
  289. if (PeekChar () != ']')
  290. break;
  291. ReadChar ();
  292. if (PeekChar () != '>')
  293. break;
  294. ReadChar ();
  295. dtdIgnoreSect--;
  296. break;
  297. }
  298. }
  299. if (dtdIgnoreSect != 0)
  300. throw new XmlException (this as IXmlLineInfo,"IGNORE section is not ended correctly.");
  301. }
  302. // The reader is positioned on the head of the name.
  303. private DTDElementDeclaration ReadElementDecl ()
  304. {
  305. DTDElementDeclaration decl = new DTDElementDeclaration (DTD);
  306. decl.IsInternalSubset = this.processingInternalSubset;
  307. if (!SkipWhitespace ())
  308. throw new XmlException (this as IXmlLineInfo,
  309. "Whitespace is required between '<!ELEMENT' and name in DTD element declaration.");
  310. TryExpandPERef ();
  311. SkipWhitespace ();
  312. decl.Name = ReadName ();
  313. if (!SkipWhitespace ())
  314. throw new XmlException (this as IXmlLineInfo,
  315. "Whitespace is required between name and content in DTD element declaration.");
  316. TryExpandPERef ();
  317. ReadContentSpec (decl);
  318. SkipWhitespace ();
  319. // This expanding is only allowed as a non-validating parser.
  320. TryExpandPERef ();
  321. ExpectAfterWhitespace ('>');
  322. return decl;
  323. }
  324. // read 'children'(BNF) of contentspec
  325. private void ReadContentSpec (DTDElementDeclaration decl)
  326. {
  327. TryExpandPERef ();
  328. SkipWhitespace ();
  329. switch(ReadChar ())
  330. {
  331. case 'E':
  332. decl.IsEmpty = true;
  333. Expect ("MPTY");
  334. break;
  335. case 'A':
  336. decl.IsAny = true;
  337. Expect ("NY");
  338. break;
  339. case '(':
  340. DTDContentModel model = decl.ContentModel;
  341. SkipWhitespace ();
  342. TryExpandPERef ();
  343. SkipWhitespace ();
  344. if(PeekChar () == '#') {
  345. // Mixed Contents. "#PCDATA" must appear first.
  346. decl.IsMixedContent = true;
  347. model.Occurence = DTDOccurence.ZeroOrMore;
  348. model.OrderType = DTDContentOrderType.Or;
  349. Expect ("#PCDATA");
  350. SkipWhitespace ();
  351. TryExpandPERef ();
  352. SkipWhitespace ();
  353. while(PeekChar () != ')') {
  354. Expect('|');
  355. SkipWhitespace ();
  356. TryExpandPERef ();
  357. SkipWhitespace ();
  358. DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
  359. // elem.LineNumber = currentInput.LineNumber;
  360. // elem.LinePosition = currentInput.LinePosition;
  361. elem.ElementName = ReadName ();
  362. this.AddContentModel (model.ChildModels, elem);
  363. SkipWhitespace ();
  364. TryExpandPERef ();
  365. SkipWhitespace ();
  366. }
  367. Expect (')');
  368. if (model.ChildModels.Count > 0)
  369. Expect ('*');
  370. else if (PeekChar () == '*')
  371. Expect ('*');
  372. } else {
  373. // Non-Mixed Contents
  374. model.ChildModels.Add (ReadCP (decl));
  375. SkipWhitespace ();
  376. do { // copied from ReadCP() ...;-)
  377. TryExpandPERef ();
  378. SkipWhitespace ();
  379. if(PeekChar ()=='|') {
  380. // CPType=Or
  381. if (model.OrderType == DTDContentOrderType.Seq)
  382. throw new XmlException (this as IXmlLineInfo,
  383. "Inconsistent choice markup in sequence cp.");
  384. model.OrderType = DTDContentOrderType.Or;
  385. ReadChar ();
  386. SkipWhitespace ();
  387. AddContentModel (model.ChildModels, ReadCP (decl));
  388. SkipWhitespace ();
  389. }
  390. else if(PeekChar () == ',')
  391. {
  392. // CPType=Seq
  393. if (model.OrderType == DTDContentOrderType.Or)
  394. throw new XmlException (this as IXmlLineInfo,
  395. "Inconsistent sequence markup in choice cp.");
  396. model.OrderType = DTDContentOrderType.Seq;
  397. ReadChar ();
  398. SkipWhitespace ();
  399. model.ChildModels.Add (ReadCP (decl));
  400. SkipWhitespace ();
  401. }
  402. else
  403. break;
  404. }
  405. while(true);
  406. Expect (')');
  407. switch(PeekChar ())
  408. {
  409. case '?':
  410. model.Occurence = DTDOccurence.Optional;
  411. ReadChar ();
  412. break;
  413. case '*':
  414. model.Occurence = DTDOccurence.ZeroOrMore;
  415. ReadChar ();
  416. break;
  417. case '+':
  418. model.Occurence = DTDOccurence.OneOrMore;
  419. ReadChar ();
  420. break;
  421. }
  422. SkipWhitespace ();
  423. }
  424. SkipWhitespace ();
  425. break;
  426. default:
  427. throw new XmlException (this as IXmlLineInfo, "ContentSpec is missing.");
  428. }
  429. }
  430. // Read 'cp' (BNF) of contentdecl (BNF)
  431. private DTDContentModel ReadCP (DTDElementDeclaration elem)
  432. {
  433. DTDContentModel model = null;
  434. TryExpandPERef ();
  435. SkipWhitespace ();
  436. if(PeekChar () == '(') {
  437. model = new DTDContentModel (DTD, elem.Name);
  438. ReadChar ();
  439. SkipWhitespace ();
  440. model.ChildModels.Add (ReadCP (elem));
  441. SkipWhitespace ();
  442. do {
  443. TryExpandPERef ();
  444. SkipWhitespace ();
  445. if(PeekChar ()=='|') {
  446. // CPType=Or
  447. if (model.OrderType == DTDContentOrderType.Seq)
  448. throw new XmlException (this as IXmlLineInfo,
  449. "Inconsistent choice markup in sequence cp.");
  450. model.OrderType = DTDContentOrderType.Or;
  451. ReadChar ();
  452. SkipWhitespace ();
  453. AddContentModel (model.ChildModels, ReadCP (elem));
  454. SkipWhitespace ();
  455. }
  456. else if(PeekChar () == ',') {
  457. // CPType=Seq
  458. if (model.OrderType == DTDContentOrderType.Or)
  459. throw new XmlException (this as IXmlLineInfo,
  460. "Inconsistent sequence markup in choice cp.");
  461. model.OrderType = DTDContentOrderType.Seq;
  462. ReadChar ();
  463. SkipWhitespace ();
  464. model.ChildModels.Add (ReadCP (elem));
  465. SkipWhitespace ();
  466. }
  467. else
  468. break;
  469. }
  470. while(true);
  471. ExpectAfterWhitespace (')');
  472. }
  473. else {
  474. TryExpandPERef ();
  475. model = new DTDContentModel (DTD, elem.Name);
  476. SkipWhitespace ();
  477. model.ElementName = ReadName ();
  478. }
  479. switch(PeekChar ()) {
  480. case '?':
  481. model.Occurence = DTDOccurence.Optional;
  482. ReadChar ();
  483. break;
  484. case '*':
  485. model.Occurence = DTDOccurence.ZeroOrMore;
  486. ReadChar ();
  487. break;
  488. case '+':
  489. model.Occurence = DTDOccurence.OneOrMore;
  490. ReadChar ();
  491. break;
  492. }
  493. return model;
  494. }
  495. private void AddContentModel (DTDContentModelCollection cmc, DTDContentModel cm)
  496. {
  497. if (cm.ElementName != null) {
  498. for (int i = 0; i < cmc.Count; i++) {
  499. if (cmc [i].ElementName == cm.ElementName) {
  500. HandleError (new XmlSchemaException ("Element content must be unique inside mixed content model.",
  501. this.LineNumber,
  502. this.LinePosition,
  503. null,
  504. this.BaseURI,
  505. null));
  506. return;
  507. }
  508. }
  509. }
  510. cmc.Add (cm);
  511. }
  512. // The reader is positioned on the first name char.
  513. private void ReadParameterEntityDecl ()
  514. {
  515. DTDParameterEntityDeclaration decl =
  516. new DTDParameterEntityDeclaration();
  517. decl.BaseURI = BaseURI;
  518. decl.Name = ReadName ();
  519. if (!SkipWhitespace ())
  520. throw new XmlException (this as IXmlLineInfo,
  521. "Whitespace is required after name in DTD parameter entity declaration.");
  522. if (PeekChar () == 'S' || PeekChar () == 'P') {
  523. // read publicId/systemId
  524. ReadExternalID ();
  525. decl.PublicId = cachedPublicId;
  526. decl.SystemId = cachedSystemId;
  527. SkipWhitespace ();
  528. decl.Resolve (this.DTD.Resolver);
  529. ResolveExternalEntityReplacementText (decl);
  530. } else {
  531. TryExpandPERef ();
  532. int quoteChar = ReadChar ();
  533. if (quoteChar != '\'' && quoteChar != '"')
  534. throw new XmlException ("quotation char was expected.");
  535. int start = CurrentTag.Length;
  536. ClearValueBuffer ();
  537. bool loop = true;
  538. while (loop) {
  539. int c = ReadChar ();
  540. switch (c) {
  541. case -1:
  542. throw new XmlException ("unexpected end of stream in entity value definition.");
  543. case '"':
  544. if (quoteChar == '"')
  545. loop = false;
  546. else
  547. AppendValueChar ('"');
  548. break;
  549. case '\'':
  550. if (quoteChar == '\'')
  551. loop = false;
  552. else
  553. AppendValueChar ('\'');
  554. break;
  555. default:
  556. if (XmlChar.IsInvalid (c))
  557. throw new XmlException (this as IXmlLineInfo, "Invalid character was used to define parameter entity.");
  558. AppendValueChar (c);
  559. break;
  560. }
  561. }
  562. decl.LiteralEntityValue = CreateValueString ();
  563. ClearValueBuffer ();
  564. ResolveInternalEntityReplacementText (decl);
  565. }
  566. ExpectAfterWhitespace ('>');
  567. if (DTD.PEDecls [decl.Name] == null) {
  568. DTD.PEDecls.Add (decl.Name, decl);
  569. }
  570. }
  571. private void ResolveExternalEntityReplacementText (DTDEntityBase decl)
  572. {
  573. if (decl.LiteralEntityValue.StartsWith ("<?xml")) {
  574. XmlTextReader xtr = new XmlTextReader (decl.LiteralEntityValue, XmlNodeType.Element, null);
  575. xtr.SkipTextDeclaration ();
  576. if (decl is DTDEntityDeclaration) {
  577. // GE - also checked as valid contents
  578. StringBuilder sb = new StringBuilder ();
  579. xtr.Normalization = this.Normalization;
  580. xtr.Read ();
  581. while (!xtr.EOF)
  582. sb.Append (xtr.ReadOuterXml ());
  583. decl.ReplacementText = sb.ToString ();
  584. }
  585. else
  586. // PE
  587. decl.ReplacementText = xtr.GetRemainder ().ReadToEnd ();
  588. }
  589. else
  590. decl.ReplacementText = decl.LiteralEntityValue;
  591. }
  592. private void ResolveInternalEntityReplacementText (DTDEntityBase decl)
  593. {
  594. string value = decl.LiteralEntityValue;
  595. int len = value.Length;
  596. ClearValueBuffer ();
  597. for (int i = 0; i < len; i++) {
  598. int ch = value [i];
  599. int end = 0;
  600. string name;
  601. switch (ch) {
  602. case '&':
  603. i++;
  604. end = value.IndexOf (';', i);
  605. if (end < i + 1)
  606. throw new XmlException (decl, "Invalid reference markup.");
  607. // expand charref
  608. if (value [i] == '#') {
  609. i++;
  610. ch = GetCharacterReference (decl, value, ref i, end);
  611. if (XmlChar.IsInvalid (ch))
  612. throw new XmlException (this as IXmlLineInfo, "Invalid character was used to define parameter entity.");
  613. } else {
  614. name = value.Substring (i, end - i);
  615. // don't expand "general" entity.
  616. AppendValueChar ('&');
  617. valueBuffer.Append (name);
  618. AppendValueChar (';');
  619. i = end;
  620. break;
  621. }
  622. if (XmlChar.IsInvalid (ch))
  623. throw new XmlException (decl, "Invalid character was found in the entity declaration.");
  624. AppendValueChar (ch);
  625. break;
  626. case '%':
  627. i++;
  628. end = value.IndexOf (';', i);
  629. if (end < i + 1)
  630. throw new XmlException (decl, "Invalid reference markup.");
  631. name = value.Substring (i, end - i);
  632. valueBuffer.Append (GetPEValue (name));
  633. i = end;
  634. break;
  635. default:
  636. AppendValueChar (ch);
  637. break;
  638. }
  639. }
  640. decl.ReplacementText = CreateValueString ();
  641. // FIXME: This check should be done on
  642. if (decl is DTDEntityDeclaration) {
  643. // GE - also checked as valid contents
  644. XmlTextReader xtr = new XmlTextReader (decl.ReplacementText, XmlNodeType.Element, null);
  645. StringBuilder sb = new StringBuilder ();
  646. xtr.Normalization = this.Normalization;
  647. xtr.Read ();
  648. while (!xtr.EOF)
  649. sb.Append (xtr.ReadOuterXml ());
  650. decl.ReplacementText = sb.ToString ();
  651. }
  652. ClearValueBuffer ();
  653. }
  654. private int GetCharacterReference (IXmlLineInfo li, string value, ref int index, int end)
  655. {
  656. int ret = 0;
  657. if (value [index] == 'x') {
  658. try {
  659. ret = int.Parse (value.Substring (index + 1, end - index - 1), NumberStyles.HexNumber);
  660. } catch (FormatException) {
  661. throw new XmlException (li, "Invalid number for a character reference.");
  662. }
  663. } else {
  664. try {
  665. ret = int.Parse (value.Substring (index, end - index));
  666. } catch (FormatException) {
  667. throw new XmlException (li, "Invalid number for a character reference.");
  668. }
  669. }
  670. index = end;
  671. return ret;
  672. }
  673. private string GetPEValue (string peName)
  674. {
  675. DTDParameterEntityDeclaration peDecl =
  676. DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
  677. if (peDecl != null) {
  678. if (peDecl.IsInternalSubset)
  679. throw new XmlException (this as IXmlLineInfo, "Parameter entity is not allowed in internal subset entity '" + peName + "'");
  680. return peDecl.ReplacementText;
  681. }
  682. // See XML 1.0 section 4.1 for both WFC and VC.
  683. if ((DTD.SystemId == null && !DTD.InternalSubsetHasPEReference) || DTD.IsStandalone)
  684. throw new XmlException (this as IXmlLineInfo,
  685. "Parameter entity " + peName + " not found.");
  686. HandleError (new XmlSchemaException (
  687. "Parameter entity " + peName + " not found.", null));
  688. return "";
  689. }
  690. private void TryExpandPERef ()
  691. {
  692. if (PeekChar () == '%') {
  693. if (this.processingInternalSubset)
  694. throw new XmlException (this as IXmlLineInfo, "Parameter entity reference is not allowed inside internal subset.");
  695. ExpandPERef ();
  696. }
  697. }
  698. // reader is positioned on '%'
  699. private void ExpandPERef ()
  700. {
  701. ReadChar ();
  702. string peName = ReadName ();
  703. Expect (';');
  704. DTDParameterEntityDeclaration peDecl =
  705. DTD.PEDecls [peName] as DTDParameterEntityDeclaration;
  706. if (peDecl == null) {
  707. HandleError (new XmlSchemaException ("Parameter entity " + peName + " not found.", null));
  708. return; // do nothing
  709. }
  710. currentInput.InsertParameterEntityBuffer (" " + peDecl.ReplacementText + " ");
  711. }
  712. // The reader is positioned on the head of the name.
  713. private DTDEntityDeclaration ReadEntityDecl ()
  714. {
  715. DTDEntityDeclaration decl = new DTDEntityDeclaration (DTD);
  716. decl.IsInternalSubset = this.processingInternalSubset;
  717. TryExpandPERef ();
  718. SkipWhitespace ();
  719. decl.Name = ReadName ();
  720. if (!SkipWhitespace ())
  721. throw new XmlException (this as IXmlLineInfo,
  722. "Whitespace is required between name and content in DTD entity declaration.");
  723. TryExpandPERef ();
  724. SkipWhitespace ();
  725. if (PeekChar () == 'S' || PeekChar () == 'P') {
  726. // external entity
  727. ReadExternalID ();
  728. decl.PublicId = cachedPublicId;
  729. decl.SystemId = cachedSystemId;
  730. if (SkipWhitespace ()) {
  731. if (PeekChar () == 'N') {
  732. // NDataDecl
  733. Expect ("NDATA");
  734. if (!SkipWhitespace ())
  735. throw new XmlException (this as IXmlLineInfo,
  736. "Whitespace is required after NDATA.");
  737. decl.NotationName = ReadName (); // ndata_name
  738. }
  739. }
  740. if (decl.NotationName == null) {
  741. decl.Resolve (this.DTD.Resolver);
  742. ResolveExternalEntityReplacementText (decl);
  743. } else {
  744. // Unparsed entity.
  745. decl.LiteralEntityValue = String.Empty;
  746. decl.ReplacementText = String.Empty;
  747. }
  748. }
  749. else {
  750. // literal entity
  751. ReadEntityValueDecl (decl);
  752. ResolveInternalEntityReplacementText (decl);
  753. }
  754. SkipWhitespace ();
  755. // This expanding is only allowed as a non-validating parser.
  756. TryExpandPERef ();
  757. ExpectAfterWhitespace ('>');
  758. return decl;
  759. }
  760. private void ReadEntityValueDecl (DTDEntityDeclaration decl)
  761. {
  762. SkipWhitespace ();
  763. // quotation char will be finally removed on unescaping
  764. int quoteChar = ReadChar ();
  765. if (quoteChar != '\'' && quoteChar != '"')
  766. throw new XmlException ("quotation char was expected.");
  767. int start = CurrentTag.Length;
  768. ClearValueBuffer ();
  769. while (PeekChar () != quoteChar) {
  770. int ch = ReadChar ();
  771. /*
  772. FIXME: Here, character reference range validity
  773. should be checked, but also should consider
  774. how to handle them e.g. &#38amp;
  775. */
  776. switch (ch) {
  777. case '%':
  778. string name = ReadName ();
  779. Expect (';');
  780. if (decl.IsInternalSubset)
  781. throw new XmlException (this as IXmlLineInfo,
  782. "Parameter entity is not allowed in internal subset entity '" + name + "'");
  783. valueBuffer.Append (GetPEValue (name));
  784. break;
  785. case -1:
  786. throw new XmlException ("unexpected end of stream.");
  787. default:
  788. if (this.normalization && XmlChar.IsInvalid (ch))
  789. throw new XmlException (this as IXmlLineInfo, "Invalid character was found in the entity declaration.");
  790. AppendValueChar (ch);
  791. break;
  792. }
  793. }
  794. // string value = Dereference (CreateValueString (), false);
  795. string value = CreateValueString ();
  796. ClearValueBuffer ();
  797. Expect (quoteChar);
  798. decl.LiteralEntityValue = value;
  799. }
  800. private DTDAttListDeclaration ReadAttListDecl ()
  801. {
  802. TryExpandPERef ();
  803. if (!SkipWhitespace ())
  804. throw new XmlException (this as IXmlLineInfo,
  805. "Whitespace is required between ATTLIST and name in DTD attlist declaration.");
  806. TryExpandPERef ();
  807. SkipWhitespace ();
  808. string name = ReadName (); // target element name
  809. DTDAttListDeclaration decl =
  810. DTD.AttListDecls [name] as DTDAttListDeclaration;
  811. if (decl == null)
  812. decl = new DTDAttListDeclaration (DTD);
  813. decl.IsInternalSubset = this.processingInternalSubset;
  814. decl.Name = name;
  815. if (!SkipWhitespace ())
  816. if (PeekChar () != '>')
  817. throw new XmlException (this as IXmlLineInfo,
  818. "Whitespace is required between name and content in non-empty DTD attlist declaration.");
  819. TryExpandPERef ();
  820. SkipWhitespace ();
  821. while (XmlChar.IsNameChar (PeekChar ())) {
  822. DTDAttributeDefinition def = ReadAttributeDefinition ();
  823. // There must not be two or more ID attributes.
  824. if (def.Datatype.TokenizedType == XmlTokenizedType.ID) {
  825. for (int i = 0; i < decl.Definitions.Count; i++) {
  826. DTDAttributeDefinition d = decl [i];
  827. if (d.Datatype.TokenizedType == XmlTokenizedType.ID) {
  828. HandleError (new XmlSchemaException ("AttList declaration must not contain two or more ID attributes.",
  829. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  830. break;
  831. }
  832. }
  833. }
  834. if (decl [def.Name] == null)
  835. decl.Add (def);
  836. SkipWhitespace ();
  837. TryExpandPERef ();
  838. SkipWhitespace ();
  839. }
  840. SkipWhitespace ();
  841. // This expanding is only allowed as a non-validating parser.
  842. TryExpandPERef ();
  843. ExpectAfterWhitespace ('>');
  844. return decl;
  845. }
  846. private DTDAttributeDefinition ReadAttributeDefinition ()
  847. {
  848. DTDAttributeDefinition def = new DTDAttributeDefinition (DTD);
  849. def.IsInternalSubset = this.processingInternalSubset;
  850. // attr_name
  851. TryExpandPERef ();
  852. SkipWhitespace ();
  853. def.Name = ReadName ();
  854. if (!SkipWhitespace ())
  855. throw new XmlException (this as IXmlLineInfo,
  856. "Whitespace is required between name and content in DTD attribute definition.");
  857. // attr_value
  858. TryExpandPERef ();
  859. SkipWhitespace ();
  860. switch(PeekChar ()) {
  861. case 'C': // CDATA
  862. Expect ("CDATA");
  863. def.Datatype = XmlSchemaDatatype.FromName ("normalizedString");
  864. break;
  865. case 'I': // ID, IDREF, IDREFS
  866. Expect ("ID");
  867. if(PeekChar () == 'R') {
  868. Expect ("REF");
  869. if(PeekChar () == 'S') {
  870. // IDREFS
  871. ReadChar ();
  872. def.Datatype = XmlSchemaDatatype.FromName ("IDREFS");
  873. }
  874. else // IDREF
  875. def.Datatype = XmlSchemaDatatype.FromName ("IDREF");
  876. }
  877. else // ID
  878. def.Datatype = XmlSchemaDatatype.FromName ("ID");
  879. break;
  880. case 'E': // ENTITY, ENTITIES
  881. Expect ("ENTIT");
  882. switch(ReadChar ()) {
  883. case 'Y': // ENTITY
  884. def.Datatype = XmlSchemaDatatype.FromName ("ENTITY");
  885. break;
  886. case 'I': // ENTITIES
  887. Expect ("ES");
  888. def.Datatype = XmlSchemaDatatype.FromName ("ENTITIES");
  889. break;
  890. }
  891. break;
  892. case 'N': // NMTOKEN, NMTOKENS, NOTATION
  893. ReadChar ();
  894. switch(PeekChar ()) {
  895. case 'M':
  896. Expect ("MTOKEN");
  897. if(PeekChar ()=='S') { // NMTOKENS
  898. ReadChar ();
  899. def.Datatype = XmlSchemaDatatype.FromName ("NMTOKENS");
  900. }
  901. else // NMTOKEN
  902. def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
  903. break;
  904. case 'O':
  905. Expect ("OTATION");
  906. def.Datatype = XmlSchemaDatatype.FromName ("NOTATION");
  907. if (!SkipWhitespace ())
  908. throw new XmlException (this as IXmlLineInfo,
  909. "Whitespace is required between name and content in DTD attribute definition.");
  910. Expect ('(');
  911. SkipWhitespace ();
  912. def.EnumeratedNotations.Add (ReadName ()); // notation name
  913. SkipWhitespace ();
  914. while(PeekChar () == '|') {
  915. ReadChar ();
  916. SkipWhitespace ();
  917. def.EnumeratedNotations.Add (ReadName ()); // notation name
  918. SkipWhitespace ();
  919. }
  920. Expect (')');
  921. break;
  922. default:
  923. throw new XmlException ("attribute declaration syntax error.");
  924. }
  925. break;
  926. default: // Enumerated Values
  927. def.Datatype = XmlSchemaDatatype.FromName ("NMTOKEN");
  928. TryExpandPERef ();
  929. ExpectAfterWhitespace ('(');
  930. SkipWhitespace ();
  931. def.EnumeratedAttributeDeclaration.Add (
  932. def.Datatype.Normalize (ReadNmToken ())); // enum value
  933. SkipWhitespace ();
  934. while(PeekChar () == '|') {
  935. ReadChar ();
  936. SkipWhitespace ();
  937. def.EnumeratedAttributeDeclaration.Add (
  938. def.Datatype.Normalize (ReadNmToken ())); // enum value
  939. SkipWhitespace ();
  940. }
  941. Expect (')');
  942. break;
  943. }
  944. TryExpandPERef ();
  945. if (!SkipWhitespace ())
  946. throw new XmlException (this as IXmlLineInfo,
  947. "Whitespace is required between type and occurence in DTD attribute definition.");
  948. // def_value
  949. ReadAttributeDefaultValue (def);
  950. return def;
  951. }
  952. private void ReadAttributeDefaultValue (DTDAttributeDefinition def)
  953. {
  954. if(PeekChar () == '#')
  955. {
  956. ReadChar ();
  957. switch(PeekChar ())
  958. {
  959. case 'R':
  960. Expect ("REQUIRED");
  961. def.OccurenceType = DTDAttributeOccurenceType.Required;
  962. break;
  963. case 'I':
  964. Expect ("IMPLIED");
  965. def.OccurenceType = DTDAttributeOccurenceType.Optional;
  966. break;
  967. case 'F':
  968. Expect ("FIXED");
  969. def.OccurenceType = DTDAttributeOccurenceType.Fixed;
  970. if (!SkipWhitespace ())
  971. throw new XmlException (this as IXmlLineInfo,
  972. "Whitespace is required between FIXED and actual value in DTD attribute definition.");
  973. def.UnresolvedDefaultValue = ReadDefaultAttribute ();
  974. break;
  975. }
  976. } else {
  977. // one of the enumerated value
  978. SkipWhitespace ();
  979. TryExpandPERef ();
  980. SkipWhitespace ();
  981. def.UnresolvedDefaultValue = ReadDefaultAttribute ();
  982. }
  983. // VC: If default value exists, it should be valid.
  984. if (def.DefaultValue != null) {
  985. string normalized = def.Datatype.Normalize (def.DefaultValue);
  986. bool breakup = false;
  987. object parsed = null;
  988. // enumeration validity
  989. if (def.EnumeratedAttributeDeclaration.Count > 0) {
  990. if (!def.EnumeratedAttributeDeclaration.Contains (normalized)) {
  991. HandleError (new XmlSchemaException ("Default value is not one of the enumerated values.",
  992. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  993. breakup = true;
  994. }
  995. }
  996. if (def.EnumeratedNotations.Count > 0) {
  997. if (!def.EnumeratedNotations.Contains (normalized)) {
  998. HandleError (new XmlSchemaException ("Default value is not one of the enumerated notation values.",
  999. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  1000. breakup = true;
  1001. }
  1002. }
  1003. // type based validity
  1004. if (!breakup) {
  1005. try {
  1006. parsed = def.Datatype.ParseValue (normalized, DTD.NameTable, null);
  1007. } catch (Exception ex) { // FIXME: (wishlist) bad catch ;-(
  1008. HandleError (new XmlSchemaException ("Invalid default value for ENTITY type.",
  1009. def.LineNumber, def.LinePosition, null, def.BaseURI, ex));
  1010. breakup = true;
  1011. }
  1012. }
  1013. if (!breakup) {
  1014. switch (def.Datatype.TokenizedType) {
  1015. case XmlTokenizedType.ENTITY:
  1016. if (DTD.EntityDecls [normalized] == null)
  1017. HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
  1018. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  1019. break;
  1020. case XmlTokenizedType.ENTITIES:
  1021. string [] entities = parsed as string [];
  1022. for (int i = 0; i < entities.Length; i++) {
  1023. string entity = entities [i];
  1024. if (DTD.EntityDecls [entity] == null)
  1025. HandleError (new XmlSchemaException ("Specified entity declaration used by default attribute value was not found.",
  1026. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  1027. }
  1028. break;
  1029. }
  1030. }
  1031. }
  1032. // Extra ID attribute validity check.
  1033. if (def.Datatype != null && def.Datatype.TokenizedType == XmlTokenizedType.ID)
  1034. if (def.UnresolvedDefaultValue != null)
  1035. HandleError (new XmlSchemaException ("ID attribute must not have fixed value constraint.",
  1036. def.LineNumber, def.LinePosition, null, def.BaseURI, null));
  1037. }
  1038. private DTDNotationDeclaration ReadNotationDecl()
  1039. {
  1040. DTDNotationDeclaration decl = new DTDNotationDeclaration (DTD);
  1041. if (!SkipWhitespace ())
  1042. throw new XmlException (this as IXmlLineInfo,
  1043. "Whitespace is required between NOTATION and name in DTD notation declaration.");
  1044. TryExpandPERef ();
  1045. SkipWhitespace ();
  1046. decl.Name = ReadName (); // notation name
  1047. /*
  1048. if (namespaces) { // copy from SetProperties ;-)
  1049. int indexOfColon = decl.Name.IndexOf (':');
  1050. if (indexOfColon == -1) {
  1051. decl.Prefix = String.Empty;
  1052. decl.LocalName = decl.Name;
  1053. } else {
  1054. decl.Prefix = decl.Name.Substring (0, indexOfColon);
  1055. decl.LocalName = decl.Name.Substring (indexOfColon + 1);
  1056. }
  1057. } else {
  1058. */
  1059. decl.Prefix = String.Empty;
  1060. decl.LocalName = decl.Name;
  1061. // }
  1062. SkipWhitespace ();
  1063. if(PeekChar () == 'P') {
  1064. decl.PublicId = ReadPubidLiteral ();
  1065. bool wsSkipped = SkipWhitespace ();
  1066. if (PeekChar () == '\'' || PeekChar () == '"') {
  1067. if (!wsSkipped)
  1068. throw new XmlException (this as IXmlLineInfo,
  1069. "Whitespace is required between public id and system id.");
  1070. decl.SystemId = ReadSystemLiteral (false);
  1071. SkipWhitespace ();
  1072. }
  1073. } else if(PeekChar () == 'S') {
  1074. decl.SystemId = ReadSystemLiteral (true);
  1075. SkipWhitespace ();
  1076. }
  1077. if(decl.PublicId == null && decl.SystemId == null)
  1078. throw new XmlException ("public or system declaration required for \"NOTATION\" declaration.");
  1079. // This expanding is only allowed as a non-validating parser.
  1080. TryExpandPERef ();
  1081. ExpectAfterWhitespace ('>');
  1082. return decl;
  1083. }
  1084. private void ReadExternalID () {
  1085. switch (PeekChar ()) {
  1086. case 'S':
  1087. cachedSystemId = ReadSystemLiteral (true);
  1088. break;
  1089. case 'P':
  1090. cachedPublicId = ReadPubidLiteral ();
  1091. if (!SkipWhitespace ())
  1092. throw new XmlException (this as IXmlLineInfo,
  1093. "Whitespace is required between PUBLIC id and SYSTEM id.");
  1094. cachedSystemId = ReadSystemLiteral (false);
  1095. break;
  1096. }
  1097. }
  1098. // The reader is positioned on the first 'S' of "SYSTEM".
  1099. private string ReadSystemLiteral (bool expectSYSTEM)
  1100. {
  1101. if(expectSYSTEM) {
  1102. Expect ("SYSTEM");
  1103. if (!SkipWhitespace ())
  1104. throw new XmlException (this as IXmlLineInfo,
  1105. "Whitespace is required after 'SYSTEM'.");
  1106. }
  1107. else
  1108. SkipWhitespace ();
  1109. int quoteChar = ReadChar (); // apos or quot
  1110. int startPos = CurrentTag.Length;
  1111. int c = 0;
  1112. ClearValueBuffer ();
  1113. while (c != quoteChar) {
  1114. c = ReadChar ();
  1115. if (c < 0)
  1116. throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
  1117. if (c != quoteChar)
  1118. AppendValueChar (c);
  1119. }
  1120. return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
  1121. }
  1122. private string ReadPubidLiteral()
  1123. {
  1124. Expect ("PUBLIC");
  1125. if (!SkipWhitespace ())
  1126. throw new XmlException (this as IXmlLineInfo,
  1127. "Whitespace is required after 'PUBLIC'.");
  1128. int quoteChar = ReadChar ();
  1129. int startPos = CurrentTag.Length;
  1130. int c = 0;
  1131. ClearValueBuffer ();
  1132. while(c != quoteChar)
  1133. {
  1134. c = ReadChar ();
  1135. if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
  1136. if(c != quoteChar && !XmlChar.IsPubidChar (c))
  1137. throw new XmlException (this as IXmlLineInfo,"character '" + (char) c + "' not allowed for PUBLIC ID");
  1138. if (c != quoteChar)
  1139. AppendValueChar (c);
  1140. }
  1141. return CreateValueString (); //currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
  1142. }
  1143. // The reader is positioned on the first character
  1144. // of the name.
  1145. internal string ReadName ()
  1146. {
  1147. return ReadNameOrNmToken(false);
  1148. }
  1149. // The reader is positioned on the first character
  1150. // of the name.
  1151. private string ReadNmToken ()
  1152. {
  1153. return ReadNameOrNmToken(true);
  1154. }
  1155. private string ReadNameOrNmToken(bool isNameToken)
  1156. {
  1157. int ch = PeekChar ();
  1158. if(isNameToken) {
  1159. if (!XmlChar.IsNameChar (ch))
  1160. throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char) ch));
  1161. }
  1162. else {
  1163. if (!XmlChar.IsFirstNameChar (ch))
  1164. throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char) ch));
  1165. }
  1166. nameLength = 0;
  1167. AppendNameChar (ReadChar ());
  1168. while (XmlChar.IsNameChar (PeekChar ())) {
  1169. AppendNameChar (ReadChar ());
  1170. }
  1171. return CreateNameString ();
  1172. }
  1173. // Read the next character and compare it against the
  1174. // specified character.
  1175. private void Expect (int expected)
  1176. {
  1177. int ch = ReadChar ();
  1178. if (ch != expected) {
  1179. throw new XmlException (this as IXmlLineInfo,
  1180. String.Format (
  1181. "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
  1182. (char) expected,
  1183. expected,
  1184. (char) ch,
  1185. ch));
  1186. }
  1187. }
  1188. private void Expect (string expected)
  1189. {
  1190. int len = expected.Length;
  1191. for(int i=0; i< len; i++)
  1192. Expect (expected[i]);
  1193. }
  1194. private void ExpectAfterWhitespace (char c)
  1195. {
  1196. while (true) {
  1197. int i = ReadChar ();
  1198. if (XmlChar.IsWhitespace (i))
  1199. continue;
  1200. if (c != i)
  1201. throw new XmlException (String.Join (String.Empty, new string [] {"Expected ", c.ToString (), ", but found " + (char) i, "[", i.ToString (), "]"}));
  1202. break;
  1203. }
  1204. }
  1205. // Does not consume the first non-whitespace character.
  1206. private bool SkipWhitespace ()
  1207. {
  1208. bool skipped = XmlChar.IsWhitespace (PeekChar ());
  1209. while (XmlChar.IsWhitespace (PeekChar ()))
  1210. ReadChar ();
  1211. return skipped;
  1212. }
  1213. /*
  1214. private string Dereference (string unresolved, bool expandPredefined)
  1215. {
  1216. StringBuilder resolved = new StringBuilder();
  1217. int pos = 0;
  1218. int next = unresolved.IndexOf ('&');
  1219. if(next < 0)
  1220. return unresolved;
  1221. while (next >= 0) {
  1222. if(pos < next)
  1223. resolved.Append (unresolved.Substring (pos, next - pos));// - 1);
  1224. int endPos = unresolved.IndexOf (';', next+1);
  1225. if (endPos < 0)
  1226. throw new XmlException (this as IXmlLineInfo, "Could not resolve entity reference since it did not end with character ';'.");
  1227. string entityName =
  1228. unresolved.Substring (next + 1, endPos - next - 1);
  1229. if(entityName [0] == '#') {
  1230. try {
  1231. int c;
  1232. // character entity
  1233. if(entityName [1] == 'x') {
  1234. // hexadecimal
  1235. c = int.Parse (entityName.Substring (2),
  1236. System.Globalization.NumberStyles.HexNumber);
  1237. } else {
  1238. // decimal
  1239. c = int.Parse (entityName.Substring (1));
  1240. }
  1241. if (c < Char.MaxValue)
  1242. resolved.Append ((char) c);
  1243. else
  1244. resolved.Append (ExpandSurrogateChar (c));
  1245. } catch (FormatException) {
  1246. throw new XmlException (this as IXmlLineInfo, "Invalid character entity reference was found.");
  1247. }
  1248. } else {
  1249. int predefined = XmlChar.GetPredefinedEntity (entityName);
  1250. if (expandPredefined && predefined >= 0)
  1251. resolved.Append (predefined);
  1252. else
  1253. // With respect to "Value", MS document is helpless
  1254. // and the implemention returns inconsistent value
  1255. // (e.g. XML: "&ent; &amp;ent;" ---> Value: "&ent; &ent;".)
  1256. resolved.Append ("&" + entityName + ";");
  1257. }
  1258. pos = endPos + 1;
  1259. if(pos > unresolved.Length)
  1260. break;
  1261. next = unresolved.IndexOf('&', pos);
  1262. }
  1263. resolved.Append (unresolved.Substring(pos));
  1264. return resolved.ToString();
  1265. }
  1266. */
  1267. private int PeekChar ()
  1268. {
  1269. return currentInput.PeekChar ();
  1270. }
  1271. private int ReadChar ()
  1272. {
  1273. return currentInput.ReadChar ();
  1274. }
  1275. private string ExpandSurrogateChar (int ch)
  1276. {
  1277. if (ch < Char.MaxValue)
  1278. return ((char) ch).ToString ();
  1279. else {
  1280. char [] tmp = new char [] {(char) (ch / 0x10000 + 0xD800 - 1), (char) (ch % 0x10000 + 0xDC00)};
  1281. return new string (tmp);
  1282. }
  1283. }
  1284. // The reader is positioned on the first character after
  1285. // the leading '<!--'.
  1286. private void ReadComment ()
  1287. {
  1288. currentInput.InitialState = false;
  1289. while (PeekChar () != -1) {
  1290. int ch = ReadChar ();
  1291. if (ch == '-' && PeekChar () == '-') {
  1292. ReadChar ();
  1293. if (PeekChar () != '>')
  1294. throw new XmlException (this as IXmlLineInfo,"comments cannot contain '--'");
  1295. ReadChar ();
  1296. break;
  1297. }
  1298. if (XmlChar.IsInvalid (ch))
  1299. throw new XmlException (this as IXmlLineInfo,
  1300. "Not allowed character was found.");
  1301. }
  1302. }
  1303. // The reader is positioned on the first character
  1304. // of the target.
  1305. //
  1306. // It may be xml declaration or processing instruction.
  1307. private void ReadProcessingInstruction ()
  1308. {
  1309. string target = ReadName ();
  1310. if (target == "xml") {
  1311. ReadTextDeclaration ();
  1312. return;
  1313. } else if (target.ToLower () == "xml")
  1314. throw new XmlException (this as IXmlLineInfo,
  1315. "Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
  1316. currentInput.InitialState = false;
  1317. if (!SkipWhitespace ())
  1318. if (PeekChar () != '?')
  1319. throw new XmlException (this as IXmlLineInfo,
  1320. "Invalid processing instruction name was found.");
  1321. // ClearValueBuffer ();
  1322. while (PeekChar () != -1) {
  1323. int ch = ReadChar ();
  1324. if (ch == '?' && PeekChar () == '>') {
  1325. ReadChar ();
  1326. break;
  1327. }
  1328. // AppendValueChar ((char)ch);
  1329. }
  1330. /*
  1331. SetProperties (
  1332. XmlNodeType.ProcessingInstruction, // nodeType
  1333. target, // name
  1334. false, // isEmptyElement
  1335. true, // clearAttributes
  1336. valueBuffer // value
  1337. );
  1338. */
  1339. }
  1340. // The reader is positioned after "<?xml "
  1341. private void ReadTextDeclaration ()
  1342. {
  1343. if (!currentInput.InitialState)
  1344. throw new XmlException (this as IXmlLineInfo,
  1345. "Text declaration cannot appear in this state.");
  1346. currentInput.InitialState = false;
  1347. SkipWhitespace ();
  1348. // version decl
  1349. if (PeekChar () == 'v') {
  1350. Expect ("version");
  1351. ExpectAfterWhitespace ('=');
  1352. SkipWhitespace ();
  1353. int quoteChar = ReadChar ();
  1354. char [] expect1_0 = new char [3];
  1355. int versionLength = 0;
  1356. switch (quoteChar) {
  1357. case '\'':
  1358. case '"':
  1359. while (PeekChar () != quoteChar) {
  1360. if (PeekChar () == -1)
  1361. throw new XmlException (this as IXmlLineInfo,
  1362. "Invalid version declaration inside text declaration.");
  1363. else if (versionLength == 3)
  1364. throw new XmlException (this as IXmlLineInfo,
  1365. "Invalid version number inside text declaration.");
  1366. else {
  1367. expect1_0 [versionLength] = (char) ReadChar ();
  1368. versionLength++;
  1369. if (versionLength == 3 && new String (expect1_0) != "1.0")
  1370. throw new XmlException (this as IXmlLineInfo,
  1371. "Invalid version number inside text declaration.");
  1372. }
  1373. }
  1374. ReadChar ();
  1375. SkipWhitespace ();
  1376. break;
  1377. default:
  1378. throw new XmlException (this as IXmlLineInfo,
  1379. "Invalid version declaration inside text declaration.");
  1380. }
  1381. }
  1382. if (PeekChar () == 'e') {
  1383. Expect ("encoding");
  1384. ExpectAfterWhitespace ('=');
  1385. SkipWhitespace ();
  1386. int quoteChar = ReadChar ();
  1387. switch (quoteChar) {
  1388. case '\'':
  1389. case '"':
  1390. while (PeekChar () != quoteChar)
  1391. if (ReadChar () == -1)
  1392. throw new XmlException (this as IXmlLineInfo,
  1393. "Invalid encoding declaration inside text declaration.");
  1394. ReadChar ();
  1395. SkipWhitespace ();
  1396. break;
  1397. default:
  1398. throw new XmlException (this as IXmlLineInfo,
  1399. "Invalid encoding declaration inside text declaration.");
  1400. }
  1401. // Encoding value should be checked inside XmlInputStream.
  1402. }
  1403. else
  1404. throw new XmlException (this as IXmlLineInfo,
  1405. "Encoding declaration is mandatory in text declaration.");
  1406. Expect ("?>");
  1407. }
  1408. // Note that now this method behaves differently from
  1409. // XmlTextReader's one. It calles AppendValueChar() internally.
  1410. private int ReadCharacterReference ()
  1411. {
  1412. int value = 0;
  1413. if (PeekChar () == 'x') {
  1414. ReadChar ();
  1415. while (PeekChar () != ';' && PeekChar () != -1) {
  1416. int ch = ReadChar ();
  1417. if (ch >= '0' && ch <= '9')
  1418. value = (value << 4) + ch - '0';
  1419. else if (ch >= 'A' && ch <= 'F')
  1420. value = (value << 4) + ch - 'A' + 10;
  1421. else if (ch >= 'a' && ch <= 'f')
  1422. value = (value << 4) + ch - 'a' + 10;
  1423. else
  1424. throw new XmlException (this as IXmlLineInfo,
  1425. String.Format (
  1426. "invalid hexadecimal digit: {0} (#x{1:X})",
  1427. (char) ch,
  1428. ch));
  1429. }
  1430. } else {
  1431. while (PeekChar () != ';' && PeekChar () != -1) {
  1432. int ch = ReadChar ();
  1433. if (ch >= '0' && ch <= '9')
  1434. value = value * 10 + ch - '0';
  1435. else
  1436. throw new XmlException (this as IXmlLineInfo,
  1437. String.Format (
  1438. "invalid decimal digit: {0} (#x{1:X})",
  1439. (char) ch,
  1440. ch));
  1441. }
  1442. }
  1443. ReadChar (); // ';'
  1444. // There is no way to save surrogate pairs...
  1445. if (XmlChar.IsInvalid (value))
  1446. throw new XmlException (this as IXmlLineInfo,
  1447. "Referenced character was not allowed in XML.");
  1448. AppendValueChar (value);
  1449. return value;
  1450. }
  1451. private void AppendNameChar (int ch)
  1452. {
  1453. CheckNameCapacity ();
  1454. if (ch < Char.MaxValue)
  1455. nameBuffer [nameLength++] = (char) ch;
  1456. else {
  1457. nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
  1458. CheckNameCapacity ();
  1459. nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
  1460. }
  1461. }
  1462. private void CheckNameCapacity ()
  1463. {
  1464. if (nameLength == nameCapacity) {
  1465. nameCapacity = nameCapacity * 2;
  1466. char [] oldNameBuffer = nameBuffer;
  1467. nameBuffer = new char [nameCapacity];
  1468. Array.Copy (oldNameBuffer, nameBuffer, nameLength);
  1469. }
  1470. }
  1471. private string CreateNameString ()
  1472. {
  1473. return DTD.NameTable.Add (nameBuffer, 0, nameLength);
  1474. }
  1475. private void AppendValueChar (int ch)
  1476. {
  1477. if (ch < Char.MaxValue)
  1478. valueBuffer.Append ((char) ch);
  1479. else
  1480. valueBuffer.Append (ExpandSurrogateChar (ch));
  1481. }
  1482. private string CreateValueString ()
  1483. {
  1484. return valueBuffer.ToString ();
  1485. }
  1486. private void ClearValueBuffer ()
  1487. {
  1488. valueBuffer.Length = 0;
  1489. }
  1490. // The reader is positioned on the quote character.
  1491. // *Keeps quote char* to value to get_QuoteChar() correctly.
  1492. private string ReadDefaultAttribute ()
  1493. {
  1494. ClearValueBuffer ();
  1495. int quoteChar = ReadChar ();
  1496. if (quoteChar != '\'' && quoteChar != '\"')
  1497. throw new XmlException (this as IXmlLineInfo,"an attribute value was not quoted");
  1498. AppendValueChar (quoteChar);
  1499. while (PeekChar () != quoteChar) {
  1500. int ch = ReadChar ();
  1501. switch (ch)
  1502. {
  1503. case '<':
  1504. throw new XmlException (this as IXmlLineInfo,"attribute values cannot contain '<'");
  1505. case -1:
  1506. throw new XmlException (this as IXmlLineInfo,"unexpected end of file in an attribute value");
  1507. case '&':
  1508. AppendValueChar (ch);
  1509. if (PeekChar () == '#')
  1510. break;
  1511. // Check XML 1.0 section 3.1 WFC.
  1512. string entName = ReadName ();
  1513. Expect (';');
  1514. if (XmlChar.GetPredefinedEntity (entName) < 0) {
  1515. DTDEntityDeclaration entDecl =
  1516. DTD == null ? null : DTD.EntityDecls [entName];
  1517. if (entDecl == null || entDecl.SystemId != null)
  1518. // WFC: Entity Declared (see 4.1)
  1519. if (DTD.IsStandalone || (DTD.SystemId == null && !DTD.InternalSubsetHasPEReference))
  1520. throw new XmlException (this as IXmlLineInfo,
  1521. "Reference to external entities is not allowed in attribute value.");
  1522. }
  1523. valueBuffer.Append (entName);
  1524. AppendValueChar (';');
  1525. break;
  1526. default:
  1527. AppendValueChar (ch);
  1528. break;
  1529. }
  1530. }
  1531. ReadChar (); // quoteChar
  1532. AppendValueChar (quoteChar);
  1533. return CreateValueString ();
  1534. }
  1535. private void PushParserInput (string url)
  1536. {
  1537. Uri baseUri = null;
  1538. try {
  1539. if (DTD.BaseURI != null && DTD.BaseURI.Length > 0)
  1540. baseUri = new Uri (DTD.BaseURI);
  1541. } catch (UriFormatException) {
  1542. }
  1543. Uri absUri = DTD.Resolver.ResolveUri (baseUri, url);
  1544. string absPath = absUri.ToString ();
  1545. foreach (XmlParserInput i in parserInputStack.ToArray ()) {
  1546. if (i.BaseURI == absPath)
  1547. throw new XmlException (this as IXmlLineInfo, "Nested inclusion is not allowed: " + url);
  1548. }
  1549. parserInputStack.Push (currentInput);
  1550. try {
  1551. Stream s = DTD.Resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
  1552. currentInput = new XmlParserInput (new XmlStreamReader (s), absPath);
  1553. } catch (Exception ex) { // FIXME: (wishlist) Bad exception catch ;-(
  1554. int line = currentInput == null ? 0 : currentInput.LineNumber;
  1555. int col = currentInput == null ? 0 : currentInput.LinePosition;
  1556. string bu = (currentInput == null) ? String.Empty : currentInput.BaseURI;
  1557. HandleError (new XmlSchemaException ("Specified external entity not found. Target URL is " + url + " .",
  1558. line, col, null, bu, ex));
  1559. currentInput = new XmlParserInput (new StringReader (String.Empty), absPath);
  1560. }
  1561. }
  1562. private void PopParserInput ()
  1563. {
  1564. currentInput = parserInputStack.Pop () as XmlParserInput;
  1565. }
  1566. private void HandleError (XmlSchemaException ex)
  1567. {
  1568. #if DTD_HANDLE_EVENTS
  1569. if (this.ValidationEventHandler != null)
  1570. ValidationEventHandler (this, new ValidationEventArgs (ex, ex.Message, XmlSeverityType.Error));
  1571. #else
  1572. DTD.AddError (ex);
  1573. #endif
  1574. }
  1575. }
  1576. }