Jo Shields 3c1f479b9d Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
2015-04-07 09:35:12 +01:00

1368 lines
33 KiB
C#

//
// Mono.Xml.DTDObjectModel
//
// Author:
// Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
//
// (C)2003 Atsushi Enomoto
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Collections.Generic;
#if NET_2_1
using XmlSchemaException = System.Xml.XmlException;
#else
using Mono.Xml.Schema;
#endif
using XmlTextReaderImpl = Mono.Xml2.XmlTextReader;
namespace Mono.Xml
{
internal class DTDObjectModel
{
// This specifies the max number of dependent external entities
// per a DTD can consume. A malicious external document server
// might send users' document processing server a large number
// of external entities.
public const int AllowedExternalEntitiesMax = 256;
DTDAutomataFactory factory;
DTDElementAutomata rootAutomata;
DTDEmptyAutomata emptyAutomata;
DTDAnyAutomata anyAutomata;
DTDInvalidAutomata invalidAutomata;
DTDElementDeclarationCollection elementDecls;
DTDAttListDeclarationCollection attListDecls;
DTDParameterEntityDeclarationCollection peDecls;
DTDEntityDeclarationCollection entityDecls;
DTDNotationDeclarationCollection notationDecls;
ArrayList validationErrors;
XmlResolver resolver;
XmlNameTable nameTable;
Hashtable externalResources;
string baseURI;
string name;
string publicId;
string systemId;
string intSubset;
bool intSubsetHasPERef;
bool isStandalone;
int lineNumber;
int linePosition;
public DTDObjectModel (XmlNameTable nameTable)
{
this.nameTable = nameTable;
elementDecls = new DTDElementDeclarationCollection (this);
attListDecls = new DTDAttListDeclarationCollection (this);
entityDecls = new DTDEntityDeclarationCollection (this);
peDecls = new DTDParameterEntityDeclarationCollection (this);
notationDecls = new DTDNotationDeclarationCollection (this);
factory = new DTDAutomataFactory (this);
validationErrors = new ArrayList ();
externalResources = new Hashtable ();
}
public string BaseURI {
get { return baseURI; }
set { baseURI = value; }
}
public bool IsStandalone {
get { return isStandalone; }
set { isStandalone = value; }
}
public string Name {
get { return name; }
set { name = value; }
}
public XmlNameTable NameTable {
get { return nameTable; }
}
public string PublicId {
get { return publicId; }
set { publicId = value; }
}
public string SystemId {
get { return systemId; }
set { systemId = value; }
}
public string InternalSubset {
get { return intSubset; }
set { intSubset = value; }
}
public bool InternalSubsetHasPEReference {
get { return intSubsetHasPERef; }
set { intSubsetHasPERef = value; }
}
public int LineNumber {
get { return lineNumber; }
set { lineNumber = value; }
}
public int LinePosition {
get { return linePosition; }
set { linePosition = value; }
}
#if !NET_2_1
internal XmlSchema CreateXsdSchema ()
{
XmlSchema s = new XmlSchema ();
s.SourceUri = BaseURI;
s.LineNumber = LineNumber;
s.LinePosition = LinePosition;
foreach (DTDElementDeclaration el in ElementDecls.Values)
s.Items.Add (el.CreateXsdElement ());
return s;
}
#endif
public string ResolveEntity (string name)
{
DTDEntityDeclaration decl = EntityDecls [name]
as DTDEntityDeclaration;
if (decl == null) {
#if NET_2_1
AddError (new XmlSchemaException (String.Format ("Required entity was not found: {0}", name), null, this.LineNumber, this.LinePosition));
#else
AddError (new XmlSchemaException ("Required entity was not found.",
this.LineNumber, this.LinePosition, null, this.BaseURI, null));
#endif
return " ";
}
else
return decl.EntityValue;
}
internal XmlResolver Resolver {
get { return resolver; }
}
public XmlResolver XmlResolver {
set { resolver = value; }
}
internal Hashtable ExternalResources {
get { return externalResources; }
}
public DTDAutomataFactory Factory {
get { return factory; }
}
public DTDElementDeclaration RootElement {
get { return ElementDecls [Name]; }
}
public DTDElementDeclarationCollection ElementDecls {
get { return elementDecls; }
}
public DTDAttListDeclarationCollection AttListDecls {
get { return attListDecls; }
}
public DTDEntityDeclarationCollection EntityDecls {
get { return entityDecls; }
}
public DTDParameterEntityDeclarationCollection PEDecls {
get { return peDecls; }
}
public DTDNotationDeclarationCollection NotationDecls {
get { return notationDecls; }
}
public DTDAutomata RootAutomata {
get {
if (rootAutomata == null)
rootAutomata = new DTDElementAutomata (this, this.Name);
return rootAutomata;
}
}
public DTDEmptyAutomata Empty {
get {
if (emptyAutomata == null)
emptyAutomata = new DTDEmptyAutomata (this);
return emptyAutomata;
}
}
public DTDAnyAutomata Any {
get {
if (anyAutomata == null)
anyAutomata = new DTDAnyAutomata (this);
return anyAutomata;
}
}
public DTDInvalidAutomata Invalid {
get {
if (invalidAutomata == null)
invalidAutomata = new DTDInvalidAutomata (this);
return invalidAutomata;
}
}
public XmlSchemaException [] Errors {
get { return validationErrors.ToArray (typeof (XmlSchemaException)) as XmlSchemaException []; }
}
public void AddError (XmlSchemaException ex)
{
validationErrors.Add (ex);
}
internal string GenerateEntityAttributeText (string entityName)
{
DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
if (entity == null)
return null;
return entity.EntityValue;
}
internal XmlTextReaderImpl GenerateEntityContentReader (string entityName, XmlParserContext context)
{
DTDEntityDeclaration entity = EntityDecls [entityName] as DTDEntityDeclaration;
if (entity == null)
return null;
if (entity.SystemId != null) {
Uri baseUri = entity.BaseURI == String.Empty ? null : new Uri (entity.BaseURI);
Stream stream = resolver.GetEntity (resolver.ResolveUri (baseUri, entity.SystemId), null, typeof (Stream)) as Stream;
return new XmlTextReaderImpl (stream, XmlNodeType.Element, context);
}
else
return new XmlTextReaderImpl (entity.EntityValue, XmlNodeType.Element, context);
}
}
class DictionaryBase : List<KeyValuePair<string,DTDNode>>
{
public IEnumerable<DTDNode> Values {
get {
foreach (KeyValuePair<string,DTDNode> p in this)
yield return p.Value;
}
}
}
internal class DTDCollectionBase : DictionaryBase
{
DTDObjectModel root;
protected DTDCollectionBase (DTDObjectModel root)
{
this.root = root;
}
protected DTDObjectModel Root {
get { return root; }
}
public DictionaryBase InnerHashtable {
get { return this; }
}
protected void BaseAdd (string name, DTDNode value)
{
base.Add (new KeyValuePair<string,DTDNode> (name, value));
}
public bool Contains (string key)
{
foreach (KeyValuePair<string,DTDNode> p in this)
if (p.Key == key)
return true;
return false;
}
protected object BaseGet (string name)
{
foreach (KeyValuePair<string,DTDNode> p in this)
if (p.Key == name)
return p.Value;
return null;
}
}
internal class DTDElementDeclarationCollection : DTDCollectionBase
{
public DTDElementDeclarationCollection (DTDObjectModel root) : base (root) {}
public DTDElementDeclaration this [string name] {
get { return Get (name); }
}
public DTDElementDeclaration Get (string name)
{
return BaseGet (name) as DTDElementDeclaration;
}
public void Add (string name, DTDElementDeclaration decl)
{
if (Contains (name)) {
Root.AddError (new XmlSchemaException (String.Format (
"Element declaration for {0} was already added.",
name), null));
return;
}
decl.SetRoot (Root);
BaseAdd (name, decl);
}
}
internal class DTDAttListDeclarationCollection : DTDCollectionBase
{
public DTDAttListDeclarationCollection (DTDObjectModel root) : base (root) {}
public DTDAttListDeclaration this [string name] {
get { return BaseGet (name) as DTDAttListDeclaration; }
}
public void Add (string name, DTDAttListDeclaration decl)
{
DTDAttListDeclaration existing = this [name];
if (existing != null) {
// It is valid, that is additive declaration.
foreach (DTDAttributeDefinition def in decl.Definitions)
if (decl.Get (def.Name) == null)
existing.Add (def);
} else {
decl.SetRoot (Root);
BaseAdd (name, decl);
}
}
}
internal class DTDEntityDeclarationCollection : DTDCollectionBase
{
public DTDEntityDeclarationCollection (DTDObjectModel root) : base (root) {}
public DTDEntityDeclaration this [string name] {
get { return BaseGet (name) as DTDEntityDeclaration; }
}
public void Add (string name, DTDEntityDeclaration decl)
{
if (Contains (name))
throw new InvalidOperationException (String.Format (
"Entity declaration for {0} was already added.",
name));
decl.SetRoot (Root);
BaseAdd (name, decl);
}
}
internal class DTDNotationDeclarationCollection : DTDCollectionBase
{
public DTDNotationDeclarationCollection (DTDObjectModel root) : base (root) {}
public DTDNotationDeclaration this [string name] {
get { return BaseGet (name) as DTDNotationDeclaration; }
}
public void Add (string name, DTDNotationDeclaration decl)
{
if (Contains (name))
throw new InvalidOperationException (String.Format (
"Notation declaration for {0} was already added.",
name));
decl.SetRoot (Root);
BaseAdd (name, decl);
}
}
// This class contains either ElementName or ChildModels.
internal class DTDContentModel : DTDNode
{
DTDObjectModel root;
DTDAutomata compiledAutomata;
string ownerElementName;
string elementName;
DTDContentOrderType orderType = DTDContentOrderType.None;
DTDContentModelCollection childModels = new DTDContentModelCollection ();
DTDOccurence occurence = DTDOccurence.One;
internal DTDContentModel (DTDObjectModel root, string ownerElementName)
{
this.root = root;
this.ownerElementName = ownerElementName;
}
public DTDContentModelCollection ChildModels {
get { return childModels; }
set { childModels = value; }
}
public DTDElementDeclaration ElementDecl {
get { return root.ElementDecls [ownerElementName]; }
}
public string ElementName {
get { return elementName; }
set { elementName = value; }
}
public DTDOccurence Occurence {
get { return occurence; }
set { occurence = value; }
}
public DTDContentOrderType OrderType {
get { return orderType; }
set { orderType = value; }
}
public DTDAutomata GetAutomata ()
{
if (compiledAutomata == null)
Compile ();
return compiledAutomata;
}
public DTDAutomata Compile ()
{
compiledAutomata = CompileInternal ();
return compiledAutomata;
}
#if !NET_2_1
internal XmlSchemaParticle CreateXsdParticle ()
{
XmlSchemaParticle p = CreateXsdParticleCore ();
if (p == null)
return null;
switch (Occurence) {
case DTDOccurence.Optional:
p.MinOccurs = 0;
break;
case DTDOccurence.OneOrMore:
p.MaxOccursString = "unbounded";
break;
case DTDOccurence.ZeroOrMore:
p.MinOccurs = 0;
p.MaxOccursString = "unbounded";
break;
}
return p;
}
XmlSchemaParticle CreateXsdParticleCore ()
{
XmlSchemaParticle p = null;
if (ElementName != null) {
XmlSchemaElement el = new XmlSchemaElement ();
SetLineInfo (el);
el.RefName = new XmlQualifiedName (ElementName);
return el;
}
else if (ChildModels.Count == 0)
return null;
else {
XmlSchemaGroupBase gb =
(OrderType == DTDContentOrderType.Seq) ?
(XmlSchemaGroupBase)
new XmlSchemaSequence () :
new XmlSchemaChoice ();
SetLineInfo (gb);
foreach (DTDContentModel cm in ChildModels.Items) {
XmlSchemaParticle c = cm.CreateXsdParticle ();
if (c != null)
gb.Items.Add (c);
}
p = gb;
}
return p;
}
#endif
private DTDAutomata CompileInternal ()
{
if (ElementDecl.IsAny)
return root.Any;
if (ElementDecl.IsEmpty)
return root.Empty;
DTDAutomata basis = GetBasicContentAutomata ();
switch (Occurence) {
case DTDOccurence.One:
return basis;
case DTDOccurence.Optional:
return Choice (root.Empty, basis);
case DTDOccurence.OneOrMore:
return new DTDOneOrMoreAutomata (root, basis);
case DTDOccurence.ZeroOrMore:
return Choice (root.Empty, new DTDOneOrMoreAutomata (root, basis));
}
throw new InvalidOperationException ();
}
private DTDAutomata GetBasicContentAutomata ()
{
if (ElementName != null)
return new DTDElementAutomata (root, ElementName);
switch (ChildModels.Count) {
case 0:
return root.Empty;
case 1:
return ChildModels [0].GetAutomata ();
}
DTDAutomata current = null;
int childCount = ChildModels.Count;
switch (OrderType) {
case DTDContentOrderType.Seq:
current = Sequence (
ChildModels [childCount - 2].GetAutomata (),
ChildModels [childCount - 1].GetAutomata ());
for (int i = childCount - 2; i > 0; i--)
current = Sequence (
ChildModels [i - 1].GetAutomata (), current);
return current;
case DTDContentOrderType.Or:
current = Choice (
ChildModels [childCount - 2].GetAutomata (),
ChildModels [childCount - 1].GetAutomata ());
for (int i = childCount - 2; i > 0; i--)
current = Choice (
ChildModels [i - 1].GetAutomata (), current);
return current;
default:
throw new InvalidOperationException ("Invalid pattern specification");
}
}
private DTDAutomata Sequence (DTDAutomata l, DTDAutomata r)
{
return root.Factory.Sequence (l, r);
}
private DTDAutomata Choice (DTDAutomata l, DTDAutomata r)
{
return l.MakeChoice (r);
}
}
internal class DTDContentModelCollection
{
ArrayList contentModel = new ArrayList ();
public DTDContentModelCollection ()
{
}
public IList Items {
get { return contentModel; }
}
public DTDContentModel this [int i] {
get { return contentModel [i] as DTDContentModel; }
}
public int Count {
get { return contentModel.Count; }
}
public void Add (DTDContentModel model)
{
contentModel.Add (model);
}
}
internal abstract class DTDNode : IXmlLineInfo
{
DTDObjectModel root;
bool isInternalSubset;
string baseURI;
int lineNumber;
int linePosition;
public virtual string BaseURI {
get { return baseURI; }
set { baseURI = value; }
}
public bool IsInternalSubset {
get { return isInternalSubset; }
set { isInternalSubset = value; }
}
public int LineNumber {
get { return lineNumber; }
set { lineNumber = value; }
}
public int LinePosition {
get { return linePosition; }
set { linePosition = value; }
}
public bool HasLineInfo ()
{
return lineNumber != 0;
}
internal void SetRoot (DTDObjectModel root)
{
this.root = root;
if (baseURI == null)
this.BaseURI = root.BaseURI;
}
protected DTDObjectModel Root {
get { return root; }
}
internal XmlException NotWFError (string message)
{
return new XmlException (this as IXmlLineInfo, BaseURI, message);
}
#if !NET_2_1
public void SetLineInfo (XmlSchemaObject obj)
{
obj.SourceUri = BaseURI;
obj.LineNumber = LineNumber;
obj.LinePosition = LinePosition;
}
#endif
}
internal class DTDElementDeclaration : DTDNode
{
DTDObjectModel root;
DTDContentModel contentModel;
string name;
bool isEmpty;
bool isAny;
bool isMixedContent;
internal DTDElementDeclaration (DTDObjectModel root)
{
this.root = root;
}
public string Name {
get { return name; }
set { name = value; }
}
public bool IsEmpty {
get { return isEmpty; }
set { isEmpty = value; }
}
public bool IsAny {
get { return isAny; }
set { isAny = value; }
}
public bool IsMixedContent {
get { return isMixedContent; }
set { isMixedContent = value; }
}
public DTDContentModel ContentModel {
get {
if (contentModel == null)
contentModel = new DTDContentModel (root, Name);
return contentModel;
}
}
public DTDAttListDeclaration Attributes {
get {
return Root.AttListDecls [Name];
}
}
#if !NET_2_1
internal XmlSchemaElement CreateXsdElement ()
{
XmlSchemaElement el = new XmlSchemaElement ();
SetLineInfo (el);
el.Name = Name;
XmlSchemaComplexType ct = new XmlSchemaComplexType ();
el.SchemaType = ct;
if (Attributes != null) {
SetLineInfo (ct);
foreach (DTDAttributeDefinition a in
Attributes.Definitions)
ct.Attributes.Add (a.CreateXsdAttribute ());
}
if (IsEmpty)
; // nothing to do
else if (IsAny) {
XmlSchemaAny any = new XmlSchemaAny ();
any.MinOccurs = 0;
any.MaxOccursString = "unbounded";
ct.Particle = any;
}
else {
if (IsMixedContent)
ct.IsMixed = true;
ct.Particle = ContentModel.CreateXsdParticle ();
}
/*
if (IsEmpty) {
el.SchemaType = new XmlSchemaComplexType ();
SetLineInfo (el.SchemaType);
}
else if (IsAny)
el.SchemaTypeName = new XmlQualifiedName (
"anyType", XmlSchema.Namespace);
else {
XmlSchemaComplexType ct = new XmlSchemaComplexType ();
SetLineInfo (ct);
if (Attributes != null)
foreach (DTDAttributeDefinition a in
Attributes.Definitions)
ct.Attributes.Add (a.CreateXsdAttribute ());
if (IsMixedContent)
ct.IsMixed = true;
ct.Particle = ContentModel.CreateXsdParticle ();
el.SchemaType = ct;
}
*/
return el;
}
#endif
}
internal class DTDAttributeDefinition : DTDNode
{
string name;
XmlSchemaDatatype datatype;
ArrayList enumeratedLiterals;
string unresolvedDefault;
ArrayList enumeratedNotations;
DTDAttributeOccurenceType occurenceType = DTDAttributeOccurenceType.None;
string resolvedDefaultValue;
string resolvedNormalizedDefaultValue;
internal DTDAttributeDefinition (DTDObjectModel root)
{
this.SetRoot (root);
}
public string Name {
get { return name; }
set { name =value; }
}
public XmlSchemaDatatype Datatype {
get { return datatype; }
set { datatype = value; }
}
public DTDAttributeOccurenceType OccurenceType {
get { return this.occurenceType; }
set { this.occurenceType = value; }
}
// entity reference inside enumerated values are not allowed,
// but on the other hand, they are allowed inside default value.
// Then I decided to use string ArrayList for enumerated values,
// and unresolved string value for DefaultValue.
public ArrayList EnumeratedAttributeDeclaration {
get {
if (enumeratedLiterals == null)
enumeratedLiterals = new ArrayList ();
return this.enumeratedLiterals;
}
}
public ArrayList EnumeratedNotations {
get {
if (enumeratedNotations == null)
enumeratedNotations = new ArrayList ();
return this.enumeratedNotations;
}
}
public string DefaultValue {
get {
if (resolvedDefaultValue == null)
resolvedDefaultValue = ComputeDefaultValue ();
return resolvedDefaultValue;
}
}
public string NormalizedDefaultValue {
get {
if (resolvedNormalizedDefaultValue == null) {
string s = ComputeDefaultValue ();
try {
object o = Datatype.ParseValue (s, null, null);
resolvedNormalizedDefaultValue =
(o is string []) ?
String.Join (" ", (string []) o) :
o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : o.ToString ();
} catch (Exception) {
// This is for non-error-reporting reader
resolvedNormalizedDefaultValue = Datatype.Normalize (s);
}
}
return resolvedNormalizedDefaultValue;
}
}
public string UnresolvedDefaultValue {
get { return this.unresolvedDefault; }
set { this.unresolvedDefault = value; }
}
public char QuoteChar {
get {
return UnresolvedDefaultValue.Length > 0 ?
this.UnresolvedDefaultValue [0] :
'"';
}
}
#if !NET_2_1
internal XmlSchemaAttribute CreateXsdAttribute ()
{
XmlSchemaAttribute a = new XmlSchemaAttribute ();
SetLineInfo (a);
a.Name = Name;
a.DefaultValue = resolvedNormalizedDefaultValue;
if (OccurenceType != DTDAttributeOccurenceType.Required)
a.Use = XmlSchemaUse.Optional;
XmlQualifiedName qname = XmlQualifiedName.Empty;
ArrayList enumeration = null;
if (enumeratedNotations != null && enumeratedNotations.Count > 0) {
qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace);
enumeration = enumeratedNotations;
}
else if (enumeratedLiterals != null)
enumeration = enumeratedLiterals;
else {
switch (Datatype.TokenizedType) {
case XmlTokenizedType.ID:
qname = new XmlQualifiedName ("ID", XmlSchema.Namespace); break;
case XmlTokenizedType.IDREF:
qname = new XmlQualifiedName ("IDREF", XmlSchema.Namespace); break;
case XmlTokenizedType.IDREFS:
qname = new XmlQualifiedName ("IDREFS", XmlSchema.Namespace); break;
case XmlTokenizedType.ENTITY:
qname = new XmlQualifiedName ("ENTITY", XmlSchema.Namespace); break;
case XmlTokenizedType.ENTITIES:
qname = new XmlQualifiedName ("ENTITIES", XmlSchema.Namespace); break;
case XmlTokenizedType.NMTOKEN:
qname = new XmlQualifiedName ("NMTOKEN", XmlSchema.Namespace); break;
case XmlTokenizedType.NMTOKENS:
qname = new XmlQualifiedName ("NMTOKENS", XmlSchema.Namespace); break;
case XmlTokenizedType.NOTATION:
qname = new XmlQualifiedName ("NOTATION", XmlSchema.Namespace); break;
}
}
if (enumeration != null) {
XmlSchemaSimpleType st = new XmlSchemaSimpleType ();
SetLineInfo (st);
XmlSchemaSimpleTypeRestriction r =
new XmlSchemaSimpleTypeRestriction ();
SetLineInfo (r);
r.BaseTypeName = qname;
if (enumeratedNotations != null) {
foreach (string name in enumeratedNotations) {
XmlSchemaEnumerationFacet f =
new XmlSchemaEnumerationFacet ();
SetLineInfo (f);
r.Facets.Add (f);
f.Value = name;
}
}
st.Content = r;
}
else if (qname != XmlQualifiedName.Empty)
a.SchemaTypeName = qname;
return a;
}
#endif
internal string ComputeDefaultValue ()
{
if (UnresolvedDefaultValue == null)
return null;
StringBuilder sb = new StringBuilder ();
int pos = 0;
int next = 0;
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, CultureInfo.InvariantCulture));
} else {
sb.Append (value.Substring (pos, next - 1));
string name = value.Substring (next + 1, semicolon - 2);
int predefined = XmlChar.GetPredefinedEntity (name);
if (predefined >= 0)
sb.Append (predefined);
else
sb.Append (Root.ResolveEntity (name));
}
pos = semicolon + 1;
}
sb.Append (value.Substring (pos));
// strip quote chars
string ret = sb.ToString (1, sb.Length - 2);
sb.Length = 0;
return ret;
}
}
internal class DTDAttListDeclaration : DTDNode
{
string name;
Hashtable attributeOrders = new Hashtable ();
ArrayList attributes = new ArrayList ();
internal DTDAttListDeclaration (DTDObjectModel root)
{
SetRoot (root);
}
public string Name {
get { return name; }
set { name = value; }
}
public DTDAttributeDefinition this [int i] {
get { return Get (i); }
}
public DTDAttributeDefinition this [string name] {
get { return Get (name); }
}
public DTDAttributeDefinition Get (int i)
{
return attributes [i] as DTDAttributeDefinition;
}
public DTDAttributeDefinition Get (string name)
{
object o = attributeOrders [name];
if (o != null)
return attributes [(int) o] as DTDAttributeDefinition;
else
return null;
}
public IList Definitions {
get { return attributes; }
}
public void Add (DTDAttributeDefinition def)
{
if (attributeOrders [def.Name] != null)
throw new InvalidOperationException (String.Format (
"Attribute definition for {0} was already added at element {1}.",
def.Name, this.Name));
def.SetRoot (Root);
attributeOrders.Add (def.Name, attributes.Count);
attributes.Add (def);
}
public int Count {
get { return attributeOrders.Count; }
}
}
internal class DTDEntityBase : DTDNode
{
string name;
string publicId;
string systemId;
string literalValue;
string replacementText;
string uriString;
Uri absUri;
bool isInvalid;
// Exception loadException;
bool loadFailed;
XmlResolver resolver;
protected DTDEntityBase (DTDObjectModel root)
{
SetRoot (root);
}
internal bool IsInvalid {
get { return isInvalid; }
set { isInvalid = value; }
}
public bool LoadFailed {
get { return loadFailed; }
set { loadFailed = value; }
}
public string Name {
get { return name; }
set { name = value; }
}
public string PublicId {
get { return publicId; }
set { publicId = value; }
}
public string SystemId {
get { return systemId; }
set { systemId = value; }
}
public string LiteralEntityValue {
get { return literalValue; }
set { literalValue = value; }
}
public string ReplacementText {
get { return replacementText; }
set { replacementText = value; }
}
public XmlResolver XmlResolver {
set { resolver = value; }
}
public string ActualUri {
get {
if (uriString == null) {
if (resolver == null || SystemId == null || SystemId.Length == 0)
uriString = BaseURI;
else {
Uri baseUri = null;
try {
if (BaseURI != null && BaseURI.Length > 0)
baseUri = new Uri (BaseURI);
} catch (UriFormatException) {
}
absUri = resolver.ResolveUri (baseUri, SystemId);
uriString = absUri != null ? absUri.ToString () : String.Empty;
}
}
return uriString;
}
}
public void Resolve ()
{
if (ActualUri == String.Empty) {
LoadFailed = true;
LiteralEntityValue = String.Empty;
return;
}
if (Root.ExternalResources.ContainsKey (ActualUri))
LiteralEntityValue = (string) Root.ExternalResources [ActualUri];
Stream s = null;
try {
s = resolver.GetEntity (absUri, null, typeof (Stream)) as Stream;
XmlTextReaderImpl xtr = new XmlTextReaderImpl (ActualUri, s, Root.NameTable);
// Don't skip Text declaration here. LiteralEntityValue contains it. See spec 4.5
LiteralEntityValue = xtr.GetRemainder ().ReadToEnd ();
Root.ExternalResources.Add (ActualUri, LiteralEntityValue);
if (Root.ExternalResources.Count > DTDObjectModel.AllowedExternalEntitiesMax)
throw new InvalidOperationException ("The total amount of external entities exceeded the allowed number.");
} catch (Exception) {
// loadException = ex;
LiteralEntityValue = String.Empty;
LoadFailed = true;
// throw NotWFError ("Cannot resolve external entity. URI is " + ActualUri + " .");
} finally {
if (s != null)
s.Close ();
}
}
}
internal class DTDEntityDeclaration : DTDEntityBase
{
string entityValue;
string notationName;
ArrayList ReferencingEntities = new ArrayList ();
bool scanned;
bool recursed;
bool hasExternalReference;
internal DTDEntityDeclaration (DTDObjectModel root) : base (root)
{
}
public string NotationName {
get { return notationName; }
set { notationName = value; }
}
public bool HasExternalReference {
get {
if (!scanned)
ScanEntityValue (new ArrayList ());
return hasExternalReference;
}
}
public string EntityValue {
get {
if (this.IsInvalid)
return String.Empty;
if (PublicId == null && SystemId == null && LiteralEntityValue == null)
return String.Empty;
if (entityValue == null) {
if (NotationName != null)
entityValue = "";
else if (SystemId == null || SystemId == String.Empty) {
entityValue = ReplacementText;
if (entityValue == null)
entityValue = String.Empty;
} else {
entityValue = ReplacementText;
}
// Check illegal recursion.
ScanEntityValue (new ArrayList ());
}
return entityValue;
}
}
// It returns whether the entity contains references to external entities.
public void ScanEntityValue (ArrayList refs)
{
// To modify this code, beware nesting between this and EntityValue.
string value = EntityValue;
if (this.SystemId != null)
hasExternalReference = true;
if (recursed)
throw NotWFError ("Entity recursion was found.");
recursed = true;
if (scanned) {
foreach (string referenced in refs)
if (this.ReferencingEntities.Contains (referenced))
throw NotWFError (String.Format (
"Nested entity was found between {0} and {1}",
referenced, Name));
recursed = false;
return;
}
int len = value.Length;
int start = 0;
for (int i=0; i<len; i++) {
switch (value [i]) {
case '&':
start = i+1;
break;
case ';':
if (start == 0)
break;
string name = value.Substring (start, i - start);
if (name.Length == 0)
throw NotWFError ("Entity reference name is missing.");
if (name [0] == '#')
break; // character reference
if (XmlChar.GetPredefinedEntity (name) >= 0)
break; // predefined reference
this.ReferencingEntities.Add (name);
DTDEntityDeclaration decl = Root.EntityDecls [name];
if (decl != null) {
if (decl.SystemId != null)
hasExternalReference = true;
refs.Add (Name);
decl.ScanEntityValue (refs);
foreach (string str in decl.ReferencingEntities)
ReferencingEntities.Add (str);
refs.Remove (Name);
value = value.Remove (start - 1, name.Length + 2);
value = value.Insert (start - 1, decl.EntityValue);
i -= name.Length + 1; // not +2, because of immediate i++ .
len = value.Length;
}
start = 0;
break;
}
}
if (start != 0)
#if NET_2_1
Root.AddError (new XmlSchemaException (this, this.BaseURI, "Invalid reference character '&' is specified."));
#else
Root.AddError (new XmlSchemaException ("Invalid reference character '&' is specified.",
this.LineNumber, this.LinePosition, null, this.BaseURI, null));
#endif
scanned = true;
recursed = false;
}
}
internal class DTDNotationDeclaration : DTDNode
{
string name;
string localName;
string prefix;
string publicId;
string systemId;
public string Name {
get { return name; }
set { name = value; }
}
public string PublicId {
get { return publicId; }
set { publicId = value; }
}
public string SystemId {
get { return systemId; }
set { systemId = value; }
}
public string LocalName {
get { return localName; }
set { localName = value; }
}
public string Prefix {
get { return prefix; }
set { prefix = value; }
}
internal DTDNotationDeclaration (DTDObjectModel root)
{
SetRoot (root);
}
}
internal class DTDParameterEntityDeclarationCollection
{
Hashtable peDecls = new Hashtable ();
DTDObjectModel root;
public DTDParameterEntityDeclarationCollection (DTDObjectModel root)
{
this.root = root;
}
public DTDParameterEntityDeclaration this [string name] {
get { return peDecls [name] as DTDParameterEntityDeclaration; }
}
public void Add (string name, DTDParameterEntityDeclaration decl)
{
// PEDecl can be overriden.
if (peDecls [name] != null)
return;
decl.SetRoot (root);
peDecls.Add (name, decl);
}
public ICollection Keys {
get { return peDecls.Keys; }
}
public ICollection Values {
get { return peDecls.Values; }
}
}
internal class DTDParameterEntityDeclaration : DTDEntityBase
{
internal DTDParameterEntityDeclaration (DTDObjectModel root) : base (root)
{
}
}
internal enum DTDContentOrderType
{
None,
Seq,
Or
}
internal enum DTDAttributeOccurenceType
{
None,
Required,
Optional,
Fixed
}
internal enum DTDOccurence
{
One,
Optional,
ZeroOrMore,
OneOrMore
}
}