// Author: Dwivedi, Ajay kumar
// Adwiv@Yahoo.com
using System;
using System.Collections;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using System.ComponentModel;
namespace System.Xml.Schema
{
///
/// Summary description for XmlSchema.
///
[XmlRoot("schema",Namespace="http://www.w3.org/2001/XMLSchema")]
public class XmlSchema : XmlSchemaObject
{
//public constants
public const string Namespace = "http://www.w3.org/2001/XMLSchema";
public const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
//private fields
private XmlSchemaForm attributeFormDefault ;
private XmlSchemaObjectTable attributeGroups ;
private XmlSchemaObjectTable attributes ;
private XmlSchemaDerivationMethod blockDefault ;
private XmlSchemaForm elementFormDefault ;
private XmlSchemaObjectTable elements ;
private XmlSchemaDerivationMethod finalDefault ;
private XmlSchemaObjectTable groups ;
private string id ;
private XmlSchemaObjectCollection includes ;
private XmlSchemaObjectCollection items ;
private XmlSchemaObjectTable notations ;
private XmlSchemaObjectTable schemaTypes ;
private string targetNamespace ;
private XmlAttribute[] unhandledAttributes ;
private string version;
private string language;
// Compiler specific things
private XmlSchemaInfo info;
private static string xmlname = "schema";
public XmlSchema()
{
attributeFormDefault= XmlSchemaForm.None;
blockDefault = XmlSchemaDerivationMethod.None;
elementFormDefault = XmlSchemaForm.None;
finalDefault = XmlSchemaDerivationMethod.None;
includes = new XmlSchemaObjectCollection();
isCompiled = false;
items = new XmlSchemaObjectCollection();
attributeGroups = new XmlSchemaObjectTable();
attributes = new XmlSchemaObjectTable();
elements = new XmlSchemaObjectTable();
groups = new XmlSchemaObjectTable();
notations = new XmlSchemaObjectTable();
schemaTypes = new XmlSchemaObjectTable();
}
#region Properties
[DefaultValue(XmlSchemaForm.None)]
[System.Xml.Serialization.XmlAttribute("attributeFormDefault")]
public XmlSchemaForm AttributeFormDefault
{
get{ return attributeFormDefault; }
set{ this.attributeFormDefault = value;}
}
[DefaultValue(XmlSchemaDerivationMethod.None)]
[System.Xml.Serialization.XmlAttribute("blockDefault")]
public XmlSchemaDerivationMethod BlockDefault
{
get{ return blockDefault;}
set{ blockDefault = value;}
}
[DefaultValue(XmlSchemaDerivationMethod.None)]
[System.Xml.Serialization.XmlAttribute("finalDefault")]
public XmlSchemaDerivationMethod FinalDefault
{
get{ return finalDefault;}
set{ finalDefault = value;}
}
[DefaultValue(XmlSchemaForm.None)]
[System.Xml.Serialization.XmlAttribute("elementFormDefault")]
public XmlSchemaForm ElementFormDefault
{
get{ return elementFormDefault;}
set{ elementFormDefault = value;}
}
[System.Xml.Serialization.XmlAttribute("targetNamespace")]
public string TargetNamespace
{
get{ return targetNamespace;}
set{ targetNamespace = value;}
}
[System.Xml.Serialization.XmlAttribute("version")]
public string Version
{
get{ return version;}
set{ version = value;}
}
[XmlElement("include",typeof(XmlSchemaInclude),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("import",typeof(XmlSchemaImport),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("redefine",typeof(XmlSchemaRedefine),Namespace="http://www.w3.org/2001/XMLSchema")]
public XmlSchemaObjectCollection Includes
{
get{ return includes;}
}
[XmlElement("simpleType",typeof(XmlSchemaSimpleType),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("complexType",typeof(XmlSchemaComplexType),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("group",typeof(XmlSchemaGroup),Namespace="http://www.w3.org/2001/XMLSchema")]
//Only Schema's attributeGroup has type XmlSchemaAttributeGroup.
//Others (complextype, restrictions etc) must have XmlSchemaAttributeGroupRef
[XmlElement("attributeGroup",typeof(XmlSchemaAttributeGroup),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("element",typeof(XmlSchemaElement),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("attribute",typeof(XmlSchemaAttribute),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("notation",typeof(XmlSchemaNotation),Namespace="http://www.w3.org/2001/XMLSchema")]
[XmlElement("annotation",typeof(XmlSchemaAnnotation),Namespace="http://www.w3.org/2001/XMLSchema")]
public XmlSchemaObjectCollection Items
{
get{ return items;}
}
[XmlIgnore]
public bool IsCompiled
{
get{ return isCompiled;}
}
[XmlIgnore]
public XmlSchemaObjectTable Attributes
{
get{ return attributes;}
}
[XmlIgnore]
public XmlSchemaObjectTable AttributeGroups
{
get{ return attributeGroups; }
}
[XmlIgnore]
public XmlSchemaObjectTable SchemaTypes
{
get{ return schemaTypes; }
}
[XmlIgnore]
public XmlSchemaObjectTable Elements
{
get{ return elements;}
}
[System.Xml.Serialization.XmlAttribute("id")]
public string Id
{
get{ return id;}
set{ id = value;}
}
[XmlAnyAttribute]
public XmlAttribute[] UnhandledAttributes
{
get
{
if(unhandledAttributeList != null)
{
unhandledAttributes = (XmlAttribute[]) unhandledAttributeList.ToArray(typeof(XmlAttribute));
unhandledAttributeList = null;
}
return unhandledAttributes;
}
set
{
unhandledAttributes = value;
unhandledAttributeList = null;
}
}
[XmlIgnore]
public XmlSchemaObjectTable Groups
{
get{ return groups;}
}
[XmlIgnore]
public XmlSchemaObjectTable Notations
{
get{ return notations;}
}
// New attribute defined in W3C schema element
[System.Xml.Serialization.XmlAttribute("xml:lang")]
public string Language
{
get{ return language; }
set{ language = value; }
}
#endregion
#region Compile
// Methods
///
/// This compile method does two things:
/// 1. It compiles and fills the PSVI dataset
/// 2. Validates the schema by calling Validate method.
/// Every XmlSchemaObject has a Compile Method which gets called.
///
///
/// 1. blockDefault must be one of #all | List of (extension | restriction | substitution)
/// 2. finalDefault must be one of (#all | List of (extension | restriction| union| list))
/// 3. id must be of type ID
/// 4. targetNamespace should be any uri
/// 5. version should be a normalizedString
/// 6. xml:lang should be a language
///
[MonoTODO]
public void Compile(ValidationEventHandler handler)
{
// Create the xmlschemainfo object which we use to pass variables like targetnamespace;
info = new XmlSchemaInfo();
//1. Union and List are not allowed in block default
if(BlockDefault != XmlSchemaDerivationMethod.All)
{
if((BlockDefault & XmlSchemaDerivationMethod.List)!=0 )
error(handler, "list is not allowed in blockDefault attribute");
if((BlockDefault & XmlSchemaDerivationMethod.Union)!=0 )
error(handler, "union is not allowed in blockDefault attribute");
}
//2. Substitution is not allowed in finaldefault.
if(FinalDefault != XmlSchemaDerivationMethod.All)
{
if((FinalDefault & XmlSchemaDerivationMethod.Substitution)!=0 )
error(handler, "substitution is not allowed in finalDefault attribute");
}
//3. id must be of type ID
XmlSchemaUtil.CompileID(Id, this, info.IDCollection, handler);
//4. targetNamespace should be of type anyURI or absent
if(TargetNamespace != null)
{
if(!XmlSchemaUtil.CheckAnyUri(TargetNamespace))
error(handler, TargetNamespace+" is not a valid value for targetNamespace attribute of schema");
else
info.TargetNamespace = TargetNamespace;
}
//5. version should be of type normalizedString
if(!XmlSchemaUtil.CheckNormalizedString(Version))
error(handler, Version + "is not a valid value for version attribute of schema");
//6. xml:lang must be a language
if(!XmlSchemaUtil.CheckLanguage(Language))
error(handler, Language + " is not a valid language");
// elementFormDefault defaults to UnQualified
if(ElementFormDefault != XmlSchemaForm.Qualified)
info.ElementFormDefault = XmlSchemaForm.Unqualified;
else
info.ElementFormDefault = XmlSchemaForm.Qualified;
// attributeFormDefault defaults to UnQualified
if(AttributeFormDefault != XmlSchemaForm.Qualified)
info.AttributeFormDefault = XmlSchemaForm.Unqualified;
else
info.AttributeFormDefault = XmlSchemaForm.Qualified;
if(FinalDefault == XmlSchemaDerivationMethod.All)
info.FinalDefault = XmlSchemaDerivationMethod.All;
else // If finalDefault is None, info's finalDefault is set to empty
info.FinalDefault = (FinalDefault & (XmlSchemaDerivationMethod.Extension | XmlSchemaDerivationMethod.Restriction));
if(BlockDefault == XmlSchemaDerivationMethod.All)
info.BlockDefault = XmlSchemaDerivationMethod.All;
else // If finalDefault is None, info's blockDefault is set to empty
info.BlockDefault = (blockDefault & (XmlSchemaDerivationMethod.Extension |
XmlSchemaDerivationMethod.Restriction | XmlSchemaDerivationMethod.Substitution));
// Compile the content of this schema
foreach(XmlSchemaObject obj in Includes)
{
if(obj is XmlSchemaExternal)
{
//FIXME: Kuch to karo! (Do Something ;)
}
else
{
error(handler,"Object of Type "+obj.GetType().Name+" is not valid in Includes Property of XmlSchema");
}
}
foreach(XmlSchemaObject obj in Items)
{
if(obj is XmlSchemaAnnotation)
{
int numerr = ((XmlSchemaAnnotation)obj).Compile(handler,info);
errorCount += numerr;
if( numerr == 0)
{
//FIXME: What PSVI set do we add this to?
}
}
else if(obj is XmlSchemaAttribute)
{
XmlSchemaAttribute attr = (XmlSchemaAttribute) obj;
attr.parentIsSchema = true;
int numerr = attr.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
Attributes.Add(attr.QualifiedName, attr);
}
}
else if(obj is XmlSchemaAttributeGroup)
{
XmlSchemaAttributeGroup attrgrp = (XmlSchemaAttributeGroup) obj;
int numerr = attrgrp.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
AttributeGroups.Add(attrgrp.QualifiedName, attrgrp);
}
}
else if(obj is XmlSchemaComplexType)
{
XmlSchemaComplexType ctype = (XmlSchemaComplexType) obj;
ctype.istoplevel = true;
int numerr = ctype.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
schemaTypes.Add(ctype.QualifiedName, ctype);
}
}
else if(obj is XmlSchemaSimpleType)
{
XmlSchemaSimpleType stype = (XmlSchemaSimpleType) obj;
stype.islocal = false; //This simple type is toplevel
int numerr = stype.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
SchemaTypes.Add(stype.QualifiedName, stype);
}
}
else if(obj is XmlSchemaElement)
{
XmlSchemaElement elem = (XmlSchemaElement) obj;
elem.parentIsSchema = true;
int numerr = elem.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
Elements.Add(elem.QualifiedName,elem);
}
}
else if(obj is XmlSchemaGroup)
{
XmlSchemaGroup grp = (XmlSchemaGroup) obj;
int numerr = grp.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
Groups.Add(grp.QualifiedName,grp);
}
}
else if(obj is XmlSchemaNotation)
{
XmlSchemaNotation ntn = (XmlSchemaNotation) obj;
int numerr = ntn.Compile(handler,info);
errorCount += numerr;
if(numerr == 0)
{
Notations.Add(ntn.QualifiedName, ntn);
}
}
else
{
ValidationHandler.RaiseValidationError(handler,this,
"Object of Type "+obj.GetType().Name+" is not valid in Item Property of Schema");
}
}
Validate(handler);
}
#endregion
[MonoTODO]
private void Validate(ValidationEventHandler handler)
{
info.SchemaTypes = SchemaTypes;
foreach(XmlSchemaAttribute attr in Attributes.Values)
{
attr.Validate(handler, info);
}
foreach(XmlSchemaAttributeGroup attrgrp in AttributeGroups.Values)
{
attrgrp.Validate(handler);
}
foreach(XmlSchemaType type in SchemaTypes.Values)
{
if(type is XmlSchemaComplexType)
{
((XmlSchemaComplexType)type).Validate(handler);
}
else
((XmlSchemaSimpleType)type).Validate(handler, info);
}
foreach(XmlSchemaElement elem in Elements.Values)
{
elem.Validate(handler);
}
foreach(XmlSchemaGroup grp in Groups.Values)
{
grp.Validate(handler);
}
foreach(XmlSchemaNotation ntn in Notations.Values)
{
ntn.Validate(handler);
}
}
#region Read
public static XmlSchema Read(TextReader reader, ValidationEventHandler validationEventHandler)
{
return Read(new XmlTextReader(reader),validationEventHandler);
}
public static XmlSchema Read(Stream stream, ValidationEventHandler validationEventHandler)
{
return Read(new XmlTextReader(stream),validationEventHandler);
}
public static XmlSchema Read(XmlReader rdr, ValidationEventHandler validationEventHandler)
{
//XmlSerializer xser = new XmlSerializer(typeof(XmlSchema));
//return (XmlSchema) xser.Deserialize(reader);
XmlSchemaReader reader = new XmlSchemaReader(rdr, validationEventHandler);
while(reader.ReadNextElement())
{
switch(reader.NodeType)
{
case XmlNodeType.Element:
if(reader.LocalName == "schema")
{
XmlSchema schema = new XmlSchema();
schema.LineNumber = reader.LineNumber;
schema.LinePosition = reader.LinePosition;
schema.SourceUri = reader.BaseURI;
ReadAttributes(schema, reader, validationEventHandler);
//IsEmptyElement does not behave properly if reader is
//positioned at an attribute.
reader.MoveToElement();
if(!reader.IsEmptyElement)
{
ReadContent(schema, reader, validationEventHandler);
}
return schema;
}
else
{
//Schema can't be generated. Throw an exception
throw new XmlSchemaException("The root element must be schema", null);
}
default:
error(validationEventHandler, "This should never happen. XmlSchema.Read 1 ",null);
break;
}
}
throw new XmlSchemaException("The top level schema must have namespace "+XmlSchema.Namespace, null);
}
private static void ReadAttributes(XmlSchema schema, XmlSchemaReader reader, ValidationEventHandler h)
{
Exception ex;
reader.MoveToElement();
while(reader.MoveToNextAttribute())
{
switch(reader.Name)
{
case "attributeFormDefault" :
schema.attributeFormDefault = XmlSchemaUtil.ReadFormAttribute(reader,out ex);
if(ex != null)
error(h, reader.Value + " is not a valid value for attributeFormDefault.", ex);
break;
case "blockDefault" :
schema.blockDefault = XmlSchemaUtil.ReadDerivationAttribute(reader,out ex, "blockDefault");
if(ex != null)
warn(h, ex.Message, ex);
break;
case "elementFormDefault":
schema.elementFormDefault = XmlSchemaUtil.ReadFormAttribute(reader, out ex);
if(ex != null)
error(h, reader.Value + " is not a valid value for elementFormDefault.", ex);
break;
case "finalDefault":
schema.finalDefault = XmlSchemaUtil.ReadDerivationAttribute(reader, out ex, "finalDefault");
if(ex != null)
warn(h, ex.Message , ex);
break;
case "id":
schema.id = reader.Value;
break;
case "targetNamespace":
schema.targetNamespace = reader.Value;
break;
case "version":
schema.version = reader.Value;
break;
case "xml:lang":
schema.language = reader.Value;
break;
default:
if((reader.NamespaceURI == "" && reader.Name != "xmlns") || reader.NamespaceURI == XmlSchema.Namespace)
error(h, reader.Name + " attribute is not allowed in schema element",null);
else
{
XmlSchemaUtil.ReadUnhandledAttribute(reader,schema);
}
break;
}
}
}
private static void ReadContent(XmlSchema schema, XmlSchemaReader reader, ValidationEventHandler h)
{
reader.MoveToElement();
if(reader.LocalName != "schema" && reader.NamespaceURI != XmlSchema.Namespace && reader.NodeType != XmlNodeType.Element)
error(h, "UNREACHABLE CODE REACHED: Method: Schema.ReadContent, " + reader.LocalName + ", " + reader.NamespaceURI,null);
//(include | import | redefine | annotation)*,
//((simpleType | complexType | group | attributeGroup | element | attribute | notation | annotation)*
int level = 1;
while(reader.ReadNextElement())
{
if(reader.NodeType == XmlNodeType.EndElement)
{
if(reader.LocalName != xmlname)
error(h,"Should not happen :2: XmlSchema.Read, name="+reader.Name,null);
break;
}
if(level <= 1)
{
if(reader.LocalName == "include")
{
XmlSchemaInclude include = XmlSchemaInclude.Read(reader,h);
if(include != null)
schema.includes.Add(include);
continue;
}
if(reader.LocalName == "import")
{
XmlSchemaImport import = XmlSchemaImport.Read(reader,h);
if(import != null)
schema.includes.Add(import);
continue;
}
if(reader.LocalName == "redefine")
{
XmlSchemaRedefine redefine = XmlSchemaRedefine.Read(reader,h);
if(redefine != null)
schema.includes.Add(redefine);
continue;
}
if(reader.LocalName == "annotation")
{
XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);
if(annotation != null)
schema.items.Add(annotation);
continue;
}
}
if(level <=2)
{
level = 2;
if(reader.LocalName == "simpleType")
{
XmlSchemaSimpleType stype = XmlSchemaSimpleType.Read(reader,h);
if(stype != null)
schema.items.Add(stype);
continue;
}
if(reader.LocalName == "complexType")
{
XmlSchemaComplexType ctype = XmlSchemaComplexType.Read(reader,h);
if(ctype != null)
schema.items.Add(ctype);
continue;
}
if(reader.LocalName == "group")
{
XmlSchemaGroup group = XmlSchemaGroup.Read(reader,h);
if(group != null)
schema.items.Add(group);
continue;
}
if(reader.LocalName == "attributeGroup")
{
XmlSchemaAttributeGroup attributeGroup = XmlSchemaAttributeGroup.Read(reader,h);
if(attributeGroup != null)
schema.items.Add(attributeGroup);
continue;
}
if(reader.LocalName == "element")
{
XmlSchemaElement element = XmlSchemaElement.Read(reader,h);
if(element != null)
schema.items.Add(element);
continue;
}
if(reader.LocalName == "attribute")
{
XmlSchemaAttribute attr = XmlSchemaAttribute.Read(reader,h);
if(attr != null)
schema.items.Add(attr);
continue;
}
if(reader.LocalName == "notation")
{
XmlSchemaNotation notation = XmlSchemaNotation.Read(reader,h);
if(notation != null)
schema.items.Add(notation);
continue;
}
if(reader.LocalName == "annotation")
{
XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);
if(annotation != null)
schema.items.Add(annotation);
continue;
}
}
reader.RaiseInvalidElementError();
}
}
#endregion
#region write
public void Write(System.IO.Stream stream)
{
Write(stream,null);
}
public void Write(System.IO.TextWriter writer)
{
Write(writer,null);
}
public void Write(System.Xml.XmlWriter writer)
{
Write(writer,null);
}
public void Write(System.IO.Stream stream, System.Xml.XmlNamespaceManager namespaceManager)
{
Write(new XmlTextWriter(stream,null),namespaceManager);
}
public void Write(System.IO.TextWriter writer, System.Xml.XmlNamespaceManager namespaceManager)
{
XmlTextWriter xwriter = new XmlTextWriter(writer);
xwriter.Formatting = Formatting.Indented;
Write(xwriter,namespaceManager);
}
public void Write(System.Xml.XmlWriter writer, System.Xml.XmlNamespaceManager namespaceManager)
{
if(Namespaces == null)
{
Namespaces = new XmlSerializerNamespaces();
}
//Add the xml schema namespace.
if(Namespaces.Count == 0)
{
Namespaces.Add("xs", XmlSchema.Namespace);
if (TargetNamespace != null && TargetNamespace != String.Empty)
Namespaces.Add("tns", TargetNamespace);
}
if(namespaceManager != null)
{
foreach(string name in namespaceManager)
{
//xml and xmlns namespaced are added by default in namespaceManager.
//So we should ignore them
if(name!="xml" && name != "xmlns")
Namespaces.Add(name,namespaceManager.LookupNamespace(name));
}
}
XmlSerializer xser = new XmlSerializer(typeof(XmlSchema));
xser.Serialize(writer,this,Namespaces);
writer.Flush();
}
#endregion
}
}