private bool ParseXmlDeclaration(bool isTextDecl)
{
while (_ps.charsUsed - _ps.charPos < 6)
{ // minimum "<?xml "
if (ReadData() == 0)
{
goto NoXmlDecl;
}
}
if (!XmlConvert.StrEqual(_ps.chars, _ps.charPos, 5, XmlDeclarationBeginning) ||
_xmlCharType.IsNameSingleChar(_ps.chars[_ps.charPos + 5])
#if XML10_FIFTH_EDITION
|| xmlCharType.IsNCNameHighSurrogateChar( ps.chars[ps.charPos + 5] )
#endif
)
{
goto NoXmlDecl;
}
if (!isTextDecl)
{
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos + 2);
_curNode.SetNamedNode(XmlNodeType.XmlDeclaration, _xml);
}
_ps.charPos += 5;
// parsing of text declarations cannot change global stringBuidler or curNode as we may be in the middle of a text node
Debug.Assert(_stringBuilder.Length == 0 || isTextDecl);
StringBuilder sb = isTextDecl ? new StringBuilder() : _stringBuilder;
// parse version, encoding & standalone attributes
int xmlDeclState = 0; // <?xml (0) version='1.0' (1) encoding='__' (2) standalone='__' (3) ?>
Encoding encoding = null;
for (;;)
{
int originalSbLen = sb.Length;
int wsCount = EatWhitespaces(xmlDeclState == 0 ? null : sb);
// end of xml declaration
if (_ps.chars[_ps.charPos] == '?')
{
sb.Length = originalSbLen;
if (_ps.chars[_ps.charPos + 1] == '>')
{
if (xmlDeclState == 0)
{
Throw(isTextDecl ? SR.Xml_InvalidTextDecl : SR.Xml_InvalidXmlDecl);
}
_ps.charPos += 2;
if (!isTextDecl)
{
_curNode.SetValue(sb.ToString());
sb.Length = 0;
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.ResetAttributesRootLevel;
}
// switch to encoding specified in xml declaration
if (encoding == null)
{
if (isTextDecl)
{
Throw(SR.Xml_InvalidTextDecl);
}
if (_afterResetState)
{
// check for invalid encoding switches to default encoding
string encodingName = _ps.encoding.WebName;
if (encodingName != "utf-8" && encodingName != "utf-16" &&
encodingName != "utf-16BE" && !(_ps.encoding is Ucs4Encoding))
{
Throw(SR.Xml_EncodingSwitchAfterResetState, (_ps.encoding.GetByteCount("A") == 1) ? "UTF-8" : "UTF-16");
}
}
if (_ps.decoder is SafeAsciiDecoder)
{
SwitchEncodingToUTF8();
}
}
else
{
SwitchEncoding(encoding);
}
_ps.appendMode = false;
return true;
}
else if (_ps.charPos + 1 == _ps.charsUsed)
{
goto ReadData;
}
else
{
ThrowUnexpectedToken("'>'");
}
}
if (wsCount == 0 && xmlDeclState != 0)
{
ThrowUnexpectedToken("?>");
}
// read attribute name
int nameEndPos = ParseName();
NodeData attr = null;
switch (_ps.chars[_ps.charPos])
{
case 'v':
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, nameEndPos - _ps.charPos, "version") && xmlDeclState == 0)
{
if (!isTextDecl)
{
attr = AddAttributeNoChecks("version", 1);
}
break;
}
goto default;
case 'e':
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, nameEndPos - _ps.charPos, "encoding") &&
(xmlDeclState == 1 || (isTextDecl && xmlDeclState == 0)))
{
if (!isTextDecl)
{
attr = AddAttributeNoChecks("encoding", 1);
}
xmlDeclState = 1;
break;
}
goto default;
case 's':
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, nameEndPos - _ps.charPos, "standalone") &&
(xmlDeclState == 1 || xmlDeclState == 2) && !isTextDecl)
{
if (!isTextDecl)
{
attr = AddAttributeNoChecks("standalone", 1);
}
xmlDeclState = 2;
break;
}
goto default;
default:
Throw(isTextDecl ? SR.Xml_InvalidTextDecl : SR.Xml_InvalidXmlDecl);
break;
}
if (!isTextDecl)
{
attr.SetLineInfo(_ps.LineNo, _ps.LinePos);
}
sb.Append(_ps.chars, _ps.charPos, nameEndPos - _ps.charPos);
_ps.charPos = nameEndPos;
// parse equals and quote char;
if (_ps.chars[_ps.charPos] != '=')
{
EatWhitespaces(sb);
if (_ps.chars[_ps.charPos] != '=')
{
ThrowUnexpectedToken("=");
}
}
sb.Append('=');
_ps.charPos++;
char quoteChar = _ps.chars[_ps.charPos];
if (quoteChar != '"' && quoteChar != '\'')
{
EatWhitespaces(sb);
quoteChar = _ps.chars[_ps.charPos];
if (quoteChar != '"' && quoteChar != '\'')
{
ThrowUnexpectedToken("\"", "'");
}
}
sb.Append(quoteChar);
_ps.charPos++;
if (!isTextDecl)
{
attr.quoteChar = quoteChar;
attr.SetLineInfo2(_ps.LineNo, _ps.LinePos);
}
// parse attribute value
int pos = _ps.charPos;
char[] chars;
Continue:
chars = _ps.chars;
unsafe
{
while (_xmlCharType.IsAttributeValueChar(chars[pos]))
{
pos++;
}
}
if (_ps.chars[pos] == quoteChar)
{
switch (xmlDeclState)
{
// version
case 0:
#if XML10_FIFTH_EDITION
// VersionNum ::= '1.' [0-9]+ (starting with XML Fifth Edition)
if (pos - ps.charPos >= 3 &&
ps.chars[ps.charPos] == '1' &&
ps.chars[ps.charPos + 1] == '.' &&
XmlCharType.IsOnlyDigits(ps.chars, ps.charPos + 2, pos - ps.charPos - 2))
{
#else
// VersionNum ::= '1.0' (XML Fourth Edition and earlier)
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, pos - _ps.charPos, "1.0"))
{
#endif
if (!isTextDecl)
{
attr.SetValue(_ps.chars, _ps.charPos, pos - _ps.charPos);
}
xmlDeclState = 1;
}
else
{
string badVersion = new string(_ps.chars, _ps.charPos, pos - _ps.charPos);
Throw(SR.Xml_InvalidVersionNumber, badVersion);
}
break;
case 1:
string encName = new string(_ps.chars, _ps.charPos, pos - _ps.charPos);
encoding = CheckEncoding(encName);
if (!isTextDecl)
{
attr.SetValue(encName);
}
xmlDeclState = 2;
break;
case 2:
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, pos - _ps.charPos, "yes"))
{
_standalone = true;
}
else if (XmlConvert.StrEqual(_ps.chars, _ps.charPos, pos - _ps.charPos, "no"))
{
_standalone = false;
}
else
{
Debug.Assert(!isTextDecl);
Throw(SR.Xml_InvalidXmlDecl, _ps.LineNo, _ps.LinePos - 1);
}
if (!isTextDecl)
{
attr.SetValue(_ps.chars, _ps.charPos, pos - _ps.charPos);
}
xmlDeclState = 3;
break;
default:
Debug.Assert(false);
break;
}
sb.Append(chars, _ps.charPos, pos - _ps.charPos);
sb.Append(quoteChar);
_ps.charPos = pos + 1;
continue;
}
else if (pos == _ps.charsUsed)
{
if (ReadData() != 0)
{
goto Continue;
}
else
{
Throw(SR.Xml_UnclosedQuote);
}
}
else
{
Throw(isTextDecl ? SR.Xml_InvalidTextDecl : SR.Xml_InvalidXmlDecl);
}
ReadData:
if (_ps.isEof || ReadData() == 0)
{
Throw(SR.Xml_UnexpectedEOF1);
}
}
NoXmlDecl:
// no xml declaration
if (!isTextDecl)
{
_parsingFunction = _nextParsingFunction;
}
if (_afterResetState)
{
// check for invalid encoding switches to default encoding
string encodingName = _ps.encoding.WebName;
if (encodingName != "utf-8" && encodingName != "utf-16" &&
encodingName != "utf-16BE" && !(_ps.encoding is Ucs4Encoding))
{
Throw(SR.Xml_EncodingSwitchAfterResetState, (_ps.encoding.GetByteCount("A") == 1) ? "UTF-8" : "UTF-16");
}
}
if (_ps.decoder is SafeAsciiDecoder)
{
SwitchEncodingToUTF8();
}
_ps.appendMode = false;
return false;
}