1 //------------------------------------------------------------------------------
2 // <copyright file="ShapeGenerator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">derekdb</owner>
6 //------------------------------------------------------------------------------
7 #if ENABLEDATABINDING
8 using System;
9 using System.Xml;
10 using System.Xml.Schema;
11 using System.Xml.XPath;
12 using System.Collections;
13 using System.Diagnostics;
14 using System.ComponentModel;
15 using System.Text;
16 
17 namespace System.Xml.XPath.DataBinding
18 {
19     internal sealed class ShapeGenerator
20     {
21         private Hashtable elementTypesProcessed;
22         private IXmlNamespaceResolver nsResolver;
23 
ShapeGenerator(IXmlNamespaceResolver nsResolver)24         public ShapeGenerator(IXmlNamespaceResolver nsResolver) {
25             this.elementTypesProcessed = new Hashtable();
26             this.nsResolver = nsResolver;
27         }
28 
GenerateFromSchema(XmlSchemaElement xse)29         public Shape GenerateFromSchema(XmlSchemaElement xse) {
30             XmlQualifiedName xseName = xse.QualifiedName;
31             XmlSchemaType schemaType = xse.ElementSchemaType;
32             XmlSchemaComplexType complexType = schemaType as XmlSchemaComplexType;
33             if (null != complexType) {
34                 XmlSchemaParticle particle = null;
35                 Shape rootShape = null;
36 
37                 XmlSchemaContentType contentType = complexType.ElementDecl.ContentValidator.ContentType;
38                 switch (contentType) {
39                     case XmlSchemaContentType.Mixed:
40                     case XmlSchemaContentType.TextOnly:
41                         rootShape = new Shape(GenName(xseName) + "_Text", BindingType.Text);
42                         rootShape.AddParticle(xse);
43                     break;
44 
45                     case XmlSchemaContentType.Empty:
46                         rootShape = new Shape(null, BindingType.Sequence);
47                         break;
48 
49                     case XmlSchemaContentType.ElementOnly:
50                         particle = complexType.ContentTypeParticle;
51                         rootShape = ProcessParticle(particle, null);
52                     break;
53 
54                 }
55 
56                 Debug.Assert(rootShape != null);
57                 if (complexType.AttributeUses.Values.Count > 0) {
58                     if (rootShape.BindingType != BindingType.Sequence) {
59                         Shape s = new Shape(null, BindingType.Sequence);
60                         s.AddSubShape(rootShape);
61                         rootShape = s;
62                     }
63                     int pos = 0;
64                     string[] names = rootShape.SubShapeNames();
65 
66                     ICollection attributes = complexType.AttributeUses.Values;
67                     XmlSchemaAttribute[] xsaArray = new XmlSchemaAttribute[attributes.Count];
68                     attributes.CopyTo(xsaArray, 0);
69                     Array.Sort(xsaArray, new XmlSchemaAttributeComparer());
70                     foreach(XmlSchemaAttribute xsa in xsaArray) {
71                         string name = GenAttrName(xsa.QualifiedName, names);
72                         Shape attrShape = new Shape(name, BindingType.Attribute);
73                         attrShape.AddParticle(xsa);
74                         rootShape.AddAttrShapeAt(attrShape, pos++);
75                     }
76                 }
77 
78                 if (rootShape.BindingType != BindingType.Text) {
79                     rootShape.Name = GenName(xseName);
80                     rootShape.ContainerDecl = xse;
81                 }
82                 return rootShape;
83             }
84             else { // simple type
85                 Shape s = new Shape(GenName(xseName), BindingType.Text);
86                 s.AddParticle(xse);
87                 return s;
88             }
89         }
90 
GenerateFromSchema(XmlSchemaAttribute xsa)91         public Shape GenerateFromSchema(XmlSchemaAttribute xsa) {
92             Shape s = new Shape(GenName(xsa.QualifiedName), BindingType.Attribute);
93             s.AddParticle(xsa);
94             return s;
95         }
96 
ProcessParticle(XmlSchemaParticle xsp, Shape parent)97         Shape ProcessParticle(XmlSchemaParticle xsp, Shape parent) {
98             Shape s;
99             if (xsp == XmlSchemaParticle.Empty) {
100                 return null;
101             }
102             if (xsp is XmlSchemaElement) {
103                 s = ProcessParticleElement((XmlSchemaElement)xsp);
104             }
105             else if (xsp is XmlSchemaSequence) {
106                 s = ProcessParticleGroup((XmlSchemaSequence)xsp, BindingType.Sequence);
107             }
108             else if (xsp is XmlSchemaChoice) {
109                 s = ProcessParticleGroup((XmlSchemaChoice)xsp, BindingType.Choice);
110             }
111             else if (xsp is XmlSchemaAll) {
112                 s = ProcessParticleGroup((XmlSchemaAll)xsp, BindingType.All);
113             }
114             else { //XmlSchemaAny
115                 return null; //Ignore Any in the content model
116             }
117             if (xsp.MaxOccurs > 1) {
118                 Shape rep = new Shape(s.Name, BindingType.Repeat);
119                 rep.AddSubShape(s);
120                 s = rep;
121             }
122             if (parent != null)
123                 parent.AddSubShape(s);
124             return s;
125         }
126 
ProcessParticleElement(XmlSchemaElement xse)127         Shape ProcessParticleElement(XmlSchemaElement xse) {
128             // watch out for recursive schema
129             Shape s = (Shape)this.elementTypesProcessed[xse];
130             if (null != s)
131                 return s;
132 
133             bool complex = xse.ElementSchemaType is XmlSchemaComplexType;
134             s = new Shape(GenName(xse.QualifiedName), complex ? BindingType.ElementNested : BindingType.Element);
135             s.AddParticle(xse);
136 
137             if (complex) {
138                 this.elementTypesProcessed.Add(xse, s);
139                 s.NestedShape = GenerateFromSchema(xse);
140                 this.elementTypesProcessed.Remove(xse);
141             }
142             return s;
143         }
144 
ProcessParticleGroup(XmlSchemaGroupBase xsg, BindingType bt)145         Shape ProcessParticleGroup(XmlSchemaGroupBase xsg, BindingType bt) {
146             Shape s = new Shape(null, bt);
147             StringBuilder sb = new StringBuilder();
148             foreach (XmlSchemaParticle xsp in xsg.Items) {
149                 Shape sub = ProcessParticle(xsp, s);
150                 if (sub != null) { //sub can be null if the child particle is xs:any
151                     if (sb.Length > 0)
152                         sb.Append('_');
153                     sb.Append(sub.Name);
154                 }
155             }
156             // need to also test if paretn != null for this to work
157             //if (s.IsGroup && s.SubShapes.Count == 1) {
158             //    Shape sub = (Shape)s.SubShapes[0];
159             //    s.Clear();
160             //    return sub;
161             //}
162             s.Name = sb.ToString();
163             return s;
164         }
165 
GenName(XmlQualifiedName xqn)166         string GenName(XmlQualifiedName xqn) {
167             string ns = xqn.Namespace;
168             string ln = xqn.Name;
169             if (ns.Length != 0) {
170                 string prefix = (null==this.nsResolver) ? null : this.nsResolver.LookupPrefix(ns);
171             if (prefix != null && prefix.Length != 0)
172                     return String.Concat(prefix, ":", ln);
173             }
174             return ln;
175         }
176 
GenAttrName(XmlQualifiedName xqn, string[] names)177         string GenAttrName(XmlQualifiedName xqn, string[] names) {
178             string name = GenName(xqn);
179             if (null != names) {
180                 for (int i=0; i<names.Length; i++) {
181                     if (name == names[i]) {
182                         return String.Concat("@", name);
183                     }
184                 }
185             }
186             return name;
187         }
188 
ResetState()189         public void ResetState() {
190             this.elementTypesProcessed.Clear();
191         }
192 
193         class XmlSchemaAttributeComparer : IComparer {
Compare(object a, object b)194             public virtual int Compare(object a, object b) {
195                 XmlSchemaAttribute xsaA = (XmlSchemaAttribute)a;
196                 XmlSchemaAttribute xsaB = (XmlSchemaAttribute)b;
197                 return XmlQualifiedName.Compare(xsaA.QualifiedName, xsaB.QualifiedName);
198             }
199         }
200     }
201 }
202 #endif