XmlBinaryDictionaryWriter.cs 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. //
  2. // XmlBinaryDictionaryWriter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2005, 2007 Novell, Inc. http://www.novell.com
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Specialized;
  32. using System.Collections.Generic;
  33. using System.Globalization;
  34. using System.IO;
  35. using System.Linq;
  36. using System.Text;
  37. using BF = System.Xml.XmlBinaryFormat;
  38. namespace System.Xml
  39. {
  40. internal partial class XmlBinaryDictionaryWriter : XmlDictionaryWriter
  41. {
  42. class MyBinaryWriter : BinaryWriter
  43. {
  44. public MyBinaryWriter (Stream s)
  45. : base (s)
  46. {
  47. }
  48. public void WriteFlexibleInt (int value)
  49. {
  50. Write7BitEncodedInt (value);
  51. }
  52. }
  53. #region Fields
  54. MyBinaryWriter original, writer, buffer_writer;
  55. IXmlDictionary dict_ext;
  56. XmlDictionary dict_int = new XmlDictionary ();
  57. XmlBinaryWriterSession session;
  58. bool owns_stream;
  59. Encoding utf8Enc = new UTF8Encoding ();
  60. MemoryStream buffer = new MemoryStream ();
  61. const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
  62. const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
  63. WriteState state = WriteState.Start;
  64. bool open_start_element = false;
  65. // transient current node info
  66. List<KeyValuePair<string,object>> namespaces = new List<KeyValuePair<string,object>> ();
  67. string xml_lang = null;
  68. XmlSpace xml_space = XmlSpace.None;
  69. int ns_index = 0;
  70. // stacked info
  71. Stack<int> ns_index_stack = new Stack<int> ();
  72. Stack<string> xml_lang_stack = new Stack<string> ();
  73. Stack<XmlSpace> xml_space_stack = new Stack<XmlSpace> ();
  74. Stack<string> element_ns_stack = new Stack<string> ();
  75. string element_ns = String.Empty;
  76. int element_count;
  77. string element_prefix; // only meaningful at Element state
  78. // current attribute info
  79. string attr_value;
  80. string current_attr_prefix;
  81. object current_attr_name, current_attr_ns;
  82. bool attr_typed_value;
  83. SaveTarget save_target;
  84. enum SaveTarget {
  85. None,
  86. Namespaces,
  87. XmlLang,
  88. XmlSpace
  89. }
  90. // XmlWriterSettings support
  91. #endregion
  92. #region Constructors
  93. public XmlBinaryDictionaryWriter (Stream stream,
  94. IXmlDictionary dictionary,
  95. XmlBinaryWriterSession session, bool ownsStream)
  96. {
  97. if (dictionary == null)
  98. dictionary = new XmlDictionary ();
  99. if (session == null)
  100. session = new XmlBinaryWriterSession ();
  101. original = new MyBinaryWriter (stream);
  102. this.writer = original;
  103. buffer_writer = new MyBinaryWriter (buffer);
  104. this.dict_ext = dictionary;
  105. this.session = session;
  106. owns_stream = ownsStream;
  107. AddNamespace ("xml", "http://www.w3.org/XML/1998/namespace");
  108. AddNamespace ("xml", "http://www.w3.org/2000/xmlns/");
  109. ns_index = 2;
  110. }
  111. #endregion
  112. #region Properties
  113. public override WriteState WriteState {
  114. get { return state; }
  115. }
  116. public override string XmlLang {
  117. get { return xml_lang; }
  118. }
  119. public override XmlSpace XmlSpace {
  120. get { return xml_space; }
  121. }
  122. #endregion
  123. #region Methods
  124. private void AddMissingElementXmlns ()
  125. {
  126. // push new namespaces to manager.
  127. for (int i = ns_index; i < namespaces.Count; i++) {
  128. var ent = namespaces [i];
  129. string prefix = (string) ent.Key;
  130. string ns = ent.Value as string;
  131. XmlDictionaryString dns = ent.Value as XmlDictionaryString;
  132. if (ns != null) {
  133. if (prefix.Length > 0) {
  134. writer.Write (BF.PrefixNSString);
  135. writer.Write (prefix);
  136. }
  137. else
  138. writer.Write (BF.DefaultNSString);
  139. writer.Write (ns);
  140. } else {
  141. if (prefix.Length > 0) {
  142. writer.Write (BF.PrefixNSIndex);
  143. writer.Write (prefix);
  144. }
  145. else
  146. writer.Write (BF.DefaultNSIndex);
  147. WriteDictionaryIndex (dns);
  148. }
  149. }
  150. ns_index = namespaces.Count;
  151. }
  152. private void CheckState ()
  153. {
  154. if (state == WriteState.Closed) {
  155. throw new InvalidOperationException ("The Writer is closed.");
  156. }
  157. }
  158. void ProcessStateForContent ()
  159. {
  160. CheckState ();
  161. if (state == WriteState.Element)
  162. CloseStartElement ();
  163. ProcessPendingBuffer (false, false);
  164. if (state != WriteState.Attribute)
  165. writer = buffer_writer;
  166. }
  167. void ProcessTypedValue ()
  168. {
  169. ProcessStateForContent ();
  170. if (state == WriteState.Attribute) {
  171. if (attr_typed_value)
  172. throw new InvalidOperationException (String.Format ("A typed value for the attribute '{0}' in namespace '{1}' was already written", current_attr_name, current_attr_ns));
  173. attr_typed_value = true;
  174. }
  175. }
  176. void ProcessPendingBuffer (bool last, bool endElement)
  177. {
  178. if (buffer.Position > 0) {
  179. byte [] arr = buffer.GetBuffer ();
  180. if (endElement)
  181. arr [0]++;
  182. original.Write (arr, 0, (int) buffer.Position);
  183. buffer.SetLength (0);
  184. }
  185. if (last)
  186. writer = original;
  187. }
  188. public override void Close ()
  189. {
  190. CloseOpenAttributeAndElements ();
  191. if (owns_stream)
  192. writer.Close ();
  193. else if (state != WriteState.Closed)
  194. writer.Flush ();
  195. state = WriteState.Closed;
  196. }
  197. private void CloseOpenAttributeAndElements ()
  198. {
  199. CloseStartElement ();
  200. while (element_count > 0)
  201. WriteEndElement ();
  202. }
  203. private void CloseStartElement ()
  204. {
  205. if (!open_start_element)
  206. return;
  207. if (state == WriteState.Attribute)
  208. WriteEndAttribute ();
  209. AddMissingElementXmlns ();
  210. state = WriteState.Content;
  211. open_start_element = false;
  212. }
  213. public override void Flush ()
  214. {
  215. writer.Flush ();
  216. }
  217. public override string LookupPrefix (string ns)
  218. {
  219. if (ns == null || ns == String.Empty)
  220. throw new ArgumentException ("The Namespace cannot be empty.");
  221. var de = namespaces.LastOrDefault (i => i.Value.ToString () == ns);
  222. return de.Key; // de is KeyValuePair and its default key is null.
  223. }
  224. public override void WriteBase64 (byte[] buffer, int index, int count)
  225. {
  226. if (count < 0)
  227. throw new IndexOutOfRangeException ("Negative count");
  228. ProcessStateForContent ();
  229. if (count < 0x100) {
  230. writer.Write (BF.Bytes8);
  231. writer.Write ((byte) count);
  232. writer.Write (buffer, index, count);
  233. } else if (count < 0x10000) {
  234. writer.Write (BF.Bytes8);
  235. writer.Write ((ushort) count);
  236. writer.Write (buffer, index, count);
  237. } else {
  238. writer.Write (BF.Bytes32);
  239. writer.Write (count);
  240. writer.Write (buffer, index, count);
  241. }
  242. }
  243. public override void WriteCData (string text)
  244. {
  245. if (text.IndexOf ("]]>") >= 0)
  246. throw new ArgumentException ("CDATA section cannot contain text \"]]>\".");
  247. ProcessStateForContent ();
  248. WriteTextBinary (text);
  249. }
  250. public override void WriteCharEntity (char ch)
  251. {
  252. WriteChars (new char [] {ch}, 0, 1);
  253. }
  254. public override void WriteChars (char[] buffer, int index, int count)
  255. {
  256. ProcessStateForContent ();
  257. int blen = Encoding.UTF8.GetByteCount (buffer, index, count);
  258. if (blen == 0)
  259. writer.Write (BF.EmptyText);
  260. else if (count == 1 && buffer [0] == '0')
  261. writer.Write (BF.Zero);
  262. else if (count == 1 && buffer [0] == '1')
  263. writer.Write (BF.One);
  264. else if (blen < 0x100) {
  265. writer.Write (BF.Chars8);
  266. writer.Write ((byte) blen);
  267. writer.Write (buffer, index, count);
  268. } else if (blen < 0x10000) {
  269. writer.Write (BF.Chars16);
  270. writer.Write ((ushort) blen);
  271. writer.Write (buffer, index, count);
  272. } else {
  273. writer.Write (BF.Chars32);
  274. writer.Write (blen);
  275. writer.Write (buffer, index, count);
  276. }
  277. }
  278. public override void WriteComment (string text)
  279. {
  280. if (text.EndsWith("-"))
  281. throw new ArgumentException ("An XML comment cannot contain \"--\" inside.");
  282. else if (text.IndexOf("--") > 0)
  283. throw new ArgumentException ("An XML comment cannot end with \"-\".");
  284. ProcessStateForContent ();
  285. if (state == WriteState.Attribute)
  286. throw new InvalidOperationException ("Comment node is not allowed inside an attribute");
  287. writer.Write (BF.Comment);
  288. writer.Write (text);
  289. }
  290. public override void WriteDocType (string name, string pubid, string sysid, string subset)
  291. {
  292. throw new NotSupportedException ("This XmlWriter implementation does not support document type.");
  293. }
  294. public override void WriteEndAttribute ()
  295. {
  296. if (state != WriteState.Attribute)
  297. throw new InvalidOperationException("Token EndAttribute in state Start would result in an invalid XML document.");
  298. CheckState ();
  299. if (attr_value == null)
  300. attr_value = String.Empty;
  301. switch (save_target) {
  302. case SaveTarget.XmlLang:
  303. xml_lang = attr_value;
  304. goto default;
  305. case SaveTarget.XmlSpace:
  306. switch (attr_value) {
  307. case "preserve":
  308. xml_space = XmlSpace.Preserve;
  309. break;
  310. case "default":
  311. xml_space = XmlSpace.Default;
  312. break;
  313. default:
  314. throw new ArgumentException (String.Format ("Invalid xml:space value: '{0}'", attr_value));
  315. }
  316. goto default;
  317. case SaveTarget.Namespaces:
  318. if (current_attr_name.ToString ().Length > 0 && attr_value.Length == 0)
  319. throw new ArgumentException ("Cannot use prefix with an empty namespace.");
  320. AddNamespaceChecked (current_attr_name.ToString (), attr_value);
  321. break;
  322. default:
  323. if (!attr_typed_value)
  324. WriteTextBinary (attr_value);
  325. break;
  326. }
  327. if (current_attr_prefix.Length > 0 && save_target != SaveTarget.Namespaces)
  328. AddNamespaceChecked (current_attr_prefix, current_attr_ns);
  329. state = WriteState.Element;
  330. current_attr_prefix = null;
  331. current_attr_name = null;
  332. current_attr_ns = null;
  333. attr_value = null;
  334. attr_typed_value = false;
  335. }
  336. public override void WriteEndDocument ()
  337. {
  338. CloseOpenAttributeAndElements ();
  339. switch (state) {
  340. case WriteState.Start:
  341. throw new InvalidOperationException ("Document has not started.");
  342. case WriteState.Prolog:
  343. throw new ArgumentException ("This document does not have a root element.");
  344. }
  345. state = WriteState.Start;
  346. }
  347. bool SupportsCombinedEndElementSupport (byte operation)
  348. {
  349. switch (operation) {
  350. case BF.Comment:
  351. return false;
  352. }
  353. return true;
  354. }
  355. public override void WriteEndElement ()
  356. {
  357. if (element_count-- == 0)
  358. throw new InvalidOperationException("There was no XML start tag open.");
  359. if (state == WriteState.Attribute)
  360. WriteEndAttribute ();
  361. // Comment+EndElement does not exist
  362. bool needExplicitEndElement = buffer.Position == 0 || !SupportsCombinedEndElementSupport (buffer.GetBuffer () [0]);
  363. ProcessPendingBuffer (true, !needExplicitEndElement);
  364. CheckState ();
  365. AddMissingElementXmlns ();
  366. if (needExplicitEndElement)
  367. writer.Write (BF.EndElement);
  368. element_ns = element_ns_stack.Pop ();
  369. xml_lang = xml_lang_stack.Pop ();
  370. xml_space = xml_space_stack.Pop ();
  371. int cur = namespaces.Count;
  372. ns_index = ns_index_stack.Pop ();
  373. namespaces.RemoveRange (ns_index, cur - ns_index);
  374. open_start_element = false;
  375. Depth--;
  376. }
  377. public override void WriteEntityRef (string name)
  378. {
  379. throw new NotSupportedException ("This XmlWriter implementation does not support entity references.");
  380. }
  381. public override void WriteFullEndElement ()
  382. {
  383. WriteEndElement ();
  384. }
  385. public override void WriteProcessingInstruction (string name, string text)
  386. {
  387. if (name != "xml")
  388. throw new ArgumentException ("Processing instructions are not supported. ('xml' is allowed for XmlDeclaration; this is because of design problem of ECMA XmlWriter)");
  389. // Otherwise, silently ignored. WriteStartDocument()
  390. // is still callable after this method(!)
  391. }
  392. public override void WriteQualifiedName (XmlDictionaryString local, XmlDictionaryString ns)
  393. {
  394. string prefix = namespaces.LastOrDefault (i => i.Value.ToString () == ns.ToString ()).Key;
  395. bool use_dic = prefix != null;
  396. if (prefix == null)
  397. prefix = LookupPrefix (ns.Value);
  398. if (prefix == null)
  399. throw new ArgumentException (String.Format ("Namespace URI '{0}' is not bound to any of the prefixes", ns));
  400. ProcessTypedValue ();
  401. if (use_dic && prefix.Length == 1) {
  402. writer.Write (BF.QNameIndex);
  403. writer.Write ((byte) (prefix [0] - 'a'));
  404. WriteDictionaryIndex (local);
  405. } else {
  406. // QNameIndex is not applicable.
  407. WriteString (prefix);
  408. WriteString (":");
  409. WriteString (local);
  410. }
  411. }
  412. public override void WriteRaw (string data)
  413. {
  414. WriteString (data);
  415. }
  416. public override void WriteRaw (char[] buffer, int index, int count)
  417. {
  418. WriteChars (buffer, index, count);
  419. }
  420. void CheckStateForAttribute ()
  421. {
  422. CheckState ();
  423. if (state != WriteState.Element)
  424. throw new InvalidOperationException ("Token StartAttribute in state " + WriteState + " would result in an invalid XML document.");
  425. }
  426. string CreateNewPrefix ()
  427. {
  428. return CreateNewPrefix (String.Empty);
  429. }
  430. string CreateNewPrefix (string p)
  431. {
  432. for (char c = 'a'; c <= 'z'; c++)
  433. if (!namespaces.Any (iter => iter.Key == p + c))
  434. return p + c;
  435. for (char c = 'a'; c <= 'z'; c++) {
  436. var s = CreateNewPrefix (c.ToString ());
  437. if (s != null)
  438. return s;
  439. }
  440. throw new InvalidOperationException ("too many prefix population");
  441. }
  442. bool CollectionContains (ICollection col, string value)
  443. {
  444. foreach (string v in col)
  445. if (v == value)
  446. return true;
  447. return false;
  448. }
  449. void ProcessStartAttributeCommon (ref string prefix, string localName, string ns, object nameObj, object nsObj)
  450. {
  451. // dummy prefix is created here, while the caller
  452. // still uses empty string as the prefix there.
  453. if (prefix.Length == 0 && ns.Length > 0) {
  454. prefix = LookupPrefix (ns);
  455. // Not only null but also ""; when the returned
  456. // string is empty, then it still needs a dummy
  457. // prefix, since default namespace does not
  458. // apply to attribute
  459. if (String.IsNullOrEmpty (prefix))
  460. prefix = CreateNewPrefix ();
  461. }
  462. else if (prefix.Length > 0 && ns.Length == 0) {
  463. switch (prefix) {
  464. case "xml":
  465. nsObj = ns = XmlNamespace;
  466. break;
  467. case "xmlns":
  468. nsObj = ns = XmlnsNamespace;
  469. break;
  470. default:
  471. throw new ArgumentException ("Cannot use prefix with an empty namespace.");
  472. }
  473. }
  474. // here we omit such cases that it is used for writing
  475. // namespace-less xml, unlike XmlTextWriter.
  476. if (prefix == "xmlns" && ns != XmlnsNamespace)
  477. throw new ArgumentException (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
  478. CheckStateForAttribute ();
  479. state = WriteState.Attribute;
  480. save_target = SaveTarget.None;
  481. switch (prefix) {
  482. case "xml":
  483. // MS.NET looks to allow other names than
  484. // lang and space (e.g. xml:link, xml:hack).
  485. ns = XmlNamespace;
  486. switch (localName) {
  487. case "lang":
  488. save_target = SaveTarget.XmlLang;
  489. break;
  490. case "space":
  491. save_target = SaveTarget.XmlSpace;
  492. break;
  493. }
  494. break;
  495. case "xmlns":
  496. save_target = SaveTarget.Namespaces;
  497. break;
  498. }
  499. current_attr_prefix = prefix;
  500. current_attr_name = nameObj;
  501. current_attr_ns = nsObj;
  502. }
  503. public override void WriteStartAttribute (string prefix, string localName, string ns)
  504. {
  505. if (prefix == null)
  506. prefix = String.Empty;
  507. if (ns == null)
  508. ns = String.Empty;
  509. if (localName == "xmlns" && prefix.Length == 0) {
  510. prefix = "xmlns";
  511. localName = String.Empty;
  512. }
  513. ProcessStartAttributeCommon (ref prefix, localName, ns, localName, ns);
  514. // for namespace nodes we don't write attribute node here.
  515. if (save_target == SaveTarget.Namespaces)
  516. return;
  517. byte op = prefix.Length == 1 && 'a' <= prefix [0] && prefix [0] <= 'z' ?
  518. (byte) (prefix [0] - 'a' + BF.PrefixNAttrStringStart) :
  519. prefix.Length == 0 ? BF.AttrString :
  520. BF.AttrStringPrefix;
  521. if (BF.PrefixNAttrStringStart <= op && op <= BF.PrefixNAttrStringEnd) {
  522. writer.Write (op);
  523. writer.Write (localName);
  524. } else {
  525. writer.Write (op);
  526. if (prefix.Length > 0)
  527. writer.Write (prefix);
  528. writer.Write (localName);
  529. }
  530. }
  531. public override void WriteStartDocument ()
  532. {
  533. WriteStartDocument (false);
  534. }
  535. public override void WriteStartDocument (bool standalone)
  536. {
  537. if (state != WriteState.Start)
  538. throw new InvalidOperationException("WriteStartDocument should be the first call.");
  539. CheckState ();
  540. // write nothing to stream.
  541. state = WriteState.Prolog;
  542. }
  543. void PrepareStartElement ()
  544. {
  545. ProcessPendingBuffer (true, false);
  546. CheckState ();
  547. CloseStartElement ();
  548. Depth++;
  549. element_ns_stack.Push (element_ns);
  550. xml_lang_stack.Push (xml_lang);
  551. xml_space_stack.Push (xml_space);
  552. ns_index_stack.Push (ns_index);
  553. }
  554. public override void WriteStartElement (string prefix, string localName, string ns)
  555. {
  556. PrepareStartElement ();
  557. if ((prefix != null && prefix != String.Empty) && ((ns == null) || (ns == String.Empty)))
  558. throw new ArgumentException ("Cannot use a prefix with an empty namespace.");
  559. if (ns == null)
  560. ns = String.Empty;
  561. if (ns == String.Empty)
  562. prefix = String.Empty;
  563. if (prefix == null)
  564. prefix = String.Empty;
  565. byte op = prefix.Length == 1 && 'a' <= prefix [0] && prefix [0] <= 'z' ?
  566. (byte) (prefix [0] - 'a' + BF.PrefixNElemStringStart) :
  567. prefix.Length == 0 ? BF.ElemString :
  568. BF.ElemStringPrefix;
  569. if (BF.PrefixNElemStringStart <= op && op <= BF.PrefixNElemStringEnd) {
  570. writer.Write (op);
  571. writer.Write (localName);
  572. } else {
  573. writer.Write (op);
  574. if (prefix.Length > 0)
  575. writer.Write (prefix);
  576. writer.Write (localName);
  577. }
  578. OpenElement (prefix, ns);
  579. }
  580. void OpenElement (string prefix, object nsobj)
  581. {
  582. string ns = nsobj.ToString ();
  583. state = WriteState.Element;
  584. open_start_element = true;
  585. element_prefix = prefix;
  586. element_count++;
  587. element_ns = nsobj.ToString ();
  588. if (element_ns != String.Empty && LookupPrefix (element_ns) != prefix)
  589. AddNamespace (prefix, nsobj);
  590. }
  591. void AddNamespace (string prefix, object nsobj)
  592. {
  593. namespaces.Add (new KeyValuePair<string,object> (prefix, nsobj));
  594. }
  595. void CheckIfTextAllowed ()
  596. {
  597. switch (state) {
  598. case WriteState.Start:
  599. case WriteState.Prolog:
  600. throw new InvalidOperationException ("Token content in state Prolog would result in an invalid XML document.");
  601. }
  602. }
  603. public override void WriteString (string text)
  604. {
  605. if (text == null)
  606. throw new ArgumentNullException ("text");
  607. CheckIfTextAllowed ();
  608. if (text == null)
  609. text = String.Empty;
  610. ProcessStateForContent ();
  611. if (state == WriteState.Attribute)
  612. attr_value += text;
  613. else
  614. WriteTextBinary (text);
  615. }
  616. public override void WriteString (XmlDictionaryString text)
  617. {
  618. if (text == null)
  619. throw new ArgumentNullException ("text");
  620. CheckIfTextAllowed ();
  621. if (text == null)
  622. text = XmlDictionaryString.Empty;
  623. ProcessStateForContent ();
  624. if (state == WriteState.Attribute)
  625. attr_value += text.Value;
  626. else if (text.Equals (XmlDictionary.Empty))
  627. writer.Write (BF.EmptyText);
  628. else {
  629. writer.Write (BF.TextIndex);
  630. WriteDictionaryIndex (text);
  631. }
  632. }
  633. public override void WriteSurrogateCharEntity (char lowChar, char highChar)
  634. {
  635. WriteChars (new char [] {highChar, lowChar}, 0, 2);
  636. }
  637. public override void WriteWhitespace (string ws)
  638. {
  639. for (int i = 0; i < ws.Length; i++) {
  640. switch (ws [i]) {
  641. case ' ': case '\t': case '\r': case '\n':
  642. continue;
  643. default:
  644. throw new ArgumentException ("Invalid Whitespace");
  645. }
  646. }
  647. ProcessStateForContent ();
  648. WriteTextBinary (ws);
  649. }
  650. public override void WriteXmlnsAttribute (string prefix, string namespaceUri)
  651. {
  652. if (namespaceUri == null)
  653. throw new ArgumentNullException ("namespaceUri");
  654. if (String.IsNullOrEmpty (prefix))
  655. prefix = CreateNewPrefix ();
  656. CheckStateForAttribute ();
  657. AddNamespaceChecked (prefix, namespaceUri);
  658. state = WriteState.Element;
  659. }
  660. void AddNamespaceChecked (string prefix, object ns)
  661. {
  662. switch (ns.ToString ()) {
  663. case XmlnsNamespace:
  664. case XmlNamespace:
  665. return;
  666. }
  667. if (prefix == null)
  668. throw new InvalidOperationException ();
  669. var o = namespaces.LastOrDefault (i => i.Key == prefix);
  670. if (o.Key != null) { // i.e. exists
  671. if (o.Value.ToString () != ns.ToString ()) {
  672. if (namespaces.LastIndexOf (o) >= ns_index)
  673. throw new ArgumentException (String.Format ("The prefix '{0}' is already mapped to another namespace URI '{1}' in this element scope and cannot be mapped to '{2}'", prefix ?? "(null)", o.Value ?? "(null)", ns.ToString ()));
  674. else
  675. AddNamespace (prefix, ns);
  676. }
  677. }
  678. else
  679. AddNamespace (prefix, ns);
  680. }
  681. #region DictionaryString
  682. void WriteDictionaryIndex (XmlDictionaryString ds)
  683. {
  684. XmlDictionaryString ds2;
  685. bool isSession = false;
  686. int idx = ds.Key;
  687. if (ds.Dictionary != dict_ext) {
  688. isSession = true;
  689. if (dict_int.TryLookup (ds.Value, out ds2))
  690. ds = ds2;
  691. if (!session.TryLookup (ds, out idx))
  692. session.TryAdd (dict_int.Add (ds.Value), out idx);
  693. }
  694. if (idx >= 0x80) {
  695. writer.Write ((byte) (0x80 + ((idx % 0x80) << 1) + (isSession ? 1 : 0)));
  696. writer.Write ((byte) ((byte) (idx / 0x80) << 1));
  697. }
  698. else
  699. writer.Write ((byte) (((idx % 0x80) << 1) + (isSession ? 1 : 0)));
  700. }
  701. public override void WriteStartElement (string prefix, XmlDictionaryString localName, XmlDictionaryString namespaceUri)
  702. {
  703. PrepareStartElement ();
  704. if (prefix == null)
  705. prefix = String.Empty;
  706. byte op = prefix.Length == 1 && 'a' <= prefix [0] && prefix [0] <= 'z' ?
  707. (byte) (prefix [0] - 'a' + BF.PrefixNElemIndexStart) :
  708. prefix.Length == 0 ? BF.ElemIndex :
  709. BF.ElemIndexPrefix;
  710. if (BF.PrefixNElemIndexStart <= op && op <= BF.PrefixNElemIndexEnd) {
  711. writer.Write (op);
  712. WriteDictionaryIndex (localName);
  713. } else {
  714. writer.Write (op);
  715. if (prefix.Length > 0)
  716. writer.Write (prefix);
  717. WriteDictionaryIndex (localName);
  718. }
  719. OpenElement (prefix, namespaceUri);
  720. }
  721. public override void WriteStartAttribute (string prefix, XmlDictionaryString localName, XmlDictionaryString ns)
  722. {
  723. if (localName == null)
  724. throw new ArgumentNullException ("localName");
  725. if (prefix == null)
  726. prefix = String.Empty;
  727. if (ns == null)
  728. ns = XmlDictionaryString.Empty;
  729. if (localName.Value == "xmlns" && prefix.Length == 0) {
  730. prefix = "xmlns";
  731. localName = XmlDictionaryString.Empty;
  732. }
  733. ProcessStartAttributeCommon (ref prefix, localName.Value, ns.Value, localName, ns);
  734. if (save_target == SaveTarget.Namespaces)
  735. return;
  736. if (prefix.Length == 1 && 'a' <= prefix [0] && prefix [0] <= 'z') {
  737. writer.Write ((byte) (prefix [0] - 'a' + BF.PrefixNAttrIndexStart));
  738. WriteDictionaryIndex (localName);
  739. } else {
  740. byte op = ns.Value.Length == 0 ? BF.AttrIndex :BF.AttrIndexPrefix;
  741. // Write to Stream
  742. writer.Write (op);
  743. if (prefix.Length > 0)
  744. writer.Write (prefix);
  745. WriteDictionaryIndex (localName);
  746. }
  747. }
  748. public override void WriteXmlnsAttribute (string prefix, XmlDictionaryString namespaceUri)
  749. {
  750. if (namespaceUri == null)
  751. throw new ArgumentNullException ("namespaceUri");
  752. if (String.IsNullOrEmpty (prefix))
  753. prefix = CreateNewPrefix ();
  754. CheckStateForAttribute ();
  755. AddNamespaceChecked (prefix, namespaceUri);
  756. state = WriteState.Element;
  757. }
  758. #endregion
  759. #region WriteValue
  760. public override void WriteValue (bool value)
  761. {
  762. ProcessTypedValue ();
  763. writer.Write ((byte) (value ? BF.BoolTrue : BF.BoolFalse));
  764. }
  765. public override void WriteValue (int value)
  766. {
  767. WriteValue ((long) value);
  768. }
  769. public override void WriteValue (long value)
  770. {
  771. ProcessTypedValue ();
  772. if (value == 0)
  773. writer.Write (BF.Zero);
  774. else if (value == 1)
  775. writer.Write (BF.One);
  776. else if (value < 0 || value > uint.MaxValue) {
  777. writer.Write (BF.Int64);
  778. for (int i = 0; i < 8; i++) {
  779. writer.Write ((byte) (value & 0xFF));
  780. value >>= 8;
  781. }
  782. } else if (value <= byte.MaxValue) {
  783. writer.Write (BF.Int8);
  784. writer.Write ((byte) value);
  785. } else if (value <= short.MaxValue) {
  786. writer.Write (BF.Int16);
  787. writer.Write ((byte) (value & 0xFF));
  788. writer.Write ((byte) (value >> 8));
  789. } else if (value <= int.MaxValue) {
  790. writer.Write (BF.Int32);
  791. for (int i = 0; i < 4; i++) {
  792. writer.Write ((byte) (value & 0xFF));
  793. value >>= 8;
  794. }
  795. }
  796. }
  797. public override void WriteValue (float value)
  798. {
  799. ProcessTypedValue ();
  800. writer.Write (BF.Single);
  801. WriteValueContent (value);
  802. }
  803. void WriteValueContent (float value)
  804. {
  805. writer.Write (value);
  806. }
  807. public override void WriteValue (double value)
  808. {
  809. ProcessTypedValue ();
  810. writer.Write (BF.Double);
  811. WriteValueContent (value);
  812. }
  813. void WriteValueContent (double value)
  814. {
  815. writer.Write (value);
  816. }
  817. public override void WriteValue (decimal value)
  818. {
  819. ProcessTypedValue ();
  820. writer.Write (BF.Decimal);
  821. WriteValueContent (value);
  822. }
  823. void WriteValueContent (decimal value)
  824. {
  825. int [] bits = Decimal.GetBits (value);
  826. // so, looks like it is saved as its internal form,
  827. // not the returned order.
  828. // BinaryWriter.Write(Decimal) is useless here.
  829. writer.Write (bits [3]);
  830. writer.Write (bits [2]);
  831. writer.Write (bits [0]);
  832. writer.Write (bits [1]);
  833. }
  834. public override void WriteValue (DateTime value)
  835. {
  836. ProcessTypedValue ();
  837. writer.Write (BF.DateTime);
  838. WriteValueContent (value);
  839. }
  840. void WriteValueContent (DateTime value)
  841. {
  842. writer.Write (value.ToBinary ());
  843. }
  844. public override void WriteValue (Guid value)
  845. {
  846. ProcessTypedValue ();
  847. writer.Write (BF.Guid);
  848. WriteValueContent (value);
  849. }
  850. void WriteValueContent (Guid value)
  851. {
  852. byte [] bytes = value.ToByteArray ();
  853. writer.Write (bytes, 0, bytes.Length);
  854. }
  855. public override void WriteValue (UniqueId value)
  856. {
  857. if (value == null)
  858. throw new ArgumentNullException ("value");
  859. Guid guid;
  860. if (value.TryGetGuid (out guid)) {
  861. // this conditional branching is required for
  862. // attr_typed_value not being true.
  863. ProcessTypedValue ();
  864. writer.Write (BF.UniqueId);
  865. byte [] bytes = guid.ToByteArray ();
  866. writer.Write (bytes, 0, bytes.Length);
  867. } else {
  868. WriteValue (value.ToString ());
  869. }
  870. }
  871. public override void WriteValue (TimeSpan value)
  872. {
  873. ProcessTypedValue ();
  874. writer.Write (BF.TimeSpan);
  875. WriteValueContent (value);
  876. }
  877. void WriteValueContent (TimeSpan value)
  878. {
  879. WriteBigEndian (value.Ticks, 8);
  880. }
  881. #endregion
  882. private void WriteBigEndian (long value, int digits)
  883. {
  884. long v = 0;
  885. for (int i = 0; i < digits; i++) {
  886. v = (v << 8) + (value & 0xFF);
  887. value >>= 8;
  888. }
  889. for (int i = 0; i < digits; i++) {
  890. writer.Write ((byte) (v & 0xFF));
  891. v >>= 8;
  892. }
  893. }
  894. private void WriteTextBinary (string text)
  895. {
  896. if (text.Length == 0)
  897. writer.Write (BF.EmptyText);
  898. else {
  899. char [] arr = text.ToCharArray ();
  900. WriteChars (arr, 0, arr.Length);
  901. }
  902. }
  903. #endregion
  904. #region Write typed array content
  905. // they are packed in WriteValue(), so we cannot reuse
  906. // them for array content.
  907. void WriteValueContent (bool value)
  908. {
  909. writer.Write (value ? (byte) 1 : (byte) 0);
  910. }
  911. void WriteValueContent (short value)
  912. {
  913. writer.Write (value);
  914. }
  915. void WriteValueContent (int value)
  916. {
  917. writer.Write (value);
  918. }
  919. void WriteValueContent (long value)
  920. {
  921. writer.Write (value);
  922. }
  923. #endregion
  924. }
  925. }