+2003-08-07 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
+
+ * DTDObjectModel.cs :
+ - Added validation error check (and AddError(), Errors).
+ - Fixed ComputeDefaultValue() to handle various references correctly.
+ - DTDEntityDeclaration.EntityValue became property, and added
+ LiteralEntityValue. The new one holds resolved value.
+ Added ResolveExternalEntity(). It now required root in .ctor().
+ * DTDValidatingReader.cs :
+ - Now it handles namespaced attributes (as input to xsd validator).
+ - Added XmlResolver property as usual XmlReader.
+ - Added currentEntityHandling field so that it can stand changing
+ XmlValidatingReader's EntityHandling dynamically.
+ - FilterNormalization() now requires name for getting datatypes and
+ can stand for non-current attribute normalization.
+ - Splitted ReadContent() from Read() that can be called recursively
+ when expanding entities.
+ - Now handles Entity not found error *after* resolution of entities,
+ as MS.NET does.
+ - Read()ing DTD checks its Errors and raises validation error events.
+ In some situations, DTD parsing may detect VC error, not WFC error.
+ It also strictly checks NData existence.
+ - Handling of entity-resolved text is a bit changed and Read()ing
+ element, endElement, cdata now changed to switch collecting text
+ and collected text.
+ - content type ANY should allow texts.
+ - Added enumerated attribute validity check.
+ - Added Name/Names creation rule check for ID/IDREF/IDREFS.
+ - Added entity existence check for ENTITY/ENTITIES attributes.
+ - Added NMTOKEN creation rule check for NMTOKEN/NMTOKENS.
+ - Fixed to remove extraneous #REQUIRED check.
+ - Contributing default attribute is now only applied to the case
+ ValidationType is DTD or None.
+ - ResolveEntity() now handles external entities.
+ Added Mono.Xml.IXmlParserContext interface support.
+ * XmlDocumentType.cs : Fixed to use BaseURI to build DTD model.
+ * XmlTextReader.cs :
+ - Fixed ReadAttributeValue() to reset returnEntityReference.
+ - Added bool MaybeTextDecl only for ResolveEntity().
+ - Fixed ReadWhitespace(). In fact its value is considered even if it
+ is in the end of the XML.
+ - Fixed ReadXmlDeclaration(). Now skips text declaration.
+ - CompileDTDSubset() now checks IGNORE/INCLUDE section end balances.
+ - ReadContentSpec() should set OrderType = OR for mixed model.
+ Fixed to set content element name correctly.
+ - ImportAsPERef() now skips Text declaration.
+ - Type of enumerated default attributes shold be string.
+ - Undeclared PE error is now handled as DTD's VC error, not
+ XmlTextReader's WFC error.
+ * XmlValidatingReader.cs : BaseURI should provide that of
+ original XmlReader's when read was not started yet.
+ Fixed XmlResolver to delegate to DTDValidatingReader.
+
+ * XmlAttributeCollection.cs : Acquiring attList declaration is
+ insufficient especially in case of lack of elementdecl.
+ * XmlChar.cs : (Maybe this is compact than XmlConstructs.)
+ added IsWhitespace(),IsNCNameChar(),IsName(),IsNCName(),IsNmToken(),
+ IsPubid(). Copies IsValidIANAEncoding() from XmlConstructs.
+
+ * XmlInputStream.cs : Removed "version" declaration check. It should
+ be done by XmlTextReader.
+
+ * XmlNodeReader.cs : added internal GetInternalParserContext() and now
+ it can be used in XmlValidatingReader.ResolveEntity().
+ ResolveEntity() sets XmlTextReader.MaybeTextDecl.
+
2003-08-05 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
* DTDValidatingReader.cs :
//
using System;
using System.Collections;
+using System.Globalization;
+using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using Mono.Xml.Schema;
+using Mono.Xml.Native;
namespace Mono.Xml
{
DTDAttListDeclarationCollection attListDecls;
DTDEntityDeclarationCollection entityDecls;
DTDNotationDeclarationCollection notationDecls;
+ ArrayList validationErrors;
public DTDObjectModel ()
{
entityDecls = new DTDEntityDeclarationCollection (this);
notationDecls = new DTDNotationDeclarationCollection (this);
factory = new DTDAutomataFactory (this);
+ validationErrors = new ArrayList ();
}
public string BaseURI;
return invalidAutomata;
}
}
+
+ public XmlSchemaException [] Errors {
+ get { return validationErrors.ToArray (typeof (XmlSchemaException)) as XmlSchemaException []; }
+ }
+
+ public void AddError (XmlSchemaException ex)
+ {
+ validationErrors.Add (ex);
+ }
}
public class DTDElementDeclarationCollection
public void Add (string name, DTDElementDeclaration decl)
{
- if (elementDecls [name] != null)
- throw new InvalidOperationException (String.Format (
+ if (elementDecls [name] != null) {
+ this.root.AddError (new XmlSchemaException (String.Format (
"Element declaration for {0} was already added.",
- name));
+ name), null));
+ return;
+ }
decl.SetRoot (root);
elementDecls.Add (name, decl);
}
StringBuilder sb = new StringBuilder ();
int pos = 0;
int next = 0;
- while ((next = this.UnresolvedDefaultValue.IndexOf ('&', pos)) >= 0) {
- sb.Append (this.UnresolvedDefaultValue.Substring (pos, next - 1));
- int semicolon = this.UnresolvedDefaultValue.IndexOf (';', next);
- string name = this.UnresolvedDefaultValue.Substring (pos + 1, semicolon - 1);
- sb.Append (Root.ResolveEntity (name));
+ string value = this.UnresolvedDefaultValue;
+ while ((next = value.IndexOf ('&', pos)) >= 0) {
+ int semicolon = value.IndexOf (';', next);
+ if (value [next + 1] == '#') {
+ // character reference.
+ char c = value [next + 2];
+ NumberStyles style = NumberStyles.Integer;
+ string spec;
+ if (c == 'x' || c == 'X') {
+ spec = value.Substring (next + 3, semicolon - next - 3);
+ style |= NumberStyles.HexNumber;
+ }
+ else
+ spec = value.Substring (next + 2, semicolon - next - 2);
+ sb.Append ((char) int.Parse (spec, style));
+ } else {
+ sb.Append (value.Substring (pos, next - 1));
+ string name = value.Substring (pos + 1, semicolon - 1);
+ switch (name) {
+ case "lt":
+ sb.Append ("<");
+ break;
+ case "gt":
+ sb.Append (">");
+ break;
+ case "amp":
+ sb.Append ("&");
+ break;
+ case "quot":
+ sb.Append ("\"");
+ break;
+ case "apos":
+ sb.Append ("'");
+ break;
+ default:
+ sb.Append (Root.ResolveEntity (name));
+ break;
+ }
+ }
+ pos = semicolon + 1;
}
- sb.Append (this.UnresolvedDefaultValue.Substring (pos));
+ sb.Append (value.Substring (pos));
// strip quote chars
string ret = sb.ToString (1, sb.Length - 2);
sb.Length = 0;
public class DTDEntityDeclaration : DTDNode
{
+ string entityValue;
+
public string Name;
public string PublicId;
public string SystemId;
public string NotationName;
- // FIXME: should have more complex value than simple string
- public string EntityValue;
+ public string LiteralEntityValue;
public bool IsInternalSubset;
- internal DTDEntityDeclaration () {}
+ public string EntityValue {
+ get {
+ if (entityValue == null) {
+ if (SystemId == null)
+ entityValue = LiteralEntityValue;
+ else {
+ // FIXME: should use specified XmlUrlResolver.
+ entityValue = ResolveExternalEntity (new XmlUrlResolver ());
+ }
+ }
+ return entityValue;
+ }
+ }
+
+ private string ResolveExternalEntity (XmlResolver resolver)
+ {
+ string baseUri = Root.BaseURI;
+ if (baseUri == "")
+ baseUri = null;
+ Uri uri = resolver.ResolveUri (
+ baseUri != null ? new Uri (baseUri) : null, SystemId);
+ Stream stream = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
+ XmlStreamReader reader = new XmlStreamReader (stream, false);
+
+ StringBuilder sb = new StringBuilder ();
+
+ bool checkTextDecl = true;
+ while (reader.Peek () != -1) {
+ sb.Append (reader.Read ());
+ if (checkTextDecl && sb.Length == 6) {
+ if (sb.ToString () == "<?xml ") {
+ // Skip Text declaration.
+ sb.Length = 0;
+ while (reader.Peek () == '>' || reader.Peek () == -1)
+ reader.Read ();
+ }
+ checkTextDecl = false;
+ }
+ }
+ return sb.ToString ();
+ }
+
+ internal DTDEntityDeclaration (DTDObjectModel root)
+ {
+ this.SetRoot (root);
+ }
}
public class DTDNotationDeclaration : DTDNode
using System;
using System.Collections.Specialized;
using System.Collections;
+using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
automataStack = new Stack ();
attributes = new StringCollection ();
attributeValues = new NameValueCollection ();
+ attributeLocalNames = new NameValueCollection ();
+ attributeNamespaces = new NameValueCollection ();
this.validatingReader = validatingReader;
valueBuilder = new StringBuilder ();
idList = new ArrayList ();
missingIDReferences = new ArrayList ();
+ resolver = new XmlUrlResolver ();
}
Stack entityReaderStack;
string currentElement;
string currentAttribute;
string currentTextValue;
+ string constructingTextValue;
bool shouldResetCurrentTextValue;
bool consumedAttribute;
bool insideContent;
bool isStandalone;
StringCollection attributes;
NameValueCollection attributeValues;
+ NameValueCollection attributeLocalNames;
+ NameValueCollection attributeNamespaces;
StringBuilder valueBuilder;
ArrayList idList;
ArrayList missingIDReferences;
+ XmlResolver resolver;
+ EntityHandling currentEntityHandling;
+ bool isSignificantWhitespace;
+ bool isWhitespace;
// This field is used to get properties and to raise events.
XmlValidatingReader validatingReader;
if (attributes.Count <= i)
throw new IndexOutOfRangeException ("Specified index is out of range: " + i);
- return FilterNormalization (attributeValues [i]);
+ return FilterNormalization (attributes [i], attributeValues [i]);
}
public override string GetAttribute (string name)
if (dtd == null)
return reader.GetAttribute (name);
- return FilterNormalization (attributeValues [name]);
+ return FilterNormalization (name, attributeValues [name]);
}
public override string GetAttribute (string name, string ns)
if (dtd == null)
return reader.GetAttribute (name, ns);
- // FIXME: check whether this way is correct.
- if (ns == String.Empty)
- return GetAttribute (name);
- else
- return FilterNormalization (reader.GetAttribute (name, ns));
+ return reader.GetAttribute (attributeLocalNames [name], ns);
}
bool IXmlLineInfo.HasLineInfo ()
return true;
}
- if (ns != String.Empty)
- throw new InvalidOperationException ("DTD validating reader does not support namespace.");
- return MoveToAttribute (name);
+ foreach (string iter in attributes)
+ if (attributeLocalNames [iter] == name)
+ return MoveToAttribute (iter);
+ return false;
}
public override bool MoveToElement ()
return false;
}
+ [MonoTODO ("Handling of whitespace entities are still not enough.")]
public override bool Read ()
{
if (currentTextValue != null)
consumedAttribute = false;
attributes.Clear ();
attributeValues.Clear ();
-
- return ReadContent () || currentTextValue != null;
+ attributeNamespaces.Clear ();
+ isWhitespace = false;
+ isSignificantWhitespace = false;
+
+ bool b = ReadContent () || currentTextValue != null;
+ if (!b && this.missingIDReferences.Count > 0) {
+ this.HandleError ("Missing ID reference was found: " +
+ String.Join (",", missingIDReferences.ToArray (typeof (string)) as string []),
+ XmlSeverityType.Error);
+ // Don't output the same errors so many times.
+ this.missingIDReferences.Clear ();
+ }
+ currentEntityHandling = validatingReader.EntityHandling;
+ return b;
}
private bool ReadContent ()
{
if (nextEntityReader != null) {
- if (DTD == null || DTD.EntityDecls [Name] == null)
- throw new XmlException ("Entity '" + Name + "' was not declared.");
+ if (DTD == null || DTD.EntityDecls [reader.Name] == null)
+ throw new XmlException ("Entity '" + reader.Name + "' was not declared.");
entityReaderStack.Push (reader);
- entityReaderNameStack.Push (Name);
+ entityReaderNameStack.Push (reader.Name);
entityReaderDepthStack.Push (Depth);
reader = sourceTextReader = nextEntityReader;
nextEntityReader = null;
return ReadContent ();
- } else if (NodeType == XmlNodeType.EndEntity) {
+ } else if (reader.EOF && entityReaderStack.Count > 0) {
reader = entityReaderStack.Pop () as XmlReader;
entityReaderNameStack.Pop ();
entityReaderDepthStack.Pop ();
}
if (!b) {
- if (entityReaderStack.Count > 0)
- return true; // EndEntity
+ if (entityReaderStack.Count > 0) {
+ if (validatingReader.EntityHandling == EntityHandling.ExpandEntities)
+ return ReadContent ();
+ else
+ return true; // EndEntity
+ }
if (elementStack.Count != 0)
throw new InvalidOperationException ("Unexpected end of XmlReader.");
reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
}
this.dtd = xmlTextReader.DTD;
+
+ // Validity Constraints Check.
+ if (DTD.Errors.Length > 0)
+ foreach (XmlSchemaException ex in DTD.Errors)
+ HandleError (ex.Message, XmlSeverityType.Error);
+
+ // NData target exists.
+ foreach (DTDEntityDeclaration ent in dtd.EntityDecls.Values)
+ if (ent.NotationName != null && dtd.NotationDecls [ent.NotationName] == null)
+ this.HandleError ("Target notation was not found for NData in entity declaration " + ent.Name + ".",
+ XmlSeverityType.Error);
+ // NOTATION exists for attribute default values
+ foreach (DTDAttListDeclaration attListIter in dtd.AttListDecls.Values)
+ foreach (DTDAttributeDefinition def in attListIter.Definitions)
+ if (def.Datatype.TokenizedType == XmlTokenizedType.NOTATION) {
+ foreach (string notation in def.EnumeratedNotations)
+ if (dtd.NotationDecls [notation] == null)
+ this.HandleError ("Target notation was not found for NOTATION typed attribute default " + def.Name + ".",
+ XmlSeverityType.Error);
+ }
+
break;
case XmlNodeType.Element:
- if (currentTextValue != null)
+ if (constructingTextValue != null) {
+ currentTextValue = constructingTextValue;
+ constructingTextValue = null;
return true;
+ }
elementStack.Push (reader.Name);
// startElementDeriv
// If no schema specification, then skip validation.
break;
case XmlNodeType.EndElement:
- if (currentTextValue != null)
+ if (constructingTextValue != null) {
+ currentTextValue = constructingTextValue;
+ constructingTextValue = null;
return true;
+ }
elementStack.Pop ();
// endElementDeriv
// If no schema specification, then skip validation.
break;
case XmlNodeType.CDATA:
- if (currentTextValue != null)
+ if (currentTextValue != null) {
+ currentTextValue = constructingTextValue;
+ constructingTextValue = null;
return true;
+ }
goto case XmlNodeType.Text;
case XmlNodeType.SignificantWhitespace:
+// isSignificantWhitespace = true;
+ goto case XmlNodeType.Text;
case XmlNodeType.Text:
// If no schema specification, then skip validation.
if (currentAutomata == null)
DTDElementDeclaration elem = dtd.ElementDecls [elementStack.Peek () as string];
// Here element should have been already validated, so
// if no matching declaration is found, simply ignore.
- if (elem != null && !elem.IsMixedContent) {
+ if (elem != null && !elem.IsMixedContent && !elem.IsAny) {
HandleError (String.Format ("Current element {0} does not allow character data content.", elementStack.Peek () as string),
XmlSeverityType.Error);
// FIXME: validation recovery code here.
currentAutomata = previousAutomata;
}
if (validatingReader.EntityHandling == EntityHandling.ExpandEntities) {
- currentTextValue += reader.Value;
+ constructingTextValue += reader.Value;
+ return ReadContent ();
+ }
+ break;
+ case XmlNodeType.Whitespace:
+// if (!isSignificantWhitespace)
+// isWhitespace = true;
+ if (validatingReader.EntityHandling == EntityHandling.ExpandEntities) {
+ constructingTextValue += reader.Value;
return ReadContent ();
}
break;
// If it was invalid, simply add specified attributes.
do {
attributes.Add (reader.Name);
+ attributeLocalNames.Add (reader.Name, reader.LocalName);
+ attributeNamespaces.Add (reader.Name, reader.NamespaceURI);
attributeValues.Add (reader.Name, reader.Value);
} while (reader.MoveToNextAttribute ());
reader.MoveToElement ();
if (validatingReader != null)
this.validatingReader.OnValidationEvent (this,
- new ValidationEventArgs (ex, message, severity));
+ new ValidationEventArgs (ex, ex.Message, severity));
else
throw ex;
}
while (reader.MoveToNextAttribute ()) {
string attrName = reader.Name;
attributes.Add (attrName);
+ attributeLocalNames.Add (attrName, reader.LocalName);
+ attributeNamespaces.Add (attrName, reader.NamespaceURI);
bool hasError = false;
while (reader.ReadAttributeValue ()) {
if (reader.NodeType == XmlNodeType.EntityReference) {
XmlSeverityType.Error);
// FIXME: validation recovery code here.
} else {
- // check identity constraint
+ // check enumeration constraint
+ if (def.EnumeratedAttributeDeclaration.Count > 0)
+ if (!def.EnumeratedAttributeDeclaration.Contains (attrValue))
+ HandleError (String.Format ("Attribute enumeration constraint error in attribute {0}, value {1}.",
+ reader.Name, attrValue), XmlSeverityType.Error);
+ if (def.EnumeratedNotations.Count > 0)
+ if (!def.EnumeratedNotations.Contains (attrValue))
+ HandleError (String.Format ("Attribute notation enumeration constraint error in attribute {0}, value {1}.",
+ reader.Name, attrValue), XmlSeverityType.Error);
+
+ // check type constraint
switch (def.Datatype.TokenizedType) {
case XmlTokenizedType.ID:
- if (this.idList.Contains (attrValue)) {
+ if (!XmlChar.IsName (FilterNormalization (def.Name, attrValue)))
+ HandleError (String.Format ("ID attribute value must match the creation rule Name: {0}", attrValue),
+ XmlSeverityType.Error);
+ else if (this.idList.Contains (attrValue)) {
HandleError (String.Format ("Node with ID {0} was already appeared.", attrValue),
XmlSeverityType.Error);
- // FIXME: validation recovery code here.
} else {
if (missingIDReferences.Contains (attrValue))
missingIDReferences.Remove (attrValue);
}
break;
case XmlTokenizedType.IDREF:
+ if (!XmlChar.IsName (attrValue))
+ HandleError (String.Format ("IDREF attribute value must match the creation rule Name: {0}", attrValue),
+ XmlSeverityType.Error);
if (!idList.Contains (attrValue))
missingIDReferences.Add (attrValue);
break;
case XmlTokenizedType.IDREFS:
string [] idrefs = def.Datatype.ParseValue (attrValue, NameTable, null) as string [];
- foreach (string idref in idrefs)
- if (!idList.Contains (attrValue))
+ foreach (string idref in idrefs) {
+ if (!XmlChar.IsName (FilterNormalization (def.Name, idref)))
+ HandleError (String.Format ("Each ID in IDREFS attribute value must match the creation rule Name: {0}", attrValue),
+ XmlSeverityType.Error);
+ if (!idList.Contains (idref))
missingIDReferences.Add (attrValue);
+ }
break;
- }
-
- switch (def.OccurenceType) {
- case DTDAttributeOccurenceType.Required:
- if (attrValue == String.Empty) {
- HandleError (String.Format ("Required attribute {0} in element {1} not found .",
- def.Name, decl.Name),
+ case XmlTokenizedType.ENTITY:
+ if (dtd.EntityDecls [attrValue] == null)
+ HandleError (String.Format ("Reference to undeclared entity was found in attribute {0}.", reader.Name),
XmlSeverityType.Error);
- // FIXME: validation recovery code here.
- }
break;
- case DTDAttributeOccurenceType.Fixed:
- if (attrValue != def.DefaultValue) {
- HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
- def.Name, decl.Name, attrValue),
+ case XmlTokenizedType.ENTITIES:
+ string [] entrefs = def.Datatype.ParseValue (attrValue, NameTable, null) as string [];
+ foreach (string entref in entrefs)
+ if (dtd.EntityDecls [entref] == null)
+ HandleError (String.Format ("Reference to undeclared entity was found in attribute {0}.", reader.Name),
+ XmlSeverityType.Error);
+ break;
+ case XmlTokenizedType.NMTOKEN:
+ if (!XmlChar.IsNmToken (FilterNormalization (def.Name, attrValue)))
+ HandleError (String.Format ("NMTOKEN attribute value must match the creation rule NMTOKEN. Name={0}", reader.Name),
XmlSeverityType.Error);
- // FIXME: validation recovery code here.
- }
+ break;
+ case XmlTokenizedType.NMTOKENS:
+ string [] tokens = def.Datatype.ParseValue (attrValue, NameTable, null) as string [];
+ foreach (string token in tokens)
+ if (!XmlChar.IsNmToken (FilterNormalization (def.Name, token)))
+ HandleError (String.Format ("Name Token in NMTOKENS attribute value must match the creation rule NMTOKEN. Name={0}", reader.Name),
+ XmlSeverityType.Error);
break;
}
+
+ if (def.OccurenceType == DTDAttributeOccurenceType.Fixed &&
+ attrValue != def.DefaultValue) {
+ HandleError (String.Format ("Fixed attribute {0} in element {1} has invalid value {2}.",
+ def.Name, decl.Name, attrValue),
+ XmlSeverityType.Error);
+ // FIXME: validation recovery code here.
+ }
}
}
// Check if all required attributes exist, and/or
foreach (DTDAttributeDefinition def in decl.Definitions)
if (!attributes.Contains (def.Name)) {
if (def.OccurenceType == DTDAttributeOccurenceType.Required) {
- HandleError (String.Format ("Required attribute {0} was not found.", decl.Name),
+ HandleError (String.Format ("Required attribute {0} in element {1} not found .",
+ def.Name, decl.Name),
XmlSeverityType.Error);
// FIXME: validation recovery code here.
}
else if (def.DefaultValue != null) {
- attributes.Add (def.Name);
- attributeValues.Add (def.Name, def.DefaultValue);
+ switch (validatingReader.ValidationType) {
+ case ValidationType.DTD:
+ case ValidationType.None:
+ // Other than them, ignore DTD defaults.
+ attributes.Add (def.Name);
+ int colonAt = def.Name.IndexOf (':');
+ attributeLocalNames.Add (def.Name, colonAt < 0 ? def.Name : def.Name.Substring (colonAt + 1));
+ attributeNamespaces.Add (def.Name, colonAt < 0 ? def.Name : def.Name.Substring (0, colonAt));
+ attributeValues.Add (def.Name, def.DefaultValue);
+ break;
+ }
}
}
if (consumedAttribute)
return false;
if (NodeType == XmlNodeType.Attribute &&
- validatingReader.EntityHandling == EntityHandling.ExpandEntities) {
+ currentEntityHandling == EntityHandling.ExpandEntities) {
consumedAttribute = true;
return true;
}
public override void ResolveEntity ()
{
- if (NodeType != XmlNodeType.EntityReference)
+ // "reader." is required since NodeType must not be entityref by nature.
+ if (reader.NodeType != XmlNodeType.EntityReference)
throw new InvalidOperationException ("The current node is not an Entity Reference");
- DTDEntityDeclaration entity = DTD != null ? DTD.EntityDecls [Name] as DTDEntityDeclaration : null;
-
- // MS.NET seems simply ignoring undeclared entity reference here ;-(
- string replacementText =
- (entity != null) ? entity.EntityValue : String.Empty;
+ DTDEntityDeclaration entity = DTD != null ? DTD.EntityDecls [reader.Name] as DTDEntityDeclaration : null;
- XmlNodeType xmlReaderNodeType =
- (currentAttribute != null) ? XmlNodeType.Attribute : XmlNodeType.Element;
+ XmlParserContext ctx = null;
+ if (sourceTextReader != null)
+ ctx = sourceTextReader.GetInternalParserContext ();
+ else if (reader is XmlNodeReader)
+ ctx = ((XmlNodeReader) reader).GetInternalParserContext ();
+ else if (reader is IHasXmlParserContext)
+ ctx = ((IHasXmlParserContext) reader).ParserContext;
- if (sourceTextReader == null)
+ if (ctx == null)
throw new NotSupportedException (
"Entity resolution from non-XmlTextReader XmlReader could not be supported.");
- XmlParserContext ctx = sourceTextReader.GetInternalParserContext ();
- // FIXME: is seems impossible to get namespaceManager from XmlReader.
-// ctx = new XmlParserContext (document.NameTable,
-// new XmlNamespaceManager (NameTable),
-// DTD,
-// BaseURI, XmlLang, XmlSpace, Encoding.Unicode);
- nextEntityReader = new XmlTextReader (replacementText, xmlReaderNodeType, ctx);
+ XmlNodeType xmlReaderNodeType =
+ (currentAttribute != null) ? XmlNodeType.Attribute : XmlNodeType.Element;
+
+ // MS.NET seems simply ignoring undeclared entity reference here ;-(
+ if (entity != null && entity.SystemId != null) {
+ Uri baseUri = entity.BaseURI == null ? null : new Uri (entity.BaseURI);
+ Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
+ nextEntityReader = new XmlTextReader (stream, xmlReaderNodeType, ctx);
+ } else {
+ string replacementText =
+ (entity != null) ? entity.EntityValue : String.Empty;
+ nextEntityReader = new XmlTextReader (replacementText, xmlReaderNodeType, ctx);
+ }
+ nextEntityReader.MaybeTextDecl = true;
}
public override int AttributeCount {
if (NodeType != XmlNodeType.EndEntity)
baseNum++;
}
+ if (currentTextValue != null && reader.NodeType == XmlNodeType.EndElement)
+ baseNum++;
+
return IsDefault ? baseNum + 1 : baseNum;
}
}
public override XmlNodeType NodeType {
get {
if (currentTextValue != null)
- return XmlNodeType.Text;
+ return isSignificantWhitespace ? XmlNodeType.SignificantWhitespace :
+ isWhitespace ? XmlNodeType.Whitespace :
+ XmlNodeType.Text;
if (entityReaderStack.Count > 0 && reader.EOF)
return XmlNodeType.EndEntity;
}
char [] whitespaceChars = new char [] {' '};
- private string FilterNormalization (string rawValue)
+ private string FilterNormalization (string attrName, string rawValue)
{
if (DTD != null &&
NodeType == XmlNodeType.Attribute &&
sourceTextReader != null &&
sourceTextReader.Normalization) {
DTDAttributeDefinition def =
- dtd.AttListDecls [currentElement] [currentAttribute] as DTDAttributeDefinition;
+ dtd.AttListDecls [currentElement].Get (attrName);
valueBuilder.Append (rawValue);
valueBuilder.Replace ('\r', ' ');
valueBuilder.Replace ('\n', ' ');
}
// As to this property, MS.NET seems ignorant of EntityHandling...
else if (NodeType == XmlNodeType.Attribute)// &&
- // validatingReader.EntityHandling == EntityHandling.ExpandEntities)
- return FilterNormalization (attributeValues [currentAttribute]);
+ // currentEntityHandling == EntityHandling.ExpandEntities)
+ return FilterNormalization (Name, attributeValues [currentAttribute]);
else if (consumedAttribute)
- return FilterNormalization (attributeValues [this.currentAttribute]);
+ return FilterNormalization (Name, attributeValues [this.currentAttribute]);
else
- return FilterNormalization (reader.Value);
+ return FilterNormalization (Name, reader.Value);
}
}
}
}
+ public XmlResolver XmlResolver {
+ set {
+ resolver = value;
+ }
+ }
+
public override XmlSpace XmlSpace {
get {
string val = this ["xml:space"];
XmlDocumentType doctype = node.OwnerDocument.DocumentType;
if (doctype == null || doctype.DTD == null)
return existing;
- DTDElementDeclaration elem = doctype.DTD.ElementDecls [ownerElement.Name];
- DTDAttributeDefinition attdef = elem == null ? null : elem.Attributes [node.Name];
+ DTDAttListDeclaration attList = doctype.DTD.AttListDecls [ownerElement.Name];
+ DTDAttributeDefinition attdef = attList == null ? null : attList.Get (node.Name);
if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
return existing;
return ch == 0x20 || ch == 0x9 || ch == 0xD || ch == 0xA;\r
}\r
\r
+ internal static bool IsWhitespace (string str)\r
+ {\r
+ foreach (char c in str)\r
+ if (!IsWhitespace (c))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
internal static bool IsFirstNameChar(int ch)\r
{\r
bool result = false;\r
return result;\r
}\r
\r
+ internal static bool IsNCNameChar(int ch)\r
+ {\r
+ bool result = false;\r
+\r
+ if (ch >= 0 && ch <= 0xFFFF && ch != ':')\r
+ {\r
+ result = (nameBitmap[(namePages[ch >> 8] << 3) + ((ch & 0xFF) >> 5)] & (1 << (ch & 0x1F))) != 0;\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ internal static bool IsName (string str)\r
+ {\r
+ if (str.Length == 0)\r
+ return false;\r
+ if (!IsFirstNameChar (str [0]))\r
+ return false;\r
+ foreach (char c in str)\r
+ if (!IsNameChar (c))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ internal static bool IsNCName (string str)\r
+ {\r
+ if (str.Length == 0)\r
+ return false;\r
+ if (!IsFirstNameChar (str [0]))\r
+ return false;\r
+ foreach (char c in str)\r
+ if (!IsNCNameChar (c))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ internal static bool IsNmToken (string str)\r
+ {\r
+ if (str.Length == 0)\r
+ return false;\r
+ foreach (char c in str)\r
+ if (!IsNameChar (c))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
internal static bool IsPubidChar(int ch)\r
{\r
return IsWhitespace(ch) | ('a' <= ch && ch <= 'z') | ('A' <= ch && ch <= 'Z') | ('0' <= ch && ch <= '9') | "-'()+,./:=?;!*#@$_%".IndexOf((char)ch) >= 0;\r
}\r
\r
+ internal static bool IsPubid (string str)\r
+ {\r
+ foreach (char c in str)\r
+ if (!IsPubidChar (c))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ // encodings (copied from XmlConstructs.cs)\r
+\r
+ /// <summary>\r
+ /// Returns true if the encoding name is a valid IANA encoding.\r
+ /// This method does not verify that there is a decoder available\r
+ /// for this encoding, only that the characters are valid for an\r
+ /// IANA encoding name.\r
+ /// </summary>\r
+ /// <param name="ianaEncoding">The encoding to check.</param>\r
+ /// <returns></returns>\r
+ internal static bool IsValidIANAEncoding(String ianaEncoding) \r
+ {\r
+ if (ianaEncoding != null) \r
+ {\r
+ int length = ianaEncoding.Length;\r
+ if (length > 0) \r
+ {\r
+ char c = ianaEncoding[0];\r
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) \r
+ {\r
+ for (int i = 1; i < length; i++) \r
+ {\r
+ c = ianaEncoding[i];\r
+ if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&\r
+ (c < '0' || c > '9') && c != '.' && c != '_' &&\r
+ c != '-') \r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
private static byte[] firstNamePages =\r
{\r
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,\r
// (C) Ximian, Inc.\r
//\r
using System;\r
+using System.IO;\r
using System.Collections;\r
using Mono.Xml;\r
\r
this.systemId = systemId;\r
this.internalSubset = internalSubset;\r
\r
- XmlTextReader xtr = new XmlTextReader ("", XmlNodeType.Document, null);\r
+ XmlTextReader xtr = new XmlTextReader (BaseURI, new StringReader (""), doc.NameTable);\r
xtr.GenerateDTDObjectModel (name, publicId, systemId, internalSubset);\r
this.dtd = xtr.DTD;\r
\r
XmlNode n = new XmlEntity (decl.Name, decl.NotationName,
decl.PublicId, decl.SystemId, OwnerDocument);
// FIXME: Value is more complex, similar to Attribute.
- n.insertBeforeIntern (OwnerDocument.CreateTextNode (decl.EntityValue), null);
+ n.insertBeforeIntern (OwnerDocument.CreateTextNode (decl.LiteralEntityValue), null);
entities.Nodes.Add (n);
}
foreach (DTDNotationDeclaration decl in DTD.NotationDecls.Values) {
// version. It is optional here.
if (c != 'v') {
- if (isDocumentEntity)
- throw new XmlException ("invalid xml declaration.");
+ // FIXME: temporarily comment out here.
+// if (isDocumentEntity)
+// throw new XmlException ("invalid xml declaration.");
} else {
ms.WriteByte ((byte)'v');
while (loop++ >= 0 && c >= 0) {
ms.WriteByte ((byte)c);
}
string encodingName = Encoding.UTF8.GetString (ms.GetBuffer (), start, (int)ms.Position - start);
- if (!XmlConstructs.IsValidIANAEncoding (encodingName))
+ if (!XmlChar.IsValidIANAEncoding (encodingName))
throw encodingException;
ms.WriteByte ((byte)quoteChar);
enc = Encoding.GetEncoding (encodingName);
return null;
}
+ internal XmlParserContext GetInternalParserContext ()
+ {
+ if (entityReader != null)
+ return entityReader.GetInternalParserContext ();
+ else
+ return new XmlParserContext (document.NameTable,
+ current.ConstructNamespaceManager (),
+ XmlLang, XmlSpace);
+ }
+
public override string LookupNamespace (string prefix)
{
if (entityReader != null && entityReader.ReadState != ReadState.Initial)
BaseURI, XmlLang, XmlSpace, Encoding.Unicode);
}
entityReader = new XmlTextReader (replacementText, xmlReaderNodeType, ctx);
+ entityReader.MaybeTextDecl = true;
}
public override void Skip ()
value,
false);
+ returnEntityReference = false;
return true;
}
#region Internals
// Parsed DTD Objects
internal DTDObjectModel DTD;
+ internal bool MaybeTextDecl {
+ set { if (value) this.maybeTextDecl = 2; }
+ }
#endregion
#region Privates
case ' ':
if (whitespaceHandling == WhitespaceHandling.All ||
whitespaceHandling == WhitespaceHandling.Significant)
- return ReadWhitespace ();
-
- SkipWhitespace ();
- return ReadContent ();
+ ReadWhitespace ();
+ else {
+ SkipWhitespace ();
+ return ReadContent ();
+ }
+ break;
case -1:
if (depth > 0)
throw new XmlException ("unexpected end of file. Current depth is " + depth);
break;
}
}
+ if (NodeType == XmlNodeType.XmlDeclaration && maybeTextDecl == 1)
+ return ReadContent ();
return this.ReadState != ReadState.EndOfFile;
}
ClearAttributes ();
SkipWhitespace ();
- if (XmlConstructs.IsNameStart (PeekChar ()))
+ if (XmlChar.IsFirstNameChar (PeekChar ()))
ReadAttributes (false);
string baseUri = GetAttribute ("xml:base");
Expect (';');
string name = CreateNameString ();
- if (XmlConstructs.IsValidName (name) >= 0)
+ if (!XmlChar.IsName (name))
throw new XmlException (this as IXmlLineInfo,
"Invalid entity reference name was found.");
AddAttribute (name, value);
- if (XmlConstructs.IsSpace (PeekChar ()))
- SkipWhitespace ();
- else
+ if (!SkipWhitespace ())
requireWhitespace = true;
peekChar = PeekChar ();
if (peekChar == '?' && allowPIEnd)
throw new XmlException (this as IXmlLineInfo,
"XML declaration cannot appear in this state.");
}
- currentState = XmlNodeType.XmlDeclaration;
+ // Is this required?
+// if (maybeTextDecl != 0)
+// currentState = XmlNodeType.XmlDeclaration;
ClearAttributes ();
string message = null;
if (parserInputStack.Count == 0) {
- if (orderedAttributes [0] as string != "version" || version != "1.0")
+ if (maybeTextDecl == 0 && (orderedAttributes [0] as string != "version" || version != "1.0"))
message = "Version 1.0 declaration is required in XML Declaration.";
else if (orderedAttributes.Count > 1 &&
(orderedAttributes [1] as string != "encoding" &&
if (this ["standalone"] != null)
throw new XmlException (this as IXmlLineInfo,
"Invalid text declaration.");
- maybeTextDecl = 0;
+ if (maybeTextDecl == 2)
+ maybeTextDecl = 1;
SetProperties (
XmlNodeType.XmlDeclaration, // nodeType
break;
}
+ if (XmlConstructs.IsInvalid (ch))
+ throw new XmlException (this as IXmlLineInfo,
+ "Not allowed character was found.");
+
AppendValueChar ((char)ch);
}
} while (nodeType != XmlNodeType.None || parserInputStack.Count > originalParserDepth + 1);
PopParserInput ();
}
+ // TODO: Check entity nesting
return DTD;
}
}
break;
case ']':
+ if (dtdIncludeSect == 0)
+ throw new XmlException (this as IXmlLineInfo, "Unbalanced end of INCLUDE/IGNORE section.");
// End of inclusion
Expect ("]]>");
dtdIncludeSect--;
LOOPBACK:
if (PeekChar () == '%') {
ReadChar ();
- if (!XmlConstructs.IsSpace (PeekChar ())) {
+ if (!SkipWhitespace ()) {
ExpandPERef ();
goto LOOPBACK;
-// throw new XmlException (this as IXmlLineInfo,"expected whitespace between '%' and name.");
} else {
- SkipWhitespace ();
TryExpandPERef ();
- if (XmlConstructs.IsName (PeekChar ()))
+ if (XmlChar.IsNameChar (PeekChar ()))
ReadParameterEntityDecl ();
else
throw new XmlException (this as IXmlLineInfo,"expected name character");
if(PeekChar () == '#') {
// Mixed Contents. "#PCDATA" must appear first.
decl.IsMixedContent = true;
+ model.Occurence = DTDOccurence.ZeroOrMore;
+ model.OrderType = DTDContentOrderType.Or;
Expect ("#PCDATA");
SkipWhitespace ();
TryExpandPERef ();
TryExpandPERef ();
SkipWhitespace ();
DTDContentModel elem = new DTDContentModel (DTD, decl.Name);
- model.ElementName = ReadName ();
+ elem.ElementName = ReadName ();
model.ChildModels.Add (elem);
SkipWhitespace ();
TryExpandPERef ();
}
Expect (')');
- if (model.ChildModels.Count > 0) {
+ if (model.ChildModels.Count > 0)
Expect ('*');
- model.Occurence = DTDOccurence.ZeroOrMore;
- }
else if (PeekChar () == '*')
Expect ('*');
} else {
sb = new StringBuilder ();
else
sb.Length = 0;
- while (PeekChar () != -1)
+ bool checkTextDecl = true;
+ while (PeekChar () != -1) {
sb.Append (ReadChar ());
+ if (checkTextDecl && sb.Length == 6) {
+ if (sb.ToString () == "<?xml ") {
+ // Skip Text declaration.
+ sb.Length = 0;
+ while (PeekChar () == '>' || PeekChar () == -1)
+ ReadChar ();
+ }
+ }
+ }
PopParserInput ();
appendStr = sb.ToString ();
} else {
// The reader is positioned on the head of the name.
private DTDEntityDeclaration ReadEntityDecl ()
{
- DTDEntityDeclaration decl = new DTDEntityDeclaration ();
+ DTDEntityDeclaration decl = new DTDEntityDeclaration (DTD);
decl.IsInternalSubset = isIntSubset;
decl.Name = ReadName ();
if (!SkipWhitespace ())
}
}
else {
- // general entity
- decl.EntityValue = ReadEntityValueDecl ();
+ // literal entity
+ decl.LiteralEntityValue = ReadEntityValueDecl ();
}
SkipWhitespace ();
// This expanding is only allowed as a non-validating parser.
TryExpandPERef ();
SkipWhitespace ();
- while (XmlConstructs.IsName ((char) PeekChar ())) {
+ while (XmlChar.IsNameChar ((char) PeekChar ())) {
DTDAttributeDefinition def = ReadAttributeDefinition ();
if (decl [def.Name] == null)
decl.Add (def);
}
break;
default: // Enumerated Values
+ def.Datatype = XmlSchemaDatatype.FromName ("string");
TryExpandPERef ();
Expect ('(');
SkipWhitespace ();
{
if (PeekChar () == '%') {
ReadChar ();
- if (!XmlConstructs.IsName (PeekChar ()))
+ if (!XmlChar.IsNameChar (PeekChar ()))
return;
ExpandPERef ();
}
{
DTDParameterEntityDeclaration decl =
parameterEntities [peName] as DTDParameterEntityDeclaration;
- if (decl == null)
- throw new XmlException ("undeclared parameter entity: '" + peName + "'");
+ if (decl == null) {
+ DTD.AddError (new XmlSchemaException (
+ "undeclared parameter entity: '" + peName + "'", null));
+ return;
+ }
if (decl.SystemId != null) {
PushParserInput (decl.SystemId);
// currentInput.InsertParameterEntityBuffer (this.GetExternalTextMarkup (decl));
{
c = ReadChar ();
if(c < 0) throw new XmlException (this as IXmlLineInfo,"Unexpected end of stream in ExternalID.");
- if(c != quoteChar && !XmlConstructs.IsPubid (c))
+ if(c != quoteChar && !XmlChar.IsPubidChar (c))
throw new XmlException (this as IXmlLineInfo,"character '" + (char)c + "' not allowed for PUBLIC ID");
}
return currentTag.ToString (startPos, currentTag.Length - 1 - startPos);
{
int ch = PeekChar ();
if(isNameToken) {
- if (!XmlConstructs.IsName ((char) ch))
+ if (!XmlChar.IsNameChar ((char) ch))
throw new XmlException (this as IXmlLineInfo,String.Format ("a nmtoken did not start with a legal character {0} ({1})", ch, (char)ch));
}
else {
- if (!XmlConstructs.IsNameStart ((char) ch))
+ if (!XmlChar.IsFirstNameChar (ch))
throw new XmlException (this as IXmlLineInfo,String.Format ("a name did not start with a legal character {0} ({1})", ch, (char)ch));
}
AppendNameChar (ReadChar ());
- while (XmlConstructs.IsName (PeekChar ())) {
+ while (XmlChar.IsNameChar (PeekChar ())) {
AppendNameChar (ReadChar ());
}
private bool SkipWhitespace ()
{
//FIXME: Should not skip if whitespaceHandling == WhiteSpaceHandling.None
- bool skipped = XmlConstructs.IsSpace (PeekChar ());
- while (XmlConstructs.IsSpace (PeekChar ()))
+ bool skipped = XmlChar.IsWhitespace (PeekChar ());
+ while (XmlChar.IsWhitespace (PeekChar ()))
ReadChar ();
return skipped;
}
- private bool ReadWhitespace ()
+ private void ReadWhitespace ()
{
if (currentState == XmlNodeType.None)
currentState = XmlNodeType.XmlDeclaration;
int ch = PeekChar ();
do {
AppendValueChar (ReadChar ());
- } while ((ch = PeekChar ()) != -1 && XmlConstructs.IsSpace (ch));
+ } while ((ch = PeekChar ()) != -1 && XmlChar.IsWhitespace (ch));
if (currentState == XmlNodeType.Element && ch != -1 && ch != '<')
ReadText (false);
valueBuffer,
true);
- return (PeekChar () != -1);
+ return; // (PeekChar () != -1);
}
// read entity reference from attribute string and if parsable then return the value.
public override string BaseURI {
[MonoTODO]
- get { return validatingReader == null ? String.Empty : validatingReader.BaseURI; }
+ get { return validatingReader == null ? sourceReader.BaseURI : validatingReader.BaseURI; }
}
public override bool CanResolveEntity {
get { throw new NotImplementedException (); }
}
- [MonoTODO]
public EntityHandling EntityHandling {
get { return entityHandling; }
set { entityHandling = value; }
public XmlResolver XmlResolver {
[MonoTODO]
- set { resolver = value; }
+ set {
+ resolver = value;
+ DTDValidatingReader dvr = validatingReader as DTDValidatingReader;
+ if (dvr != null)
+ dvr.XmlResolver = value;
+// XmlSchemaValidatingReader xsvr = validatingReader as XmlSchemaValidatingReader;
+// if (xsvr != null)
+// xsvr.XmlResolver = value;
+ }
}
public override XmlSpace XmlSpace {