private void ParseAttributeValueSlow(int curPos, char quoteChar, NodeData attr)
{
int pos = curPos;
char[] chars = _ps.chars;
int attributeBaseEntityId = _ps.entityId;
int valueChunkStartPos = 0;
LineInfo valueChunkLineInfo = new LineInfo(_ps.lineNo, _ps.LinePos);
NodeData lastChunk = null;
Debug.Assert(_stringBuilder.Length == 0);
for (;;)
{
// parse the rest of the attribute value
unsafe
{
while (_xmlCharType.IsAttributeValueChar(chars[pos]))
{
pos++;
}
}
if (pos - _ps.charPos > 0)
{
_stringBuilder.Append(chars, _ps.charPos, pos - _ps.charPos);
_ps.charPos = pos;
}
if (chars[pos] == quoteChar && attributeBaseEntityId == _ps.entityId)
{
break;
}
else
{
switch (chars[pos])
{
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
if (_normalize)
{
_stringBuilder.Append((char)0x20); // CDATA normalization of 0xA
_ps.charPos++;
}
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
pos += 2;
if (_normalize)
{
_stringBuilder.Append(_ps.eolNormalized ? "\u0020\u0020" : "\u0020"); // CDATA normalization of 0xD 0xA
_ps.charPos = pos;
}
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
pos++;
if (_normalize)
{
_stringBuilder.Append((char)0x20); // CDATA normalization of 0xD and 0xD 0xA
_ps.charPos = pos;
}
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
// tab
case (char)0x9:
pos++;
if (_normalize)
{
_stringBuilder.Append((char)0x20); // CDATA normalization of 0x9
_ps.charPos++;
}
continue;
case '"':
case '\'':
case '>':
pos++;
continue;
// attribute values cannot contain '<'
case '<':
Throw(pos, SR.Xml_BadAttributeChar, XmlException.BuildCharExceptionArgs('<', '\0'));
break;
// entity referece
case '&':
if (pos - _ps.charPos > 0)
{
_stringBuilder.Append(chars, _ps.charPos, pos - _ps.charPos);
}
_ps.charPos = pos;
int enclosingEntityId = _ps.entityId;
LineInfo entityLineInfo = new LineInfo(_ps.lineNo, _ps.LinePos + 1);
switch (HandleEntityReference(true, EntityExpandType.All, out pos))
{
case EntityType.CharacterDec:
case EntityType.CharacterHex:
case EntityType.CharacterNamed:
break;
case EntityType.Unexpanded:
if (_parsingMode == ParsingMode.Full && _ps.entityId == attributeBaseEntityId)
{
// construct text value chunk
int valueChunkLen = _stringBuilder.Length - valueChunkStartPos;
if (valueChunkLen > 0)
{
NodeData textChunk = new NodeData();
textChunk.lineInfo = valueChunkLineInfo;
textChunk.depth = attr.depth + 1;
textChunk.SetValueNode(XmlNodeType.Text, _stringBuilder.ToString(valueChunkStartPos, valueChunkLen));
AddAttributeChunkToList(attr, textChunk, ref lastChunk);
}
// parse entity name
_ps.charPos++;
string entityName = ParseEntityName();
// construct entity reference chunk
NodeData entityChunk = new NodeData();
entityChunk.lineInfo = entityLineInfo;
entityChunk.depth = attr.depth + 1;
entityChunk.SetNamedNode(XmlNodeType.EntityReference, entityName);
AddAttributeChunkToList(attr, entityChunk, ref lastChunk);
// append entity ref to the attribute value
_stringBuilder.Append('&');
_stringBuilder.Append(entityName);
_stringBuilder.Append(';');
// update info for the next attribute value chunk
valueChunkStartPos = _stringBuilder.Length;
valueChunkLineInfo.Set(_ps.LineNo, _ps.LinePos);
_fullAttrCleanup = true;
}
else
{
_ps.charPos++;
ParseEntityName();
}
pos = _ps.charPos;
break;
case EntityType.ExpandedInAttribute:
if (_parsingMode == ParsingMode.Full && enclosingEntityId == attributeBaseEntityId)
{
// construct text value chunk
int valueChunkLen = _stringBuilder.Length - valueChunkStartPos;
if (valueChunkLen > 0)
{
NodeData textChunk = new NodeData();
textChunk.lineInfo = valueChunkLineInfo;
textChunk.depth = attr.depth + 1;
textChunk.SetValueNode(XmlNodeType.Text, _stringBuilder.ToString(valueChunkStartPos, valueChunkLen));
AddAttributeChunkToList(attr, textChunk, ref lastChunk);
}
// construct entity reference chunk
NodeData entityChunk = new NodeData();
entityChunk.lineInfo = entityLineInfo;
entityChunk.depth = attr.depth + 1;
entityChunk.SetNamedNode(XmlNodeType.EntityReference, _ps.entity.Name);
AddAttributeChunkToList(attr, entityChunk, ref lastChunk);
_fullAttrCleanup = true;
// Note: info for the next attribute value chunk will be updated once we
// get out of the expanded entity
}
pos = _ps.charPos;
break;
default:
pos = _ps.charPos;
break;
}
chars = _ps.chars;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReadData;
}
// surrogate chars
else
{
char ch = chars[pos];
if (XmlCharType.IsHighSurrogate(ch))
{
if (pos + 1 == _ps.charsUsed)
{
goto ReadData;
}
pos++;
if (XmlCharType.IsLowSurrogate(chars[pos]))
{
pos++;
continue;
}
}
ThrowInvalidChar(chars, _ps.charsUsed, pos);
break;
}
}
}
ReadData:
// read new characters into the buffer
if (ReadData() == 0)
{
if (_ps.charsUsed - _ps.charPos > 0)
{
if (_ps.chars[_ps.charPos] != (char)0xD)
{
Debug.Assert(false, "We should never get to this point.");
Throw(SR.Xml_UnexpectedEOF1);
}
Debug.Assert(_ps.isEof);
}
else
{
if (!InEntity)
{
if (_fragmentType == XmlNodeType.Attribute)
{
if (attributeBaseEntityId != _ps.entityId)
{
Throw(SR.Xml_EntityRefNesting);
}
break;
}
Throw(SR.Xml_UnclosedQuote);
}
if (HandleEntityEnd(true))
{ // no EndEntity reporting while parsing attributes
Debug.Assert(false);
Throw(SR.Xml_InternalError);
}
// update info for the next attribute value chunk
if (attributeBaseEntityId == _ps.entityId)
{
valueChunkStartPos = _stringBuilder.Length;
valueChunkLineInfo.Set(_ps.LineNo, _ps.LinePos);
}
}
}
pos = _ps.charPos;
chars = _ps.chars;
}
if (attr.nextAttrValueChunk != null)
{
// construct last text value chunk
int valueChunkLen = _stringBuilder.Length - valueChunkStartPos;
if (valueChunkLen > 0)
{
NodeData textChunk = new NodeData();
textChunk.lineInfo = valueChunkLineInfo;
textChunk.depth = attr.depth + 1;
textChunk.SetValueNode(XmlNodeType.Text, _stringBuilder.ToString(valueChunkStartPos, valueChunkLen));
AddAttributeChunkToList(attr, textChunk, ref lastChunk);
}
}
_ps.charPos = pos + 1;
attr.SetValue(_stringBuilder.ToString());
_stringBuilder.Length = 0;
}