XmlTextReader.cs 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331
  1. // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2. //
  3. // System.Xml.XmlTextReader.cs
  4. //
  5. // Author:
  6. // Jason Diamond ([email protected])
  7. //
  8. // (C) 2001 Jason Diamond http://injektilo.org/
  9. //
  10. // FIXME:
  11. // This can only parse basic XML: elements, attributes, processing
  12. // instructions, and comments are OK.
  13. //
  14. // It barfs on DOCTYPE declarations.
  15. //
  16. // There's also no checking being done for either well-formedness
  17. // or validity.
  18. //
  19. // ParserContext and NameTables aren't being used yet.
  20. //
  21. // Some thought needs to be given to performance. There's too many
  22. // strings being allocated.
  23. //
  24. // None of the MoveTo methods have been implemented yet.
  25. //
  26. // LineNumber and LinePosition aren't being tracked.
  27. //
  28. // xml:space, xml:lang, and xml:base aren't being tracked.
  29. //
  30. // Depth isn't being tracked.
  31. using System;
  32. using System.Collections;
  33. using System.IO;
  34. using System.Net;
  35. using System.Text;
  36. namespace System.Xml
  37. {
  38. public class XmlTextReader : XmlReader, IXmlLineInfo
  39. {
  40. // constructors
  41. protected XmlTextReader()
  42. {
  43. Init();
  44. }
  45. public XmlTextReader(Stream input)
  46. {
  47. Init();
  48. reader = new StreamReader(
  49. input,
  50. Encoding.UTF8,
  51. true);
  52. }
  53. public XmlTextReader(string url)
  54. {
  55. Init();
  56. WebClient client = new WebClient();
  57. reader = new StreamReader(
  58. client.OpenRead(url),
  59. Encoding.UTF8,
  60. true);
  61. }
  62. public XmlTextReader(TextReader input)
  63. {
  64. Init();
  65. reader = input;
  66. }
  67. public XmlTextReader(Stream input, XmlNameTable nameTable)
  68. {
  69. this.nameTable = nameTable;
  70. // TODO: implement me.
  71. throw new NotImplementedException();
  72. }
  73. public XmlTextReader(string baseURI, Stream input)
  74. {
  75. // TODO: implement me.
  76. throw new NotImplementedException();
  77. }
  78. public XmlTextReader(string baseURI, TextReader input)
  79. {
  80. // TODO: implement me.
  81. throw new NotImplementedException();
  82. }
  83. public XmlTextReader(string url, XmlNameTable nameTable)
  84. {
  85. this.nameTable = nameTable;
  86. // TODO: implement me.
  87. throw new NotImplementedException();
  88. }
  89. public XmlTextReader(
  90. TextReader input,
  91. XmlNameTable nameTable)
  92. {
  93. this.nameTable = nameTable;
  94. // TODO: implement me.
  95. throw new NotImplementedException();
  96. }
  97. public XmlTextReader(
  98. Stream inputFragment,
  99. XmlNodeType fragmentType,
  100. XmlParserContext context)
  101. {
  102. // TODO: implement me.
  103. throw new NotImplementedException();
  104. }
  105. public XmlTextReader(
  106. string baseURI,
  107. Stream input,
  108. XmlNameTable nameTable)
  109. {
  110. this.nameTable = nameTable;
  111. // TODO: implement me.
  112. throw new NotImplementedException();
  113. }
  114. public XmlTextReader(
  115. string baseURI,
  116. TextReader input,
  117. XmlNameTable nameTable)
  118. {
  119. this.nameTable = nameTable;
  120. // TODO: implement me.
  121. throw new NotImplementedException();
  122. }
  123. public XmlTextReader(
  124. string fragment,
  125. XmlNodeType fragmentType,
  126. XmlParserContext context)
  127. {
  128. // TODO: implement me.
  129. throw new NotImplementedException();
  130. }
  131. // properties
  132. public override int AttributeCount
  133. {
  134. get
  135. {
  136. return attributes.Count;
  137. }
  138. }
  139. public override string BaseURI
  140. {
  141. get
  142. {
  143. // TODO: implement me.
  144. return null;
  145. }
  146. }
  147. public override bool CanResolveEntity
  148. {
  149. get
  150. {
  151. // XmlTextReaders don't resolve entities.
  152. return false;
  153. }
  154. }
  155. public override int Depth
  156. {
  157. get
  158. {
  159. // TODO: implement me.
  160. return depth > 0 ? depth : 0;
  161. }
  162. }
  163. public Encoding Encoding
  164. {
  165. get
  166. {
  167. // TODO: implement me.
  168. return null;
  169. }
  170. }
  171. public override bool EOF
  172. {
  173. get
  174. {
  175. return
  176. readState == ReadState.EndOfFile ||
  177. readState == ReadState.Closed;
  178. }
  179. }
  180. public override bool HasValue
  181. {
  182. get
  183. {
  184. return value != String.Empty;
  185. }
  186. }
  187. public override bool IsDefault
  188. {
  189. get
  190. {
  191. // XmlTextReader does not expand default attributes.
  192. return false;
  193. }
  194. }
  195. public override bool IsEmptyElement
  196. {
  197. get
  198. {
  199. return isEmptyElement;
  200. }
  201. }
  202. public override string this[int i]
  203. {
  204. get
  205. {
  206. return GetAttribute(i);
  207. }
  208. }
  209. public override string this[string name]
  210. {
  211. get
  212. {
  213. return GetAttribute(name);
  214. }
  215. }
  216. public override string this[
  217. string localName,
  218. string namespaceName]
  219. {
  220. get
  221. {
  222. return GetAttribute(localName, namespaceName);
  223. }
  224. }
  225. public int LineNumber
  226. {
  227. get
  228. {
  229. // TODO: implement me.
  230. return 0;
  231. }
  232. }
  233. public int LinePosition
  234. {
  235. get
  236. {
  237. // TODO: implement me.
  238. return 0;
  239. }
  240. }
  241. public override string LocalName
  242. {
  243. get
  244. {
  245. return localName;
  246. }
  247. }
  248. public override string Name
  249. {
  250. get
  251. {
  252. return name;
  253. }
  254. }
  255. public bool Namespaces
  256. {
  257. get
  258. {
  259. // TODO: implement me.
  260. return false;
  261. }
  262. set
  263. {
  264. // TODO: implement me.
  265. }
  266. }
  267. public override string NamespaceURI
  268. {
  269. get
  270. {
  271. return namespaceURI;
  272. }
  273. }
  274. public override XmlNameTable NameTable
  275. {
  276. get
  277. {
  278. // TODO: implement me.
  279. return null;
  280. }
  281. }
  282. public override XmlNodeType NodeType
  283. {
  284. get
  285. {
  286. return nodeType;
  287. }
  288. }
  289. public bool Normalization
  290. {
  291. get
  292. {
  293. // TODO: implement me.
  294. return false;
  295. }
  296. set
  297. {
  298. // TODO: implement me.
  299. }
  300. }
  301. public override string Prefix
  302. {
  303. get
  304. {
  305. return prefix;
  306. }
  307. }
  308. public override char QuoteChar
  309. {
  310. get
  311. {
  312. // TODO: implement me.
  313. return '"';
  314. }
  315. }
  316. public override ReadState ReadState
  317. {
  318. get
  319. {
  320. return readState;
  321. }
  322. }
  323. public override string Value
  324. {
  325. get
  326. {
  327. return value;
  328. }
  329. }
  330. public WhitespaceHandling WhitespaceHandling
  331. {
  332. get
  333. {
  334. // TODO: implement me.
  335. return WhitespaceHandling.All;
  336. }
  337. set
  338. {
  339. // TODO: implement me.
  340. }
  341. }
  342. public override string XmlLang
  343. {
  344. get
  345. {
  346. // TODO: implement me.
  347. return null;
  348. }
  349. }
  350. public XmlResolver XmlResolver
  351. {
  352. set
  353. {
  354. // TODO: implement me.
  355. }
  356. }
  357. public override XmlSpace XmlSpace
  358. {
  359. get
  360. {
  361. // TODO: implement me.
  362. return XmlSpace.Default;
  363. }
  364. }
  365. // methods
  366. public override void Close()
  367. {
  368. readState = ReadState.Closed;
  369. }
  370. public override string GetAttribute(int i)
  371. {
  372. // TODO: implement me.
  373. return null;
  374. }
  375. public override string GetAttribute(string name)
  376. {
  377. return (string)attributes[name];
  378. }
  379. public override string GetAttribute(
  380. string localName,
  381. string namespaceURI)
  382. {
  383. foreach (DictionaryEntry entry in attributes)
  384. {
  385. string thisName = (string)entry.Key;
  386. int indexOfColon = thisName.IndexOf(':');
  387. if (indexOfColon != -1)
  388. {
  389. string thisLocalName = thisName.Substring(indexOfColon + 1);
  390. if (localName == thisLocalName)
  391. {
  392. string thisPrefix = thisName.Substring(0, indexOfColon);
  393. string thisNamespaceURI = LookupNamespace(thisPrefix);
  394. if (namespaceURI == thisNamespaceURI)
  395. {
  396. return (string)attributes[thisName];
  397. }
  398. }
  399. }
  400. else if (localName == "xmlns" && namespaceURI == "http://www.w3.org/2000/xmlns/" && thisName == "xmlns")
  401. {
  402. return (string)attributes[thisName];
  403. }
  404. }
  405. return String.Empty;
  406. }
  407. public TextReader GetRemainder()
  408. {
  409. // TODO: implement me.
  410. return null;
  411. }
  412. // Why does this use explicit interface implementation?
  413. bool IXmlLineInfo.HasLineInfo()
  414. {
  415. // TODO: implement me.
  416. return false;
  417. }
  418. public override string LookupNamespace(string prefix)
  419. {
  420. return namespaceManager.LookupNamespace(prefix);
  421. }
  422. public override void MoveToAttribute(int i)
  423. {
  424. // TODO: implement me.
  425. }
  426. public override bool MoveToAttribute(string name)
  427. {
  428. // TODO: implement me.
  429. return false;
  430. }
  431. public override bool MoveToAttribute(
  432. string localName,
  433. string namespaceName)
  434. {
  435. // TODO: implement me.
  436. return false;
  437. }
  438. public override bool MoveToElement()
  439. {
  440. // TODO: implement me.
  441. return false;
  442. }
  443. public override bool MoveToFirstAttribute()
  444. {
  445. // TODO: implement me.
  446. return false;
  447. }
  448. public override bool MoveToNextAttribute()
  449. {
  450. // TODO: implement me.
  451. return false;
  452. }
  453. public override bool Read()
  454. {
  455. bool more = false;
  456. readState = ReadState.Interactive;
  457. more = ReadContent();
  458. return more;
  459. }
  460. public override bool ReadAttributeValue()
  461. {
  462. // TODO: implement me.
  463. return false;
  464. }
  465. public int ReadBase64(byte[] buffer, int offset, int length)
  466. {
  467. // TODO: implement me.
  468. return 0;
  469. }
  470. public int ReadBinHex(byte[] buffer, int offset, int length)
  471. {
  472. // TODO: implement me.
  473. return 0;
  474. }
  475. public int ReadChars(char[] buffer, int offset, int length)
  476. {
  477. // TODO: implement me.
  478. return 0;
  479. }
  480. public override string ReadInnerXml()
  481. {
  482. // TODO: implement me.
  483. return null;
  484. }
  485. public override string ReadOuterXml()
  486. {
  487. // TODO: implement me.
  488. return null;
  489. }
  490. public override string ReadString()
  491. {
  492. // TODO: implement me.
  493. return null;
  494. }
  495. public override void ResolveEntity()
  496. {
  497. // XmlTextReaders don't resolve entities.
  498. throw new InvalidOperationException("XmlTextReaders don't resolve entities.");
  499. }
  500. // privates
  501. private TextReader reader;
  502. private ReadState readState;
  503. private int depth;
  504. private bool depthDown;
  505. private XmlNameTable nameTable;
  506. private XmlNamespaceManager namespaceManager;
  507. private bool popScope;
  508. private XmlNodeType nodeType;
  509. private string name;
  510. private string prefix;
  511. private string localName;
  512. private string namespaceURI;
  513. private bool isEmptyElement;
  514. private string value;
  515. private Hashtable attributes;
  516. private bool returnEntityReference;
  517. private string entityReferenceName;
  518. private char[] nameBuffer;
  519. private int nameLength;
  520. private int nameCapacity;
  521. private const int initialNameCapacity = 256;
  522. private char[] valueBuffer;
  523. private int valueLength;
  524. private int valueCapacity;
  525. private const int initialValueCapacity = 8192;
  526. private void Init()
  527. {
  528. namespaceManager = new XmlNamespaceManager(nameTable);
  529. popScope = false;
  530. readState = ReadState.Initial;
  531. depth = -1;
  532. depthDown = false;
  533. nodeType = XmlNodeType.None;
  534. name = String.Empty;
  535. prefix = String.Empty;
  536. localName = string.Empty;
  537. isEmptyElement = false;
  538. value = String.Empty;
  539. attributes = new Hashtable();
  540. returnEntityReference = false;
  541. entityReferenceName = String.Empty;
  542. nameBuffer = new char[initialNameCapacity];
  543. nameLength = 0;
  544. nameCapacity = initialNameCapacity;
  545. valueBuffer = new char[initialValueCapacity];
  546. valueLength = 0;
  547. valueCapacity = initialValueCapacity;
  548. }
  549. // Use this method rather than setting the properties
  550. // directly so that all the necessary properties can
  551. // be changed in harmony with each other. Maybe the
  552. // fields should be in a seperate class to help enforce
  553. // this.
  554. private void SetProperties(
  555. XmlNodeType nodeType,
  556. string name,
  557. bool isEmptyElement,
  558. string value,
  559. bool clearAttributes)
  560. {
  561. this.nodeType = nodeType;
  562. this.name = name;
  563. this.isEmptyElement = isEmptyElement;
  564. this.value = value;
  565. if (clearAttributes)
  566. {
  567. ClearAttributes();
  568. }
  569. int indexOfColon = name.IndexOf(':');
  570. if (indexOfColon == -1)
  571. {
  572. prefix = String.Empty;
  573. localName = name;
  574. }
  575. else
  576. {
  577. prefix = name.Substring(0, indexOfColon);
  578. localName = name.Substring(indexOfColon + 1);
  579. }
  580. namespaceURI = LookupNamespace(prefix);
  581. }
  582. private void AddAttribute(string name, string value)
  583. {
  584. attributes.Add(name, value);
  585. }
  586. private void ClearAttributes()
  587. {
  588. if (attributes.Count > 0)
  589. {
  590. attributes.Clear();
  591. }
  592. }
  593. private int PeekChar()
  594. {
  595. return reader.Peek();
  596. }
  597. private int ReadChar()
  598. {
  599. return reader.Read();
  600. }
  601. // This should really keep track of some state so
  602. // that it's not possible to have more than one document
  603. // element or text outside of the document element.
  604. private bool ReadContent()
  605. {
  606. bool more = false;
  607. if (popScope)
  608. {
  609. namespaceManager.PopScope();
  610. popScope = false;
  611. }
  612. if (depthDown)
  613. {
  614. --depth;
  615. }
  616. if (returnEntityReference)
  617. {
  618. ++depth;
  619. SetEntityReferenceProperties();
  620. more = true;
  621. }
  622. else
  623. {
  624. switch (PeekChar())
  625. {
  626. case '<':
  627. ReadChar();
  628. ReadTag();
  629. more = true;
  630. break;
  631. case -1:
  632. readState = ReadState.EndOfFile;
  633. SetProperties(
  634. XmlNodeType.None, // nodeType
  635. String.Empty, // name
  636. false, // isEmptyElement
  637. String.Empty, // value
  638. true // clearAttributes
  639. );
  640. more = false;
  641. break;
  642. default:
  643. ReadText();
  644. more = true;
  645. break;
  646. }
  647. }
  648. return more;
  649. }
  650. private void SetEntityReferenceProperties()
  651. {
  652. SetProperties(
  653. XmlNodeType.EntityReference, // nodeType
  654. entityReferenceName, // name
  655. false, // isEmptyElement
  656. String.Empty, // value
  657. true // clearAttributes
  658. );
  659. returnEntityReference = false;
  660. entityReferenceName = String.Empty;
  661. }
  662. // The leading '<' has already been consumed.
  663. private void ReadTag()
  664. {
  665. switch (PeekChar())
  666. {
  667. case '/':
  668. ReadChar();
  669. ReadEndTag();
  670. break;
  671. case '?':
  672. ReadChar();
  673. ReadProcessingInstruction();
  674. break;
  675. case '!':
  676. ReadChar();
  677. ReadDeclaration();
  678. break;
  679. default:
  680. ReadStartTag();
  681. break;
  682. }
  683. }
  684. // The leading '<' has already been consumed.
  685. private void ReadStartTag()
  686. {
  687. namespaceManager.PushScope();
  688. string name = ReadName();
  689. SkipWhitespace();
  690. bool isEmptyElement = false;
  691. ClearAttributes();
  692. if (XmlChar.IsFirstNameChar(PeekChar()))
  693. {
  694. ReadAttributes();
  695. }
  696. if (PeekChar() == '/')
  697. {
  698. ReadChar();
  699. isEmptyElement = true;
  700. depthDown = true;
  701. popScope = true;
  702. }
  703. Expect('>');
  704. ++depth;
  705. SetProperties(
  706. XmlNodeType.Element, // nodeType
  707. name, // name
  708. isEmptyElement, // isEmptyElement
  709. String.Empty, // value
  710. false // clearAttributes
  711. );
  712. }
  713. // The reader is positioned on the first character
  714. // of the element's name.
  715. private void ReadEndTag()
  716. {
  717. string name = ReadName();
  718. SkipWhitespace();
  719. Expect('>');
  720. --depth;
  721. SetProperties(
  722. XmlNodeType.EndElement, // nodeType
  723. name, // name
  724. false, // isEmptyElement
  725. String.Empty, // value
  726. true // clearAttributes
  727. );
  728. popScope = true;
  729. }
  730. private void AppendNameChar(int ch)
  731. {
  732. CheckNameCapacity();
  733. nameBuffer[nameLength++] = (char)ch;
  734. }
  735. private void CheckNameCapacity()
  736. {
  737. if (nameLength == nameCapacity)
  738. {
  739. nameCapacity = nameCapacity * 2;
  740. char[] oldNameBuffer = nameBuffer;
  741. nameBuffer = new char[nameCapacity];
  742. Array.Copy(oldNameBuffer, nameBuffer, nameLength);
  743. }
  744. }
  745. private string CreateNameString()
  746. {
  747. return new String(nameBuffer, 0, nameLength);
  748. }
  749. private void AppendValueChar(int ch)
  750. {
  751. CheckValueCapacity();
  752. valueBuffer[valueLength++] = (char)ch;
  753. }
  754. private void CheckValueCapacity()
  755. {
  756. if (valueLength == valueCapacity)
  757. {
  758. valueCapacity = valueCapacity * 2;
  759. char[] oldValueBuffer = valueBuffer;
  760. valueBuffer = new char[valueCapacity];
  761. Array.Copy(oldValueBuffer, valueBuffer, valueLength);
  762. }
  763. }
  764. private string CreateValueString()
  765. {
  766. return new String(valueBuffer, 0, valueLength);
  767. }
  768. // The reader is positioned on the first character
  769. // of the text.
  770. private void ReadText()
  771. {
  772. valueLength = 0;
  773. int ch = PeekChar();
  774. while (ch != '<' && ch != -1)
  775. {
  776. if (ch == '&')
  777. {
  778. ReadChar();
  779. if (ReadReference(false))
  780. {
  781. break;
  782. }
  783. }
  784. else
  785. {
  786. AppendValueChar(ReadChar());
  787. }
  788. ch = PeekChar();
  789. }
  790. if (returnEntityReference && valueLength == 0)
  791. {
  792. ++depth;
  793. SetEntityReferenceProperties();
  794. }
  795. else
  796. {
  797. if (depth >= 0)
  798. {
  799. ++depth;
  800. depthDown = true;
  801. }
  802. SetProperties(
  803. XmlNodeType.Text, // nodeType
  804. String.Empty, // name
  805. false, // isEmptyElement
  806. CreateValueString(), // value
  807. true // clearAttributes
  808. );
  809. }
  810. }
  811. // The leading '&' has already been consumed.
  812. // Returns true if the entity reference isn't a simple
  813. // character reference or one of the predefined entities.
  814. // This allows the ReadText method to break so that the
  815. // next call to Read will return the EntityReference node.
  816. private bool ReadReference(bool ignoreEntityReferences)
  817. {
  818. if (PeekChar() == '#')
  819. {
  820. ReadChar();
  821. ReadCharacterReference();
  822. }
  823. else
  824. {
  825. ReadEntityReference(ignoreEntityReferences);
  826. }
  827. return returnEntityReference;
  828. }
  829. private void ReadCharacterReference()
  830. {
  831. int value = 0;
  832. if (PeekChar() == 'x')
  833. {
  834. ReadChar();
  835. while (PeekChar() != ';' && PeekChar() != -1)
  836. {
  837. int ch = ReadChar();
  838. if (ch >= '0' && ch <= '9')
  839. {
  840. value = (value << 4) + ch - '0';
  841. }
  842. else if (ch >= 'A' && ch <= 'F')
  843. {
  844. value = (value << 4) + ch - 'A' + 10;
  845. }
  846. else if (ch >= 'a' && ch <= 'f')
  847. {
  848. value = (value << 4) + ch - 'a' + 10;
  849. }
  850. else
  851. {
  852. throw new Exception(
  853. String.Format(
  854. "invalid hexadecimal digit: {0} (#x{1:X})",
  855. (char)ch,
  856. ch));
  857. }
  858. }
  859. }
  860. else
  861. {
  862. while (PeekChar() != ';' && PeekChar() != -1)
  863. {
  864. int ch = ReadChar();
  865. if (ch >= '0' && ch <= '9')
  866. {
  867. value = value * 10 + ch - '0';
  868. }
  869. else
  870. {
  871. throw new Exception(
  872. String.Format(
  873. "invalid decimal digit: {0} (#x{1:X})",
  874. (char)ch,
  875. ch));
  876. }
  877. }
  878. }
  879. ReadChar(); // ';'
  880. AppendValueChar(value);
  881. }
  882. private void ReadEntityReference(bool ignoreEntityReferences)
  883. {
  884. nameLength = 0;
  885. int ch = PeekChar();
  886. while (ch != ';' && ch != -1)
  887. {
  888. AppendNameChar(ReadChar());
  889. ch = PeekChar();
  890. }
  891. Expect(';');
  892. string name = CreateNameString();
  893. switch (name)
  894. {
  895. case "lt":
  896. AppendValueChar('<');
  897. break;
  898. case "gt":
  899. AppendValueChar('>');
  900. break;
  901. case "amp":
  902. AppendValueChar('&');
  903. break;
  904. case "apos":
  905. AppendValueChar('\'');
  906. break;
  907. case "quot":
  908. AppendValueChar('"');
  909. break;
  910. default:
  911. if (ignoreEntityReferences)
  912. {
  913. AppendValueChar('&');
  914. foreach (char ch2 in name)
  915. {
  916. AppendValueChar(ch2);
  917. }
  918. AppendValueChar(';');
  919. }
  920. else
  921. {
  922. returnEntityReference = true;
  923. entityReferenceName = name;
  924. }
  925. break;
  926. }
  927. }
  928. // The reader is positioned on the first character of
  929. // the attribute name.
  930. private void ReadAttributes()
  931. {
  932. do
  933. {
  934. string name = ReadName();
  935. SkipWhitespace();
  936. Expect('=');
  937. SkipWhitespace();
  938. string value = ReadAttribute();
  939. SkipWhitespace();
  940. if (name == "xmlns")
  941. {
  942. namespaceManager.AddNamespace(String.Empty, value);
  943. }
  944. else if (name.StartsWith("xmlns:"))
  945. {
  946. namespaceManager.AddNamespace(name.Substring(6), value);
  947. }
  948. AddAttribute(name, value);
  949. }
  950. while (PeekChar() != '/' && PeekChar() != '>' && PeekChar() != -1);
  951. }
  952. // The reader is positioned on the quote character.
  953. private string ReadAttribute()
  954. {
  955. int quoteChar = ReadChar();
  956. if (quoteChar != '\'' && quoteChar != '\"')
  957. {
  958. throw new Exception("an attribute value was not quoted");
  959. }
  960. valueLength = 0;
  961. while (PeekChar() != quoteChar)
  962. {
  963. int ch = ReadChar();
  964. switch (ch)
  965. {
  966. case '<':
  967. throw new Exception("attribute values cannot contain '<'");
  968. case '&':
  969. ReadReference(true);
  970. break;
  971. case -1:
  972. throw new Exception("unexpected end of file in an attribute value");
  973. default:
  974. AppendValueChar(ch);
  975. break;
  976. }
  977. }
  978. ReadChar(); // quoteChar
  979. return CreateValueString();
  980. }
  981. // The reader is positioned on the first character
  982. // of the target.
  983. private void ReadProcessingInstruction()
  984. {
  985. string target = ReadName();
  986. SkipWhitespace();
  987. valueLength = 0;
  988. while (PeekChar() != -1)
  989. {
  990. int ch = ReadChar();
  991. if (ch == '?' && PeekChar() == '>')
  992. {
  993. ReadChar();
  994. break;
  995. }
  996. AppendValueChar((char)ch);
  997. }
  998. SetProperties(
  999. XmlNodeType.ProcessingInstruction, // nodeType
  1000. target, // name
  1001. false, // isEmptyElement
  1002. CreateValueString(), // value
  1003. true // clearAttributes
  1004. );
  1005. }
  1006. // The reader is positioned on the first character after
  1007. // the leading '<!'.
  1008. private void ReadDeclaration()
  1009. {
  1010. int ch = PeekChar();
  1011. switch (ch)
  1012. {
  1013. case '-':
  1014. Expect('-');
  1015. Expect('-');
  1016. ReadComment();
  1017. break;
  1018. case '[':
  1019. ReadChar();
  1020. Expect('C');
  1021. Expect('D');
  1022. Expect('A');
  1023. Expect('T');
  1024. Expect('A');
  1025. Expect('[');
  1026. ReadCDATA();
  1027. break;
  1028. }
  1029. }
  1030. // The reader is positioned on the first character after
  1031. // the leading '<!--'.
  1032. private void ReadComment()
  1033. {
  1034. valueLength = 0;
  1035. while (PeekChar() != -1)
  1036. {
  1037. int ch = ReadChar();
  1038. if (ch == '-' && PeekChar() == '-')
  1039. {
  1040. ReadChar();
  1041. if (PeekChar() != '>')
  1042. {
  1043. throw new Exception("comments cannot contain '--'");
  1044. }
  1045. ReadChar();
  1046. break;
  1047. }
  1048. AppendValueChar((char)ch);
  1049. }
  1050. SetProperties(
  1051. XmlNodeType.Comment, // nodeType
  1052. String.Empty, // name
  1053. false, // isEmptyElement
  1054. CreateValueString(), // value
  1055. true // clearAttributes
  1056. );
  1057. }
  1058. // The reader is positioned on the first character after
  1059. // the leading '<![CDATA['.
  1060. private void ReadCDATA()
  1061. {
  1062. valueLength = 0;
  1063. while (PeekChar() != -1)
  1064. {
  1065. int ch = ReadChar();
  1066. if (ch == ']' && PeekChar() == ']')
  1067. {
  1068. ch = ReadChar(); // ']'
  1069. if (PeekChar() == '>')
  1070. {
  1071. ReadChar(); // '>'
  1072. break;
  1073. }
  1074. else
  1075. {
  1076. AppendValueChar(']');
  1077. AppendValueChar(']');
  1078. ch = ReadChar();
  1079. }
  1080. }
  1081. AppendValueChar((char)ch);
  1082. }
  1083. ++depth;
  1084. SetProperties(
  1085. XmlNodeType.CDATA, // nodeType
  1086. String.Empty, // name
  1087. false, // isEmptyElement
  1088. CreateValueString(), // value
  1089. true // clearAttributes
  1090. );
  1091. }
  1092. // The reader is positioned on the first character
  1093. // of the name.
  1094. private string ReadName()
  1095. {
  1096. if (!XmlChar.IsFirstNameChar(PeekChar()))
  1097. {
  1098. throw new Exception("a name did not start with a legal character");
  1099. }
  1100. nameLength = 0;
  1101. AppendNameChar(ReadChar());
  1102. while (XmlChar.IsNameChar(PeekChar()))
  1103. {
  1104. AppendNameChar(ReadChar());
  1105. }
  1106. return CreateNameString();
  1107. }
  1108. // Read the next character and compare it against the
  1109. // specified character.
  1110. private void Expect(int expected)
  1111. {
  1112. int ch = ReadChar();
  1113. if (ch != expected)
  1114. {
  1115. throw new Exception(String.Format(
  1116. "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
  1117. (char)expected,
  1118. expected,
  1119. (char)ch,
  1120. ch));
  1121. }
  1122. }
  1123. // Does not consume the first non-whitespace character.
  1124. private void SkipWhitespace()
  1125. {
  1126. while (XmlChar.IsWhitespace(PeekChar()))
  1127. {
  1128. ReadChar();
  1129. }
  1130. }
  1131. }
  1132. }