2 // System.Xml.XmlInputStream
3 // encoding-specification-wise XML input stream and reader
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
8 // (C)2003 Atsushi Enomoto
15 namespace Mono.Xml.Native
\r
17 #region XmlStreamReader
18 public class XmlStreamReader : StreamReader
22 XmlStreamReader (XmlInputStream input)
23 : base (input, input.ActualEncoding != null ? input.ActualEncoding : Encoding.UTF8)
28 public XmlStreamReader (Stream input)
29 : this (new XmlInputStream (input, true))
33 public XmlStreamReader (Stream input, bool docent)
34 : this (new XmlInputStream (input, docent))
38 public XmlStreamReader (string url)
43 public XmlStreamReader (string url, bool docent)
44 : this (new XmlInputStream (url, docent))
48 public override void Close ()
53 protected override void Dispose (bool disposing)
55 base.Dispose (disposing);
63 class XmlInputStream : Stream
\r
67 byte[] buffer = new byte[256];
\r
70 bool isDocumentEntity; // allow omitting "version" or not.
\r
72 static XmlException encodingException = new XmlException ("invalid encoding specification.");
\r
74 public XmlInputStream (string url)
\r
79 public XmlInputStream (string url, bool docent)
\r
81 this.isDocumentEntity = docent;
\r
84 Uri uri = new Uri (url);
\r
85 Initialize (new MemoryStream (new System.Net.WebClient ().DownloadData (url)));
\r
86 } catch (UriFormatException ex) {
\r
87 Initialize (new FileStream (url, FileMode.Open, FileAccess.Read));
\r
90 Initialize (new FileStream (url, FileMode.Open, FileAccess.Read));
\r
94 public XmlInputStream (Stream stream)
\r
95 : this (stream, true)
\r
99 public XmlInputStream (Stream stream, bool docent)
\r
101 this.isDocumentEntity = docent;
\r
102 Initialize (stream);
\r
105 private void Initialize (Stream stream)
\r
107 // FIXME: seems too waste...
\r
108 MemoryStream ms = new MemoryStream ();
\r
109 this.stream = stream;
\r
110 int c = stream.ReadByte ();
\r
113 c = stream.ReadByte ();
\r
115 // BOM-ed little endian utf-16
\r
116 enc = Encoding.Unicode;
\r
118 // It doesn't start from "<?xml" then its encoding is utf-8
\r
119 enc = Encoding.UTF8;
\r
120 ms.WriteByte ((byte)0xFF);
\r
121 ms.WriteByte ((byte)c);
\r
125 c = stream.ReadByte ();
\r
127 // BOM-ed big endian utf-16
\r
128 enc = Encoding.BigEndianUnicode;
\r
131 // It doesn't start from "<?xml" then its encoding is utf-8
\r
132 enc = Encoding.UTF8;
\r
133 ms.WriteByte ((byte)0xFE);
\r
134 ms.WriteByte ((byte)c);
\r
138 enc = Encoding.UTF8;
\r
143 ms.WriteByte ((byte)0xEF);
\r
144 ms.WriteByte ((byte)0xBB);
\r
145 ms.WriteByte ((byte)c);
\r
148 ms.WriteByte ((byte)0xEF);
\r
152 // try to get encoding name from XMLDecl.
\r
153 ms.WriteByte ((byte)'<');
\r
154 int size = stream.Read (buffer, 1, 4);
\r
155 ms.Write (buffer, 1, 4);
\r
156 if (Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {
\r
158 c = SkipWhitespace (ms);
\r
160 // version. It is optional here.
\r
162 if (isDocumentEntity)
\r
163 throw new XmlException ("invalid xml declaration.");
\r
165 ms.WriteByte ((byte)'v');
\r
166 while (loop++ >= 0 && c >= 0) {
\r
167 c = stream.ReadByte ();
\r
168 ms.WriteByte ((byte)c);
\r
169 if (c == '0') { // 0 of 1.0
\r
170 ms.WriteByte ((byte)stream.ReadByte ());
\r
174 c = SkipWhitespace (ms);
\r
178 ms.WriteByte ((byte)'e');
\r
179 size = stream.Read (buffer, 0, 7);
\r
180 ms.Write (buffer, 0, 7);
\r
181 if (Encoding.ASCII.GetString(buffer, 0, 7) == "ncoding") {
\r
182 c = this.SkipWhitespace(ms);
\r
184 throw encodingException;
\r
185 ms.WriteByte ((byte)'=');
\r
186 c = this.SkipWhitespace (ms);
\r
188 ms.WriteByte ((byte)c);
\r
189 int start = (int)ms.Position;
\r
190 while (loop++ >= 0) {
\r
191 c = stream.ReadByte ();
\r
192 if (c == quoteChar)
\r
195 throw encodingException;
\r
196 ms.WriteByte ((byte)c);
\r
198 string encodingName = Encoding.UTF8.GetString (ms.GetBuffer (), start, (int)ms.Position - start);
\r
199 if (!XmlConstructs.IsValidIANAEncoding (encodingName))
\r
200 throw encodingException;
\r
201 ms.WriteByte ((byte)quoteChar);
\r
202 enc = Encoding.GetEncoding (encodingName);
\r
205 ms.Write (buffer, 0, size);
\r
208 ms.WriteByte ((byte)c);
\r
210 buffer = ms.ToArray ();
\r
211 bufLength = buffer.Length;
\r
215 buffer [0] = (byte)c;
\r
217 enc = Encoding.UTF8;
\r
222 // skips whitespace and returns misc char that was read from stream
\r
223 private int SkipWhitespace (MemoryStream ms) // ms may be null
\r
227 while (loop++ >= 0) { // defends infinite loop (expecting overflow)
\r
228 c = stream.ReadByte ();
\r
230 case '\r': goto case ' ';
\r
231 case '\n': goto case ' ';
\r
232 case '\t': goto case ' ';
\r
235 ms.WriteByte ((byte)c);
\r
241 throw new InvalidOperationException ();
\r
244 public Encoding ActualEncoding {
\r
245 get { return enc; }
\r
248 #region Public Overrides
\r
249 public override bool CanRead {
\r
250 get { return stream.CanRead; }
\r
253 public override bool CanSeek {
\r
254 get { return false; } //stream.CanSeek; }
\r
257 public override bool CanWrite {
\r
258 get { return false; }
\r
261 public override long Length {
\r
263 return stream.Length;
\r
267 public override long Position {
\r
269 return stream.Position + bufLength;
\r
272 if(value < bufLength)
\r
273 bufPos = (int)value;
\r
275 stream.Position = value - bufLength;
\r
279 public override void Close ()
\r
284 public override void Flush ()
\r
289 public override int Read (byte[] buffer, int offset, int count)
\r
292 if (count <= bufLength - bufPos) { // all from buffer
\r
293 Array.Copy (this.buffer, bufPos, buffer, offset, count);
\r
297 int bufRest = bufLength - bufPos;
\r
298 if (bufLength > bufPos) {
\r
299 Array.Copy (this.buffer, bufPos, buffer, offset, bufRest);
\r
303 stream.Read (buffer, offset + bufRest, count - bufRest);
\r
308 public override int ReadByte ()
\r
310 if (bufLength > bufPos) {
\r
311 return buffer [bufPos++];
\r
313 return stream.ReadByte ();
\r
316 public override long Seek (long offset, System.IO.SeekOrigin origin)
\r
318 int bufRest = bufLength - bufPos;
\r
319 if (origin == SeekOrigin.Current)
\r
320 if (offset < bufRest)
\r
321 return buffer [bufPos + offset];
\r
323 return stream.Seek (offset - bufRest, origin);
\r
325 return stream.Seek (offset, origin);
\r
328 public override void SetLength (long value)
\r
330 stream.SetLength (value);
\r
333 public override void Write (byte[] buffer, int offset, int count)
\r
335 throw new NotSupportedException ();
\r