XmlTextReader.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  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. }
  401. return String.Empty;
  402. }
  403. public TextReader GetRemainder()
  404. {
  405. // TODO: implement me.
  406. return null;
  407. }
  408. // Why does this use explicit interface implementation?
  409. bool IXmlLineInfo.HasLineInfo()
  410. {
  411. // TODO: implement me.
  412. return false;
  413. }
  414. public override string LookupNamespace(string prefix)
  415. {
  416. return namespaceManager.LookupNamespace(prefix);
  417. }
  418. public override void MoveToAttribute(int i)
  419. {
  420. // TODO: implement me.
  421. }
  422. public override bool MoveToAttribute(string name)
  423. {
  424. // TODO: implement me.
  425. return false;
  426. }
  427. public override bool MoveToAttribute(
  428. string localName,
  429. string namespaceName)
  430. {
  431. // TODO: implement me.
  432. return false;
  433. }
  434. public override bool MoveToElement()
  435. {
  436. // TODO: implement me.
  437. return false;
  438. }
  439. public override bool MoveToFirstAttribute()
  440. {
  441. // TODO: implement me.
  442. return false;
  443. }
  444. public override bool MoveToNextAttribute()
  445. {
  446. // TODO: implement me.
  447. return false;
  448. }
  449. public override bool Read()
  450. {
  451. bool more = false;
  452. readState = ReadState.Interactive;
  453. more = ReadContent();
  454. return more;
  455. }
  456. public override bool ReadAttributeValue()
  457. {
  458. // TODO: implement me.
  459. return false;
  460. }
  461. public int ReadBase64(byte[] buffer, int offset, int length)
  462. {
  463. // TODO: implement me.
  464. return 0;
  465. }
  466. public int ReadBinHex(byte[] buffer, int offset, int length)
  467. {
  468. // TODO: implement me.
  469. return 0;
  470. }
  471. public int ReadChars(char[] buffer, int offset, int length)
  472. {
  473. // TODO: implement me.
  474. return 0;
  475. }
  476. public override string ReadInnerXml()
  477. {
  478. // TODO: implement me.
  479. return null;
  480. }
  481. public override string ReadOuterXml()
  482. {
  483. // TODO: implement me.
  484. return null;
  485. }
  486. public override string ReadString()
  487. {
  488. // TODO: implement me.
  489. return null;
  490. }
  491. public override void ResolveEntity()
  492. {
  493. // XmlTextReaders don't resolve entities.
  494. throw new InvalidOperationException("XmlTextReaders don't resolve entities.");
  495. }
  496. // privates
  497. private TextReader reader;
  498. private ReadState readState;
  499. private int depth;
  500. private bool depthDown;
  501. private XmlNameTable nameTable;
  502. private XmlNamespaceManager namespaceManager;
  503. private bool popScope;
  504. private XmlNodeType nodeType;
  505. private string name;
  506. private string prefix;
  507. private string localName;
  508. private string namespaceURI;
  509. private bool isEmptyElement;
  510. private string value;
  511. private Hashtable attributes;
  512. private bool returnEntityReference;
  513. private string entityReferenceName;
  514. private char[] nameBuffer;
  515. private int nameLength;
  516. private int nameCapacity;
  517. private const int initialNameCapacity = 256;
  518. private char[] valueBuffer;
  519. private int valueLength;
  520. private int valueCapacity;
  521. private const int initialValueCapacity = 8192;
  522. private void Init()
  523. {
  524. namespaceManager = new XmlNamespaceManager(nameTable);
  525. popScope = false;
  526. readState = ReadState.Initial;
  527. depth = -1;
  528. depthDown = false;
  529. nodeType = XmlNodeType.None;
  530. name = String.Empty;
  531. prefix = String.Empty;
  532. localName = string.Empty;
  533. isEmptyElement = false;
  534. value = String.Empty;
  535. attributes = new Hashtable();
  536. returnEntityReference = false;
  537. entityReferenceName = String.Empty;
  538. nameBuffer = new char[initialNameCapacity];
  539. nameLength = 0;
  540. nameCapacity = initialNameCapacity;
  541. valueBuffer = new char[initialValueCapacity];
  542. valueLength = 0;
  543. valueCapacity = initialValueCapacity;
  544. }
  545. // Use this method rather than setting the properties
  546. // directly so that all the necessary properties can
  547. // be changed in harmony with each other. Maybe the
  548. // fields should be in a seperate class to help enforce
  549. // this.
  550. private void SetProperties(
  551. XmlNodeType nodeType,
  552. string name,
  553. bool isEmptyElement,
  554. string value,
  555. bool clearAttributes)
  556. {
  557. this.nodeType = nodeType;
  558. this.name = name;
  559. this.isEmptyElement = isEmptyElement;
  560. this.value = value;
  561. if (clearAttributes)
  562. {
  563. ClearAttributes();
  564. }
  565. int indexOfColon = name.IndexOf(':');
  566. if (indexOfColon == -1)
  567. {
  568. prefix = String.Empty;
  569. localName = name;
  570. }
  571. else
  572. {
  573. prefix = name.Substring(0, indexOfColon);
  574. localName = name.Substring(indexOfColon + 1);
  575. }
  576. namespaceURI = LookupNamespace(prefix);
  577. }
  578. private void AddAttribute(string name, string value)
  579. {
  580. attributes.Add(name, value);
  581. }
  582. private void ClearAttributes()
  583. {
  584. if (attributes.Count > 0)
  585. {
  586. attributes.Clear();
  587. }
  588. }
  589. private int PeekChar()
  590. {
  591. return reader.Peek();
  592. }
  593. private int ReadChar()
  594. {
  595. return reader.Read();
  596. }
  597. // This should really keep track of some state so
  598. // that it's not possible to have more than one document
  599. // element or text outside of the document element.
  600. private bool ReadContent()
  601. {
  602. bool more = false;
  603. if (popScope)
  604. {
  605. namespaceManager.PopScope();
  606. popScope = false;
  607. }
  608. if (depthDown)
  609. {
  610. --depth;
  611. }
  612. if (returnEntityReference)
  613. {
  614. ++depth;
  615. SetEntityReferenceProperties();
  616. more = true;
  617. }
  618. else
  619. {
  620. switch (PeekChar())
  621. {
  622. case '<':
  623. ReadChar();
  624. ReadTag();
  625. more = true;
  626. break;
  627. case -1:
  628. readState = ReadState.EndOfFile;
  629. SetProperties(
  630. XmlNodeType.None, // nodeType
  631. String.Empty, // name
  632. false, // isEmptyElement
  633. String.Empty, // value
  634. true // clearAttributes
  635. );
  636. more = false;
  637. break;
  638. default:
  639. ReadText();
  640. more = true;
  641. break;
  642. }
  643. }
  644. return more;
  645. }
  646. private void SetEntityReferenceProperties()
  647. {
  648. SetProperties(
  649. XmlNodeType.EntityReference, // nodeType
  650. entityReferenceName, // name
  651. false, // isEmptyElement
  652. String.Empty, // value
  653. true // clearAttributes
  654. );
  655. returnEntityReference = false;
  656. entityReferenceName = String.Empty;
  657. }
  658. // The leading '<' has already been consumed.
  659. private void ReadTag()
  660. {
  661. switch (PeekChar())
  662. {
  663. case '/':
  664. ReadChar();
  665. ReadEndTag();
  666. break;
  667. case '?':
  668. ReadChar();
  669. ReadProcessingInstruction();
  670. break;
  671. case '!':
  672. ReadChar();
  673. ReadDeclaration();
  674. break;
  675. default:
  676. ReadStartTag();
  677. break;
  678. }
  679. }
  680. // The leading '<' has already been consumed.
  681. private void ReadStartTag()
  682. {
  683. namespaceManager.PushScope();
  684. string name = ReadName();
  685. SkipWhitespace();
  686. bool isEmptyElement = false;
  687. ClearAttributes();
  688. if (XmlChar.IsFirstNameChar(PeekChar()))
  689. {
  690. ReadAttributes();
  691. }
  692. if (PeekChar() == '/')
  693. {
  694. ReadChar();
  695. isEmptyElement = true;
  696. depthDown = true;
  697. popScope = true;
  698. }
  699. Expect('>');
  700. ++depth;
  701. SetProperties(
  702. XmlNodeType.Element, // nodeType
  703. name, // name
  704. isEmptyElement, // isEmptyElement
  705. String.Empty, // value
  706. false // clearAttributes
  707. );
  708. }
  709. // The reader is positioned on the first character
  710. // of the element's name.
  711. private void ReadEndTag()
  712. {
  713. string name = ReadName();
  714. SkipWhitespace();
  715. Expect('>');
  716. --depth;
  717. SetProperties(
  718. XmlNodeType.EndElement, // nodeType
  719. name, // name
  720. false, // isEmptyElement
  721. String.Empty, // value
  722. true // clearAttributes
  723. );
  724. popScope = true;
  725. }
  726. private void AppendNameChar(int ch)
  727. {
  728. CheckNameCapacity();
  729. nameBuffer[nameLength++] = (char)ch;
  730. }
  731. private void CheckNameCapacity()
  732. {
  733. if (nameLength == nameCapacity)
  734. {
  735. nameCapacity = nameCapacity * 2;
  736. char[] oldNameBuffer = nameBuffer;
  737. nameBuffer = new char[nameCapacity];
  738. Array.Copy(oldNameBuffer, nameBuffer, nameLength);
  739. }
  740. }
  741. private string CreateNameString()
  742. {
  743. return new String(nameBuffer, 0, nameLength);
  744. }
  745. private void AppendValueChar(int ch)
  746. {
  747. CheckValueCapacity();
  748. valueBuffer[valueLength++] = (char)ch;
  749. }
  750. private void CheckValueCapacity()
  751. {
  752. if (valueLength == valueCapacity)
  753. {
  754. valueCapacity = valueCapacity * 2;
  755. char[] oldValueBuffer = valueBuffer;
  756. valueBuffer = new char[valueCapacity];
  757. Array.Copy(oldValueBuffer, valueBuffer, valueLength);
  758. }
  759. }
  760. private string CreateValueString()
  761. {
  762. return new String(valueBuffer, 0, valueLength);
  763. }
  764. // The reader is positioned on the first character
  765. // of the text.
  766. private void ReadText()
  767. {
  768. valueLength = 0;
  769. int ch = PeekChar();
  770. while (ch != '<' && ch != -1)
  771. {
  772. if (ch == '&')
  773. {
  774. ReadChar();
  775. if (ReadReference(false))
  776. {
  777. break;
  778. }
  779. }
  780. else
  781. {
  782. AppendValueChar(ReadChar());
  783. }
  784. ch = PeekChar();
  785. }
  786. if (returnEntityReference && valueLength == 0)
  787. {
  788. ++depth;
  789. SetEntityReferenceProperties();
  790. }
  791. else
  792. {
  793. if (depth >= 0)
  794. {
  795. ++depth;
  796. depthDown = true;
  797. }
  798. SetProperties(
  799. XmlNodeType.Text, // nodeType
  800. String.Empty, // name
  801. false, // isEmptyElement
  802. CreateValueString(), // value
  803. true // clearAttributes
  804. );
  805. }
  806. }
  807. // The leading '&' has already been consumed.
  808. // Returns true if the entity reference isn't a simple
  809. // character reference or one of the predefined entities.
  810. // This allows the ReadText method to break so that the
  811. // next call to Read will return the EntityReference node.
  812. private bool ReadReference(bool ignoreEntityReferences)
  813. {
  814. if (PeekChar() == '#')
  815. {
  816. ReadChar();
  817. ReadCharacterReference();
  818. }
  819. else
  820. {
  821. ReadEntityReference(ignoreEntityReferences);
  822. }
  823. return returnEntityReference;
  824. }
  825. private void ReadCharacterReference()
  826. {
  827. int value = 0;
  828. if (PeekChar() == 'x')
  829. {
  830. ReadChar();
  831. while (PeekChar() != ';' && PeekChar() != -1)
  832. {
  833. int ch = ReadChar();
  834. if (ch >= '0' && ch <= '9')
  835. {
  836. value = (value << 4) + ch - '0';
  837. }
  838. else if (ch >= 'A' && ch <= 'F')
  839. {
  840. value = (value << 4) + ch - 'A' + 10;
  841. }
  842. else if (ch >= 'a' && ch <= 'f')
  843. {
  844. value = (value << 4) + ch - 'a' + 10;
  845. }
  846. else
  847. {
  848. throw new Exception(
  849. String.Format(
  850. "invalid hexadecimal digit: {0} (#x{1:X})",
  851. (char)ch,
  852. ch));
  853. }
  854. }
  855. }
  856. else
  857. {
  858. while (PeekChar() != ';' && PeekChar() != -1)
  859. {
  860. int ch = ReadChar();
  861. if (ch >= '0' && ch <= '9')
  862. {
  863. value = value * 10 + ch - '0';
  864. }
  865. else
  866. {
  867. throw new Exception(
  868. String.Format(
  869. "invalid decimal digit: {0} (#x{1:X})",
  870. (char)ch,
  871. ch));
  872. }
  873. }
  874. }
  875. ReadChar(); // ';'
  876. AppendValueChar(value);
  877. }
  878. private void ReadEntityReference(bool ignoreEntityReferences)
  879. {
  880. nameLength = 0;
  881. int ch = PeekChar();
  882. while (ch != ';' && ch != -1)
  883. {
  884. AppendNameChar(ReadChar());
  885. ch = PeekChar();
  886. }
  887. Expect(';');
  888. string name = CreateNameString();
  889. switch (name)
  890. {
  891. case "lt":
  892. AppendValueChar('<');
  893. break;
  894. case "gt":
  895. AppendValueChar('>');
  896. break;
  897. case "amp":
  898. AppendValueChar('&');
  899. break;
  900. case "apos":
  901. AppendValueChar('\'');
  902. break;
  903. case "quot":
  904. AppendValueChar('"');
  905. break;
  906. default:
  907. if (ignoreEntityReferences)
  908. {
  909. AppendValueChar('&');
  910. foreach (char ch2 in name)
  911. {
  912. AppendValueChar(ch2);
  913. }
  914. AppendValueChar(';');
  915. }
  916. else
  917. {
  918. returnEntityReference = true;
  919. entityReferenceName = name;
  920. }
  921. break;
  922. }
  923. }
  924. // The reader is positioned on the first character of
  925. // the attribute name.
  926. private void ReadAttributes()
  927. {
  928. do
  929. {
  930. string name = ReadName();
  931. SkipWhitespace();
  932. Expect('=');
  933. SkipWhitespace();
  934. string value = ReadAttribute();
  935. SkipWhitespace();
  936. if (name == "xmlns")
  937. {
  938. namespaceManager.AddNamespace(String.Empty, value);
  939. }
  940. else if (name.StartsWith("xmlns:"))
  941. {
  942. namespaceManager.AddNamespace(name.Substring(6), value);
  943. }
  944. else
  945. {
  946. AddAttribute(name, value);
  947. }
  948. }
  949. while (PeekChar() != '/' && PeekChar() != '>' && PeekChar() != -1);
  950. }
  951. // The reader is positioned on the quote character.
  952. private string ReadAttribute()
  953. {
  954. int quoteChar = ReadChar();
  955. if (quoteChar != '\'' && quoteChar != '\"')
  956. {
  957. throw new Exception("an attribute value was not quoted");
  958. }
  959. valueLength = 0;
  960. while (PeekChar() != quoteChar)
  961. {
  962. int ch = ReadChar();
  963. switch (ch)
  964. {
  965. case '<':
  966. throw new Exception("attribute values cannot contain '<'");
  967. case '&':
  968. ReadReference(true);
  969. break;
  970. case -1:
  971. throw new Exception("unexpected end of file in an attribute value");
  972. default:
  973. AppendValueChar(ch);
  974. break;
  975. }
  976. }
  977. ReadChar(); // quoteChar
  978. return CreateValueString();
  979. }
  980. // The reader is positioned on the first character
  981. // of the target.
  982. private void ReadProcessingInstruction()
  983. {
  984. string target = ReadName();
  985. SkipWhitespace();
  986. valueLength = 0;
  987. while (PeekChar() != -1)
  988. {
  989. int ch = ReadChar();
  990. if (ch == '?' && PeekChar() == '>')
  991. {
  992. ReadChar();
  993. break;
  994. }
  995. AppendValueChar((char)ch);
  996. }
  997. SetProperties(
  998. XmlNodeType.ProcessingInstruction, // nodeType
  999. target, // name
  1000. false, // isEmptyElement
  1001. CreateValueString(), // value
  1002. true // clearAttributes
  1003. );
  1004. }
  1005. // The reader is positioned on the first character after
  1006. // the leading '<!'.
  1007. private void ReadDeclaration()
  1008. {
  1009. int ch = PeekChar();
  1010. switch (ch)
  1011. {
  1012. case '-':
  1013. Expect('-');
  1014. Expect('-');
  1015. ReadComment();
  1016. break;
  1017. case '[':
  1018. ReadChar();
  1019. Expect('C');
  1020. Expect('D');
  1021. Expect('A');
  1022. Expect('T');
  1023. Expect('A');
  1024. Expect('[');
  1025. ReadCDATA();
  1026. break;
  1027. }
  1028. }
  1029. // The reader is positioned on the first character after
  1030. // the leading '<!--'.
  1031. private void ReadComment()
  1032. {
  1033. valueLength = 0;
  1034. while (PeekChar() != -1)
  1035. {
  1036. int ch = ReadChar();
  1037. if (ch == '-' && PeekChar() == '-')
  1038. {
  1039. ReadChar();
  1040. if (PeekChar() != '>')
  1041. {
  1042. throw new Exception("comments cannot contain '--'");
  1043. }
  1044. ReadChar();
  1045. break;
  1046. }
  1047. AppendValueChar((char)ch);
  1048. }
  1049. SetProperties(
  1050. XmlNodeType.Comment, // nodeType
  1051. String.Empty, // name
  1052. false, // isEmptyElement
  1053. CreateValueString(), // value
  1054. true // clearAttributes
  1055. );
  1056. }
  1057. // The reader is positioned on the first character after
  1058. // the leading '<![CDATA['.
  1059. private void ReadCDATA()
  1060. {
  1061. valueLength = 0;
  1062. while (PeekChar() != -1)
  1063. {
  1064. int ch = ReadChar();
  1065. if (ch == ']' && PeekChar() == ']')
  1066. {
  1067. ch = ReadChar(); // ']'
  1068. if (PeekChar() == '>')
  1069. {
  1070. ReadChar(); // '>'
  1071. break;
  1072. }
  1073. else
  1074. {
  1075. AppendValueChar(']');
  1076. AppendValueChar(']');
  1077. ch = ReadChar();
  1078. }
  1079. }
  1080. AppendValueChar((char)ch);
  1081. }
  1082. ++depth;
  1083. SetProperties(
  1084. XmlNodeType.CDATA, // nodeType
  1085. String.Empty, // name
  1086. false, // isEmptyElement
  1087. CreateValueString(), // value
  1088. true // clearAttributes
  1089. );
  1090. }
  1091. // The reader is positioned on the first character
  1092. // of the name.
  1093. private string ReadName()
  1094. {
  1095. if (!XmlChar.IsFirstNameChar(PeekChar()))
  1096. {
  1097. throw new Exception("a name did not start with a legal character");
  1098. }
  1099. nameLength = 0;
  1100. AppendNameChar(ReadChar());
  1101. while (XmlChar.IsNameChar(PeekChar()))
  1102. {
  1103. AppendNameChar(ReadChar());
  1104. }
  1105. return CreateNameString();
  1106. }
  1107. // Read the next character and compare it against the
  1108. // specified character.
  1109. private void Expect(int expected)
  1110. {
  1111. int ch = ReadChar();
  1112. if (ch != expected)
  1113. {
  1114. throw new Exception(String.Format(
  1115. "expected '{0}' ({1:X}) but found '{2}' ({3:X})",
  1116. (char)expected,
  1117. expected,
  1118. (char)ch,
  1119. ch));
  1120. }
  1121. }
  1122. // Does not consume the first non-whitespace character.
  1123. private void SkipWhitespace()
  1124. {
  1125. while (XmlChar.IsWhitespace(PeekChar()))
  1126. {
  1127. ReadChar();
  1128. }
  1129. }
  1130. }
  1131. }