private void ParseAttributes()
{
int pos = _ps.charPos;
char[] chars = _ps.chars;
NodeData attr = null;
Debug.Assert(_attrCount == 0);
for (;;)
{
// eat whitespaces
int lineNoDelta = 0;
char tmpch0;
unsafe
{
while (_xmlCharType.IsWhiteSpace(tmpch0 = chars[pos]))
{
if (tmpch0 == (char)0xA)
{
OnNewLine(pos + 1);
lineNoDelta++;
}
else if (tmpch0 == (char)0xD)
{
if (chars[pos + 1] == (char)0xA)
{
OnNewLine(pos + 2);
lineNoDelta++;
pos++;
}
else if (pos + 1 != _ps.charsUsed)
{
OnNewLine(pos + 1);
lineNoDelta++;
}
else
{
_ps.charPos = pos;
goto ReadData;
}
}
pos++;
}
}
char tmpch1;
int startNameCharSize = 0;
unsafe
{
if (_xmlCharType.IsStartNCNameSingleChar(tmpch1 = chars[pos]))
{
startNameCharSize = 1;
}
#if XML10_FIFTH_EDITION
else if (pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], tmpch1))
{
startNameCharSize = 2;
}
#endif
}
if (startNameCharSize == 0)
{
// element end
if (tmpch1 == '>')
{
Debug.Assert(_curNode.type == XmlNodeType.Element);
_ps.charPos = pos + 1;
_parsingFunction = ParsingFunction.MoveToElementContent;
goto End;
}
// empty element end
else if (tmpch1 == '/')
{
Debug.Assert(_curNode.type == XmlNodeType.Element);
if (pos + 1 == _ps.charsUsed)
{
goto ReadData;
}
if (chars[pos + 1] == '>')
{
_ps.charPos = pos + 2;
_curNode.IsEmptyElement = true;
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.PopEmptyElementContext;
goto End;
}
else
{
ThrowUnexpectedToken(pos + 1, ">");
}
}
else if (pos == _ps.charsUsed)
{
goto ReadData;
}
else if (tmpch1 != ':' || _supportNamespaces)
{
Throw(pos, SR.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs(chars, _ps.charsUsed, pos));
}
}
if (pos == _ps.charPos)
{
ThrowExpectingWhitespace(pos);
}
_ps.charPos = pos;
// save attribute name line position
int attrNameLinePos = _ps.LinePos;
#if DEBUG
int attrNameLineNo = _ps.LineNo;
#endif
// parse attribute name
int colonPos = -1;
// PERF: we intentionally don't call ParseQName here to parse the element name unless a special
// case occurs (like end of buffer, invalid name char)
pos += startNameCharSize; // start name char has already been checked
// parse attribute name
ContinueParseName:
char tmpch2;
unsafe
{
for (;;)
{
if (_xmlCharType.IsNCNameSingleChar(tmpch2 = chars[pos]))
{
pos++;
}
#if XML10_FIFTH_EDITION
else if (pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], tmpch2))
{
pos += 2;
}
#endif
else
{
break;
}
}
}
// colon -> save prefix end position and check next char if it's name start char
if (tmpch2 == ':')
{
if (colonPos != -1)
{
if (_supportNamespaces)
{
Throw(pos, SR.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
}
else
{
pos++;
goto ContinueParseName;
}
}
else
{
colonPos = pos;
pos++;
unsafe
{
if (_xmlCharType.IsStartNCNameSingleChar(chars[pos]))
{
pos++;
goto ContinueParseName;
}
#if XML10_FIFTH_EDITION
else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
pos += 2;
goto ContinueParseName;
}
#endif
}
// else fallback to full name parsing routine
pos = ParseQName(out colonPos);
chars = _ps.chars;
}
}
else if (pos + 1 >= _ps.charsUsed)
{
pos = ParseQName(out colonPos);
chars = _ps.chars;
}
attr = AddAttribute(pos, colonPos);
attr.SetLineInfo(_ps.LineNo, attrNameLinePos);
#if DEBUG
Debug.Assert(attrNameLineNo == _ps.LineNo);
#endif
// parse equals and quote char;
if (chars[pos] != '=')
{
_ps.charPos = pos;
EatWhitespaces(null);
pos = _ps.charPos;
if (chars[pos] != '=')
{
ThrowUnexpectedToken("=");
}
}
pos++;
char quoteChar = chars[pos];
if (quoteChar != '"' && quoteChar != '\'')
{
_ps.charPos = pos;
EatWhitespaces(null);
pos = _ps.charPos;
quoteChar = chars[pos];
if (quoteChar != '"' && quoteChar != '\'')
{
ThrowUnexpectedToken("\"", "'");
}
}
pos++;
_ps.charPos = pos;
attr.quoteChar = quoteChar;
attr.SetLineInfo2(_ps.LineNo, _ps.LinePos);
// parse attribute value
char tmpch3;
unsafe
{
while (_xmlCharType.IsAttributeValueChar(tmpch3 = chars[pos]))
{
pos++;
}
}
if (tmpch3 == quoteChar)
{
#if DEBUG
if (normalize)
{
string val = new string(chars, ps.charPos, pos - ps.charPos);
Debug.Assert(val == XmlComplianceUtil.CDataNormalize(val), "The attribute value is not CDATA normalized!");
}
#endif
attr.SetValue(chars, _ps.charPos, pos - _ps.charPos);
pos++;
_ps.charPos = pos;
}
else
{
ParseAttributeValueSlow(pos, quoteChar, attr);
pos = _ps.charPos;
chars = _ps.chars;
}
// handle special attributes:
if (attr.prefix.Length == 0)
{
// default namespace declaration
if (Ref.Equal(attr.localName, _xmlNs))
{
OnDefaultNamespaceDecl(attr);
}
}
else
{
// prefixed namespace declaration
if (Ref.Equal(attr.prefix, _xmlNs))
{
OnNamespaceDecl(attr);
}
// xml: attribute
else if (Ref.Equal(attr.prefix, _xml))
{
OnXmlReservedAttribute(attr);
}
}
continue;
ReadData:
_ps.lineNo -= lineNoDelta;
if (ReadData() != 0)
{
pos = _ps.charPos;
chars = _ps.chars;
}
else
{
ThrowUnclosedElements();
}
}
End:
if (_addDefaultAttributesAndNormalize)
{
AddDefaultAttributesAndNormalize();
}
// lookup namespaces: element
ElementNamespaceLookup();
// lookup namespaces: attributes
if (_attrNeedNamespaceLookup)
{
AttributeNamespaceLookup();
_attrNeedNamespaceLookup = false;
}
// check duplicate attributes
if (_attrDuplWalkCount >= MaxAttrDuplWalkCount)
{
AttributeDuplCheck();
}
}