// Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
//
// (C) 2001, 2002 Jason Diamond http://injektilo.org/
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+//#define USE_NAME_BUFFER
+
+//
+// Optimization TODOs:
+//
+// - support PushbackChar() which reverts one character read.
+// - ReadTextReader() should always keep one pushback buffer
+// as pushback safety net.
+// - Replace (peek,read) * n -> read * n + pushback
+//
using System;
using System.Collections;
public XmlTextReader (string url, XmlNameTable nt)
{
- Uri uri = resolver.ResolveUri (null, url);
- string uriString = uri != null ? uri.ToString () : String.Empty;
- Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
+ string uriString;
+ Stream stream = GetStreamFromUrl (url, out uriString);
XmlParserContext ctx = new XmlParserContext (nt,
new XmlNamespaceManager (nt),
String.Empty,
XmlSpace.None);
- this.InitializeContext (uriString, ctx, new XmlStreamReader (s), XmlNodeType.Document);
+ this.InitializeContext (uriString, ctx, new XmlStreamReader (stream), XmlNodeType.Document);
}
public XmlTextReader (TextReader input, XmlNameTable nt)
{
}
+ // This is used in XmlReader.Create() to indicate that string
+ // argument is uri, not an xml fragment.
+ internal XmlTextReader (bool dummy, XmlResolver resolver, string url, XmlNodeType fragType, XmlParserContext context)
+ {
+ if (resolver == null)
+ resolver = new XmlUrlResolver ();
+
+ this.XmlResolver = resolver;
+ string uriString;
+ Stream stream = GetStreamFromUrl (url, out uriString);
+ this.InitializeContext (uriString, context, new XmlStreamReader (stream), fragType);
+ }
+
public XmlTextReader (Stream xmlFragment, XmlNodeType fragType, XmlParserContext context)
: this (context != null ? context.BaseURI : String.Empty,
new XmlStreamReader (xmlFragment),
{
}
- XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
+ internal XmlTextReader (string url, TextReader fragment, XmlNodeType fragType, XmlParserContext context)
{
InitializeContext (url, context, fragment, fragType);
}
+ private Stream GetStreamFromUrl (string url, out string absoluteUriString)
+ {
+ Uri uri = resolver.ResolveUri (null, url);
+ absoluteUriString = uri != null ? uri.ToString () : String.Empty;
+ return resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
+ }
+
#endregion
#region Properties
{
return parserContext.NamespaceManager.GetNamespacesInScope (scope);
}
+
+ IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
+ {
+ return GetNamespacesInScope (scope);
+ }
#endif
public TextReader GetRemainder ()
{
- if (peekCharsIndex == peekCharsLength)
+ if (peekCharsLength < 0)
return reader;
return new StringReader (new string (peekChars, peekCharsIndex, peekCharsLength - peekCharsIndex) + reader.ReadToEnd ());
}
return LookupNamespace (prefix, false);
}
-#if NET_2_0
- public override string LookupNamespace (string prefix, bool atomizedName)
-#else
- internal override string LookupNamespace (string prefix, bool atomizedName)
-#endif
+ private string LookupNamespace (string prefix, bool atomizedNames)
{
- return parserContext.NamespaceManager.LookupNamespace (prefix, atomizedName);
+ string s = parserContext.NamespaceManager.LookupNamespace (
+ prefix, atomizedNames);
+ return s == String.Empty ? null : s;
}
#if NET_2_0
public override bool Read ()
{
+ curNodePeekIndex = peekCharsIndex;
+ preserveCurrentTag = true;
+
if (startNodeType == XmlNodeType.Attribute) {
if (currentAttribute == 0)
return false; // already read.
+ SkipTextDeclaration ();
ClearAttributes ();
IncrementAttributeToken ();
ReadAttributeValueTokens ('"');
readState = ReadState.Interactive;
return true;
}
+ if (readState == ReadState.Initial && currentState == XmlNodeType.Element)
+ SkipTextDeclaration ();
if (Binary != null)
Binary.Reset ();
return ReadCharsInternal (buffer, offset, length);
}
-#if NET_1_0
- public override string ReadInnerXml ()
- {
- return ReadInnerXmlInternal ();
- }
-
- public override string ReadOuterXml ()
- {
- return ReadOuterXmlInternal ();
- }
-
- public override string ReadString ()
- {
- return ReadStringInternal ();
- }
-#endif
-
public void ResetState ()
{
throw new InvalidOperationException ("Cannot call ResetState when parsing an XML fragment.");
}
#if NET_2_0
- [MonoTODO ("Implement for performance reason")]
+ [MonoTODO] // FIXME: Implement, for performance improvement
public override void Skip ()
{
base.Skip ();
#region Internals
// Parsed DTD Objects
+ // Note that thgis property must be kept since dtd2xsd uses it.
internal DTDObjectModel DTD {
get { return parserContext.Dtd; }
}
private bool popScope;
- private string [] elementNames;
+ struct TagName
+ {
+ public TagName (string n, string l, string p)
+ {
+ Name = n;
+ LocalName = l;
+ Prefix = p;
+ }
+
+ public readonly string Name;
+ public readonly string LocalName;
+ public readonly string Prefix;
+ }
+
+ private TagName [] elementNames;
int elementNameStackPos;
private bool allowMultipleRoot;
private bool returnEntityReference;
private string entityReferenceName;
+#if USE_NAME_BUFFER
private char [] nameBuffer;
private int nameLength;
private int nameCapacity;
private const int initialNameCapacity = 32;
+#endif
private StringBuilder valueBuffer;
- private char [] currentTagBuffer;
- private int currentTagLength;
- private int currentTagCapacity;
- private const int initialCurrentTagCapacity = 256;
-
private TextReader reader;
private char [] peekChars;
private int peekCharsIndex;
private int peekCharsLength;
+ private int curNodePeekIndex;
+ private bool preserveCurrentTag;
private const int peekCharCapacity = 1024;
private int line;
private bool closeInput = true;
private EntityHandling entityHandling; // 2.0
+ private NameTable whitespacePool;
+ private char [] whitespaceCache;
+
private XmlException NotWFError (string message)
{
return new XmlException (this as IXmlLineInfo, BaseURI, message);
depthUp = false;
popScope = allowMultipleRoot = false;
- elementNames = new string [10];
+ elementNames = new TagName [10];
elementNameStackPos = 0;
isStandalone = false;
returnEntityReference = false;
entityReferenceName = String.Empty;
+#if USE_NAME_BUFFER
nameBuffer = new char [initialNameCapacity];
nameLength = 0;
nameCapacity = initialNameCapacity;
+#endif
valueBuffer = new StringBuilder ();
- currentTagBuffer = new char [initialCurrentTagCapacity];
- currentTagLength = 0;
- currentTagCapacity = initialCurrentTagCapacity;
-
peekCharsIndex = 0;
- peekCharsLength = 0;
if (peekChars == null)
peekChars = new char [peekCharCapacity];
+ peekCharsLength = -1;
+ curNodePeekIndex = -1; // read from start
line = 1;
column = 1;
switch (fragType) {
case XmlNodeType.Attribute:
reader = new StringReader (fragment.ReadToEnd ().Replace ("\"", """));
- SkipTextDeclaration ();
break;
case XmlNodeType.Element:
currentState = XmlNodeType.Element;
allowMultipleRoot = true;
- SkipTextDeclaration ();
break;
case XmlNodeType.Document:
break;
}
#if NET_2_0
- [MonoTODO ("Test")]
internal ConformanceLevel Conformance {
+ get { return allowMultipleRoot ? ConformanceLevel.Fragment : ConformanceLevel.Document; }
set {
if (value == ConformanceLevel.Fragment) {
currentState = XmlNodeType.Element;
private void ClearAttributes ()
{
- for (int i = 0; i < attributeCount; i++)
- attributeTokens [i].Clear ();
+ //for (int i = 0; i < attributeCount; i++)
+ // attributeTokens [i].Clear ();
attributeCount = 0;
currentAttribute = -1;
currentAttributeValue = -1;
}
- private int PeekChar ()
+ private int PeekSurrogate (int c)
{
- if (peekCharsLength == peekCharsIndex) {
- if (!ReadTextReader ())
- return -1;
- return PeekChar ();
- }
-
- char c = peekChars [peekCharsIndex];
- if (c == 0)
- return -1;
- if (!char.IsSurrogate (c))
- return c;
- if (peekCharsLength == peekCharsIndex+1) {
- if (!ReadTextReader ())
+ if (peekCharsLength <= peekCharsIndex + 1) {
+ if (!ReadTextReader (c))
//FIXME: copy MS.NET behaviour when unpaired surrogate found
- return peekChars [peekCharsIndex];
- return PeekChar ();
+ return c;
}
-
- char highhalfChar = peekChars [peekCharsIndex];
- char lowhalfChar = peekChars [peekCharsIndex+1];
+
+ int highhalfChar = peekChars [peekCharsIndex];
+ int lowhalfChar = peekChars [peekCharsIndex+1];
if (((highhalfChar & 0xFC00) != 0xD800) || ((lowhalfChar & 0xFC00) != 0xDC00))
//FIXME: copy MS.NET behaviour when unpaired surrogate found
return 0x10000 + (highhalfChar-0xD800)*0x400 + (lowhalfChar-0xDC00);
}
+ private int PeekChar ()
+ {
+ if (peekCharsIndex < peekCharsLength) {
+ int c = peekChars [peekCharsIndex];
+ if (c == 0)
+ return -1;
+ if (c < 0xD800 || c >= 0xDFFF)
+ return c;
+ return PeekSurrogate (c);
+ } else {
+ if (!ReadTextReader (-1))
+ return -1;
+ return PeekChar ();
+ }
+ }
+
private int ReadChar ()
{
int ch = PeekChar ();
-
peekCharsIndex++;
if (ch >= 0x10000)
if (ch == '\n') {
line++;
column = 1;
- } else if (ch == -1) {
- return -1;
- } else {
+ } else if (ch != -1) {
column++;
}
- if (currentState != XmlNodeType.Element)
- AppendCurrentTagChar (ch);
return ch;
}
- private bool ReadTextReader ()
+ private void Advance (int ch) {
+ peekCharsIndex++;
+
+ if (ch >= 0x10000)
+ peekCharsIndex++; //Increment by 2 when a compound UCS-4 character was found
+
+ if (ch == '\n') {
+ line++;
+ column = 1;
+ } else if (ch != -1) {
+ column++;
+ }
+ }
+
+ private bool ReadTextReader (int remained)
{
- peekCharsIndex = 0;
- peekCharsLength = reader.Read (peekChars, 0, peekCharCapacity);
- return (peekCharsLength != 0);
+ if (peekCharsLength < 0) { // initialized buffer
+ peekCharsLength = reader.Read (peekChars, 0, peekChars.Length);
+ return peekCharsLength > 0;
+ }
+ int offset = remained >= 0 ? 1 : 0;
+ int copysize = peekCharsLength - curNodePeekIndex;
+
+ // It must assure that current tag content always exists
+ // in peekChars.
+ if (!preserveCurrentTag) {
+ curNodePeekIndex = 0;
+ peekCharsIndex = 0;
+ //copysize = 0;
+ } else if (peekCharsLength < peekChars.Length) {
+ // NonBlockingStreamReader returned less bytes
+ // than the size of the buffer. In that case,
+ // just refill the buffer.
+ } else if (curNodePeekIndex <= (peekCharsLength >> 1)) {
+ // extend the buffer
+ char [] tmp = new char [peekChars.Length * 2];
+ Array.Copy (peekChars, curNodePeekIndex,
+ tmp, 0, copysize);
+ peekChars = tmp;
+ curNodePeekIndex = 0;
+ peekCharsIndex = copysize;
+ } else {
+ Array.Copy (peekChars, curNodePeekIndex,
+ peekChars, 0, copysize);
+ curNodePeekIndex = 0;
+ peekCharsIndex = copysize;
+ }
+ if (remained >= 0)
+ peekChars [peekCharsIndex] = (char) remained;
+ int count = peekChars.Length - peekCharsIndex - offset;
+ if (count > peekCharCapacity)
+ count = peekCharCapacity;
+ int read = reader.Read (
+ peekChars, peekCharsIndex + offset, count);
+ int remainingSize = offset + read;
+ peekCharsLength = peekCharsIndex + remainingSize;
+
+ return (remainingSize != 0);
}
private bool ReadContent ()
{
- currentTagLength = 0;
if (popScope) {
parserContext.NamespaceManager.PopScope ();
parserContext.PopScope ();
} else {
switch (c) {
case '<':
- ReadChar ();
+ Advance (c);
switch (PeekChar ())
{
case '/':
- ReadChar ();
+ Advance ('/');
ReadEndTag ();
break;
case '?':
- ReadChar ();
+ Advance ('?');
ReadProcessingInstruction ();
break;
case '!':
- ReadChar ();
+ Advance ('!');
ReadDeclaration ();
break;
default:
case '\n':
case '\t':
case ' ':
- if (whitespaceHandling == WhitespaceHandling.All ||
- whitespaceHandling == WhitespaceHandling.Significant)
- ReadWhitespace ();
- else {
- SkipWhitespace ();
+ if (!ReadWhitespace ())
+ // skip
return ReadContent ();
- }
break;
default:
ReadText (true);
attributeTokens [i].FillNamespace ();
// quick name check
+ if (namespaces)
+ for (int i = 0; i < attributeCount; i++)
+ if (attributeTokens [i].Prefix == "xmlns" &&
+ attributeTokens [i].Value == String.Empty)
+ throw NotWFError ("Empty namespace URI cannot be mapped to non-empty prefix.");
+
for (int i = 0; i < attributeCount; i++) {
for (int j = i + 1; j < attributeCount; j++)
if (Object.ReferenceEquals (attributeTokens [i].Name, attributeTokens [j].Name) ||
}
if (PeekChar () == '/') {
- ReadChar ();
+ Advance ('/');
isEmptyElement = true;
popScope = true;
}
else {
depthUp = true;
- PushElementName (name);
+ PushElementName (name, localName, prefix);
}
parserContext.PushScope ();
CheckCurrentStateUpdate ();
}
- private void PushElementName (string name)
+ private void PushElementName (string name, string local, string prefix)
{
if (elementNames.Length == elementNameStackPos) {
- string [] newArray = new string [elementNames.Length * 2];
+ TagName [] newArray = new TagName [elementNames.Length * 2];
Array.Copy (elementNames, 0, newArray, 0, elementNameStackPos);
elementNames = newArray;
}
- elementNames [elementNameStackPos++] = name;
+ elementNames [elementNameStackPos++] =
+ new TagName (name, local, prefix);
}
// The reader is positioned on the first character
currentLinkedNodeLineNumber = line;
currentLinkedNodeLinePosition = column;
- string prefix, localName;
- string name = ReadName (out prefix, out localName);
if (elementNameStackPos == 0)
throw NotWFError ("closing element without matching opening element");
- string expected = elementNames [--elementNameStackPos];
- if (expected != name)
- throw NotWFError (String.Format ("unmatched closing element: expected {0} but found {1}", expected, name));
+ TagName expected = elementNames [--elementNameStackPos];
+ Expect (expected.Name);
ExpectAfterWhitespace ('>');
SetProperties (
XmlNodeType.EndElement, // nodeType
- name, // name
- prefix, // prefix
- localName, // localName
+ expected.Name, // name
+ expected.Prefix, // prefix
+ expected.LocalName, // localName
false, // isEmptyElement
null, // value
true // clearAttributes
);
- if (prefix.Length > 0)
- currentToken.NamespaceURI = LookupNamespace (prefix, true);
+ if (expected.Prefix.Length > 0)
+ currentToken.NamespaceURI = LookupNamespace (expected.Prefix, true);
else if (namespaces)
currentToken.NamespaceURI = parserContext.NamespaceManager.DefaultNamespace;
currentState = XmlNodeType.EndElement;
}
+#if USE_NAME_BUFFER
private void AppendSurrogatePairNameChar (int ch)
{
- nameBuffer [nameLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
+ nameBuffer [nameLength++] = (char) ((ch - 0x10000) / 0x400 + 0xD800);
if (nameLength == nameCapacity)
ExpandNameCapacity ();
- nameBuffer [nameLength++] = (char) (ch % 0x10000 + 0xDC00);
+ nameBuffer [nameLength++] = (char) ((ch - 0x10000) % 0x400 + 0xDC00);
}
private void ExpandNameCapacity ()
nameBuffer = new char [nameCapacity];
Array.Copy (oldNameBuffer, nameBuffer, nameLength);
}
+#endif
private void AppendValueChar (int ch)
{
private void AppendSurrogatePairValueChar (int ch)
{
- valueBuffer.Append ((char) (ch / 0x10000 + 0xD800 - 1));
- valueBuffer.Append ((char) (ch % 0x10000 + 0xDC00));
+ valueBuffer.Append ((char) ((ch - 0x10000) / 0x400 + 0xD800));
+ valueBuffer.Append ((char) ((ch - 0x10000) % 0x400 + 0xDC00));
}
private string CreateValueString ()
{
+ // Since whitespace strings are mostly identical
+ // depending on the Depth, we make use of NameTable
+ // to atomize whitespace strings.
+ switch (NodeType) {
+ case XmlNodeType.Whitespace:
+ case XmlNodeType.SignificantWhitespace:
+ int len = valueBuffer.Length;
+ if (whitespaceCache == null)
+ whitespaceCache = new char [32];
+ if (len >= whitespaceCache.Length)
+ break;
+ if (whitespacePool == null)
+ whitespacePool = new NameTable ();
+#if NET_2_0
+ valueBuffer.CopyTo (0, whitespaceCache, 0, len);
+#else
+ for (int i = 0; i < len; i++)
+ whitespaceCache [i] = valueBuffer [i];
+#endif
+ return whitespacePool.Add (whitespaceCache, 0, valueBuffer.Length);
+ }
return (valueBuffer.Capacity < 100) ?
valueBuffer.ToString (0, valueBuffer.Length) :
valueBuffer.ToString ();
valueBuffer.Length = 0;
}
- private void AppendCurrentTagChar (int ch)
- {
- if (currentTagLength == currentTagCapacity)
- ExpandCurrentTagCapacity ();
- if (ch < Char.MaxValue)
- currentTagBuffer [currentTagLength++] = (char) ch;
- else {
- currentTagBuffer [currentTagLength++] = (char) (ch / 0x10000 + 0xD800 - 1);
- if (currentTagLength == currentTagCapacity)
- ExpandCurrentTagCapacity ();
- currentTagBuffer [currentTagLength++] = (char) (ch % 0x10000 + 0xDC00);
- }
- }
-
- private void ExpandCurrentTagCapacity ()
- {
- currentTagCapacity = currentTagCapacity * 2;
- char [] oldCurrentTagBuffer = currentTagBuffer;
- currentTagBuffer = new char [currentTagCapacity];
- Array.Copy (oldCurrentTagBuffer, currentTagBuffer, currentTagLength);
- }
-
// The reader is positioned on the first character
// of the text.
private void ReadText (bool notWhitespace)
{
if (currentState != XmlNodeType.Element)
throw NotWFError ("Text node cannot appear in this state.");
+ preserveCurrentTag = false;
if (notWhitespace)
ClearValueBuffer ();
private int ReadReference (bool ignoreEntityReferences)
{
if (PeekChar () == '#') {
- ReadChar ();
+ Advance ('#');
return ReadCharacterReference ();
} else
return ReadEntityReference (ignoreEntityReferences);
private int ReadCharacterReference ()
{
int value = 0;
+ int ch;
if (PeekChar () == 'x') {
- ReadChar ();
+ Advance ('x');
- while (PeekChar () != ';' && PeekChar () != -1) {
- int ch = ReadChar ();
+ while ((ch = PeekChar ()) != ';' && ch != -1) {
+ Advance (ch);
if (ch >= '0' && ch <= '9')
value = (value << 4) + ch - '0';
ch));
}
} else {
- while (PeekChar () != ';' && PeekChar () != -1) {
- int ch = ReadChar ();
+ while ((ch = PeekChar ()) != ';' && ch != -1) {
+ Advance (ch);
if (ch >= '0' && ch <= '9')
value = value * 10 + ch - '0';
currentAttributeValue = -1;
}
- private void AddDtdAttribute (string name, string value)
+ private void AddAttributeWithValue (string name, string value)
{
IncrementAttributeToken ();
XmlAttributeTokenInfo ati = attributeTokens [currentAttribute];
ati.NamespaceURI = String.Empty;
IncrementAttributeValueToken ();
XmlTokenInfo vti = attributeValueTokens [currentAttributeValue];
- vti.Value = value;
SetTokenProperties (vti,
XmlNodeType.Text,
String.Empty,
false,
value,
false);
+ ati.Value = value;
attributeCount++;
}
goto default;
case '&':
if (PeekChar () == '#') {
- ReadChar ();
+ Advance ('#');
ch = ReadCharacterReference ();
AppendValueChar (ch);
break;
private void ReadProcessingInstruction ()
{
string target = ReadName ();
- if (target == "xml") {
- ReadXmlDeclaration ();
- return;
- } else if (target.ToLower (CultureInfo.InvariantCulture) == "xml")
+ if (target != "xml" && target.ToLower (CultureInfo.InvariantCulture) == "xml")
throw NotWFError ("Not allowed processing instruction name which starts with 'X', 'M', 'L' was found.");
- if (currentState == XmlNodeType.None)
- currentState = XmlNodeType.XmlDeclaration;
-
if (!SkipWhitespace ())
if (PeekChar () != '?')
throw NotWFError ("Invalid processing instruction name was found.");
ClearValueBuffer ();
- while (PeekChar () != -1) {
- int ch = ReadChar ();
+ int ch;
+ while ((ch = PeekChar ()) != -1) {
+ Advance (ch);
if (ch == '?' && PeekChar () == '>') {
- ReadChar ();
+ Advance ('>');
break;
}
AppendValueChar (ch);
}
- SetProperties (
- XmlNodeType.ProcessingInstruction, // nodeType
- target, // name
- String.Empty, // prefix
- target, // localName
- false, // isEmptyElement
- null, // value: create only when required
- true // clearAttributes
- );
+ if (Object.ReferenceEquals (target, XmlNamespaceManager.PrefixXml))
+ VerifyXmlDeclaration ();
+ else {
+ if (currentState == XmlNodeType.None)
+ currentState = XmlNodeType.XmlDeclaration;
+
+ SetProperties (
+ XmlNodeType.ProcessingInstruction, // nodeType
+ target, // name
+ String.Empty, // prefix
+ target, // localName
+ false, // isEmptyElement
+ null, // value: create only when required
+ true // clearAttributes
+ );
+ }
}
- // The reader is positioned after "<?xml "
- private void ReadXmlDeclaration ()
+ void VerifyXmlDeclaration ()
{
- if (currentState != XmlNodeType.None) {
+ if (!allowMultipleRoot && currentState != XmlNodeType.None)
throw NotWFError ("XML declaration cannot appear in this state.");
- }
- currentState = XmlNodeType.XmlDeclaration;
- ClearAttributes ();
-
- ReadAttributes (true); // They must have "version."
- string version = GetAttribute ("version");
-
- string message = null;
+ currentState = XmlNodeType.XmlDeclaration;
- if (attributeTokens [0].Name != "version" || version != "1.0")
- message = "Version 1.0 declaration is required in XML Declaration.";
- else if (attributeCount > 1 &&
- (attributeTokens [1].Name != "encoding" &&
- attributeTokens [1].Name != "standalone"))
- message = "Invalid Xml Declaration markup was found.";
- else if (attributeCount > 2 && attributeTokens [2].Name != "standalone")
- message = "Invalid Xml Declaration markup was found.";
- string sa = GetAttribute ("standalone");
- if (sa != null && sa != "yes" && sa != "no")
- message = String.Format ("Only 'yes' or 'no' is allowed for standalone. Value was '{0}'", sa);
+ string text = CreateValueString ();
- this.isStandalone = (sa == "yes");
+ ClearAttributes ();
- if (message != null)
- throw NotWFError (message);
+ int idx = 0;
+
+ string encoding = null, standalone = null;
+ string name, value;
+ ParseAttributeFromString (text, ref idx, out name, out value);
+ if (name != "version" || value != "1.0")
+ throw NotWFError ("'version' is expected.");
+ name = String.Empty;
+ if (SkipWhitespaceInString (text, ref idx) && idx < text.Length)
+ ParseAttributeFromString (text, ref idx, out name, out value);
+ if (name == "encoding") {
+ if (!XmlChar.IsValidIANAEncoding (value))
+ throw NotWFError ("'encoding' must be a valid IANA encoding name.");
+ if (reader is XmlStreamReader)
+ parserContext.Encoding = ((XmlStreamReader) reader).Encoding;
+ else
+ parserContext.Encoding = Encoding.Unicode;
+ encoding = value;
+ name = String.Empty;
+ if (SkipWhitespaceInString (text, ref idx) && idx < text.Length)
+ ParseAttributeFromString (text, ref idx, out name, out value);
+ }
+ if (name == "standalone") {
+ this.isStandalone = value == "yes";
+ if (value != "yes" && value != "no")
+ throw NotWFError ("Only 'yes' or 'no' is allow for 'standalone'");
+ standalone = value;
+ SkipWhitespaceInString (text, ref idx);
+ }
+ else if (name.Length != 0)
+ throw NotWFError (String.Format ("Unexpected token: '{0}'", name));
+
+ if (idx < text.Length)
+ throw NotWFError ("'?' is expected.");
+
+ AddAttributeWithValue ("version", "1.0");
+ if (encoding != null)
+ AddAttributeWithValue ("encoding", encoding);
+ if (standalone != null)
+ AddAttributeWithValue ("standalone", standalone);
+ currentAttribute = currentAttributeValue = -1;
SetProperties (
XmlNodeType.XmlDeclaration, // nodeType
String.Empty, // prefix
"xml", // localName
false, // isEmptyElement
- new string (currentTagBuffer, 6, currentTagLength - 6), // value
+ text, // value
false // clearAttributes
);
+ }
- Expect ("?>");
+ bool SkipWhitespaceInString (string text, ref int idx)
+ {
+ int start = idx;
+ while (idx < text.Length && XmlChar.IsWhitespace (text [idx]))
+ idx++;
+ return idx - start > 0;
}
- private void SkipTextDeclaration ()
+ private void ParseAttributeFromString (string src,
+ ref int idx, out string name, out string value)
{
- this.currentState = XmlNodeType.Element;
+ while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+ idx++;
+
+ int start = idx;
+ while (idx < src.Length && XmlChar.IsNameChar (src [idx]))
+ idx++;
+ name = src.Substring (start, idx - start);
+
+ while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+ idx++;
+ if (idx == src.Length || src [idx] != '=')
+ throw NotWFError (String.Format ("'=' is expected after {0}", name));
+ idx++;
+
+ while (idx < src.Length && XmlChar.IsWhitespace (src [idx]))
+ idx++;
+
+ if (idx == src.Length || src [idx] != '"' && src [idx] != '\'')
+ throw NotWFError ("'\"' or '\'' is expected.");
+ char quote = src [idx];
+ idx++;
+ start = idx;
+
+ while (idx < src.Length && src [idx] != quote)
+ idx++;
+ idx++;
+
+ value = src.Substring (start, idx - start - 1);
+ }
+
+ internal void SkipTextDeclaration ()
+ {
if (PeekChar () != '<')
return;
ReadChar ();
}
if (new string (peekChars, 2, 4) != "xml ") {
- if (new string (peekChars, 2, 3).ToLower (CultureInfo.InvariantCulture) == "xml") {
+ if (new string (peekChars, 2, 4).ToLower (CultureInfo.InvariantCulture) == "xml ") {
throw NotWFError ("Processing instruction name must not be character sequence 'X' 'M' 'L' with case insensitivity.");
}
peekCharsIndex = 0;
}
// Encoding value should be checked inside XmlInputStream.
}
- else
+#if NET_2_0
+ // this condition is to check if this instance is
+ // not created by XmlReader.Create() (which just
+ // omits strict text declaration check).
+ else if (Conformance == ConformanceLevel.Auto)
throw NotWFError ("Encoding declaration is mandatory in text declaration.");
+#endif
Expect ("?>");
+
+ curNodePeekIndex = peekCharsIndex; // without this it causes incorrect value start indication.
}
// The reader is positioned on the first character after
if (currentState == XmlNodeType.None)
currentState = XmlNodeType.XmlDeclaration;
+ preserveCurrentTag = false;
+
ClearValueBuffer ();
- while (PeekChar () != -1) {
- int ch = ReadChar ();
+ int ch;
+ while ((ch = PeekChar ()) != -1) {
+ Advance (ch);
if (ch == '-' && PeekChar () == '-') {
- ReadChar ();
+ Advance ('-');
if (PeekChar () != '>')
throw NotWFError ("comments cannot contain '--'");
- ReadChar ();
+ Advance ('>');
break;
}
{
if (currentState != XmlNodeType.Element)
throw NotWFError ("CDATA section cannot appear in this state.");
+ preserveCurrentTag = false;
ClearValueBuffer ();
ReadChar ();
intSubsetStartLine = this.LineNumber;
intSubsetStartColumn = this.LinePosition;
- int startPos = currentTagLength;
+ ClearValueBuffer ();
ReadInternalSubset ();
- int endPos = currentTagLength - 1;
- parserContext.InternalSubset = new string (currentTagBuffer, startPos, endPos - startPos);
+ parserContext.InternalSubset = CreateValueString ();
}
// end of DOCTYPE decl.
ExpectAfterWhitespace ('>');
);
if (publicId != null)
- AddDtdAttribute ("PUBLIC", publicId);
+ AddAttributeWithValue ("PUBLIC", publicId);
if (systemId != null)
- AddDtdAttribute ("SYSTEM", systemId);
+ AddAttributeWithValue ("SYSTEM", systemId);
currentAttribute = currentAttributeValue = -1;
}
get { return stateStack.Peek (); }
}
+ private int ReadValueChar ()
+ {
+ int ret = ReadChar ();
+ AppendValueChar (ret);
+ return ret;
+ }
+
+ private void ExpectAndAppend (string s)
+ {
+ Expect (s);
+ valueBuffer.Append (s);
+ }
+
// Simply read but not generate any result.
private void ReadInternalSubset ()
{
bool continueParse = true;
while (continueParse) {
- switch (ReadChar ()) {
+ switch (ReadValueChar ()) {
case ']':
switch (State) {
case DtdInputState.Free:
+ // chop extra ']'
+ valueBuffer.Remove (valueBuffer.Length - 1, 1);
continueParse = false;
break;
case DtdInputState.InsideDoubleQuoted:
case DtdInputState.Comment:
continue; // well-formed
}
- int c = ReadChar ();
+ int c = ReadValueChar ();
switch (c) {
case '?':
stateStack.Push (DtdInputState.PI);
break;
case '!':
- switch (ReadChar ()) {
+ switch (ReadValueChar ()) {
case 'E':
- switch (ReadChar ()) {
+ switch (ReadValueChar ()) {
case 'L':
- Expect ("EMENT");
+ ExpectAndAppend ("EMENT");
stateStack.Push (DtdInputState.ElementDecl);
break;
case 'N':
- Expect ("TITY");
+ ExpectAndAppend ("TITY");
stateStack.Push (DtdInputState.EntityDecl);
break;
default:
}
break;
case 'A':
- Expect ("TTLIST");
+ ExpectAndAppend ("TTLIST");
stateStack.Push (DtdInputState.AttlistDecl);
break;
case 'N':
- Expect ("OTATION");
+ ExpectAndAppend ("OTATION");
stateStack.Push (DtdInputState.NotationDecl);
break;
case '-':
- Expect ('-');
+ ExpectAndAppend ("-");
stateStack.Push (DtdInputState.Comment);
break;
}
break;
case '?':
if (State == DtdInputState.PI) {
- if (ReadChar () == '>')
+ if (ReadValueChar () == '>')
stateStack.Pop ();
}
break;
case '-':
if (State == DtdInputState.Comment) {
if (PeekChar () == '-') {
- ReadChar ();
- Expect ('>');
+ ReadValueChar ();
+ ExpectAndAppend (">");
stateStack.Pop ();
}
}
private string ReadName (out string prefix, out string localName)
{
- // FIXME: need to reject non-QName names?
+#if !USE_NAME_BUFFER
+ bool savePreserve = preserveCurrentTag;
+ preserveCurrentTag = true;
+ int startOffset = peekCharsIndex - curNodePeekIndex;
+ int ch = PeekChar ();
+ if (!XmlChar.IsFirstNameChar (ch))
+ throw NotWFError (String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
+ Advance (ch);
+ int length = 1;
+ int colonAt = -1;
+
+ while (XmlChar.IsNameChar ((ch = PeekChar ()))) {
+ Advance (ch);
+ if (ch == ':' && namespaces && colonAt < 0)
+ colonAt = length;
+ length++;
+ }
+
+ int start = curNodePeekIndex + startOffset;
+
+ string name = parserContext.NameTable.Add (
+ peekChars, start, length);
+
+ if (colonAt > 0) {
+ prefix = parserContext.NameTable.Add (
+ peekChars, start, colonAt);
+ localName = parserContext.NameTable.Add (
+ peekChars, start + colonAt + 1, length - colonAt - 1);
+ } else {
+ prefix = String.Empty;
+ localName = name;
+ }
+
+ preserveCurrentTag = savePreserve;
+
+ return name;
+#else
int ch = PeekChar ();
if (!XmlChar.IsFirstNameChar (ch))
throw NotWFError (String.Format (CultureInfo.InvariantCulture, "a name did not start with a legal character {0} ({1})", ch, (char) ch));
nameLength = 0;
- ch = ReadChar ();
+ Advance (ch);
// AppendNameChar (ch);
{
- if (nameLength == nameCapacity)
- ExpandNameCapacity ();
+ // nameBuffer.Length is always non-0 so no need to ExpandNameCapacity () here
if (ch < Char.MaxValue)
nameBuffer [nameLength++] = (char) ch;
else
int colonAt = -1;
- while (XmlChar.IsNameChar (PeekChar ())) {
- ch = ReadChar ();
+ while (XmlChar.IsNameChar ((ch = PeekChar ()))) {
+ Advance (ch);
- if (namespaces && colonAt < 0 && ch == ':')
+ if (ch == ':' && namespaces && colonAt < 0)
colonAt = nameLength;
// AppendNameChar (ch);
{
string name = parserContext.NameTable.Add (nameBuffer, 0, nameLength);
- if (namespaces && colonAt > 0) {
+ if (colonAt > 0) {
prefix = parserContext.NameTable.Add (nameBuffer, 0, colonAt);
localName = parserContext.NameTable.Add (nameBuffer, colonAt + 1, nameLength - colonAt - 1);
- }
- else {
+ } else {
prefix = String.Empty;
localName = name;
}
return name;
+#endif
}
// Read the next character and compare it against the
private void Expect (string expected)
{
- int len = expected.Length;
- for(int i=0; i< len; i++)
- Expect (expected[i]);
+ for (int i = 0; i < expected.Length; i++)
+ if (ReadChar () != expected [i])
+ throw NotWFError (String.Format (CultureInfo.InvariantCulture,
+ "'{0}' is expected", expected));
}
private void ExpectAfterWhitespace (char c)
bool skipped = (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
if (!skipped)
return false;
- ReadChar ();
+ Advance (ch);
// FIXME: It should be inlined by the JIT.
// while (XmlChar.IsWhitespace (PeekChar ()))
// ReadChar ();
while ((ch = PeekChar ()) == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD)
- ReadChar ();
+ Advance (ch);
return skipped;
}
- private void ReadWhitespace ()
+ private bool ReadWhitespace ()
{
if (currentState == XmlNodeType.None)
currentState = XmlNodeType.XmlDeclaration;
- ClearValueBuffer ();
+ bool savePreserve = preserveCurrentTag;
+ preserveCurrentTag = true;
+ int startOffset = peekCharsIndex - curNodePeekIndex; // it should be 0 for now though.
+
int ch = PeekChar ();
do {
- // FIXME: it might be optimized by the JIT later,
-// AppendValueChar (ReadChar ());
- {
- ch = ReadChar ();
- if (ch < Char.MaxValue)
- valueBuffer.Append ((char) ch);
- else
- AppendSurrogatePairValueChar (ch);
- }
+ Advance (ch);
+ ch = PeekChar ();
// FIXME: It should be inlined by the JIT.
// } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
- ch = PeekChar ();
} while (ch == 0x20 || ch == 0x9 || ch == 0xA || ch == 0xD);
- if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
+ bool isText = currentState == XmlNodeType.Element && ch != -1 && ch != '<';
+
+ if (!isText && (whitespaceHandling == WhitespaceHandling.None ||
+ whitespaceHandling == WhitespaceHandling.Significant && XmlSpace != XmlSpace.Preserve))
+ return false;
+
+ ClearValueBuffer ();
+ valueBuffer.Append (peekChars, curNodePeekIndex, peekCharsIndex - curNodePeekIndex - startOffset);
+ preserveCurrentTag = savePreserve;
+
+ if (isText) {
ReadText (false);
- else {
+ } else {
XmlNodeType nodeType = (this.XmlSpace == XmlSpace.Preserve) ?
XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
SetProperties (nodeType,
true);
}
- return;
+ return true;
}
// Returns -1 if it should throw an error.
case -1:
throw NotWFError ("Unexpected end of xml.");
case '<':
- ReadChar ();
+ Advance (c);
if (PeekChar () != '/') {
buffer [bufIndex++] = '<';
continue;
Read (); // move to the next node
return i;
default:
- ReadChar ();
+ Advance (c);
if (c < Char.MaxValue)
buffer [bufIndex++] = (char) c;
else {
- buffer [bufIndex++] = (char) (c / 0x10000 + 0xD800 - 1);
- buffer [bufIndex++] = (char) (c % 0x10000 + 0xDC00);
+ buffer [bufIndex++] = (char) ((c - 0x10000) / 0x400 + 0xD800);
+ buffer [bufIndex++] = (char) ((c - 0x10000) % 0x400 + 0xDC00);
}
break;
}
continue;
ReadChar ();
string name = ReadName ();
- if (name != elementNames [elementNameStackPos - 1])
+ if (name != elementNames [elementNameStackPos - 1].Name)
continue;
Expect ('>');
depth--;
- elementNames [--elementNameStackPos] = null;
return Read ();
}
} while (true);