//
// (C)2003 Atsushi Enomoto
//
-using System;\r
-using System.IO;\r
-using System.Text;\r
-using System.Xml;\r
-\r
-namespace Mono.Xml.Native\r
-{\r
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace System.Xml
+{
#region XmlStreamReader
- public class XmlStreamReader : StreamReader
+ internal class XmlStreamReader : StreamReader
{
- public XmlStreamReader (XmlInputStream input)
+ XmlInputStream input;
+
+ XmlStreamReader (XmlInputStream input)
: base (input, input.ActualEncoding != null ? input.ActualEncoding : Encoding.UTF8)
{
+ this.input = input;
}
public XmlStreamReader (Stream input)
{
}
- public XmlStreamReader (string url)
- : this (new XmlInputStream (url))
+ public override void Close ()
+ {
+ this.input.Close ();
+ }
+
+ protected override void Dispose (bool disposing)
{
+ base.Dispose (disposing);
+ if (disposing) {
+ Close ();
+ }
}
+
}
#endregion
-\r
- public class XmlInputStream : Stream\r
- {\r
- Encoding enc;\r
- Stream stream;\r
- byte[] buffer = new byte[256];\r
- int bufLength;\r
- int bufPos;\r
-\r
- static XmlException encodingException = new XmlException ("invalid encoding specification.");\r
-\r
- public XmlInputStream (string url)\r
- {\r
-#if NetworkEnabled\r
- try {\r
- Uri uri = new Uri (url);\r
- Initialize (new MemoryStream (new System.Net.WebClient ().DownloadData (url)));\r
- } catch (UriFormatException ex) {\r
- Initialize (new FileStream (url, FileMode.Open));\r
- }\r
-#else\r
- Initialize (new FileStream (url, FileMode.Open));\r
-#endif\r
- }\r
-\r
- public XmlInputStream (Stream stream)\r
- {\r
- Initialize (stream);\r
- }\r
-\r
- private void Initialize (Stream stream)\r
- {\r
- // FIXME: seems too waste...\r
- MemoryStream ms = new MemoryStream ();\r
- this.stream = stream;\r
- int c = stream.ReadByte ();\r
- switch (c) {\r
- case 0xFF:\r
- c = stream.ReadByte ();\r
- if (c == 0xFE) {\r
- // BOM-ed little endian utf-16\r
- enc = Encoding.Unicode;\r
- } else {\r
- // It doesn't start from "<?xml" then its encoding is utf-8\r
- enc = Encoding.UTF8;\r
- ms.WriteByte ((byte)0xFF);\r
- ms.WriteByte ((byte)c);\r
- }\r
- break;\r
- case 0xFE:\r
- c = stream.ReadByte ();\r
- if (c == 0xFF) {\r
- // BOM-ed big endian utf-16\r
- enc = Encoding.BigEndianUnicode;\r
- return;\r
- } else {\r
- // It doesn't start from "<?xml" then its encoding is utf-8\r
- enc = Encoding.UTF8;\r
- ms.WriteByte ((byte)0xFE);\r
- ms.WriteByte ((byte)c);\r
- }\r
- break;\r
- case 0xEF:\r
- enc = Encoding.UTF8;\r
- c = ReadByte ();\r
- if (c == 0xBB) {\r
- c = ReadByte ();\r
- if (c != 0xBF) {\r
- ms.WriteByte ((byte)0xEF);\r
- ms.WriteByte ((byte)0xBB);\r
- ms.WriteByte ((byte)c);\r
- }\r
- } else {\r
- ms.WriteByte ((byte)0xEF);\r
- }\r
- break;\r
- case '<':\r
- // try to get encoding name from XMLDecl.\r
- ms.WriteByte ((byte)'<');\r
- int size = stream.Read (buffer, 1, 4);\r
- ms.Write (buffer, 1, 4);\r
- if (Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {\r
- int loop = 0;\r
- c = SkipWhitespace (ms);\r
- // version\r
- if (c != 'v' || stream.ReadByte () != 'e')\r
- throw new XmlException ("invalid xml declaration.");\r
- ms.WriteByte ((byte)'v');\r
- ms.WriteByte ((byte)'e');\r
- while (loop++ >= 0) {\r
- c = stream.ReadByte ();\r
- ms.WriteByte ((byte)c);\r
- if (c == '0') {\r
- ms.WriteByte ((byte)stream.ReadByte ());\r
- break;\r
- }\r
- }\r
- c = SkipWhitespace (ms);\r
- if (c == 'e') {\r
- ms.WriteByte ((byte)'e');\r
- size = stream.Read (buffer, 0, 7);\r
- ms.Write (buffer, 0, 7);\r
- if (Encoding.ASCII.GetString(buffer, 0, 7) == "ncoding") {\r
- c = this.SkipWhitespace(ms);\r
- if (c != '=')\r
- throw encodingException;\r
- ms.WriteByte ((byte)'=');\r
- c = this.SkipWhitespace (ms);\r
- int quoteChar = c;\r
- ms.WriteByte ((byte)c);\r
- int start = (int)ms.Position;\r
- while (loop++ >= 0) {\r
- c = stream.ReadByte ();\r
- if (c == quoteChar)\r
- break;\r
- else if (c < 0)\r
- throw encodingException;\r
- ms.WriteByte ((byte)c);\r
- }\r
- string encodingName = Encoding.UTF8.GetString (ms.GetBuffer (), start, (int)ms.Position - start);\r
- if (!XmlConstructs.IsValidIANAEncoding (encodingName))\r
- throw encodingException;\r
- ms.WriteByte ((byte)quoteChar);\r
- enc = Encoding.GetEncoding (encodingName);\r
- }\r
- else\r
- ms.Write (buffer, 0, size);\r
- }\r
- else\r
- ms.WriteByte ((byte)c);\r
- }\r
- buffer = ms.ToArray ();\r
- bufLength = buffer.Length;\r
- bufPos = 0;\r
- break;\r
- default:\r
- buffer [0] = (byte)c;\r
- bufLength = 1;\r
- enc = Encoding.UTF8;\r
- break;\r
- }\r
- }\r
-\r
- // skips whitespace and returns misc char that was read from stream\r
- private int SkipWhitespace (MemoryStream ms) // ms may be null\r
- {\r
- int loop = 0;\r
- int c;\r
- while (loop++ >= 0) { // defends infinite loop (expecting overflow)\r
- c = stream.ReadByte ();\r
- switch (c) {\r
- case '\r': goto case ' ';\r
- case '\n': goto case ' ';\r
- case '\t': goto case ' ';\r
- case ' ':\r
- if (ms != null)\r
- ms.WriteByte ((byte)c);\r
- continue;\r
- default:\r
- return c;\r
- }\r
- }\r
- throw new InvalidOperationException ();\r
- }\r
-\r
- public Encoding ActualEncoding {\r
- get { return enc; }\r
- }\r
-\r
- #region Public Overrides\r
- public override bool CanRead {\r
- get { return stream.CanRead; }\r
- }\r
-\r
- public override bool CanSeek {\r
- get { return false; } //stream.CanSeek; }\r
- }\r
-\r
- public override bool CanWrite {\r
- get { return false; }\r
- }\r
-\r
- public override long Length {\r
- get {\r
- return stream.Length;\r
- }\r
- }\r
-\r
- public override long Position {\r
- get {\r
- return stream.Position + bufLength;\r
- }\r
- set {\r
- if(value < bufLength)\r
- bufPos = (int)value;\r
- else\r
- stream.Position = value - bufLength;\r
- }\r
- }\r
-\r
- public override void Flush()\r
- {\r
- stream.Flush ();\r
- }\r
-\r
- public override int Read (byte[] buffer, int offset, int count)\r
- {\r
- int ret;\r
- if (count <= bufLength - bufPos) { // all from buffer\r
- Array.Copy (this.buffer, bufPos, buffer, offset, count);\r
- bufPos += count;\r
- ret = count;\r
- } else {\r
- int bufRest = bufLength - bufPos;\r
- if (bufLength > bufPos) {\r
- Array.Copy (this.buffer, bufPos, buffer, offset, bufRest);\r
- bufPos += bufRest;\r
- }\r
- ret = bufRest +\r
- stream.Read (buffer, offset + bufRest, count - bufRest);\r
- }\r
- return ret;\r
- }\r
-\r
- public override int ReadByte ()\r
- {\r
- if (bufLength > bufPos) {\r
- return buffer [bufPos++];\r
- }\r
- return stream.ReadByte ();\r
- }\r
-\r
- public override long Seek (long offset, System.IO.SeekOrigin origin)\r
- {\r
- int bufRest = bufLength - bufPos;\r
- if (origin == SeekOrigin.Current)\r
- if (offset < bufRest)\r
- return buffer [bufPos + offset];\r
- else\r
- return stream.Seek (offset - bufRest, origin);\r
- else\r
- return stream.Seek (offset, origin);\r
- }\r
-\r
- public override void SetLength (long value)\r
- {\r
- stream.SetLength (value);\r
- }\r
-\r
- public override void Write (byte[] buffer, int offset, int count)\r
- {\r
- throw new NotSupportedException ();\r
- }\r
- #endregion\r
- }\r
-}\r
+
+ class XmlInputStream : Stream
+ {
+ Encoding enc;
+ Stream stream;
+ byte[] buffer;
+ int bufLength;
+ int bufPos;
+
+ static XmlException encodingException = new XmlException ("invalid encoding specification.");
+
+ public XmlInputStream (Stream stream)
+ {
+ Initialize (stream);
+ }
+
+ private void Initialize (Stream stream)
+ {
+ buffer = new byte [64];
+ this.stream = stream;
+ enc = Encoding.UTF8; // Default to UTF8 if we can't guess it
+ bufLength = stream.Read (buffer, 0, buffer.Length);
+ if (bufLength == -1 || bufLength == 0) {
+ return;
+ }
+
+ int c = ReadByteSpecial ();
+ switch (c) {
+ case 0xFF:
+ c = ReadByteSpecial ();
+ if (c == 0xFE) {
+ // BOM-ed little endian utf-16
+ enc = Encoding.Unicode;
+ } else {
+ // It doesn't start from "<?xml" then its encoding is utf-8
+ bufPos = 0;
+ }
+ break;
+ case 0xFE:
+ c = ReadByteSpecial ();
+ if (c == 0xFF) {
+ // BOM-ed big endian utf-16
+ enc = Encoding.BigEndianUnicode;
+ return;
+ } else {
+ // It doesn't start from "<?xml" then its encoding is utf-8
+ bufPos = 0;
+ }
+ break;
+ case 0xEF:
+ c = ReadByteSpecial ();
+ if (c == 0xBB) {
+ c = ReadByteSpecial ();
+ if (c != 0xBF) {
+ bufPos = 0;
+ }
+ } else {
+ buffer [--bufPos] = 0xEF;
+ }
+ break;
+ case '<':
+ // try to get encoding name from XMLDecl.
+ if (bufLength >= 5 && Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {
+ bufPos += 4;
+ c = SkipWhitespace ();
+
+ // version. It is optional here.
+ if (c == 'v') {
+ while (c >= 0) {
+ c = ReadByteSpecial ();
+ if (c == '0') { // 0 of 1.0
+ ReadByteSpecial ();
+ break;
+ }
+ }
+ c = SkipWhitespace ();
+ }
+
+ if (c == 'e') {
+ int remaining = bufLength - bufPos;
+ if (remaining >= 7 && Encoding.ASCII.GetString(buffer, bufPos, 7) == "ncoding") {
+ bufPos += 7;
+ c = SkipWhitespace();
+ if (c != '=')
+ throw encodingException;
+ c = SkipWhitespace ();
+ int quoteChar = c;
+ StringBuilder sb = new StringBuilder ();
+ while (true) {
+ c = ReadByteSpecial ();
+ if (c == quoteChar)
+ break;
+ else if (c < 0)
+ throw encodingException;
+
+ sb.Append ((char) c);
+ }
+ string encodingName = sb.ToString ();
+ if (!XmlChar.IsValidIANAEncoding (encodingName))
+ throw encodingException;
+ enc = Encoding.GetEncoding (encodingName);
+ }
+ }
+ }
+ bufPos = 0;
+ break;
+ default:
+ bufPos = 0;
+ break;
+ }
+ }
+
+ // Just like readbyte, but grows the buffer too.
+ int ReadByteSpecial ()
+ {
+ if (bufLength > bufPos)
+ return buffer [bufPos++];
+
+ byte [] newbuf = new byte [buffer.Length * 2];
+ Buffer.BlockCopy (buffer, 0, newbuf, 0, bufLength);
+ int nbytes = stream.Read (newbuf, bufLength, buffer.Length);
+ if (nbytes == -1 || nbytes == 0)
+ return -1;
+
+ bufLength += nbytes;
+ buffer = newbuf;
+ return buffer [bufPos++];
+ }
+
+ // skips whitespace and returns misc char that was read from stream
+ private int SkipWhitespace ()
+ {
+ int c;
+ while (true) {
+ c = ReadByteSpecial ();
+ switch ((char) c) {
+ case '\r': goto case ' ';
+ case '\n': goto case ' ';
+ case '\t': goto case ' ';
+ case ' ':
+ continue;
+ default:
+ return c;
+ }
+ }
+ throw new InvalidOperationException ();
+ }
+
+ public Encoding ActualEncoding {
+ get { return enc; }
+ }
+
+ #region Public Overrides
+ public override bool CanRead {
+ get {
+ if (bufLength > bufPos)
+ return true;
+ else
+ return stream.CanRead;
+ }
+ }
+
+ // FIXME: It should support base stream's CanSeek.
+ public override bool CanSeek {
+ get { return false; } // stream.CanSeek; }
+ }
+
+ public override bool CanWrite {
+ get { return false; }
+ }
+
+ public override long Length {
+ get {
+ return stream.Length;
+ }
+ }
+
+ public override long Position {
+ get {
+ return stream.Position - bufLength + bufPos;
+ }
+ set {
+ if(value < bufLength)
+ bufPos = (int)value;
+ else
+ stream.Position = value - bufLength;
+ }
+ }
+
+ public override void Close ()
+ {
+ stream.Close ();
+ }
+
+ public override void Flush ()
+ {
+ stream.Flush ();
+ }
+
+ public override int Read (byte[] buffer, int offset, int count)
+ {
+ int ret;
+ if (count <= bufLength - bufPos) { // all from buffer
+ Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, count);
+ bufPos += count;
+ ret = count;
+ } else {
+ int bufRest = bufLength - bufPos;
+ if (bufLength > bufPos) {
+ Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, bufRest);
+ bufPos += bufRest;
+ }
+ ret = bufRest +
+ stream.Read (buffer, offset + bufRest, count - bufRest);
+ }
+ return ret;
+ }
+
+ public override int ReadByte ()
+ {
+ if (bufLength > bufPos) {
+ return buffer [bufPos++];
+ }
+ return stream.ReadByte ();
+ }
+
+ public override long Seek (long offset, System.IO.SeekOrigin origin)
+ {
+ int bufRest = bufLength - bufPos;
+ if (origin == SeekOrigin.Current)
+ if (offset < bufRest)
+ return buffer [bufPos + offset];
+ else
+ return stream.Seek (offset - bufRest, origin);
+ else
+ return stream.Seek (offset, origin);
+ }
+
+ public override void SetLength (long value)
+ {
+ stream.SetLength (value);
+ }
+
+ public override void Write (byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException ();
+ }
+ #endregion
+ }
+}