XmlTextReader.cs 22 KB

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