in src/dotnet-svcutil/lib/src/FrameworkFork/Microsoft.Xml/Xml/Core/XmlTextReaderImpl.cs [3632:9870]
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, XmlDeclarationBegining) ||
_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);
BufferBuilder sb = isTextDecl ? new BufferBuilder() : _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 ? ResXml.Xml_InvalidTextDecl : ResXml.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(ResXml.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(ResXml.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 ? ResXml.Xml_InvalidTextDecl : ResXml.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.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0))
{
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(ResXml.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(ResXml.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(ResXml.Xml_UnclosedQuote);
}
}
else
{
Throw(isTextDecl ? ResXml.Xml_InvalidTextDecl : ResXml.Xml_InvalidXmlDecl);
}
ReadData:
if (_ps.isEof || ReadData() == 0)
{
Throw(ResXml.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(ResXml.Xml_EncodingSwitchAfterResetState, (_ps.encoding.GetByteCount("A") == 1) ? "UTF-8" : "UTF-16");
}
}
if (_ps.decoder is SafeAsciiDecoder)
{
SwitchEncodingToUTF8();
}
_ps.appendMode = false;
return false;
}
// Parses the document content
private bool ParseDocumentContent()
{
bool mangoQuirks = false;
#if FEATURE_LEGACYNETCF
// Dev11 447539 quirk:
// In Mango the default XmlTextReader is instantiated
// with v1Compat flag set to true. One of the effects
// of this settings is to eat any trailing nulls in the
// buffer and some apps depend on this behavior.
if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
mangoQuirks = true;
#endif
for (; ; )
{
bool needMoreChars = false;
int pos = _ps.charPos;
char[] chars = _ps.chars;
// some tag
if (chars[pos] == '<')
{
needMoreChars = true;
if (_ps.charsUsed - pos < 4) // minimum "<a/>"
goto ReadData;
pos++;
switch (chars[pos])
{
// processing instruction
case '?':
_ps.charPos = pos + 1;
if (ParsePI())
{
return true;
}
continue;
case '!':
pos++;
if (_ps.charsUsed - pos < 2) // minimum characters expected "--"
goto ReadData;
// comment
if (chars[pos] == '-')
{
if (chars[pos + 1] == '-')
{
_ps.charPos = pos + 2;
if (ParseComment())
{
return true;
}
continue;
}
else
{
ThrowUnexpectedToken(pos + 1, "-");
}
}
// CDATA section
else if (chars[pos] == '[')
{
if (_fragmentType != XmlNodeType.Document)
{
pos++;
if (_ps.charsUsed - pos < 6)
{
goto ReadData;
}
if (XmlConvert.StrEqual(chars, pos, 6, "CDATA["))
{
_ps.charPos = pos + 6;
ParseCData();
if (_fragmentType == XmlNodeType.None)
{
_fragmentType = XmlNodeType.Element;
}
return true;
}
else
{
ThrowUnexpectedToken(pos, "CDATA[");
}
}
else
{
Throw(_ps.charPos, ResXml.Xml_InvalidRootData);
}
}
// DOCTYPE declaration
else
{
if (_fragmentType == XmlNodeType.Document || _fragmentType == XmlNodeType.None)
{
_fragmentType = XmlNodeType.Document;
_ps.charPos = pos;
if (ParseDoctypeDecl())
{
return true;
}
continue;
}
else
{
if (ParseUnexpectedToken(pos) == "DOCTYPE")
{
Throw(ResXml.Xml_BadDTDLocation);
}
else
{
ThrowUnexpectedToken(pos, "<!--", "<[CDATA[");
}
}
}
break;
case '/':
Throw(pos + 1, ResXml.Xml_UnexpectedEndTag);
break;
// document element start tag
default:
if (_rootElementParsed)
{
if (_fragmentType == XmlNodeType.Document)
{
Throw(pos, ResXml.Xml_MultipleRoots);
}
if (_fragmentType == XmlNodeType.None)
{
_fragmentType = XmlNodeType.Element;
}
}
_ps.charPos = pos;
_rootElementParsed = true;
ParseElement();
return true;
}
}
else if (chars[pos] == '&')
{
if (_fragmentType == XmlNodeType.Document)
{
Throw(pos, ResXml.Xml_InvalidRootData);
}
else
{
if (_fragmentType == XmlNodeType.None)
{
_fragmentType = XmlNodeType.Element;
}
int i;
switch (HandleEntityReference(false, EntityExpandType.OnlyGeneral, out i))
{
case EntityType.Unexpanded:
if (_parsingFunction == ParsingFunction.EntityReference)
{
_parsingFunction = _nextParsingFunction;
}
ParseEntityReference();
return true;
case EntityType.CharacterDec:
case EntityType.CharacterHex:
case EntityType.CharacterNamed:
if (ParseText())
{
return true;
}
continue;
default:
chars = _ps.chars;
pos = _ps.charPos;
continue;
}
}
}
// end of buffer
else if (pos == _ps.charsUsed || ((_v1Compat || mangoQuirks) && chars[pos] == 0x0))
{
goto ReadData;
}
// something else -> root level whitespaces
else
{
if (_fragmentType == XmlNodeType.Document)
{
if (ParseRootLevelWhitespace())
{
return true;
}
}
else
{
if (ParseText())
{
if (_fragmentType == XmlNodeType.None && _curNode.type == XmlNodeType.Text)
{
_fragmentType = XmlNodeType.Element;
}
return true;
}
}
continue;
}
Debug.Assert(pos == _ps.charsUsed && !_ps.isEof);
ReadData:
// read new characters into the buffer
if (ReadData() != 0)
{
pos = _ps.charPos;
}
else
{
if (needMoreChars)
{
Throw(ResXml.Xml_InvalidRootData);
}
if (InEntity)
{
if (HandleEntityEnd(true))
{
SetupEndEntityNodeInContent();
return true;
}
continue;
}
Debug.Assert(_index == 0);
if (!_rootElementParsed && _fragmentType == XmlNodeType.Document)
{
ThrowWithoutLineInfo(ResXml.Xml_MissingRoot);
}
if (_fragmentType == XmlNodeType.None)
{
_fragmentType = _rootElementParsed ? XmlNodeType.Document : XmlNodeType.Element;
}
OnEof();
return false;
}
pos = _ps.charPos;
chars = _ps.chars;
}
}
// Parses element content
private bool ParseElementContent()
{
for (; ; )
{
int pos = _ps.charPos;
char[] chars = _ps.chars;
switch (chars[pos])
{
// some tag
case '<':
switch (chars[pos + 1])
{
// processing instruction
case '?':
_ps.charPos = pos + 2;
if (ParsePI())
{
return true;
}
continue;
case '!':
pos += 2;
if (_ps.charsUsed - pos < 2)
goto ReadData;
// comment
if (chars[pos] == '-')
{
if (chars[pos + 1] == '-')
{
_ps.charPos = pos + 2;
if (ParseComment())
{
return true;
}
continue;
}
else
{
ThrowUnexpectedToken(pos + 1, "-");
}
}
// CDATA section
else if (chars[pos] == '[')
{
pos++;
if (_ps.charsUsed - pos < 6)
{
goto ReadData;
}
if (XmlConvert.StrEqual(chars, pos, 6, "CDATA["))
{
_ps.charPos = pos + 6;
ParseCData();
return true;
}
else
{
ThrowUnexpectedToken(pos, "CDATA[");
}
}
else
{
if (ParseUnexpectedToken(pos) == "DOCTYPE")
{
Throw(ResXml.Xml_BadDTDLocation);
}
else
{
ThrowUnexpectedToken(pos, "<!--", "<[CDATA[");
}
}
break;
// element end tag
case '/':
_ps.charPos = pos + 2;
ParseEndElement();
return true;
default:
// end of buffer
if (pos + 1 == _ps.charsUsed)
{
goto ReadData;
}
else
{
// element start tag
_ps.charPos = pos + 1;
ParseElement();
return true;
}
}
break;
case '&':
if (ParseText())
{
return true;
}
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReadData;
}
else
{
// text node, whitespace or entity reference
if (ParseText())
{
return true;
}
continue;
}
}
ReadData:
// read new characters into the buffer
if (ReadData() == 0)
{
if (_ps.charsUsed - _ps.charPos != 0)
{
ThrowUnclosedElements();
}
if (!InEntity)
{
if (_index == 0 && _fragmentType != XmlNodeType.Document)
{
OnEof();
return false;
}
ThrowUnclosedElements();
}
if (HandleEntityEnd(true))
{
SetupEndEntityNodeInContent();
return true;
}
}
}
}
private void ThrowUnclosedElements()
{
if (_index == 0 && _curNode.type != XmlNodeType.Element)
{
Throw(_ps.charsUsed, ResXml.Xml_UnexpectedEOF1);
}
else
{
int i = (_parsingFunction == ParsingFunction.InIncrementalRead) ? _index : _index - 1;
_stringBuilder.Length = 0;
for (; i >= 0; i--)
{
NodeData el = _nodes[i];
if (el.type != XmlNodeType.Element)
{
continue;
}
_stringBuilder.Append(el.GetNameWPrefix(_nameTable));
if (i > 0)
{
_stringBuilder.Append(", ");
}
else
{
_stringBuilder.Append(".");
}
}
Throw(_ps.charsUsed, ResXml.Xml_UnexpectedEOFInElementContent, _stringBuilder.ToString());
}
}
// Parses the element start tag
private void ParseElement()
{
int pos = _ps.charPos;
char[] chars = _ps.chars;
int colonPos = -1;
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
// 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)
ContinueStartName:
// check element name start char
unsafe
{
if ((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0)
{
pos++;
}
#if XML10_FIFTH_EDITION
else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], chars[pos])) {
pos += 2;
}
#endif
else
{
goto ParseQNameSlow;
}
}
ContinueName:
unsafe
{
// parse element name
for (; ; )
{
if (((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0))
{
pos++;
}
#if XML10_FIFTH_EDITION
else if ( pos < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], chars[pos])) {
pos += 2;
}
#endif
else
{
break;
}
}
}
// colon -> save prefix end position and check next char if it's name start char
if (chars[pos] == ':')
{
if (colonPos != -1)
{
if (_supportNamespaces)
{
Throw(pos, ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
}
else
{
pos++;
goto ContinueName;
}
}
else
{
colonPos = pos;
pos++;
goto ContinueStartName;
}
}
else if (pos + 1 < _ps.charsUsed)
{
goto SetElement;
}
ParseQNameSlow:
pos = ParseQName(out colonPos);
chars = _ps.chars;
SetElement:
// push namespace context
_namespaceManager.PushScope();
// init the NodeData class
if (colonPos == -1 || !_supportNamespaces)
{
_curNode.SetNamedNode(XmlNodeType.Element,
_nameTable.Add(chars, _ps.charPos, pos - _ps.charPos));
}
else
{
int startPos = _ps.charPos;
int prefixLen = colonPos - startPos;
if (prefixLen == _lastPrefix.Length && XmlConvert.StrEqual(chars, startPos, prefixLen, _lastPrefix))
{
_curNode.SetNamedNode(XmlNodeType.Element,
_nameTable.Add(chars, colonPos + 1, pos - colonPos - 1),
_lastPrefix,
null);
}
else
{
_curNode.SetNamedNode(XmlNodeType.Element,
_nameTable.Add(chars, colonPos + 1, pos - colonPos - 1),
_nameTable.Add(chars, _ps.charPos, prefixLen),
null);
_lastPrefix = _curNode.prefix;
}
}
char ch = chars[pos];
// white space after element name -> there are probably some attributes
bool isWs;
unsafe
{
isWs = ((_xmlCharType.charProperties[ch] & XmlCharType.fWhitespace) != 0);
}
if (isWs)
{
_ps.charPos = pos;
ParseAttributes();
return;
}
// no attributes
else
{
// non-empty element
if (ch == '>')
{
_ps.charPos = pos + 1;
_parsingFunction = ParsingFunction.MoveToElementContent;
}
// empty element
else if (ch == '/')
{
if (pos + 1 == _ps.charsUsed)
{
_ps.charPos = pos;
if (ReadData() == 0)
{
Throw(pos, ResXml.Xml_UnexpectedEOF, ">");
}
pos = _ps.charPos;
chars = _ps.chars;
}
if (chars[pos + 1] == '>')
{
_curNode.IsEmptyElement = true;
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.PopEmptyElementContext;
_ps.charPos = pos + 2;
}
else
{
ThrowUnexpectedToken(pos, ">");
}
}
// something else after the element name
else
{
Throw(pos, ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(chars, _ps.charsUsed, pos));
}
// add default attributes & strip spaces in attributes with type other than CDATA
if (_addDefaultAttributesAndNormalize)
{
AddDefaultAttributesAndNormalize();
}
// lookup element namespace
ElementNamespaceLookup();
}
}
private void AddDefaultAttributesAndNormalize()
{
Debug.Assert(_curNode.type == XmlNodeType.Element);
IDtdAttributeListInfo attlistInfo = _dtdInfo.LookupAttributeList(_curNode.localName, _curNode.prefix);
if (attlistInfo == null)
{
return;
}
// fix non-CDATA attribute value
if (_normalize && attlistInfo.HasNonCDataAttributes)
{
// go through the attributes and normalize it if not CDATA type
for (int i = _index + 1; i < _index + 1 + _attrCount; i++)
{
NodeData attr = _nodes[i];
IDtdAttributeInfo attributeInfo = attlistInfo.LookupAttribute(attr.prefix, attr.localName);
if (attributeInfo != null && attributeInfo.IsNonCDataType)
{
if (DtdValidation && _standalone && attributeInfo.IsDeclaredInExternal)
{
// VC constraint:
// The standalone document declaration must have the value "no" if any external markup declarations
// contain declarations of attributes with values subject to normalization, where the attribute appears in
// the document with a value which will change as a result of normalization,
string oldValue = attr.StringValue;
attr.TrimSpacesInValue();
if (oldValue != attr.StringValue)
{
SendValidationEvent(XmlSeverityType.Error, ResXml.Sch_StandAloneNormalization, attr.GetNameWPrefix(_nameTable), attr.LineNo, attr.LinePos);
}
}
else
attr.TrimSpacesInValue();
}
}
}
// add default attributes
IEnumerable<IDtdDefaultAttributeInfo> defaultAttributes = attlistInfo.LookupDefaultAttributes();
if (defaultAttributes != null)
{
int originalAttrCount = _attrCount;
NodeData[] nameSortedAttributes = null;
if (_attrCount >= MaxAttrDuplWalkCount)
{
nameSortedAttributes = new NodeData[_attrCount];
Array.Copy(_nodes, _index + 1, nameSortedAttributes, 0, _attrCount);
Array.Sort<object>(nameSortedAttributes, DtdDefaultAttributeInfoToNodeDataComparer.Instance);
}
foreach (IDtdDefaultAttributeInfo defaultAttributeInfo in defaultAttributes)
{
if (AddDefaultAttributeDtd(defaultAttributeInfo, true, nameSortedAttributes))
{
if (DtdValidation && _standalone && defaultAttributeInfo.IsDeclaredInExternal)
{
string prefix = defaultAttributeInfo.Prefix;
string qname = (prefix.Length == 0) ? defaultAttributeInfo.LocalName : (prefix + ':' + defaultAttributeInfo.LocalName);
SendValidationEvent(XmlSeverityType.Error, ResXml.Sch_UnSpecifiedDefaultAttributeInExternalStandalone, qname, _curNode.LineNo, _curNode.LinePos);
}
}
}
if (originalAttrCount == 0 && _attrNeedNamespaceLookup)
{
AttributeNamespaceLookup();
_attrNeedNamespaceLookup = false;
}
}
}
// parses the element end tag
private void ParseEndElement()
{
// check if the end tag name equals start tag name
NodeData startTagNode = _nodes[_index - 1];
int prefLen = startTagNode.prefix.Length;
int locLen = startTagNode.localName.Length;
while (_ps.charsUsed - _ps.charPos < prefLen + locLen + 1)
{
if (ReadData() == 0)
{
break;
}
}
int nameLen;
char[] chars = _ps.chars;
if (startTagNode.prefix.Length == 0)
{
if (!XmlConvert.StrEqual(chars, _ps.charPos, locLen, startTagNode.localName))
{
ThrowTagMismatch(startTagNode);
}
nameLen = locLen;
}
else
{
int colonPos = _ps.charPos + prefLen;
if (!XmlConvert.StrEqual(chars, _ps.charPos, prefLen, startTagNode.prefix) ||
chars[colonPos] != ':' ||
!XmlConvert.StrEqual(chars, colonPos + 1, locLen, startTagNode.localName))
{
ThrowTagMismatch(startTagNode);
}
nameLen = locLen + prefLen + 1;
}
LineInfo endTagLineInfo = new LineInfo(_ps.lineNo, _ps.LinePos);
int pos;
for (; ; )
{
pos = _ps.charPos + nameLen;
chars = _ps.chars;
if (pos == _ps.charsUsed)
{
goto ReadData;
}
unsafe
{
if (((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0) ||
(chars[pos] == ':')
#if XML10_FIFTH_EDITION
|| xmlCharType.IsNCNameHighSurrogateChar( chars[pos] )
#endif
)
{
ThrowTagMismatch(startTagNode);
}
}
// eat whitespaces
if (chars[pos] != '>')
{
char tmpCh;
while (_xmlCharType.IsWhiteSpace(tmpCh = chars[pos]))
{
pos++;
switch (tmpCh)
{
case (char)0xA:
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos] == (char)0xA)
{
pos++;
}
else if (pos == _ps.charsUsed && !_ps.isEof)
{
break;
}
OnNewLine(pos);
continue;
}
}
}
if (chars[pos] == '>')
{
break;
}
else if (pos == _ps.charsUsed)
{
goto ReadData;
}
else
{
ThrowUnexpectedToken(pos, ">");
}
Debug.Assert(false, "We should never get to this point.");
ReadData:
if (ReadData() == 0)
{
ThrowUnclosedElements();
}
}
Debug.Assert(_index > 0);
_index--;
_curNode = _nodes[_index];
// set the element data
Debug.Assert(_curNode == startTagNode);
startTagNode.lineInfo = endTagLineInfo;
startTagNode.type = XmlNodeType.EndElement;
_ps.charPos = pos + 1;
// set next parsing function
_nextParsingFunction = (_index > 0) ? _parsingFunction : ParsingFunction.DocumentContent;
_parsingFunction = ParsingFunction.PopElementContext;
}
private void ThrowTagMismatch(NodeData startTag)
{
if (startTag.type == XmlNodeType.Element)
{
// parse the bad name
int colonPos;
int endPos = ParseQName(out colonPos);
string[] args = new string[4];
args[0] = startTag.GetNameWPrefix(_nameTable);
args[1] = startTag.lineInfo.lineNo.ToString();
args[2] = startTag.lineInfo.linePos.ToString();
args[3] = new string(_ps.chars, _ps.charPos, endPos - _ps.charPos);
Throw(ResXml.Xml_TagMismatchEx, args);
}
else
{
Debug.Assert(startTag.type == XmlNodeType.EntityReference);
Throw(ResXml.Xml_UnexpectedEndTag);
}
}
// Reads the attributes
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.charProperties[tmpch0 = chars[pos]] & XmlCharType.fWhitespace) != 0))
{
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.charProperties[tmpch1 = chars[pos]] & XmlCharType.fNCStartNameSC) != 0)
{
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, ResXml.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.charProperties[tmpch2 = chars[pos]] & XmlCharType.fNCNameSC) != 0))
{
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, ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
}
else
{
pos++;
goto ContinueParseName;
}
}
else
{
colonPos = pos;
pos++;
unsafe
{
if (((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0))
{
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.charProperties[tmpch3 = chars[pos]] & XmlCharType.fAttrValue) != 0))
{
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();
}
}
private void ElementNamespaceLookup()
{
Debug.Assert(_curNode.type == XmlNodeType.Element);
if (_curNode.prefix.Length == 0)
{
_curNode.ns = _xmlContext.defaultNamespace;
}
else
{
_curNode.ns = LookupNamespace(_curNode);
}
}
private void AttributeNamespaceLookup()
{
for (int i = _index + 1; i < _index + _attrCount + 1; i++)
{
NodeData at = _nodes[i];
if (at.type == XmlNodeType.Attribute && at.prefix.Length > 0)
{
at.ns = LookupNamespace(at);
}
}
}
private void AttributeDuplCheck()
{
if (_attrCount < MaxAttrDuplWalkCount)
{
for (int i = _index + 1; i < _index + 1 + _attrCount; i++)
{
NodeData attr1 = _nodes[i];
for (int j = i + 1; j < _index + 1 + _attrCount; j++)
{
if (Ref.Equal(attr1.localName, _nodes[j].localName) && Ref.Equal(attr1.ns, _nodes[j].ns))
{
Throw(ResXml.Xml_DupAttributeName, _nodes[j].GetNameWPrefix(_nameTable), _nodes[j].LineNo, _nodes[j].LinePos);
}
}
}
}
else
{
if (_attrDuplSortingArray == null || _attrDuplSortingArray.Length < _attrCount)
{
_attrDuplSortingArray = new NodeData[_attrCount];
}
Array.Copy(_nodes, _index + 1, _attrDuplSortingArray, 0, _attrCount);
Array.Sort(_attrDuplSortingArray, 0, _attrCount);
NodeData attr1 = _attrDuplSortingArray[0];
for (int i = 1; i < _attrCount; i++)
{
NodeData attr2 = _attrDuplSortingArray[i];
if (Ref.Equal(attr1.localName, attr2.localName) && Ref.Equal(attr1.ns, attr2.ns))
{
Throw(ResXml.Xml_DupAttributeName, attr2.GetNameWPrefix(_nameTable), attr2.LineNo, attr2.LinePos);
}
attr1 = attr2;
}
}
}
private void OnDefaultNamespaceDecl(NodeData attr)
{
if (!_supportNamespaces)
{
return;
}
string ns = _nameTable.Add(attr.StringValue);
attr.ns = _nameTable.Add(XmlReservedNs.NsXmlNs);
if (!_curNode.xmlContextPushed)
{
PushXmlContext();
}
_xmlContext.defaultNamespace = ns;
AddNamespace(string.Empty, ns, attr);
}
private void OnNamespaceDecl(NodeData attr)
{
if (!_supportNamespaces)
{
return;
}
string ns = _nameTable.Add(attr.StringValue);
if (ns.Length == 0)
{
Throw(ResXml.Xml_BadNamespaceDecl, attr.lineInfo2.lineNo, attr.lineInfo2.linePos - 1);
}
AddNamespace(attr.localName, ns, attr);
}
private void OnXmlReservedAttribute(NodeData attr)
{
switch (attr.localName)
{
// xml:space
case "space":
if (!_curNode.xmlContextPushed)
{
PushXmlContext();
}
switch (XmlConvert.TrimString(attr.StringValue))
{
case "preserve":
_xmlContext.xmlSpace = XmlSpace.Preserve;
break;
case "default":
_xmlContext.xmlSpace = XmlSpace.Default;
break;
default:
Throw(ResXml.Xml_InvalidXmlSpace, attr.StringValue, attr.lineInfo.lineNo, attr.lineInfo.linePos);
break;
}
break;
// xml:lang
case "lang":
if (!_curNode.xmlContextPushed)
{
PushXmlContext();
}
_xmlContext.xmlLang = attr.StringValue;
break;
}
}
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.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0))
{
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, ResXml.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(ResXml.Xml_UnexpectedEOF1);
}
Debug.Assert(_ps.isEof);
}
else
{
if (!InEntity)
{
if (_fragmentType == XmlNodeType.Attribute)
{
if (attributeBaseEntityId != _ps.entityId)
{
Throw(ResXml.Xml_EntityRefNesting);
}
break;
}
Throw(ResXml.Xml_UnclosedQuote);
}
if (HandleEntityEnd(true))
{ // no EndEntity reporting while parsing attributes
Debug.Assert(false);
Throw(ResXml.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;
}
private void AddAttributeChunkToList(NodeData attr, NodeData chunk, ref NodeData lastChunk)
{
if (lastChunk == null)
{
Debug.Assert(attr.nextAttrValueChunk == null);
lastChunk = chunk;
attr.nextAttrValueChunk = chunk;
}
else
{
lastChunk.nextAttrValueChunk = chunk;
lastChunk = chunk;
}
}
// Parses text or white space node.
// Returns true if a node has been parsed and its data set to curNode.
// Returns false when a white space has been parsed and ignored (according to current whitespace handling) or when parsing mode is not Full.
// Also returns false if there is no text to be parsed.
private bool ParseText()
{
int startPos;
int endPos;
int orChars = 0;
// skip over the text if not in full parsing mode
if (_parsingMode != ParsingMode.Full)
{
while (!ParseText(out startPos, out endPos, ref orChars)) ;
goto IgnoredNode;
}
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
Debug.Assert(_stringBuilder.Length == 0);
// the whole value is in buffer
if (ParseText(out startPos, out endPos, ref orChars))
{
if (endPos - startPos == 0)
{
goto IgnoredNode;
}
XmlNodeType nodeType = GetTextNodeType(orChars);
if (nodeType == XmlNodeType.None)
{
goto IgnoredNode;
}
Debug.Assert(endPos - startPos > 0);
_curNode.SetValueNode(nodeType, _ps.chars, startPos, endPos - startPos);
return true;
}
// only piece of the value was returned
else
{
// V1 compatibility mode -> cache the whole value
if (_v1Compat)
{
do
{
if (endPos - startPos > 0)
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
} while (!ParseText(out startPos, out endPos, ref orChars));
if (endPos - startPos > 0)
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
Debug.Assert(_stringBuilder.Length > 0);
XmlNodeType nodeType = GetTextNodeType(orChars);
if (nodeType == XmlNodeType.None)
{
_stringBuilder.Length = 0;
goto IgnoredNode;
}
_curNode.SetValueNode(nodeType, _stringBuilder.ToString());
_stringBuilder.Length = 0;
return true;
}
// V2 reader -> do not cache the whole value yet, read only up to 4kB to decide whether the value is a whitespace
else
{
bool fullValue = false;
// if it's a partial text value, not a whitespace -> return
if (orChars > 0x20)
{
Debug.Assert(endPos - startPos > 0);
_curNode.SetValueNode(XmlNodeType.Text, _ps.chars, startPos, endPos - startPos);
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.PartialTextValue;
return true;
}
// partial whitespace -> read more data (up to 4kB) to decide if it is a whitespace or a text node
if (endPos - startPos > 0)
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
do
{
fullValue = ParseText(out startPos, out endPos, ref orChars);
if (endPos - startPos > 0)
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
} while (!fullValue && orChars <= 0x20 && _stringBuilder.Length < MinWhitespaceLookahedCount);
// determine the value node type
XmlNodeType nodeType = (_stringBuilder.Length < MinWhitespaceLookahedCount) ? GetTextNodeType(orChars) : XmlNodeType.Text;
if (nodeType == XmlNodeType.None)
{
// ignored whitespace -> skip over the rest of the value unless we already read it all
_stringBuilder.Length = 0;
if (!fullValue)
{
while (!ParseText(out startPos, out endPos, ref orChars)) ;
}
goto IgnoredNode;
}
// set value to curNode
_curNode.SetValueNode(nodeType, _stringBuilder.ToString());
_stringBuilder.Length = 0;
// change parsing state if the full value was not parsed
if (!fullValue)
{
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.PartialTextValue;
}
return true;
}
}
IgnoredNode:
// ignored whitespace at the end of manually resolved entity
if (_parsingFunction == ParsingFunction.ReportEndEntity)
{
SetupEndEntityNodeInContent();
_parsingFunction = _nextParsingFunction;
return true;
}
else if (_parsingFunction == ParsingFunction.EntityReference)
{
_parsingFunction = _nextNextParsingFunction;
ParseEntityReference();
return true;
}
return false;
}
// Parses a chunk of text starting at ps.charPos.
// startPos .... start position of the text chunk that has been parsed (can differ from ps.charPos before the call)
// endPos ...... end position of the text chunk that has been parsed (can differ from ps.charPos after the call)
// ourOrChars .. all parsed character bigger or equal to 0x20 or-ed (|) into a single int. It can be used for whitespace detection
// (the text has a non-whitespace character if outOrChars > 0x20).
// Returns true when the whole value has been parsed. Return false when it needs to be called again to get a next chunk of value.
private bool ParseText(out int startPos, out int endPos, ref int outOrChars)
{
char[] chars = _ps.chars;
int pos = _ps.charPos;
int rcount = 0;
int rpos = -1;
int orChars = outOrChars;
char c;
for (; ; )
{
// parse text content
unsafe
{
while (((_xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fText) != 0))
{
orChars |= (int)c;
pos++;
}
}
switch (c)
{
case (char)0x9:
pos++;
continue;
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
if (!_ps.eolNormalized && _parsingMode == ParsingMode.Full)
{
if (pos - _ps.charPos > 0)
{
if (rcount == 0)
{
rcount = 1;
rpos = pos;
}
else
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
rpos = pos - rcount;
rcount++;
}
}
else
{
_ps.charPos++;
}
}
pos += 2;
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
if (!_ps.eolNormalized)
{
chars[pos] = (char)0xA; // EOL normalization of 0xD
}
pos++;
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
// some tag
case '<':
goto ReturnPartialValue;
// entity reference
case '&':
// try to parse char entity inline
int charRefEndPos, charCount;
EntityType entityType;
if ((charRefEndPos = ParseCharRefInline(pos, out charCount, out entityType)) > 0)
{
if (rcount > 0)
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
}
rpos = pos - rcount;
rcount += (charRefEndPos - pos - charCount);
pos = charRefEndPos;
if (!_xmlCharType.IsWhiteSpace(chars[charRefEndPos - charCount]) ||
(_v1Compat && entityType == EntityType.CharacterDec))
{
orChars |= 0xFF;
}
}
else
{
if (pos > _ps.charPos)
{
goto ReturnPartialValue;
}
switch (HandleEntityReference(false, EntityExpandType.All, out pos))
{
case EntityType.Unexpanded:
// make sure we will report EntityReference after the text node
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.EntityReference;
// end the value (returns nothing)
goto NoValue;
case EntityType.CharacterDec:
if (!_v1Compat)
{
goto case EntityType.CharacterHex;
}
orChars |= 0xFF;
break;
case EntityType.CharacterHex:
case EntityType.CharacterNamed:
if (!_xmlCharType.IsWhiteSpace(_ps.chars[pos - 1]))
{
orChars |= 0xFF;
}
break;
default:
pos = _ps.charPos;
break;
}
chars = _ps.chars;
}
continue;
case ']':
if (_ps.charsUsed - pos < 3 && !_ps.isEof)
{
goto ReadData;
}
if (chars[pos + 1] == ']' && chars[pos + 2] == '>')
{
Throw(pos, ResXml.Xml_CDATAEndInText);
}
orChars |= ']';
pos++;
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++;
orChars |= ch;
continue;
}
}
int offset = pos - _ps.charPos;
if (ZeroEndingStream(pos))
{
chars = _ps.chars;
pos = _ps.charPos + offset;
goto ReturnPartialValue;
}
else
{
ThrowInvalidChar(_ps.chars, _ps.charsUsed, _ps.charPos + offset);
}
break;
}
}
ReadData:
if (pos > _ps.charPos)
{
goto ReturnPartialValue;
}
// read new characters into the buffer
if (ReadData() == 0)
{
if (_ps.charsUsed - _ps.charPos > 0)
{
if (_ps.chars[_ps.charPos] != (char)0xD && _ps.chars[_ps.charPos] != ']')
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
Debug.Assert(_ps.isEof);
}
else
{
if (!InEntity)
{
// end the value (returns nothing)
goto NoValue;
}
if (HandleEntityEnd(true))
{
// report EndEntity after the text node
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.ReportEndEntity;
// end the value (returns nothing)
goto NoValue;
}
}
}
pos = _ps.charPos;
chars = _ps.chars;
continue;
}
NoValue:
startPos = endPos = pos;
return true;
ReturnPartialValue:
if (_parsingMode == ParsingMode.Full && rcount > 0)
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
}
startPos = _ps.charPos;
endPos = pos - rcount;
_ps.charPos = pos;
outOrChars = orChars;
return c == '<';
}
// When in ParsingState.PartialTextValue, this method parses and caches the rest of the value and stores it in curNode.
private void FinishPartialValue()
{
Debug.Assert(_stringBuilder.Length == 0);
Debug.Assert(_parsingFunction == ParsingFunction.PartialTextValue ||
(_parsingFunction == ParsingFunction.InReadValueChunk && _incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue));
_curNode.CopyTo(_readValueOffset, _stringBuilder);
int startPos;
int endPos;
int orChars = 0;
while (!ParseText(out startPos, out endPos, ref orChars))
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
Debug.Assert(_stringBuilder.Length > 0);
_curNode.SetValue(_stringBuilder.ToString());
_stringBuilder.Length = 0;
}
private void FinishOtherValueIterator()
{
switch (_parsingFunction)
{
case ParsingFunction.InReadAttributeValue:
// do nothing, correct value is already in curNode
break;
case ParsingFunction.InReadValueChunk:
if (_incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue)
{
FinishPartialValue();
_incReadState = IncrementalReadState.ReadValueChunk_OnCachedValue;
}
else
{
if (_readValueOffset > 0)
{
_curNode.SetValue(_curNode.StringValue.Substring(_readValueOffset));
_readValueOffset = 0;
}
}
break;
case ParsingFunction.InReadContentAsBinary:
case ParsingFunction.InReadElementContentAsBinary:
switch (_incReadState)
{
case IncrementalReadState.ReadContentAsBinary_OnPartialValue:
FinishPartialValue();
_incReadState = IncrementalReadState.ReadContentAsBinary_OnCachedValue;
break;
case IncrementalReadState.ReadContentAsBinary_OnCachedValue:
if (_readValueOffset > 0)
{
_curNode.SetValue(_curNode.StringValue.Substring(_readValueOffset));
_readValueOffset = 0;
}
break;
case IncrementalReadState.ReadContentAsBinary_End:
_curNode.SetValue(string.Empty);
break;
}
break;
}
}
// When in ParsingState.PartialTextValue, this method skips over the rest of the partial value.
[MethodImplAttribute(MethodImplOptions.NoInlining)]
private void SkipPartialTextValue()
{
Debug.Assert(_parsingFunction == ParsingFunction.PartialTextValue || _parsingFunction == ParsingFunction.InReadValueChunk ||
_parsingFunction == ParsingFunction.InReadContentAsBinary || _parsingFunction == ParsingFunction.InReadElementContentAsBinary);
int startPos;
int endPos;
int orChars = 0;
_parsingFunction = _nextParsingFunction;
while (!ParseText(out startPos, out endPos, ref orChars)) ;
}
private void FinishReadValueChunk()
{
Debug.Assert(_parsingFunction == ParsingFunction.InReadValueChunk);
_readValueOffset = 0;
if (_incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue)
{
Debug.Assert((_index > 0) ? _nextParsingFunction == ParsingFunction.ElementContent : _nextParsingFunction == ParsingFunction.DocumentContent);
SkipPartialTextValue();
}
else
{
_parsingFunction = _nextParsingFunction;
_nextParsingFunction = _nextNextParsingFunction;
}
}
private void FinishReadContentAsBinary()
{
Debug.Assert(_parsingFunction == ParsingFunction.InReadContentAsBinary || _parsingFunction == ParsingFunction.InReadElementContentAsBinary);
_readValueOffset = 0;
if (_incReadState == IncrementalReadState.ReadContentAsBinary_OnPartialValue)
{
Debug.Assert((_index > 0) ? _nextParsingFunction == ParsingFunction.ElementContent : _nextParsingFunction == ParsingFunction.DocumentContent);
SkipPartialTextValue();
}
else
{
_parsingFunction = _nextParsingFunction;
_nextParsingFunction = _nextNextParsingFunction;
}
if (_incReadState != IncrementalReadState.ReadContentAsBinary_End)
{
while (MoveToNextContentNode(true)) ;
}
}
private void FinishReadElementContentAsBinary()
{
FinishReadContentAsBinary();
if (_curNode.type != XmlNodeType.EndElement)
{
Throw(ResXml.Xml_InvalidNodeType, _curNode.type.ToString());
}
// move off the end element
_outerReader.Read();
}
private bool ParseRootLevelWhitespace()
{
Debug.Assert(_stringBuilder.Length == 0);
XmlNodeType nodeType = GetWhitespaceType();
if (nodeType == XmlNodeType.None)
{
EatWhitespaces(null);
if (_ps.chars[_ps.charPos] == '<' || _ps.charsUsed - _ps.charPos == 0 || ZeroEndingStream(_ps.charPos))
{
return false;
}
}
else
{
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
EatWhitespaces(_stringBuilder);
if (_ps.chars[_ps.charPos] == '<' || _ps.charsUsed - _ps.charPos == 0 || ZeroEndingStream(_ps.charPos))
{
if (_stringBuilder.Length > 0)
{
_curNode.SetValueNode(nodeType, _stringBuilder.ToString());
_stringBuilder.Length = 0;
return true;
}
return false;
}
}
if (_xmlCharType.IsCharData(_ps.chars[_ps.charPos]))
{
Throw(ResXml.Xml_InvalidRootData);
}
else
{
ThrowInvalidChar(_ps.chars, _ps.charsUsed, _ps.charPos);
}
return false;
}
private void ParseEntityReference()
{
Debug.Assert(_ps.chars[_ps.charPos] == '&');
_ps.charPos++;
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
_curNode.SetNamedNode(XmlNodeType.EntityReference, ParseEntityName());
}
private EntityType HandleEntityReference(bool isInAttributeValue, EntityExpandType expandType, out int charRefEndPos)
{
Debug.Assert(_ps.chars[_ps.charPos] == '&');
if (_ps.charPos + 1 == _ps.charsUsed)
{
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
}
// numeric characters reference
if (_ps.chars[_ps.charPos + 1] == '#')
{
EntityType entityType;
charRefEndPos = ParseNumericCharRef(expandType != EntityExpandType.OnlyGeneral, null, out entityType);
Debug.Assert(entityType == EntityType.CharacterDec || entityType == EntityType.CharacterHex);
return entityType;
}
// named reference
else
{
// named character reference
charRefEndPos = ParseNamedCharRef(expandType != EntityExpandType.OnlyGeneral, null);
if (charRefEndPos >= 0)
{
return EntityType.CharacterNamed;
}
// general entity reference
// NOTE: XmlValidatingReader compatibility mode: expand all entities in attribute values
// general entity reference
if (expandType == EntityExpandType.OnlyCharacter ||
(_entityHandling != EntityHandling.ExpandEntities &&
(!isInAttributeValue || !_validatingReaderCompatFlag)))
{
return EntityType.Unexpanded;
}
int endPos;
_ps.charPos++;
int savedLinePos = _ps.LinePos;
try
{
endPos = ParseName();
}
catch (XmlException)
{
Throw(ResXml.Xml_ErrorParsingEntityName, _ps.LineNo, savedLinePos);
return EntityType.Skipped;
}
// check ';'
if (_ps.chars[endPos] != ';')
{
ThrowUnexpectedToken(endPos, ";");
}
int entityLinePos = _ps.LinePos;
string entityName = _nameTable.Add(_ps.chars, _ps.charPos, endPos - _ps.charPos);
_ps.charPos = endPos + 1;
charRefEndPos = -1;
EntityType entType = HandleGeneralEntityReference(entityName, isInAttributeValue, false, entityLinePos);
_reportedBaseUri = _ps.baseUriStr;
_reportedEncoding = _ps.encoding;
return entType;
}
}
// returns true == continue parsing
// return false == unexpanded external entity, stop parsing and return
private EntityType HandleGeneralEntityReference(string name, bool isInAttributeValue, bool pushFakeEntityIfNullResolver, int entityStartLinePos)
{
IDtdEntityInfo entity = null;
if (_dtdInfo == null && _fragmentParserContext != null && _fragmentParserContext.HasDtdInfo && _dtdProcessing == DtdProcessing.Parse)
{
ParseDtdFromParserContext();
}
if (_dtdInfo == null ||
((entity = _dtdInfo.LookupEntity(name)) == null))
{
if (_disableUndeclaredEntityCheck)
{
SchemaEntity schemaEntity = new SchemaEntity(new XmlQualifiedName(name), false);
schemaEntity.Text = string.Empty;
entity = schemaEntity;
}
else
Throw(ResXml.Xml_UndeclaredEntity, name, _ps.LineNo, entityStartLinePos);
}
if (entity.IsUnparsedEntity)
{
if (_disableUndeclaredEntityCheck)
{
SchemaEntity schemaEntity = new SchemaEntity(new XmlQualifiedName(name), false);
schemaEntity.Text = string.Empty;
entity = schemaEntity;
}
else
Throw(ResXml.Xml_UnparsedEntityRef, name, _ps.LineNo, entityStartLinePos);
}
if (_standalone && entity.IsDeclaredInExternal)
{
Throw(ResXml.Xml_ExternalEntityInStandAloneDocument, entity.Name, _ps.LineNo, entityStartLinePos);
}
if (entity.IsExternal)
{
if (isInAttributeValue)
{
Throw(ResXml.Xml_ExternalEntityInAttValue, name, _ps.LineNo, entityStartLinePos);
return EntityType.Skipped;
}
if (_parsingMode == ParsingMode.SkipContent)
{
return EntityType.Skipped;
}
if (IsResolverNull)
{
if (pushFakeEntityIfNullResolver)
{
PushExternalEntity(entity);
_curNode.entityId = _ps.entityId;
return EntityType.FakeExpanded;
}
return EntityType.Skipped;
}
else
{
PushExternalEntity(entity);
_curNode.entityId = _ps.entityId;
return (isInAttributeValue && _validatingReaderCompatFlag) ? EntityType.ExpandedInAttribute : EntityType.Expanded;
}
}
else
{
if (_parsingMode == ParsingMode.SkipContent)
{
return EntityType.Skipped;
}
PushInternalEntity(entity);
_curNode.entityId = _ps.entityId;
return (isInAttributeValue && _validatingReaderCompatFlag) ? EntityType.ExpandedInAttribute : EntityType.Expanded;
}
}
private bool InEntity
{
get
{
return _parsingStatesStackTop >= 0;
}
}
// return true if EndEntity node should be reported. The entity is stored in lastEntity.
private bool HandleEntityEnd(bool checkEntityNesting)
{
if (_parsingStatesStackTop == -1)
{
Debug.Assert(false);
Throw(ResXml.Xml_InternalError);
}
if (_ps.entityResolvedManually)
{
_index--;
if (checkEntityNesting)
{
if (_ps.entityId != _nodes[_index].entityId)
{
Throw(ResXml.Xml_IncompleteEntity);
}
}
_lastEntity = _ps.entity; // save last entity for the EndEntity node
PopEntity();
return true;
}
else
{
if (checkEntityNesting)
{
if (_ps.entityId != _nodes[_index].entityId)
{
Throw(ResXml.Xml_IncompleteEntity);
}
}
PopEntity();
_reportedEncoding = _ps.encoding;
_reportedBaseUri = _ps.baseUriStr;
return false;
}
}
private void SetupEndEntityNodeInContent()
{
Debug.Assert(_lastEntity != null);
_reportedEncoding = _ps.encoding;
_reportedBaseUri = _ps.baseUriStr;
_curNode = _nodes[_index];
Debug.Assert(_curNode.depth == _index);
_curNode.SetNamedNode(XmlNodeType.EndEntity, _lastEntity.Name);
_curNode.lineInfo.Set(_ps.lineNo, _ps.LinePos - 1);
if (_index == 0 && _parsingFunction == ParsingFunction.ElementContent)
{
_parsingFunction = ParsingFunction.DocumentContent;
}
}
private void SetupEndEntityNodeInAttribute()
{
_curNode = _nodes[_index + _attrCount + 1];
Debug.Assert(_curNode.type == XmlNodeType.EntityReference);
Debug.Assert(Ref.Equal(_lastEntity.Name, _curNode.localName));
_curNode.lineInfo.linePos += _curNode.localName.Length;
_curNode.type = XmlNodeType.EndEntity;
}
private bool ParsePI()
{
return ParsePI(null);
}
// Parses processing instruction; if piInDtdStringBuilder != null, the processing instruction is in DTD and
// it will be saved in the passed string builder (target, whitespace & value).
private bool ParsePI(BufferBuilder piInDtdStringBuilder)
{
if (_parsingMode == ParsingMode.Full)
{
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
}
Debug.Assert(_stringBuilder.Length == 0);
// parse target name
int nameEndPos = ParseName();
string target = _nameTable.Add(_ps.chars, _ps.charPos, nameEndPos - _ps.charPos);
if (string.Compare(target, "xml", StringComparison.OrdinalIgnoreCase) == 0)
{
Throw(target.Equals("xml") ? ResXml.Xml_XmlDeclNotFirst : ResXml.Xml_InvalidPIName, target);
}
_ps.charPos = nameEndPos;
if (piInDtdStringBuilder == null)
{
if (!_ignorePIs && _parsingMode == ParsingMode.Full)
{
_curNode.SetNamedNode(XmlNodeType.ProcessingInstruction, target);
}
}
else
{
piInDtdStringBuilder.Append(target);
}
// check mandatory whitespace
char ch = _ps.chars[_ps.charPos];
Debug.Assert(_ps.charPos < _ps.charsUsed);
if (EatWhitespaces(piInDtdStringBuilder) == 0)
{
if (_ps.charsUsed - _ps.charPos < 2)
{
ReadData();
}
if (ch != '?' || _ps.chars[_ps.charPos + 1] != '>')
{
Throw(ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(_ps.chars, _ps.charsUsed, _ps.charPos));
}
}
// scan processing instruction value
int startPos, endPos;
if (ParsePIValue(out startPos, out endPos))
{
if (piInDtdStringBuilder == null)
{
if (_ignorePIs)
{
return false;
}
if (_parsingMode == ParsingMode.Full)
{
_curNode.SetValue(_ps.chars, startPos, endPos - startPos);
}
}
else
{
piInDtdStringBuilder.Append(_ps.chars, startPos, endPos - startPos);
}
}
else
{
BufferBuilder sb;
if (piInDtdStringBuilder == null)
{
if (_ignorePIs || _parsingMode != ParsingMode.Full)
{
while (!ParsePIValue(out startPos, out endPos)) ;
return false;
}
sb = _stringBuilder;
Debug.Assert(_stringBuilder.Length == 0);
}
else
{
sb = piInDtdStringBuilder;
}
do
{
sb.Append(_ps.chars, startPos, endPos - startPos);
} while (!ParsePIValue(out startPos, out endPos));
sb.Append(_ps.chars, startPos, endPos - startPos);
if (piInDtdStringBuilder == null)
{
_curNode.SetValue(_stringBuilder.ToString());
_stringBuilder.Length = 0;
}
}
return true;
}
private bool ParsePIValue(out int outStartPos, out int outEndPos)
{
// read new characters into the buffer
if (_ps.charsUsed - _ps.charPos < 2)
{
if (ReadData() == 0)
{
Throw(_ps.charsUsed, ResXml.Xml_UnexpectedEOF, "PI");
}
}
int pos = _ps.charPos;
char[] chars = _ps.chars;
int rcount = 0;
int rpos = -1;
for (; ; )
{
char tmpch;
unsafe
{
while (((_xmlCharType.charProperties[tmpch = chars[pos]] & XmlCharType.fText) != 0) &&
tmpch != '?')
{
pos++;
}
}
switch (chars[pos])
{
// possibly end of PI
case '?':
if (chars[pos + 1] == '>')
{
if (rcount > 0)
{
Debug.Assert(!_ps.eolNormalized);
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
outEndPos = pos - rcount;
}
else
{
outEndPos = pos;
}
outStartPos = _ps.charPos;
_ps.charPos = pos + 2;
return true;
}
else if (pos + 1 == _ps.charsUsed)
{
goto ReturnPartial;
}
else
{
pos++;
continue;
}
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
if (!_ps.eolNormalized && _parsingMode == ParsingMode.Full)
{
// EOL normalization of 0xD 0xA
if (pos - _ps.charPos > 0)
{
if (rcount == 0)
{
rcount = 1;
rpos = pos;
}
else
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
rpos = pos - rcount;
rcount++;
}
}
else
{
_ps.charPos++;
}
}
pos += 2;
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
if (!_ps.eolNormalized)
{
chars[pos] = (char)0xA; // EOL normalization of 0xD
}
pos++;
}
else
{
goto ReturnPartial;
}
OnNewLine(pos);
continue;
case '<':
case '&':
case ']':
case (char)0x9:
pos++;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReturnPartial;
}
// surrogate characters
else
{
char ch = chars[pos];
if (XmlCharType.IsHighSurrogate(ch))
{
if (pos + 1 == _ps.charsUsed)
{
goto ReturnPartial;
}
pos++;
if (XmlCharType.IsLowSurrogate(chars[pos]))
{
pos++;
continue;
}
}
ThrowInvalidChar(chars, _ps.charsUsed, pos);
break;
}
}
}
ReturnPartial:
if (rcount > 0)
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
outEndPos = pos - rcount;
}
else
{
outEndPos = pos;
}
outStartPos = _ps.charPos;
_ps.charPos = pos;
return false;
}
private bool ParseComment()
{
if (_ignoreComments)
{
ParsingMode oldParsingMode = _parsingMode;
_parsingMode = ParsingMode.SkipNode;
ParseCDataOrComment(XmlNodeType.Comment);
_parsingMode = oldParsingMode;
return false;
}
else
{
ParseCDataOrComment(XmlNodeType.Comment);
return true;
}
}
private void ParseCData()
{
ParseCDataOrComment(XmlNodeType.CDATA);
}
// Parses CDATA section or comment
private void ParseCDataOrComment(XmlNodeType type)
{
int startPos, endPos;
if (_parsingMode == ParsingMode.Full)
{
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
Debug.Assert(_stringBuilder.Length == 0);
if (ParseCDataOrComment(type, out startPos, out endPos))
{
_curNode.SetValueNode(type, _ps.chars, startPos, endPos - startPos);
}
else
{
do
{
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
} while (!ParseCDataOrComment(type, out startPos, out endPos));
_stringBuilder.Append(_ps.chars, startPos, endPos - startPos);
_curNode.SetValueNode(type, _stringBuilder.ToString());
_stringBuilder.Length = 0;
}
}
else
{
while (!ParseCDataOrComment(type, out startPos, out endPos)) ;
}
}
// Parses a chunk of CDATA section or comment. Returns true when the end of CDATA or comment was reached.
private bool ParseCDataOrComment(XmlNodeType type, out int outStartPos, out int outEndPos)
{
if (_ps.charsUsed - _ps.charPos < 3)
{
// read new characters into the buffer
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF, (type == XmlNodeType.Comment) ? "Comment" : "CDATA");
}
}
int pos = _ps.charPos;
char[] chars = _ps.chars;
int rcount = 0;
int rpos = -1;
char stopChar = (type == XmlNodeType.Comment) ? '-' : ']';
for (; ; )
{
char tmpch;
unsafe
{
while (((_xmlCharType.charProperties[tmpch = chars[pos]] & XmlCharType.fText) != 0) &&
tmpch != stopChar)
{
pos++;
}
}
// posibbly end of comment or cdata section
if (chars[pos] == stopChar)
{
if (chars[pos + 1] == stopChar)
{
if (chars[pos + 2] == '>')
{
if (rcount > 0)
{
Debug.Assert(!_ps.eolNormalized);
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
outEndPos = pos - rcount;
}
else
{
outEndPos = pos;
}
outStartPos = _ps.charPos;
_ps.charPos = pos + 3;
return true;
}
else if (pos + 2 == _ps.charsUsed)
{
goto ReturnPartial;
}
else if (type == XmlNodeType.Comment)
{
Throw(pos, ResXml.Xml_InvalidCommentChars);
}
}
else if (pos + 1 == _ps.charsUsed)
{
goto ReturnPartial;
}
pos++;
continue;
}
else
{
switch (chars[pos])
{
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
// EOL normalization of 0xD 0xA - shift the buffer
if (!_ps.eolNormalized && _parsingMode == ParsingMode.Full)
{
if (pos - _ps.charPos > 0)
{
if (rcount == 0)
{
rcount = 1;
rpos = pos;
}
else
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
rpos = pos - rcount;
rcount++;
}
}
else
{
_ps.charPos++;
}
}
pos += 2;
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
if (!_ps.eolNormalized)
{
chars[pos] = (char)0xA; // EOL normalization of 0xD
}
pos++;
}
else
{
goto ReturnPartial;
}
OnNewLine(pos);
continue;
case '<':
case '&':
case ']':
case (char)0x9:
pos++;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReturnPartial;
}
// surrogate characters
char ch = chars[pos];
if (XmlCharType.IsHighSurrogate(ch))
{
if (pos + 1 == _ps.charsUsed)
{
goto ReturnPartial;
}
pos++;
if (XmlCharType.IsLowSurrogate(chars[pos]))
{
pos++;
continue;
}
}
ThrowInvalidChar(chars, _ps.charsUsed, pos);
break;
}
}
ReturnPartial:
if (rcount > 0)
{
ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
outEndPos = pos - rcount;
}
else
{
outEndPos = pos;
}
outStartPos = _ps.charPos;
_ps.charPos = pos;
return false; // false == parsing of comment or CData section is not finished yet, must be called again
}
}
// Parses DOCTYPE declaration
private bool ParseDoctypeDecl()
{
if (_dtdProcessing == DtdProcessing.Prohibit)
{
ThrowWithoutLineInfo(_v1Compat ? ResXml.Xml_DtdIsProhibited : ResXml.Xml_DtdIsProhibitedEx);
}
// parse 'DOCTYPE'
while (_ps.charsUsed - _ps.charPos < 8)
{
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF, "DOCTYPE");
}
}
if (!XmlConvert.StrEqual(_ps.chars, _ps.charPos, 7, "DOCTYPE"))
{
ThrowUnexpectedToken((!_rootElementParsed && _dtdInfo == null) ? "DOCTYPE" : "<!--");
}
if (!_xmlCharType.IsWhiteSpace(_ps.chars[_ps.charPos + 7]))
{
ThrowExpectingWhitespace(_ps.charPos + 7);
}
if (_dtdInfo != null)
{
Throw(_ps.charPos - 2, ResXml.Xml_MultipleDTDsProvided); // position just before <!DOCTYPE
}
if (_rootElementParsed)
{
Throw(_ps.charPos - 2, ResXml.Xml_DtdAfterRootElement);
}
_ps.charPos += 8;
EatWhitespaces(null);
// Parse DTD
if (_dtdProcessing == DtdProcessing.Parse)
{
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
ParseDtd();
_nextParsingFunction = _parsingFunction;
_parsingFunction = ParsingFunction.ResetAttributesRootLevel;
return true;
}
// Skip DTD
else
{
Debug.Assert(_dtdProcessing == DtdProcessing.Ignore);
SkipDtd();
return false;
}
}
private void ParseDtd()
{
IDtdParser dtdParser = DtdParser.Create();
_dtdInfo = dtdParser.ParseInternalDtd(new DtdParserProxy(this), true);
if ((_validatingReaderCompatFlag || !_v1Compat) && (_dtdInfo.HasDefaultAttributes || _dtdInfo.HasNonCDataAttributes))
{
_addDefaultAttributesAndNormalize = true;
}
_curNode.SetNamedNode(XmlNodeType.DocumentType, _dtdInfo.Name.ToString(), string.Empty, null);
_curNode.SetValue(_dtdInfo.InternalDtdSubset);
}
private void SkipDtd()
{
int colonPos;
// parse dtd name
int pos = ParseQName(out colonPos);
_ps.charPos = pos;
// check whitespace
EatWhitespaces(null);
// PUBLIC Id
if (_ps.chars[_ps.charPos] == 'P')
{
// make sure we have enough characters
while (_ps.charsUsed - _ps.charPos < 6)
{
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
}
// check 'PUBLIC'
if (!XmlConvert.StrEqual(_ps.chars, _ps.charPos, 6, "PUBLIC"))
{
ThrowUnexpectedToken("PUBLIC");
}
_ps.charPos += 6;
// check whitespace
if (EatWhitespaces(null) == 0)
{
ThrowExpectingWhitespace(_ps.charPos);
}
// parse PUBLIC value
SkipPublicOrSystemIdLiteral();
// check whitespace
if (EatWhitespaces(null) == 0)
{
ThrowExpectingWhitespace(_ps.charPos);
}
// parse SYSTEM value
SkipPublicOrSystemIdLiteral();
EatWhitespaces(null);
}
else if (_ps.chars[_ps.charPos] == 'S')
{
// make sure we have enough characters
while (_ps.charsUsed - _ps.charPos < 6)
{
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
}
// check 'SYSTEM'
if (!XmlConvert.StrEqual(_ps.chars, _ps.charPos, 6, "SYSTEM"))
{
ThrowUnexpectedToken("SYSTEM");
}
_ps.charPos += 6;
// check whitespace
if (EatWhitespaces(null) == 0)
{
ThrowExpectingWhitespace(_ps.charPos);
}
// parse SYSTEM value
SkipPublicOrSystemIdLiteral();
EatWhitespaces(null);
}
else if (_ps.chars[_ps.charPos] != '[' && _ps.chars[_ps.charPos] != '>')
{
Throw(ResXml.Xml_ExpectExternalOrClose);
}
// internal DTD
if (_ps.chars[_ps.charPos] == '[')
{
_ps.charPos++;
SkipUntil(']', true);
EatWhitespaces(null);
if (_ps.chars[_ps.charPos] != '>')
{
ThrowUnexpectedToken(">");
}
}
else if (_ps.chars[_ps.charPos] == '>')
{
_curNode.SetValue(string.Empty);
}
else
{
Throw(ResXml.Xml_ExpectSubOrClose);
}
_ps.charPos++;
}
private void SkipPublicOrSystemIdLiteral()
{
// check quote char
char quoteChar = _ps.chars[_ps.charPos];
if (quoteChar != '"' && quoteChar != '\'')
{
ThrowUnexpectedToken("\"", "'");
}
_ps.charPos++;
SkipUntil(quoteChar, false);
}
private void SkipUntil(char stopChar, bool recognizeLiterals)
{
bool inLiteral = false;
bool inComment = false;
bool inPI = false;
char literalQuote = '"';
char[] chars = _ps.chars;
int pos = _ps.charPos;
for (; ; )
{
char ch;
unsafe
{
while (((_xmlCharType.charProperties[ch = chars[pos]] & XmlCharType.fAttrValue) != 0) && chars[pos] != stopChar && ch != '-' && ch != '?')
{
pos++;
}
}
// closing stopChar outside of literal and ignore/include sections -> save value & return
if (ch == stopChar && !inLiteral)
{
_ps.charPos = pos + 1;
return;
}
// handle the special character
_ps.charPos = pos;
switch (ch)
{
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
pos += 2;
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
pos++;
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
// comment, PI
case '<':
// processing instruction
if (chars[pos + 1] == '?')
{
if (recognizeLiterals && !inLiteral && !inComment)
{
inPI = true;
pos += 2;
continue;
}
}
// comment
else if (chars[pos + 1] == '!')
{
if (pos + 3 >= _ps.charsUsed && !_ps.isEof)
{
goto ReadData;
}
if (chars[pos + 2] == '-' && chars[pos + 3] == '-')
{
if (recognizeLiterals && !inLiteral && !inPI)
{
inComment = true;
pos += 4;
continue;
}
}
}
// need more data
else if (pos + 1 >= _ps.charsUsed && !_ps.isEof)
{
goto ReadData;
}
pos++;
continue;
case '-':
// end of comment
if (inComment)
{
if (pos + 2 >= _ps.charsUsed && !_ps.isEof)
{
goto ReadData;
}
if (chars[pos + 1] == '-' && chars[pos + 2] == '>')
{
inComment = false;
pos += 2;
continue;
}
}
pos++;
continue;
case '?':
// end of processing instruction
if (inPI)
{
if (pos + 1 >= _ps.charsUsed && !_ps.isEof)
{
goto ReadData;
}
if (chars[pos + 1] == '>')
{
inPI = false;
pos += 1;
continue;
}
}
pos++;
continue;
case (char)0x9:
case '>':
case ']':
case '&':
pos++;
continue;
case '"':
case '\'':
if (inLiteral)
{
if (literalQuote == ch)
{
inLiteral = false;
}
}
else
{
if (recognizeLiterals && !inComment && !inPI)
{
inLiteral = true;
literalQuote = ch;
}
}
pos++;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReadData;
}
// surrogate chars
else
{
char tmpCh = chars[pos];
if (XmlCharType.IsHighSurrogate(tmpCh))
{
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(ResXml.Xml_UnexpectedEOF1);
}
Debug.Assert(_ps.isEof);
}
else
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
}
chars = _ps.chars;
pos = _ps.charPos;
}
}
private int EatWhitespaces(BufferBuilder sb)
{
int pos = _ps.charPos;
int wsCount = 0;
char[] chars = _ps.chars;
for (; ; )
{
for (; ; )
{
switch (chars[pos])
{
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
int tmp1 = pos - _ps.charPos;
if (sb != null && !_ps.eolNormalized)
{
if (tmp1 > 0)
{
sb.Append(chars, _ps.charPos, tmp1);
wsCount += tmp1;
}
_ps.charPos = pos + 1;
}
pos += 2;
}
else if (pos + 1 < _ps.charsUsed || _ps.isEof)
{
if (!_ps.eolNormalized)
{
chars[pos] = (char)0xA; // EOL normalization of 0xD
}
pos++;
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
case (char)0x9:
case (char)0x20:
pos++;
continue;
default:
if (pos == _ps.charsUsed)
{
goto ReadData;
}
else
{
int tmp2 = pos - _ps.charPos;
if (tmp2 > 0)
{
if (sb != null)
{
sb.Append(_ps.chars, _ps.charPos, tmp2);
}
_ps.charPos = pos;
wsCount += tmp2;
}
return wsCount;
}
}
}
ReadData:
int tmp3 = pos - _ps.charPos;
if (tmp3 > 0)
{
if (sb != null)
{
sb.Append(_ps.chars, _ps.charPos, tmp3);
}
_ps.charPos = pos;
wsCount += tmp3;
}
if (ReadData() == 0)
{
if (_ps.charsUsed - _ps.charPos == 0)
{
return wsCount;
}
if (_ps.chars[_ps.charPos] != (char)0xD)
{
Debug.Assert(false, "We should never get to this point.");
Throw(ResXml.Xml_UnexpectedEOF1);
}
Debug.Assert(_ps.isEof);
}
pos = _ps.charPos;
chars = _ps.chars;
}
}
private int ParseCharRefInline(int startPos, out int charCount, out EntityType entityType)
{
Debug.Assert(_ps.chars[startPos] == '&');
if (_ps.chars[startPos + 1] == '#')
{
return ParseNumericCharRefInline(startPos, true, null, out charCount, out entityType);
}
else
{
charCount = 1;
entityType = EntityType.CharacterNamed;
return ParseNamedCharRefInline(startPos, true, null);
}
}
// Parses numeric character entity reference (e.g.    ).
// - replaces the last one or two character of the entity reference (';' and the character before) with the referenced
// character or surrogates pair (if expand == true)
// - returns position of the end of the character reference, that is of the character next to the original ';'
// - if (expand == true) then ps.charPos is changed to point to the replaced character
private int ParseNumericCharRef(bool expand, BufferBuilder internalSubsetBuilder, out EntityType entityType)
{
for (; ; )
{
int newPos;
int charCount;
switch (newPos = ParseNumericCharRefInline(_ps.charPos, expand, internalSubsetBuilder, out charCount, out entityType))
{
case -2:
// read new characters in the buffer
if (ReadData() == 0)
{
Throw(ResXml.Xml_UnexpectedEOF);
}
Debug.Assert(_ps.chars[_ps.charPos] == '&');
continue;
default:
if (expand)
{
_ps.charPos = newPos - charCount;
}
return newPos;
}
}
}
// Parses numeric character entity reference (e.g.    ).
// Returns -2 if more data is needed in the buffer
// Otherwise
// - replaces the last one or two character of the entity reference (';' and the character before) with the referenced
// character or surrogates pair (if expand == true)
// - returns position of the end of the character reference, that is of the character next to the original ';'
private int ParseNumericCharRefInline(int startPos, bool expand, BufferBuilder internalSubsetBuilder, out int charCount, out EntityType entityType)
{
Debug.Assert(_ps.chars[startPos] == '&' && _ps.chars[startPos + 1] == '#');
int val;
int pos;
char[] chars;
val = 0;
string badDigitExceptionString = null;
chars = _ps.chars;
pos = startPos + 2;
charCount = 0;
int digitPos = 0;
try
{
if (chars[pos] == 'x')
{
pos++;
digitPos = pos;
badDigitExceptionString = ResXml.Xml_BadHexEntity;
for (; ; )
{
char ch = chars[pos];
if (ch >= '0' && ch <= '9')
val = checked(val * 16 + ch - '0');
else if (ch >= 'a' && ch <= 'f')
val = checked(val * 16 + 10 + ch - 'a');
else if (ch >= 'A' && ch <= 'F')
val = checked(val * 16 + 10 + ch - 'A');
else
break;
pos++;
}
entityType = EntityType.CharacterHex;
}
else if (pos < _ps.charsUsed)
{
digitPos = pos;
badDigitExceptionString = ResXml.Xml_BadDecimalEntity;
while (chars[pos] >= '0' && chars[pos] <= '9')
{
val = checked(val * 10 + chars[pos] - '0');
pos++;
}
entityType = EntityType.CharacterDec;
}
else
{
// need more data in the buffer
entityType = EntityType.Skipped;
return -2;
}
}
catch (OverflowException e)
{
_ps.charPos = pos;
entityType = EntityType.Skipped;
Throw(ResXml.Xml_CharEntityOverflow, (string)null, e);
}
if (chars[pos] != ';' || digitPos == pos)
{
if (pos == _ps.charsUsed)
{
// need more data in the buffer
return -2;
}
else
{
Throw(pos, badDigitExceptionString);
}
}
// simple character
if (val <= char.MaxValue)
{
char ch = (char)val;
if (!_xmlCharType.IsCharData(ch) &&
((_v1Compat && _normalize) || (!_v1Compat && _checkCharacters)))
{
Throw((_ps.chars[startPos + 2] == 'x') ? startPos + 3 : startPos + 2, ResXml.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(ch, '\0'));
}
if (expand)
{
if (internalSubsetBuilder != null)
{
internalSubsetBuilder.Append(_ps.chars, _ps.charPos, pos - _ps.charPos + 1);
}
chars[pos] = ch;
}
charCount = 1;
return pos + 1;
}
// surrogate
else
{
char low, high;
XmlCharType.SplitSurrogateChar(val, out low, out high);
if (_normalize)
{
if (XmlCharType.IsHighSurrogate(high))
{
if (XmlCharType.IsLowSurrogate(low))
{
goto Return;
}
}
Throw((_ps.chars[startPos + 2] == 'x') ? startPos + 3 : startPos + 2, ResXml.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(high, low));
}
Return:
Debug.Assert(pos > 0);
if (expand)
{
if (internalSubsetBuilder != null)
{
internalSubsetBuilder.Append(_ps.chars, _ps.charPos, pos - _ps.charPos + 1);
}
chars[pos - 1] = (char)high;
chars[pos] = (char)low;
}
charCount = 2;
return pos + 1;
}
}
// Parses named character entity reference (& ' < > ").
// Returns -1 if the reference is not a character entity reference.
// Otherwise
// - replaces the last character of the entity reference (';') with the referenced character (if expand == true)
// - returns position of the end of the character reference, that is of the character next to the original ';'
// - if (expand == true) then ps.charPos is changed to point to the replaced character
private int ParseNamedCharRef(bool expand, BufferBuilder internalSubsetBuilder)
{
for (; ; )
{
int newPos;
switch (newPos = ParseNamedCharRefInline(_ps.charPos, expand, internalSubsetBuilder))
{
case -1:
return -1;
case -2:
// read new characters in the buffer
if (ReadData() == 0)
{
return -1;
}
Debug.Assert(_ps.chars[_ps.charPos] == '&');
continue;
default:
if (expand)
{
_ps.charPos = newPos - 1;
}
return newPos;
}
}
}
// Parses named character entity reference (& ' < > ").
// Returns -1 if the reference is not a character entity reference.
// Returns -2 if more data is needed in the buffer
// Otherwise
// - replaces the last character of the entity reference (';') with the referenced character (if expand == true)
// - returns position of the end of the character reference, that is of the character next to the original ';'
private int ParseNamedCharRefInline(int startPos, bool expand, BufferBuilder internalSubsetBuilder)
{
Debug.Assert(startPos < _ps.charsUsed);
Debug.Assert(_ps.chars[startPos] == '&');
Debug.Assert(_ps.chars[startPos + 1] != '#');
int pos = startPos + 1;
char[] chars = _ps.chars;
char ch;
switch (chars[pos])
{
// ' or &
case 'a':
pos++;
// &
if (chars[pos] == 'm')
{
if (_ps.charsUsed - pos >= 3)
{
if (chars[pos + 1] == 'p' && chars[pos + 2] == ';')
{
pos += 3;
ch = '&';
goto FoundCharRef;
}
else
{
return -1;
}
}
}
// '
else if (chars[pos] == 'p')
{
if (_ps.charsUsed - pos >= 4)
{
if (chars[pos + 1] == 'o' && chars[pos + 2] == 's' &&
chars[pos + 3] == ';')
{
pos += 4;
ch = '\'';
goto FoundCharRef;
}
else
{
return -1;
}
}
}
else if (pos < _ps.charsUsed)
{
return -1;
}
break;
// &guot;
case 'q':
if (_ps.charsUsed - pos >= 5)
{
if (chars[pos + 1] == 'u' && chars[pos + 2] == 'o' &&
chars[pos + 3] == 't' && chars[pos + 4] == ';')
{
pos += 5;
ch = '"';
goto FoundCharRef;
}
else
{
return -1;
}
}
break;
// <
case 'l':
if (_ps.charsUsed - pos >= 3)
{
if (chars[pos + 1] == 't' && chars[pos + 2] == ';')
{
pos += 3;
ch = '<';
goto FoundCharRef;
}
else
{
return -1;
}
}
break;
// >
case 'g':
if (_ps.charsUsed - pos >= 3)
{
if (chars[pos + 1] == 't' && chars[pos + 2] == ';')
{
pos += 3;
ch = '>';
goto FoundCharRef;
}
else
{
return -1;
}
}
break;
default:
return -1;
}
// need more data in the buffer
return -2;
FoundCharRef:
Debug.Assert(pos > 0);
if (expand)
{
if (internalSubsetBuilder != null)
{
internalSubsetBuilder.Append(_ps.chars, _ps.charPos, pos - _ps.charPos);
}
_ps.chars[pos - 1] = ch;
}
return pos;
}
private int ParseName()
{
int colonPos;
return ParseQName(false, 0, out colonPos);
}
private int ParseQName(out int colonPos)
{
return ParseQName(true, 0, out colonPos);
}
private int ParseQName(bool isQName, int startOffset, out int colonPos)
{
int colonOffset = -1;
int pos = _ps.charPos + startOffset;
ContinueStartName:
char[] chars = _ps.chars;
// start name char
unsafe
{
if ((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0)
{
pos++;
}
#if XML10_FIFTH_EDITION
else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
pos += 2;
}
#endif
else
{
if (pos + 1 >= _ps.charsUsed)
{
if (ReadDataInName(ref pos))
{
goto ContinueStartName;
}
Throw(pos, ResXml.Xml_UnexpectedEOF, "Name");
}
if (chars[pos] != ':' || _supportNamespaces)
{
Throw(pos, ResXml.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs(chars, _ps.charsUsed, pos));
}
}
}
ContinueName:
// parse name
unsafe
{
for (; ; )
{
if (((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0))
{
pos++;
}
#if XML10_FIFTH_EDITION
else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
pos += 2;
}
#endif
else
{
break;
}
}
}
// colon
if (chars[pos] == ':')
{
if (_supportNamespaces)
{
if (colonOffset != -1 || !isQName)
{
Throw(pos, ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
}
colonOffset = pos - _ps.charPos;
pos++;
goto ContinueStartName;
}
else
{
colonOffset = pos - _ps.charPos;
pos++;
goto ContinueName;
}
}
// end of buffer
else if (pos == _ps.charsUsed
#if XML10_FIFTH_EDITION
|| ( pos + 1 == ps.charsUsed && xmlCharType.IsNCNameHighSurrogateChar( chars[pos] ) )
#endif
)
{
if (ReadDataInName(ref pos))
{
chars = _ps.chars;
goto ContinueName;
}
Throw(pos, ResXml.Xml_UnexpectedEOF, "Name");
}
// end of name
colonPos = (colonOffset == -1) ? -1 : _ps.charPos + colonOffset;
return pos;
}
private bool ReadDataInName(ref int pos)
{
int offset = pos - _ps.charPos;
bool newDataRead = (ReadData() != 0);
pos = _ps.charPos + offset;
return newDataRead;
}
private string ParseEntityName()
{
int endPos;
try
{
endPos = ParseName();
}
catch (XmlException)
{
Throw(ResXml.Xml_ErrorParsingEntityName);
return null;
}
// check ';'
if (_ps.chars[endPos] != ';')
{
Throw(ResXml.Xml_ErrorParsingEntityName);
}
string entityName = _nameTable.Add(_ps.chars, _ps.charPos, endPos - _ps.charPos);
_ps.charPos = endPos + 1;
return entityName;
}
private NodeData AddNode(int nodeIndex, int nodeDepth)
{
Debug.Assert(nodeIndex < _nodes.Length);
Debug.Assert(_nodes[_nodes.Length - 1] == null);
NodeData n = _nodes[nodeIndex];
if (n != null)
{
n.depth = nodeDepth;
return n;
}
return AllocNode(nodeIndex, nodeDepth);
}
private NodeData AllocNode(int nodeIndex, int nodeDepth)
{
Debug.Assert(nodeIndex < _nodes.Length);
if (nodeIndex >= _nodes.Length - 1)
{
NodeData[] newNodes = new NodeData[_nodes.Length * 2];
Array.Copy(_nodes, 0, newNodes, 0, _nodes.Length);
_nodes = newNodes;
}
Debug.Assert(nodeIndex < _nodes.Length);
NodeData node = _nodes[nodeIndex];
if (node == null)
{
node = new NodeData();
_nodes[nodeIndex] = node;
}
node.depth = nodeDepth;
return node;
}
private NodeData AddAttributeNoChecks(string name, int attrDepth)
{
NodeData newAttr = AddNode(_index + _attrCount + 1, attrDepth);
newAttr.SetNamedNode(XmlNodeType.Attribute, _nameTable.Add(name));
_attrCount++;
return newAttr;
}
private NodeData AddAttribute(int endNamePos, int colonPos)
{
// setup attribute name
if (colonPos == -1 || !_supportNamespaces)
{
string localName = _nameTable.Add(_ps.chars, _ps.charPos, endNamePos - _ps.charPos);
return AddAttribute(localName, string.Empty, localName);
}
else
{
_attrNeedNamespaceLookup = true;
int startPos = _ps.charPos;
int prefixLen = colonPos - startPos;
if (prefixLen == _lastPrefix.Length && XmlConvert.StrEqual(_ps.chars, startPos, prefixLen, _lastPrefix))
{
return AddAttribute(_nameTable.Add(_ps.chars, colonPos + 1, endNamePos - colonPos - 1),
_lastPrefix,
null);
}
else
{
string prefix = _nameTable.Add(_ps.chars, startPos, prefixLen);
_lastPrefix = prefix;
return AddAttribute(_nameTable.Add(_ps.chars, colonPos + 1, endNamePos - colonPos - 1),
prefix,
null);
}
}
}
private NodeData AddAttribute(string localName, string prefix, string nameWPrefix)
{
NodeData newAttr = AddNode(_index + _attrCount + 1, _index + 1);
// set attribute name
newAttr.SetNamedNode(XmlNodeType.Attribute, localName, prefix, nameWPrefix);
// pre-check attribute for duplicate: hash by first local name char
int attrHash = 1 << (localName[0] & 0x1F);
if ((_attrHashtable & attrHash) == 0)
{
_attrHashtable |= attrHash;
}
else
{
// there are probably 2 attributes beginning with the same letter -> check all previous
// attributes
if (_attrDuplWalkCount < MaxAttrDuplWalkCount)
{
_attrDuplWalkCount++;
for (int i = _index + 1; i < _index + _attrCount + 1; i++)
{
NodeData attr = _nodes[i];
Debug.Assert(attr.type == XmlNodeType.Attribute);
if (Ref.Equal(attr.localName, newAttr.localName))
{
_attrDuplWalkCount = MaxAttrDuplWalkCount;
break;
}
}
}
}
_attrCount++;
return newAttr;
}
private void PopElementContext()
{
// pop namespace context
_namespaceManager.PopScope();
// pop xml context
if (_curNode.xmlContextPushed)
{
PopXmlContext();
}
}
private void OnNewLine(int pos)
{
_ps.lineNo++;
_ps.lineStartPos = pos - 1;
}
private void OnEof()
{
Debug.Assert(_ps.isEof);
_curNode = _nodes[0];
_curNode.Clear(XmlNodeType.None);
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
_parsingFunction = ParsingFunction.Eof;
_readState = ReadState.EndOfFile;
_reportedEncoding = null;
}
private string LookupNamespace(NodeData node)
{
string ns = _namespaceManager.LookupNamespace(node.prefix);
if (ns != null)
{
return ns;
}
else
{
Throw(ResXml.Xml_UnknownNs, node.prefix, node.LineNo, node.LinePos);
return null;
}
}
private void AddNamespace(string prefix, string uri, NodeData attr)
{
if (uri == XmlReservedNs.NsXmlNs)
{
if (Ref.Equal(prefix, _xmlNs))
{
Throw(ResXml.Xml_XmlnsPrefix, (int)attr.lineInfo2.lineNo, (int)attr.lineInfo2.linePos);
}
else
{
Throw(ResXml.Xml_NamespaceDeclXmlXmlns, prefix, (int)attr.lineInfo2.lineNo, (int)attr.lineInfo2.linePos);
}
}
else if (uri == XmlReservedNs.NsXml)
{
if (!Ref.Equal(prefix, _xml) && !_v1Compat)
{
Throw(ResXml.Xml_NamespaceDeclXmlXmlns, prefix, (int)attr.lineInfo2.lineNo, (int)attr.lineInfo2.linePos);
}
}
if (uri.Length == 0 && prefix.Length > 0)
{
Throw(ResXml.Xml_BadNamespaceDecl, (int)attr.lineInfo.lineNo, (int)attr.lineInfo.linePos);
}
try
{
_namespaceManager.AddNamespace(prefix, uri);
}
catch (ArgumentException e)
{
ReThrow(e, (int)attr.lineInfo.lineNo, (int)attr.lineInfo.linePos);
}
#if DEBUG
if (prefix.Length == 0)
{
Debug.Assert(_xmlContext.defaultNamespace == uri);
}
#endif
}
private void ResetAttributes()
{
if (_fullAttrCleanup)
{
FullAttributeCleanup();
}
_curAttrIndex = -1;
_attrCount = 0;
_attrHashtable = 0;
_attrDuplWalkCount = 0;
}
private void FullAttributeCleanup()
{
for (int i = _index + 1; i < _index + _attrCount + 1; i++)
{
NodeData attr = _nodes[i];
attr.nextAttrValueChunk = null;
attr.IsDefaultAttribute = false;
}
_fullAttrCleanup = false;
}
private void PushXmlContext()
{
_xmlContext = new XmlContext(_xmlContext);
_curNode.xmlContextPushed = true;
}
private void PopXmlContext()
{
Debug.Assert(_curNode.xmlContextPushed);
_xmlContext = _xmlContext.previousContext;
_curNode.xmlContextPushed = false;
}
// Returns the whitespace node type according to the current whitespaceHandling setting and xml:space
private XmlNodeType GetWhitespaceType()
{
if (_whitespaceHandling != WhitespaceHandling.None)
{
if (_xmlContext.xmlSpace == XmlSpace.Preserve)
{
return XmlNodeType.SignificantWhitespace;
}
if (_whitespaceHandling == WhitespaceHandling.All)
{
return XmlNodeType.Whitespace;
}
}
return XmlNodeType.None;
}
private XmlNodeType GetTextNodeType(int orChars)
{
if (orChars > 0x20)
{
return XmlNodeType.Text;
}
else
{
return GetWhitespaceType();
}
}
// This method resolves and opens an external DTD subset or an external entity based on its SYSTEM or PUBLIC ID.
// SxS: This method may expose a name if a resource in baseUri (ref) parameter.
// [ResourceConsumption(ResourceScope.Machine)]
// [ResourceExposure(ResourceScope.Machine)]
private void PushExternalEntityOrSubset(string publicId, string systemId, Uri baseUri, string entityName)
{
Uri uri;
// First try opening the external reference by PUBLIC Id
if (!string.IsNullOrEmpty(publicId))
{
try
{
uri = _xmlResolver.ResolveUri(baseUri, publicId);
if (OpenAndPush(uri))
{
return;
}
}
catch (Exception)
{
// Intentionally empty - swallow all exception related to PUBLIC ID and try opening the entity via the SYSTEM ID
}
}
// Then try SYSTEM Id
uri = _xmlResolver.ResolveUri(baseUri, systemId);
try
{
if (OpenAndPush(uri))
{
return;
}
// resolver returned null, throw exception outside this try-catch
}
catch (Exception e)
{
if (_v1Compat)
{
throw;
}
string innerMessage;
innerMessage = e.Message;
Throw(new XmlException(entityName == null ? ResXml.Xml_ErrorOpeningExternalDtd : ResXml.Xml_ErrorOpeningExternalEntity, new string[] { uri.ToString(), innerMessage }, e, 0, 0));
}
if (entityName == null)
{
ThrowWithoutLineInfo(ResXml.Xml_CannotResolveExternalSubset, new string[] { (publicId != null ? publicId : string.Empty), systemId }, null);
}
else
{
Throw(_dtdProcessing == DtdProcessing.Ignore ? ResXml.Xml_CannotResolveEntityDtdIgnored : ResXml.Xml_CannotResolveEntity, entityName);
}
}
// This method opens the URI as a TextReader or Stream, pushes new ParsingStateState on the stack and calls InitStreamInput or InitTextReaderInput.
// Returns:
// - true when everything went ok.
// - false when XmlResolver.GetEntity returned null
// Propagates any exceptions from the XmlResolver indicating when the URI cannot be opened.
private bool OpenAndPush(Uri uri)
{
Debug.Assert(_xmlResolver != null);
// First try to get the data as a TextReader
if (_xmlResolver.SupportsType(uri, typeof(TextReader)))
{
TextReader textReader = (TextReader)_xmlResolver.GetEntity(uri, null, typeof(TextReader));
if (textReader == null)
{
return false;
}
PushParsingState();
InitTextReaderInput(uri.ToString(), uri, textReader);
}
else
{
// Then try get it as a Stream
Debug.Assert(_xmlResolver.SupportsType(uri, typeof(Stream)), "Stream must always be a supported type in XmlResolver");
Stream stream = (Stream)_xmlResolver.GetEntity(uri, null, typeof(Stream));
if (stream == null)
{
return false;
}
PushParsingState();
InitStreamInput(uri, stream, null);
}
return true;
}
// returns true if real entity has been pushed, false if fake entity (=empty content entity)
// SxS: The method neither takes any name of resource directly nor it exposes any resource to the caller.
// Entity info was created based on source document. It's OK to disable the SxS warning
private bool PushExternalEntity(IDtdEntityInfo entity)
{
Debug.Assert(entity.IsExternal);
if (!IsResolverNull)
{
Uri entityBaseUri = null;
if (!string.IsNullOrEmpty(entity.BaseUriString))
{
entityBaseUri = _xmlResolver.ResolveUri(null, entity.BaseUriString);
}
PushExternalEntityOrSubset(entity.PublicId, entity.SystemId, entityBaseUri, entity.Name);
RegisterEntity(entity);
Debug.Assert(_ps.appendMode);
int initialPos = _ps.charPos;
if (_v1Compat)
{
EatWhitespaces(null);
}
if (!ParseXmlDeclaration(true))
{
_ps.charPos = initialPos;
}
return true;
}
else
{
Encoding enc = _ps.encoding;
PushParsingState();
InitStringInput(entity.SystemId, enc, string.Empty);
RegisterEntity(entity);
RegisterConsumedCharacters(0, true);
return false;
}
}
private void PushInternalEntity(IDtdEntityInfo entity)
{
Debug.Assert(!entity.IsExternal);
Encoding enc = _ps.encoding;
PushParsingState();
InitStringInput((entity.DeclaredUriString != null) ? entity.DeclaredUriString : string.Empty, enc, entity.Text ?? string.Empty);
RegisterEntity(entity);
_ps.lineNo = entity.LineNumber;
_ps.lineStartPos = -entity.LinePosition - 1;
_ps.eolNormalized = true;
RegisterConsumedCharacters(entity.Text.Length, true);
}
private void PopEntity()
{
if (_ps.stream != null)
{
_ps.stream.Close();
}
UnregisterEntity();
PopParsingState();
_curNode.entityId = _ps.entityId;
}
private void RegisterEntity(IDtdEntityInfo entity)
{
// check entity recursion
if (_currentEntities != null)
{
if (_currentEntities.ContainsKey(entity))
{
Throw(entity.IsParameterEntity ? ResXml.Xml_RecursiveParEntity : ResXml.Xml_RecursiveGenEntity, entity.Name,
_parsingStatesStack[_parsingStatesStackTop].LineNo, _parsingStatesStack[_parsingStatesStackTop].LinePos);
}
}
// save the entity to parsing state & assign it an ID
_ps.entity = entity;
_ps.entityId = _nextEntityId++;
// register entity for recursion checkes
if (entity != null)
{
if (_currentEntities == null)
{
_currentEntities = new Dictionary<IDtdEntityInfo, IDtdEntityInfo>();
}
_currentEntities.Add(entity, entity);
}
}
private void UnregisterEntity()
{
// remove from recursion check registry
if (_ps.entity != null)
{
_currentEntities.Remove(_ps.entity);
}
}
private void PushParsingState()
{
if (_parsingStatesStack == null)
{
_parsingStatesStack = new ParsingState[InitialParsingStatesDepth];
Debug.Assert(_parsingStatesStackTop == -1);
}
else if (_parsingStatesStackTop + 1 == _parsingStatesStack.Length)
{
ParsingState[] newParsingStateStack = new ParsingState[_parsingStatesStack.Length * 2];
Array.Copy(_parsingStatesStack, 0, newParsingStateStack, 0, _parsingStatesStack.Length);
_parsingStatesStack = newParsingStateStack;
}
_parsingStatesStackTop++;
_parsingStatesStack[_parsingStatesStackTop] = _ps;
_ps.Clear();
}
private void PopParsingState()
{
Debug.Assert(_parsingStatesStackTop >= 0);
_ps.Close(true);
_ps = _parsingStatesStack[_parsingStatesStackTop--];
}
private void InitIncrementalRead(IncrementalReadDecoder decoder)
{
ResetAttributes();
decoder.Reset();
_incReadDecoder = decoder;
_incReadState = IncrementalReadState.Text;
_incReadDepth = 1;
_incReadLeftStartPos = _ps.charPos;
_incReadLeftEndPos = _ps.charPos;
_incReadLineInfo.Set(_ps.LineNo, _ps.LinePos);
_parsingFunction = ParsingFunction.InIncrementalRead;
}
private int IncrementalRead(Array array, int index, int count)
{
if (array == null)
{
throw new ArgumentNullException((_incReadDecoder is IncrementalReadCharsDecoder) ? "buffer" : "array");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException((_incReadDecoder is IncrementalReadCharsDecoder) ? "count" : "len");
}
if (index < 0)
{
throw new ArgumentOutOfRangeException((_incReadDecoder is IncrementalReadCharsDecoder) ? "index" : "offset");
}
if (array.Length - index < count)
{
throw new ArgumentException((_incReadDecoder is IncrementalReadCharsDecoder) ? "count" : "len");
}
if (count == 0)
{
return 0;
}
_curNode.lineInfo = _incReadLineInfo;
_incReadDecoder.SetNextOutputBuffer(array, index, count);
IncrementalRead();
return _incReadDecoder.DecodedCount;
}
private int IncrementalRead()
{
int charsDecoded = 0;
OuterContinue:
int charsLeft = _incReadLeftEndPos - _incReadLeftStartPos;
if (charsLeft > 0)
{
int count;
try
{
count = _incReadDecoder.Decode(_ps.chars, _incReadLeftStartPos, charsLeft);
}
catch (XmlException e)
{
ReThrow(e, (int)_incReadLineInfo.lineNo, (int)_incReadLineInfo.linePos);
return 0;
}
if (count < charsLeft)
{
_incReadLeftStartPos += count;
_incReadLineInfo.linePos += count; // we have never more then 1 line cached
return count;
}
else
{
_incReadLeftStartPos = 0;
_incReadLeftEndPos = 0;
_incReadLineInfo.linePos += count;
if (_incReadDecoder.IsFull)
{
return count;
}
}
}
int startPos = 0;
int pos = 0;
for (; ; )
{
switch (_incReadState)
{
case IncrementalReadState.Text:
case IncrementalReadState.Attributes:
case IncrementalReadState.AttributeValue:
break;
case IncrementalReadState.PI:
if (ParsePIValue(out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 2, 2, "?>"));
_ps.charPos -= 2;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.Comment:
if (ParseCDataOrComment(XmlNodeType.Comment, out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 3, 3, "-->"));
_ps.charPos -= 3;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.CDATA:
if (ParseCDataOrComment(XmlNodeType.CDATA, out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 3, 3, "]]>"));
_ps.charPos -= 3;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.EndElement:
_parsingFunction = ParsingFunction.PopElementContext;
_nextParsingFunction = (_index > 0 || _fragmentType != XmlNodeType.Document) ? ParsingFunction.ElementContent
: ParsingFunction.DocumentContent;
_outerReader.Read();
_incReadState = IncrementalReadState.End;
goto case IncrementalReadState.End;
case IncrementalReadState.End:
return charsDecoded;
case IncrementalReadState.ReadData:
if (ReadData() == 0)
{
ThrowUnclosedElements();
}
_incReadState = IncrementalReadState.Text;
startPos = _ps.charPos;
pos = startPos;
break;
default:
Debug.Assert(false);
break;
}
Debug.Assert(_incReadState == IncrementalReadState.Text ||
_incReadState == IncrementalReadState.Attributes ||
_incReadState == IncrementalReadState.AttributeValue);
char[] chars = _ps.chars;
startPos = _ps.charPos;
pos = startPos;
for (; ; )
{
_incReadLineInfo.Set(_ps.LineNo, _ps.LinePos);
char c;
unsafe
{
if (_incReadState == IncrementalReadState.Attributes)
{
while (((_xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fAttrValue) != 0) && c != '/')
{
pos++;
}
}
else
{
while (((_xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fAttrValue) != 0))
{
pos++;
}
}
}
if (chars[pos] == '&' || chars[pos] == (char)0x9)
{
pos++;
continue;
}
if (pos - startPos > 0)
{
goto AppendAndUpdateCharPos;
}
switch (chars[pos])
{
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
pos += 2;
}
else if (pos + 1 < _ps.charsUsed)
{
pos++;
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
// some tag
case '<':
if (_incReadState != IncrementalReadState.Text)
{
pos++;
continue;
}
if (_ps.charsUsed - pos < 2)
{
goto ReadData;
}
switch (chars[pos + 1])
{
// pi
case '?':
pos += 2;
_incReadState = IncrementalReadState.PI;
goto AppendAndUpdateCharPos;
// comment
case '!':
if (_ps.charsUsed - pos < 4)
{
goto ReadData;
}
if (chars[pos + 2] == '-' && chars[pos + 3] == '-')
{
pos += 4;
_incReadState = IncrementalReadState.Comment;
goto AppendAndUpdateCharPos;
}
if (_ps.charsUsed - pos < 9)
{
goto ReadData;
}
if (XmlConvert.StrEqual(chars, pos + 2, 7, "[CDATA["))
{
pos += 9;
_incReadState = IncrementalReadState.CDATA;
goto AppendAndUpdateCharPos;
}
else
{
;//Throw( );
}
break;
// end tag
case '/':
{
Debug.Assert(_ps.charPos - pos == 0);
Debug.Assert(_ps.charPos - startPos == 0);
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName(true, 2, out colonPos);
if (XmlConvert.StrEqual(chars, _ps.charPos + 2, endPos - _ps.charPos - 2, _curNode.GetNameWPrefix(_nameTable)) &&
(_ps.chars[endPos] == '>' || _xmlCharType.IsWhiteSpace(_ps.chars[endPos])))
{
if (--_incReadDepth > 0)
{
pos = endPos + 1;
continue;
}
_ps.charPos = endPos;
if (_xmlCharType.IsWhiteSpace(_ps.chars[endPos]))
{
EatWhitespaces(null);
}
if (_ps.chars[_ps.charPos] != '>')
{
ThrowUnexpectedToken(">");
}
_ps.charPos++;
_incReadState = IncrementalReadState.EndElement;
goto OuterContinue;
}
else
{
pos = endPos;
startPos = _ps.charPos;
chars = _ps.chars;
continue;
}
}
// start tag
default:
{
Debug.Assert(_ps.charPos - pos == 0);
Debug.Assert(_ps.charPos - startPos == 0);
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName(true, 1, out colonPos);
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos + 1, endPos - _ps.charPos - 1, _curNode.localName) &&
(_ps.chars[endPos] == '>' || _ps.chars[endPos] == '/' || _xmlCharType.IsWhiteSpace(_ps.chars[endPos])))
{
_incReadDepth++;
_incReadState = IncrementalReadState.Attributes;
pos = endPos;
goto AppendAndUpdateCharPos;
}
pos = endPos;
startPos = _ps.charPos;
chars = _ps.chars;
continue;
}
}
break;
// end of start tag
case '/':
if (_incReadState == IncrementalReadState.Attributes)
{
if (_ps.charsUsed - pos < 2)
{
goto ReadData;
}
if (chars[pos + 1] == '>')
{
_incReadState = IncrementalReadState.Text;
_incReadDepth--;
}
}
pos++;
continue;
// end of start tag
case '>':
if (_incReadState == IncrementalReadState.Attributes)
{
_incReadState = IncrementalReadState.Text;
}
pos++;
continue;
case '"':
case '\'':
switch (_incReadState)
{
case IncrementalReadState.AttributeValue:
if (chars[pos] == _curNode.quoteChar)
{
_incReadState = IncrementalReadState.Attributes;
}
break;
case IncrementalReadState.Attributes:
_curNode.quoteChar = chars[pos];
_incReadState = IncrementalReadState.AttributeValue;
break;
}
pos++;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReadData;
}
// surrogate chars or invalid chars are ignored
else
{
pos++;
continue;
}
}
}
ReadData:
_incReadState = IncrementalReadState.ReadData;
AppendAndUpdateCharPos:
_ps.charPos = pos;
Append:
// decode characters
int charsParsed = pos - startPos;
if (charsParsed > 0)
{
int count;
try
{
count = _incReadDecoder.Decode(_ps.chars, startPos, charsParsed);
}
catch (XmlException e)
{
ReThrow(e, (int)_incReadLineInfo.lineNo, (int)_incReadLineInfo.linePos);
return 0;
}
Debug.Assert(count == charsParsed || _incReadDecoder.IsFull, "Check if decoded consumed all characters unless it's full.");
charsDecoded += count;
if (_incReadDecoder.IsFull)
{
_incReadLeftStartPos = startPos + count;
_incReadLeftEndPos = pos;
_incReadLineInfo.linePos += count; // we have never more than 1 line cached
return charsDecoded;
}
}
}
}
private void FinishIncrementalRead()
{
_incReadDecoder = new IncrementalReadDummyDecoder();
IncrementalRead();
Debug.Assert(IncrementalRead() == 0, "Previous call of IncrementalRead should eat up all characters!");
_incReadDecoder = null;
}
private bool ParseFragmentAttribute()
{
Debug.Assert(_fragmentType == XmlNodeType.Attribute);
// if first call then parse the whole attribute value
if (_curNode.type == XmlNodeType.None)
{
_curNode.type = XmlNodeType.Attribute;
_curAttrIndex = 0;
ParseAttributeValueSlow(_ps.charPos, ' ', _curNode); // The quote char is intentionally empty (space) because we need to parse ' and " into the attribute value
}
else
{
_parsingFunction = ParsingFunction.InReadAttributeValue;
}
// return attribute value chunk
if (ReadAttributeValue())
{
Debug.Assert(_parsingFunction == ParsingFunction.InReadAttributeValue);
_parsingFunction = ParsingFunction.FragmentAttribute;
return true;
}
else
{
OnEof();
return false;
}
}
private bool ParseAttributeValueChunk()
{
char[] chars = _ps.chars;
int pos = _ps.charPos;
_curNode = AddNode(_index + _attrCount + 1, _index + 2);
_curNode.SetLineInfo(_ps.LineNo, _ps.LinePos);
if (_emptyEntityInAttributeResolved)
{
_curNode.SetValueNode(XmlNodeType.Text, string.Empty);
_emptyEntityInAttributeResolved = false;
return true;
}
Debug.Assert(_stringBuilder.Length == 0);
for (; ; )
{
unsafe
{
while (((_xmlCharType.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0))
pos++;
}
switch (chars[pos])
{
// eol D
case (char)0xD:
Debug.Assert(_ps.eolNormalized, "Entity replacement text for attribute values should be EOL-normalized!");
pos++;
continue;
// eol A, tab
case (char)0xA:
case (char)0x9:
if (_normalize)
{
chars[pos] = (char)0x20; // CDATA normalization of 0xA and 0x9
}
pos++;
continue;
case '"':
case '\'':
case '>':
pos++;
continue;
// attribute values cannot contain '<'
case '<':
Throw(pos, ResXml.Xml_BadAttributeChar, XmlException.BuildCharExceptionArgs('<', '\0'));
break;
// entity reference
case '&':
if (pos - _ps.charPos > 0)
{
_stringBuilder.Append(chars, _ps.charPos, pos - _ps.charPos);
}
_ps.charPos = pos;
// expand char entities but not general entities
switch (HandleEntityReference(true, EntityExpandType.OnlyCharacter, out pos))
{
case EntityType.CharacterDec:
case EntityType.CharacterHex:
case EntityType.CharacterNamed:
chars = _ps.chars;
if (_normalize && _xmlCharType.IsWhiteSpace(chars[_ps.charPos]) && pos - _ps.charPos == 1)
{
chars[_ps.charPos] = (char)0x20; // CDATA normalization of character references in entities
}
break;
case EntityType.Unexpanded:
if (_stringBuilder.Length == 0)
{
_curNode.lineInfo.linePos++;
_ps.charPos++;
_curNode.SetNamedNode(XmlNodeType.EntityReference, ParseEntityName());
return true;
}
else
{
goto ReturnText;
}
default:
Debug.Assert(false, "We should never get to this point.");
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:
if (pos - _ps.charPos > 0)
{
_stringBuilder.Append(chars, _ps.charPos, pos - _ps.charPos);
_ps.charPos = pos;
}
// read new characters into the buffer
if (ReadData() == 0)
{
if (_stringBuilder.Length > 0)
{
goto ReturnText;
}
else
{
if (HandleEntityEnd(false))
{
SetupEndEntityNodeInAttribute();
return true;
}
else
{
Debug.Assert(false, "We should never get to this point.");
}
}
}
pos = _ps.charPos;
chars = _ps.chars;
}
ReturnText:
if (pos - _ps.charPos > 0)
{
_stringBuilder.Append(chars, _ps.charPos, pos - _ps.charPos);
_ps.charPos = pos;
}
_curNode.SetValueNode(XmlNodeType.Text, _stringBuilder.ToString());
_stringBuilder.Length = 0;
return true;
}
private void ParseXmlDeclarationFragment()
{
try
{
ParseXmlDeclaration(false);
}
catch (XmlException e)
{
ReThrow(e, e.LineNumber, e.LinePosition - 6); // 6 == strlen( "<?xml " );
}
}
private void ThrowUnexpectedToken(int pos, string expectedToken)
{
ThrowUnexpectedToken(pos, expectedToken, null);
}
private void ThrowUnexpectedToken(string expectedToken1)
{
ThrowUnexpectedToken(expectedToken1, null);
}
private void ThrowUnexpectedToken(int pos, string expectedToken1, string expectedToken2)
{
_ps.charPos = pos;
ThrowUnexpectedToken(expectedToken1, expectedToken2);
}
private void ThrowUnexpectedToken(string expectedToken1, string expectedToken2)
{
string unexpectedToken = ParseUnexpectedToken();
if (unexpectedToken == null)
{
Throw(ResXml.Xml_UnexpectedEOF1);
}
if (expectedToken2 != null)
{
Throw(ResXml.Xml_UnexpectedTokens2, new string[3] { unexpectedToken, expectedToken1, expectedToken2 });
}
else
{
Throw(ResXml.Xml_UnexpectedTokenEx, new string[2] { unexpectedToken, expectedToken1 });
}
}
private string ParseUnexpectedToken(int pos)
{
_ps.charPos = pos;
return ParseUnexpectedToken();
}
private string ParseUnexpectedToken()
{
if (_ps.charPos == _ps.charsUsed)
{
return null;
}
if (_xmlCharType.IsNCNameSingleChar(_ps.chars[_ps.charPos]))
{
int pos = _ps.charPos + 1;
while (_xmlCharType.IsNCNameSingleChar(_ps.chars[pos]))
{
pos++;
}
return new string(_ps.chars, _ps.charPos, pos - _ps.charPos);
}
else
{
Debug.Assert(_ps.charPos < _ps.charsUsed);
return new string(_ps.chars, _ps.charPos, 1);
}
}
private void ThrowExpectingWhitespace(int pos)
{
string unexpectedToken = ParseUnexpectedToken(pos);
if (unexpectedToken == null)
{
Throw(pos, ResXml.Xml_UnexpectedEOF1);
}
else
{
Throw(pos, ResXml.Xml_ExpectingWhiteSpace, unexpectedToken);
}
}
private int GetIndexOfAttributeWithoutPrefix(string name)
{
name = _nameTable.Get(name);
if (name == null)
{
return -1;
}
for (int i = _index + 1; i < _index + _attrCount + 1; i++)
{
if (Ref.Equal(_nodes[i].localName, name) && _nodes[i].prefix.Length == 0)
{
return i;
}
}
return -1;
}
private int GetIndexOfAttributeWithPrefix(string name)
{
name = _nameTable.Add(name);
if (name == null)
{
return -1;
}
for (int i = _index + 1; i < _index + _attrCount + 1; i++)
{
if (Ref.Equal(_nodes[i].GetNameWPrefix(_nameTable), name))
{
return i;
}
}
return -1;
}
// This method is used to enable parsing of zero-terminated streams. The old XmlTextReader implementation used
// to parse such streams, we this one needs to do that as well.
// If the last characters decoded from the stream is 0 and the stream is in EOF state, this method will remove
// the character from the parsing buffer (decrements ps.charsUsed).
// Note that this method calls ReadData() which may change the value of ps.chars and ps.charPos.
private bool ZeroEndingStream(int pos)
{
if (_v1Compat && pos == _ps.charsUsed - 1 && _ps.chars[pos] == (char)0 && ReadData() == 0 && _ps.isStreamEof)
{
_ps.charsUsed--;
return true;
}
return false;
}
private void ParseDtdFromParserContext()
{
Debug.Assert(_dtdInfo == null && _fragmentParserContext != null && _fragmentParserContext.HasDtdInfo);
IDtdParser dtdParser = DtdParser.Create();
// Parse DTD
_dtdInfo = dtdParser.ParseFreeFloatingDtd(_fragmentParserContext.BaseURI, _fragmentParserContext.DocTypeName, _fragmentParserContext.PublicId,
_fragmentParserContext.SystemId, _fragmentParserContext.InternalSubset, new DtdParserProxy(this));
if ((_validatingReaderCompatFlag || !_v1Compat) && (_dtdInfo.HasDefaultAttributes || _dtdInfo.HasNonCDataAttributes))
{
_addDefaultAttributesAndNormalize = true;
}
}
private bool InitReadContentAsBinary()
{
Debug.Assert(_parsingFunction != ParsingFunction.InReadContentAsBinary);
if (_parsingFunction == ParsingFunction.InReadValueChunk)
{
throw new InvalidOperationException(ResXml.Xml_MixingReadValueChunkWithBinary);
}
if (_parsingFunction == ParsingFunction.InIncrementalRead)
{
throw new InvalidOperationException(ResXml.Xml_MixingV1StreamingWithV2Binary);
}
if (!XmlReader.IsTextualNode(_curNode.type))
{
if (!MoveToNextContentNode(false))
{
return false;
}
}
SetupReadContentAsBinaryState(ParsingFunction.InReadContentAsBinary);
_incReadLineInfo.Set(_curNode.LineNo, _curNode.LinePos);
return true;
}
private bool InitReadElementContentAsBinary()
{
Debug.Assert(_parsingFunction != ParsingFunction.InReadElementContentAsBinary);
Debug.Assert(_curNode.type == XmlNodeType.Element);
bool isEmpty = _curNode.IsEmptyElement;
// move to content or off the empty element
_outerReader.Read();
if (isEmpty)
{
return false;
}
// make sure we are on a content node
if (!MoveToNextContentNode(false))
{
if (_curNode.type != XmlNodeType.EndElement)
{
Throw(ResXml.Xml_InvalidNodeType, _curNode.type.ToString());
}
// move off end element
_outerReader.Read();
return false;
}
SetupReadContentAsBinaryState(ParsingFunction.InReadElementContentAsBinary);
_incReadLineInfo.Set(_curNode.LineNo, _curNode.LinePos);
return true;
}
private bool MoveToNextContentNode(bool moveIfOnContentNode)
{
do
{
switch (_curNode.type)
{
case XmlNodeType.Attribute:
return !moveIfOnContentNode;
case XmlNodeType.Text:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.CDATA:
if (!moveIfOnContentNode)
{
return true;
}
break;
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.Comment:
case XmlNodeType.EndEntity:
// skip comments, pis and end entity nodes
break;
case XmlNodeType.EntityReference:
_outerReader.ResolveEntity();
break;
default:
return false;
}
moveIfOnContentNode = false;
} while (_outerReader.Read());
return false;
}
private void SetupReadContentAsBinaryState(ParsingFunction inReadBinaryFunction)
{
if (_parsingFunction == ParsingFunction.PartialTextValue)
{
_incReadState = IncrementalReadState.ReadContentAsBinary_OnPartialValue;
}
else
{
_incReadState = IncrementalReadState.ReadContentAsBinary_OnCachedValue;
_nextNextParsingFunction = _nextParsingFunction;
_nextParsingFunction = _parsingFunction;
}
_readValueOffset = 0;
_parsingFunction = inReadBinaryFunction;
}
private void SetupFromParserContext(XmlParserContext context, XmlReaderSettings settings)
{
Debug.Assert(context != null);
// setup nameTable
XmlNameTable nt = settings.NameTable;
_nameTableFromSettings = (nt != null);
// get name table from namespace manager in XmlParserContext, if available;
if (context.NamespaceManager != null)
{
// must be the same as XmlReaderSettings.NameTable, or null
if (nt != null && nt != context.NamespaceManager.NameTable)
{
throw new XmlException(ResXml.Xml_NametableMismatch);
}
// get the namespace manager from context
_namespaceManager = context.NamespaceManager;
_xmlContext.defaultNamespace = _namespaceManager.LookupNamespace(string.Empty);
// get the nametable from ns manager
nt = _namespaceManager.NameTable;
Debug.Assert(nt != null);
Debug.Assert(context.NameTable == null || context.NameTable == nt, "This check should have been done in XmlParserContext constructor.");
}
// get name table directly from XmlParserContext
else if (context.NameTable != null)
{
// must be the same as XmlReaderSettings.NameTable, or null
if (nt != null && nt != context.NameTable)
{
throw new XmlException(ResXml.Xml_NametableMismatch, string.Empty);
}
nt = context.NameTable;
}
// no nametable provided -> create a new one
else if (nt == null)
{
nt = new NameTable();
Debug.Assert(_nameTableFromSettings == false);
}
_nameTable = nt;
// make sure we have namespace manager
if (_namespaceManager == null)
{
_namespaceManager = new XmlNamespaceManager(nt);
}
// copy xml:space and xml:lang
_xmlContext.xmlSpace = context.XmlSpace;
_xmlContext.xmlLang = context.XmlLang;
}
//
// DtdInfo
//
internal override IDtdInfo DtdInfo
{
get
{
return _dtdInfo;
}
}
internal void SetDtdInfo(IDtdInfo newDtdInfo)
{
Debug.Assert(_dtdInfo == null);
_dtdInfo = newDtdInfo;
if (_dtdInfo != null)
{
if ((_validatingReaderCompatFlag || !_v1Compat) && (_dtdInfo.HasDefaultAttributes || _dtdInfo.HasNonCDataAttributes))
{
_addDefaultAttributesAndNormalize = true;
}
}
}
//
// Validation support
//
internal IValidationEventHandling ValidationEventHandling
{
set
{
_validationEventHandling = value;
}
}
internal OnDefaultAttributeUseDelegate OnDefaultAttributeUse
{
set { _onDefaultAttributeUse = value; }
}
//
// Internal properties for XmlValidatingReader
//
internal bool XmlValidatingReaderCompatibilityMode
{
set
{
_validatingReaderCompatFlag = value;
// Fix for VSWhidbey 516556; These namespaces must be added to the nametable for back compat reasons.
if (value)
{
_nameTable.Add(XmlReservedNs.NsXs); // Note: this is equal to XmlReservedNs.NsXsd in Everett
_nameTable.Add(XmlReservedNs.NsXsi);
_nameTable.Add(XmlReservedNs.NsDataType);
}
}
}
internal XmlNodeType FragmentType
{
get
{
return _fragmentType;
}
}
internal void ChangeCurrentNodeType(XmlNodeType newNodeType)
{
Debug.Assert(_curNode.type == XmlNodeType.Whitespace && newNodeType == XmlNodeType.SignificantWhitespace, "Incorrect node type change!");
_curNode.type = newNodeType;
}
internal XmlResolver GetResolver()
{
if (IsResolverNull)
return null;
else
return _xmlResolver;
}
internal object InternalSchemaType
{
get
{
return _curNode.schemaType;
}
set
{
_curNode.schemaType = value;
}
}
internal object InternalTypedValue
{
get
{
return _curNode.typedValue;
}
set
{
_curNode.typedValue = value;
}
}
internal bool StandAlone
{
get
{
return _standalone;
}
}
internal override XmlNamespaceManager NamespaceManager
{
get
{
return _namespaceManager;
}
}
internal bool V1Compat
{
get
{
return _v1Compat;
}
}
internal ConformanceLevel V1ComformanceLevel
{
get
{
return _fragmentType == XmlNodeType.Element ? ConformanceLevel.Fragment : ConformanceLevel.Document;
}
}
private bool AddDefaultAttributeDtd(IDtdDefaultAttributeInfo defAttrInfo, bool definedInDtd, NodeData[] nameSortedNodeData)
{
if (defAttrInfo.Prefix.Length > 0)
{
_attrNeedNamespaceLookup = true;
}
string localName = defAttrInfo.LocalName;
string prefix = defAttrInfo.Prefix;
// check for duplicates
if (nameSortedNodeData != null)
{
if (Array.BinarySearch<object>(nameSortedNodeData, defAttrInfo, DtdDefaultAttributeInfoToNodeDataComparer.Instance) >= 0)
{
return false;
}
}
else
{
for (int i = _index + 1; i < _index + 1 + _attrCount; i++)
{
if ((object)_nodes[i].localName == (object)localName &&
(object)_nodes[i].prefix == (object)prefix)
{
return false;
}
}
}
NodeData attr = AddDefaultAttributeInternal(defAttrInfo.LocalName, null, defAttrInfo.Prefix, defAttrInfo.DefaultValueExpanded,
defAttrInfo.LineNumber, defAttrInfo.LinePosition,
defAttrInfo.ValueLineNumber, defAttrInfo.ValueLinePosition, defAttrInfo.IsXmlAttribute);
Debug.Assert(attr != null);
if (DtdValidation)
{
if (_onDefaultAttributeUse != null)
{
_onDefaultAttributeUse(defAttrInfo, this);
}
attr.typedValue = defAttrInfo.DefaultValueTyped;
}
return attr != null;
}
internal bool AddDefaultAttributeNonDtd(SchemaAttDef attrDef)
{
// atomize names - Xsd Validator does not need to have the same nametable
string localName = _nameTable.Add(attrDef.Name.Name);
string prefix = _nameTable.Add(attrDef.Prefix);
string ns = _nameTable.Add(attrDef.Name.Namespace);
// atomize namespace - Xsd Validator does not need to have the same nametable
if (prefix.Length == 0 && ns.Length > 0)
{
prefix = _namespaceManager.LookupPrefix(ns);
Debug.Assert(prefix != null);
if (prefix == null)
{
prefix = string.Empty;
}
}
// find out if the attribute is already there
for (int i = _index + 1; i < _index + 1 + _attrCount; i++)
{
if ((object)_nodes[i].localName == (object)localName &&
(((object)_nodes[i].prefix == (object)prefix) || ((object)_nodes[i].ns == (object)ns && ns != null)))
{
return false;
}
}
// attribute does not exist -> we need to add it
NodeData attr = AddDefaultAttributeInternal(localName, ns, prefix, attrDef.DefaultValueExpanded,
attrDef.LineNumber, attrDef.LinePosition,
attrDef.ValueLineNumber, attrDef.ValueLinePosition, attrDef.Reserved != SchemaAttDef.Reserve.None);
Debug.Assert(attr != null);
attr.schemaType = (attrDef.SchemaType == null) ? (object)attrDef.Datatype : (object)attrDef.SchemaType;
attr.typedValue = attrDef.DefaultValueTyped;
return true;
}
private NodeData AddDefaultAttributeInternal(string localName, string ns, string prefix, string value,
int lineNo, int linePos, int valueLineNo, int valueLinePos, bool isXmlAttribute)
{
// setup the attribute
NodeData attr = AddAttribute(localName, prefix, prefix.Length > 0 ? null : localName);
if (ns != null)
{
attr.ns = ns;
}
attr.SetValue(value);
attr.IsDefaultAttribute = true;
attr.lineInfo.Set(lineNo, linePos);
attr.lineInfo2.Set(valueLineNo, valueLinePos);
// handle special attributes:
if (attr.prefix.Length == 0)
{
// default namespace declaration
if (Ref.Equal(attr.localName, _xmlNs))
{
OnDefaultNamespaceDecl(attr);
if (!_attrNeedNamespaceLookup)
{
// change element default namespace
Debug.Assert(_nodes[_index].type == XmlNodeType.Element);
if (_nodes[_index].prefix.Length == 0)
{
_nodes[_index].ns = _xmlContext.defaultNamespace;
}
}
}
}
else
{
// prefixed namespace declaration
if (Ref.Equal(attr.prefix, _xmlNs))
{
OnNamespaceDecl(attr);
if (!_attrNeedNamespaceLookup)
{
// change namespace of current element and attributes
string pref = attr.localName;
Debug.Assert(_nodes[_index].type == XmlNodeType.Element);
for (int i = _index; i < _index + _attrCount + 1; i++)
{
if (_nodes[i].prefix.Equals(pref))
{
_nodes[i].ns = _namespaceManager.LookupNamespace(pref);
}
}
}
}
// xml: attribute
else
{
if (isXmlAttribute)
{
OnXmlReservedAttribute(attr);
}
}
}
_fullAttrCleanup = true;
return attr;
}
internal bool DisableUndeclaredEntityCheck
{
set
{
_disableUndeclaredEntityCheck = value;
}
}
private int ReadContentAsBinary(byte[] buffer, int index, int count)
{
Debug.Assert(_incReadDecoder != null);
if (_incReadState == IncrementalReadState.ReadContentAsBinary_End)
{
return 0;
}
_incReadDecoder.SetNextOutputBuffer(buffer, index, count);
for (; ; )
{
// read what is already cached in curNode
int charsRead = 0;
try
{
charsRead = _curNode.CopyToBinary(_incReadDecoder, _readValueOffset);
}
// add line info to the exception
catch (XmlException e)
{
_curNode.AdjustLineInfo(_readValueOffset, _ps.eolNormalized, ref _incReadLineInfo);
ReThrow(e, _incReadLineInfo.lineNo, _incReadLineInfo.linePos);
}
_readValueOffset += charsRead;
if (_incReadDecoder.IsFull)
{
return _incReadDecoder.DecodedCount;
}
// if on partial value, read the rest of it
if (_incReadState == IncrementalReadState.ReadContentAsBinary_OnPartialValue)
{
_curNode.SetValue(string.Empty);
// read next chunk of text
bool endOfValue = false;
int startPos = 0;
int endPos = 0;
while (!_incReadDecoder.IsFull && !endOfValue)
{
int orChars = 0;
// store current line info and parse more text
_incReadLineInfo.Set(_ps.LineNo, _ps.LinePos);
endOfValue = ParseText(out startPos, out endPos, ref orChars);
try
{
charsRead = _incReadDecoder.Decode(_ps.chars, startPos, endPos - startPos);
}
// add line info to the exception
catch (XmlException e)
{
ReThrow(e, _incReadLineInfo.lineNo, _incReadLineInfo.linePos);
}
startPos += charsRead;
}
_incReadState = endOfValue ? IncrementalReadState.ReadContentAsBinary_OnCachedValue : IncrementalReadState.ReadContentAsBinary_OnPartialValue;
_readValueOffset = 0;
if (_incReadDecoder.IsFull)
{
_curNode.SetValue(_ps.chars, startPos, endPos - startPos);
// adjust line info for the chunk that has been already decoded
AdjustLineInfo(_ps.chars, startPos - charsRead, startPos, _ps.eolNormalized, ref _incReadLineInfo);
_curNode.SetLineInfo(_incReadLineInfo.lineNo, _incReadLineInfo.linePos);
return _incReadDecoder.DecodedCount;
}
}
// reset to normal state so we can call Read() to move forward
ParsingFunction tmp = _parsingFunction;
_parsingFunction = _nextParsingFunction;
_nextParsingFunction = _nextNextParsingFunction;
// move to next textual node in the element content; throw on sub elements
if (!MoveToNextContentNode(true))
{
SetupReadContentAsBinaryState(tmp);
_incReadState = IncrementalReadState.ReadContentAsBinary_End;
return _incReadDecoder.DecodedCount;
}
SetupReadContentAsBinaryState(tmp);
_incReadLineInfo.Set(_curNode.LineNo, _curNode.LinePos);
}
}
private int ReadElementContentAsBinary(byte[] buffer, int index, int count)
{
if (count == 0)
{
return 0;
}
int decoded = ReadContentAsBinary(buffer, index, count);
if (decoded > 0)
{
return decoded;
}
// if 0 bytes returned check if we are on a closing EndElement, throw exception if not
if (_curNode.type != XmlNodeType.EndElement)
{
throw new XmlException(ResXml.Xml_InvalidNodeType, _curNode.type.ToString(), this as IXmlLineInfo);
}
// reset state
_parsingFunction = _nextParsingFunction;
_nextParsingFunction = _nextNextParsingFunction;
Debug.Assert(_parsingFunction != ParsingFunction.InReadElementContentAsBinary);
// move off the EndElement
_outerReader.Read();
return 0;
}
private void InitBase64Decoder()
{
if (_base64Decoder == null)
{
_base64Decoder = new Base64Decoder();
}
else
{
_base64Decoder.Reset();
}
_incReadDecoder = _base64Decoder;
}
private void InitBinHexDecoder()
{
if (_binHexDecoder == null)
{
_binHexDecoder = new BinHexDecoder();
}
else
{
_binHexDecoder.Reset();
}
_incReadDecoder = _binHexDecoder;
}
// SxS: URIs are resolved only to be compared. No resource exposure. It's OK to disable the SxS warning.
private bool UriEqual(Uri uri1, string uri1Str, string uri2Str, XmlResolver resolver)
{
if (resolver == null)
{
return uri1Str == uri2Str;
}
if (uri1 == null)
{
uri1 = resolver.ResolveUri(null, uri1Str);
}
Uri uri2 = resolver.ResolveUri(null, uri2Str);
return uri1.Equals(uri2);
}
/// <summary>
/// This method should be called every time the reader is about to consume some number of
/// characters from the input. It will count it agains the security counters and
/// may throw if some of the security limits are exceeded.
/// </summary>
/// <param name="characters">Number of characters to be consumed.</param>
/// <param name="inEntityReference">true if the characters are result of entity expansion.</param>
private void RegisterConsumedCharacters(long characters, bool inEntityReference)
{
Debug.Assert(characters >= 0);
if (_maxCharactersInDocument > 0)
{
long newCharactersInDocument = _charactersInDocument + characters;
if (newCharactersInDocument < _charactersInDocument)
{
// Integer overflow while counting
ThrowWithoutLineInfo(ResXml.Xml_LimitExceeded, "MaxCharactersInDocument");
}
else
{
_charactersInDocument = newCharactersInDocument;
}
if (_charactersInDocument > _maxCharactersInDocument)
{
// The limit was exceeded for the total number of characters in the document
ThrowWithoutLineInfo(ResXml.Xml_LimitExceeded, "MaxCharactersInDocument");
}
}
if (_maxCharactersFromEntities > 0 && inEntityReference)
{
long newCharactersFromEntities = _charactersFromEntities + characters;
if (newCharactersFromEntities < _charactersFromEntities)
{
// Integer overflow while counting
ThrowWithoutLineInfo(ResXml.Xml_LimitExceeded, "MaxCharactersFromEntities");
}
else
{
_charactersFromEntities = newCharactersFromEntities;
}
if (_charactersFromEntities > _maxCharactersFromEntities)
{
// The limit was exceeded for the number of characters from entities
ThrowWithoutLineInfo(ResXml.Xml_LimitExceeded, "MaxCharactersFromEntities");
}
}
}
static internal unsafe void AdjustLineInfo(char[] chars, int startPos, int endPos, bool isNormalized, ref LineInfo lineInfo)
{
Debug.Assert(startPos >= 0);
Debug.Assert(endPos < chars.Length);
Debug.Assert(startPos <= endPos);
fixed (char* pChars = &chars[startPos])
{
AdjustLineInfo(pChars, endPos - startPos, isNormalized, ref lineInfo);
}
}
static internal unsafe void AdjustLineInfo(string str, int startPos, int endPos, bool isNormalized, ref LineInfo lineInfo)
{
Debug.Assert(startPos >= 0);
Debug.Assert(endPos < str.Length);
Debug.Assert(startPos <= endPos);
fixed (char* pChars = str)
{
AdjustLineInfo(pChars + startPos, endPos - startPos, isNormalized, ref lineInfo);
}
}
static internal unsafe void AdjustLineInfo(char* pChars, int length, bool isNormalized, ref LineInfo lineInfo)
{
int lastNewLinePos = -1;
for (int i = 0; i < length; i++)
{
switch (pChars[i])
{
case '\n':
lineInfo.lineNo++;
lastNewLinePos = i;
break;
case '\r':
if (isNormalized)
{
break;
}
lineInfo.lineNo++;
lastNewLinePos = i;
if (i + 1 < length && pChars[i + 1] == '\n')
{
i++;
lastNewLinePos++;
}
break;
}
}
if (lastNewLinePos >= 0)
{
lineInfo.linePos = length - lastNewLinePos;
}
}
// StripSpaces removes spaces at the beginning and at the end of the value and replaces sequences of spaces with a single space
internal static string StripSpaces(string value)
{
int len = value.Length;
if (len <= 0)
{
return string.Empty;
}
int startPos = 0;
StringBuilder norValue = null;
while (value[startPos] == 0x20)
{
startPos++;
if (startPos == len)
{
return " ";
}
}
int i;
for (i = startPos; i < len; i++)
{
if (value[i] == 0x20)
{
int j = i + 1;
while (j < len && value[j] == 0x20)
{
j++;
}
if (j == len)
{
if (norValue == null)
{
return value.Substring(startPos, i - startPos);
}
else
{
norValue.Append(value, startPos, i - startPos);
return norValue.ToString();
}
}
if (j > i + 1)
{
if (norValue == null)
{
norValue = new StringBuilder(len);
}
norValue.Append(value, startPos, i - startPos + 1);
startPos = j;
i = j - 1;
}
}
}
if (norValue == null)
{
return (startPos == 0) ? value : value.Substring(startPos, len - startPos);
}
else
{
if (i > startPos)
{
norValue.Append(value, startPos, i - startPos);
}
return norValue.ToString();
}
}
// StripSpaces removes spaces at the beginning and at the end of the value and replaces sequences of spaces with a single space
internal static void StripSpaces(char[] value, int index, ref int len)
{
if (len <= 0)
{
return;
}
int startPos = index;
int endPos = index + len;
while (value[startPos] == 0x20)
{
startPos++;
if (startPos == endPos)
{
len = 1;
return;
}
}
int offset = startPos - index;
int i;
for (i = startPos; i < endPos; i++)
{
char ch;
if ((ch = value[i]) == 0x20)
{
int j = i + 1;
while (j < endPos && value[j] == 0x20)
{
j++;
}
if (j == endPos)
{
offset += (j - i);
break;
}
if (j > i + 1)
{
offset += (j - i - 1);
i = j - 1;
}
}
value[i - offset] = ch;
}
len -= offset;
}
internal static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count)
{
// PERF: Buffer.BlockCopy is faster than Array.Copy
Buffer.BlockCopy(src, srcOffset * sizeof(char), dst, dstOffset * sizeof(char), count * sizeof(char));
}
internal static void BlockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
{
Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);
}
}