//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// derekdb
//------------------------------------------------------------------------------
#if ENABLEDATABINDING
using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Collections;
using System.Diagnostics;
using System.ComponentModel;
using System.Text;
namespace System.Xml.XPath.DataBinding
{
internal sealed class ShapeGenerator
{
private Hashtable elementTypesProcessed;
private IXmlNamespaceResolver nsResolver;
public ShapeGenerator(IXmlNamespaceResolver nsResolver) {
this.elementTypesProcessed = new Hashtable();
this.nsResolver = nsResolver;
}
public Shape GenerateFromSchema(XmlSchemaElement xse) {
XmlQualifiedName xseName = xse.QualifiedName;
XmlSchemaType schemaType = xse.ElementSchemaType;
XmlSchemaComplexType complexType = schemaType as XmlSchemaComplexType;
if (null != complexType) {
XmlSchemaParticle particle = null;
Shape rootShape = null;
XmlSchemaContentType contentType = complexType.ElementDecl.ContentValidator.ContentType;
switch (contentType) {
case XmlSchemaContentType.Mixed:
case XmlSchemaContentType.TextOnly:
rootShape = new Shape(GenName(xseName) + "_Text", BindingType.Text);
rootShape.AddParticle(xse);
break;
case XmlSchemaContentType.Empty:
rootShape = new Shape(null, BindingType.Sequence);
break;
case XmlSchemaContentType.ElementOnly:
particle = complexType.ContentTypeParticle;
rootShape = ProcessParticle(particle, null);
break;
}
Debug.Assert(rootShape != null);
if (complexType.AttributeUses.Values.Count > 0) {
if (rootShape.BindingType != BindingType.Sequence) {
Shape s = new Shape(null, BindingType.Sequence);
s.AddSubShape(rootShape);
rootShape = s;
}
int pos = 0;
string[] names = rootShape.SubShapeNames();
ICollection attributes = complexType.AttributeUses.Values;
XmlSchemaAttribute[] xsaArray = new XmlSchemaAttribute[attributes.Count];
attributes.CopyTo(xsaArray, 0);
Array.Sort(xsaArray, new XmlSchemaAttributeComparer());
foreach(XmlSchemaAttribute xsa in xsaArray) {
string name = GenAttrName(xsa.QualifiedName, names);
Shape attrShape = new Shape(name, BindingType.Attribute);
attrShape.AddParticle(xsa);
rootShape.AddAttrShapeAt(attrShape, pos++);
}
}
if (rootShape.BindingType != BindingType.Text) {
rootShape.Name = GenName(xseName);
rootShape.ContainerDecl = xse;
}
return rootShape;
}
else { // simple type
Shape s = new Shape(GenName(xseName), BindingType.Text);
s.AddParticle(xse);
return s;
}
}
public Shape GenerateFromSchema(XmlSchemaAttribute xsa) {
Shape s = new Shape(GenName(xsa.QualifiedName), BindingType.Attribute);
s.AddParticle(xsa);
return s;
}
Shape ProcessParticle(XmlSchemaParticle xsp, Shape parent) {
Shape s;
if (xsp == XmlSchemaParticle.Empty) {
return null;
}
if (xsp is XmlSchemaElement) {
s = ProcessParticleElement((XmlSchemaElement)xsp);
}
else if (xsp is XmlSchemaSequence) {
s = ProcessParticleGroup((XmlSchemaSequence)xsp, BindingType.Sequence);
}
else if (xsp is XmlSchemaChoice) {
s = ProcessParticleGroup((XmlSchemaChoice)xsp, BindingType.Choice);
}
else if (xsp is XmlSchemaAll) {
s = ProcessParticleGroup((XmlSchemaAll)xsp, BindingType.All);
}
else { //XmlSchemaAny
return null; //Ignore Any in the content model
}
if (xsp.MaxOccurs > 1) {
Shape rep = new Shape(s.Name, BindingType.Repeat);
rep.AddSubShape(s);
s = rep;
}
if (parent != null)
parent.AddSubShape(s);
return s;
}
Shape ProcessParticleElement(XmlSchemaElement xse) {
// watch out for recursive schema
Shape s = (Shape)this.elementTypesProcessed[xse];
if (null != s)
return s;
bool complex = xse.ElementSchemaType is XmlSchemaComplexType;
s = new Shape(GenName(xse.QualifiedName), complex ? BindingType.ElementNested : BindingType.Element);
s.AddParticle(xse);
if (complex) {
this.elementTypesProcessed.Add(xse, s);
s.NestedShape = GenerateFromSchema(xse);
this.elementTypesProcessed.Remove(xse);
}
return s;
}
Shape ProcessParticleGroup(XmlSchemaGroupBase xsg, BindingType bt) {
Shape s = new Shape(null, bt);
StringBuilder sb = new StringBuilder();
foreach (XmlSchemaParticle xsp in xsg.Items) {
Shape sub = ProcessParticle(xsp, s);
if (sub != null) { //sub can be null if the child particle is xs:any
if (sb.Length > 0)
sb.Append('_');
sb.Append(sub.Name);
}
}
// need to also test if paretn != null for this to work
//if (s.IsGroup && s.SubShapes.Count == 1) {
// Shape sub = (Shape)s.SubShapes[0];
// s.Clear();
// return sub;
//}
s.Name = sb.ToString();
return s;
}
string GenName(XmlQualifiedName xqn) {
string ns = xqn.Namespace;
string ln = xqn.Name;
if (ns.Length != 0) {
string prefix = (null==this.nsResolver) ? null : this.nsResolver.LookupPrefix(ns);
if (prefix != null && prefix.Length != 0)
return String.Concat(prefix, ":", ln);
}
return ln;
}
string GenAttrName(XmlQualifiedName xqn, string[] names) {
string name = GenName(xqn);
if (null != names) {
for (int i=0; i