DTDReader.cs 46 KB

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