|
//------------------------------------------------------------------------------
// <copyright file="XmlSchemaExporter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Xml.Serialization {
using System;
using System.Collections;
using System.Xml.Schema;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter"]/*' />
///<internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public class XmlSchemaExporter {
internal const XmlSchemaForm elementFormDefault = XmlSchemaForm.Qualified;
internal const XmlSchemaForm attributeFormDefault = XmlSchemaForm.Unqualified;
XmlSchemas schemas;
Hashtable elements = new Hashtable(); // ElementAccessor -> XmlSchemaElement
Hashtable attributes = new Hashtable(); // AttributeAccessor -> XmlSchemaElement
Hashtable types = new Hashtable(); // StructMapping/EnumMapping -> XmlSchemaComplexType/XmlSchemaSimpleType
Hashtable references = new Hashtable(); // TypeMappings to keep track of circular references via anonymous types
bool needToExportRoot;
TypeScope scope;
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.XmlSchemaExporter"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public XmlSchemaExporter(XmlSchemas schemas) {
this.schemas = schemas;
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportTypeMapping"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ExportTypeMapping(XmlTypeMapping xmlTypeMapping) {
xmlTypeMapping.CheckShallow();
CheckScope(xmlTypeMapping.Scope);
ExportElement(xmlTypeMapping.Accessor);
ExportRootIfNecessary(xmlTypeMapping.Scope);
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportTypeMapping1"]/*' />
public XmlQualifiedName ExportTypeMapping(XmlMembersMapping xmlMembersMapping) {
xmlMembersMapping.CheckShallow();
CheckScope(xmlMembersMapping.Scope);
MembersMapping mapping = (MembersMapping)xmlMembersMapping.Accessor.Mapping;
if (mapping.Members.Length == 1 && mapping.Members[0].Elements[0].Mapping is SpecialMapping) {
SpecialMapping special = (SpecialMapping)mapping.Members[0].Elements[0].Mapping;
XmlSchemaType type = ExportSpecialMapping(special, xmlMembersMapping.Accessor.Namespace, false, null);
if (type != null && type.Name != null && type.Name.Length > 0) {
type.Name = xmlMembersMapping.Accessor.Name;
AddSchemaItem(type, xmlMembersMapping.Accessor.Namespace, null);
}
ExportRootIfNecessary(xmlMembersMapping.Scope);
return (new XmlQualifiedName(xmlMembersMapping.Accessor.Name, xmlMembersMapping.Accessor.Namespace));
}
return null;
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportMembersMapping"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping) {
ExportMembersMapping(xmlMembersMapping, true);
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportMembersMapping1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping, bool exportEnclosingType) {
xmlMembersMapping.CheckShallow();
MembersMapping mapping = (MembersMapping)xmlMembersMapping.Accessor.Mapping;
CheckScope(xmlMembersMapping.Scope);
if (mapping.HasWrapperElement && exportEnclosingType) {
ExportElement(xmlMembersMapping.Accessor);
}
else {
foreach (MemberMapping member in mapping.Members) {
if (member.Attribute != null)
throw new InvalidOperationException(Res.GetString(Res.XmlBareAttributeMember, member.Attribute.Name));
else if (member.Text != null)
throw new InvalidOperationException(Res.GetString(Res.XmlBareTextMember, member.Text.Name));
else if (member.Elements == null || member.Elements.Length == 0)
continue;
if (member.TypeDesc.IsArrayLike && !(member.Elements[0].Mapping is ArrayMapping))
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalArrayElement, member.Elements[0].Name));
if (exportEnclosingType) {
ExportElement(member.Elements[0]);
}
else {
ExportMapping(member.Elements[0].Mapping, member.Elements[0].Namespace, member.Elements[0].Any);
}
}
}
ExportRootIfNecessary(xmlMembersMapping.Scope);
}
private static XmlSchemaType FindSchemaType(string name, XmlSchemaObjectCollection items) {
// Have to loop through the items because schema.SchemaTypes has not been populated yet.
foreach (object o in items) {
XmlSchemaType type = o as XmlSchemaType;
if (type == null)
continue;
if (type.Name == name)
return type;
}
return null;
}
private static bool IsAnyType(XmlSchemaType schemaType, bool mixed, bool unbounded) {
XmlSchemaComplexType complexType = schemaType as XmlSchemaComplexType;
if (complexType != null) {
if (complexType.IsMixed != mixed)
return false;
if (complexType.Particle is XmlSchemaSequence) {
XmlSchemaSequence sequence = (XmlSchemaSequence) complexType.Particle;
if (sequence.Items.Count == 1 && sequence.Items[0] is XmlSchemaAny) {
XmlSchemaAny any = (XmlSchemaAny)sequence.Items[0];
return (unbounded == any.IsMultipleOccurrence);
}
}
}
return false;
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportAnyType"]/*' />
public string ExportAnyType(string ns) {
string name = "any";
int i = 0;
XmlSchema schema = schemas[ns];
if (schema != null) {
while (true) {
XmlSchemaType schemaType = FindSchemaType(name, schema.Items);
if (schemaType == null)
break;
if (IsAnyType(schemaType, true, true))
return name;
i++;
name = "any" + i.ToString(CultureInfo.InvariantCulture);
}
}
XmlSchemaComplexType type = new XmlSchemaComplexType();
type.Name = name;
type.IsMixed = true;
XmlSchemaSequence seq = new XmlSchemaSequence();
XmlSchemaAny any = new XmlSchemaAny();
any.MinOccurs = 0;
any.MaxOccurs = decimal.MaxValue;
seq.Items.Add(any);
type.Particle = seq;
AddSchemaItem(type, ns, null);
return name;
}
/// <include file='doc\XmlSchemaExporter.uex' path='docs/doc[@for="XmlSchemaExporter.ExportAnyType1"]/*' />
public string ExportAnyType(XmlMembersMapping members) {
if (members.Count == 1 && members[0].Any && members[0].ElementName.Length == 0) {
XmlMemberMapping member = members[0];
string ns = member.Namespace;
bool isUnbounded = member.Mapping.TypeDesc.IsArrayLike;
bool isMixed = isUnbounded && member.Mapping.TypeDesc.ArrayElementTypeDesc != null ? member.Mapping.TypeDesc.ArrayElementTypeDesc.IsMixed : member.Mapping.TypeDesc.IsMixed;
if (isMixed && member.Mapping.TypeDesc.IsMixed)
// special case of the single top-level XmlNode --> map it to node array to match the "mixed" any type for backward compatibility
isUnbounded = true;
// generate type name, make sure that it is backward compatible
string baseName = isMixed ? "any" : isUnbounded ? "anyElements" : "anyElement";
string name = baseName;
int i = 0;
XmlSchema schema = schemas[ns];
if (schema != null) {
while (true) {
XmlSchemaType schemaType = FindSchemaType(name, schema.Items);
if (schemaType == null)
break;
if (IsAnyType(schemaType, isMixed, isUnbounded))
return name;
i++;
name = baseName + i.ToString(CultureInfo.InvariantCulture);
}
}
XmlSchemaComplexType type = new XmlSchemaComplexType();
type.Name = name;
type.IsMixed = isMixed;
XmlSchemaSequence seq = new XmlSchemaSequence();
XmlSchemaAny any = new XmlSchemaAny();
any.MinOccurs = 0;
if (isUnbounded)
any.MaxOccurs = decimal.MaxValue;
seq.Items.Add(any);
type.Particle = seq;
AddSchemaItem(type, ns, null);
return name;
}
else {
return null;
}
}
void CheckScope(TypeScope scope) {
if (this.scope == null) {
this.scope = scope;
}
else if (this.scope != scope) {
throw new InvalidOperationException(Res.GetString(Res.XmlMappingsScopeMismatch));
}
}
XmlSchemaElement ExportElement(ElementAccessor accessor) {
if (!accessor.Mapping.IncludeInSchema && !accessor.Mapping.TypeDesc.IsRoot) {
return null;
}
if (accessor.Any && accessor.Name.Length == 0)
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalWildcard));
XmlSchemaElement element = (XmlSchemaElement)elements[accessor];
if (element != null) return element;
element = new XmlSchemaElement();
element.Name = accessor.Name;
element.IsNillable = accessor.IsNullable;
elements.Add(accessor, element);
element.Form = accessor.Form;
AddSchemaItem(element, accessor.Namespace, null);
ExportElementMapping(element, accessor.Mapping, accessor.Namespace, accessor.Any);
return element;
}
void CheckForDuplicateType(TypeMapping mapping, string newNamespace) {
if (mapping.IsAnonymousType)
return;
string newTypeName = mapping.TypeName;
XmlSchema schema = schemas[newNamespace];
if (schema != null){
foreach (XmlSchemaObject o in schema.Items) {
XmlSchemaType type = o as XmlSchemaType;
if ( type != null && type.Name == newTypeName)
throw new InvalidOperationException(Res.GetString(Res.XmlDuplicateTypeName, newTypeName, newNamespace));
}
}
}
XmlSchema AddSchema(string targetNamespace) {
XmlSchema schema = new XmlSchema();
schema.TargetNamespace = string.IsNullOrEmpty(targetNamespace) ? null : targetNamespace;
#pragma warning disable 429 // Unreachable code: the default values are constant, so will never be Unqualified
schema.ElementFormDefault = elementFormDefault == XmlSchemaForm.Unqualified ? XmlSchemaForm.None : elementFormDefault;
schema.AttributeFormDefault = attributeFormDefault == XmlSchemaForm.Unqualified ? XmlSchemaForm.None : attributeFormDefault;
#pragma warning restore 429
schemas.Add(schema);
return schema;
}
void AddSchemaItem(XmlSchemaObject item, string ns, string referencingNs) {
XmlSchema schema = schemas[ns];
if (schema == null) {
schema = AddSchema(ns);
}
if (item is XmlSchemaElement) {
XmlSchemaElement e = (XmlSchemaElement)item;
if (e.Form == XmlSchemaForm.Unqualified)
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalForm, e.Name));
e.Form = XmlSchemaForm.None;
}
else if (item is XmlSchemaAttribute) {
XmlSchemaAttribute a = (XmlSchemaAttribute)item;
if (a.Form == XmlSchemaForm.Unqualified)
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalForm, a.Name));
a.Form = XmlSchemaForm.None;
}
schema.Items.Add(item);
AddSchemaImport(ns, referencingNs);
}
void AddSchemaImport(string ns, string referencingNs) {
if (referencingNs == null) return;
if (NamespacesEqual(ns, referencingNs)) return;
XmlSchema schema = schemas[referencingNs];
if (schema == null) {
schema = AddSchema(referencingNs);
}
if (FindImport(schema, ns) == null) {
XmlSchemaImport import = new XmlSchemaImport();
if (ns != null && ns.Length > 0)
import.Namespace = ns;
schema.Includes.Add(import);
}
}
static bool NamespacesEqual(string ns1, string ns2)
{
if (ns1 == null || ns1.Length == 0)
return (ns2 == null || ns2.Length == 0);
else
return ns1 == ns2;
}
bool SchemaContainsItem(XmlSchemaObject item, string ns) {
XmlSchema schema = schemas[ns];
if (schema != null) {
return schema.Items.Contains(item);
}
return false;
}
XmlSchemaImport FindImport(XmlSchema schema, string ns) {
foreach (object item in schema.Includes) {
if (item is XmlSchemaImport) {
XmlSchemaImport import = (XmlSchemaImport)item;
if (NamespacesEqual(import.Namespace, ns)) {
return import;
}
}
}
return null;
}
void ExportMapping(Mapping mapping, string ns, bool isAny) {
if (mapping is ArrayMapping)
ExportArrayMapping((ArrayMapping)mapping, ns, null);
else if (mapping is PrimitiveMapping) {
ExportPrimitiveMapping((PrimitiveMapping)mapping, ns);
}
else if (mapping is StructMapping)
ExportStructMapping((StructMapping)mapping, ns, null);
else if (mapping is MembersMapping)
ExportMembersMapping((MembersMapping)mapping, ns);
else if (mapping is SpecialMapping)
ExportSpecialMapping((SpecialMapping)mapping, ns, isAny, null);
else if (mapping is NullableMapping)
ExportMapping(((NullableMapping)mapping).BaseMapping, ns, isAny);
else
throw new ArgumentException(Res.GetString(Res.XmlInternalError), "mapping");
}
void ExportElementMapping(XmlSchemaElement element, Mapping mapping, string ns, bool isAny) {
if (mapping is ArrayMapping)
ExportArrayMapping((ArrayMapping)mapping, ns, element);
else if (mapping is PrimitiveMapping) {
PrimitiveMapping pm = (PrimitiveMapping)mapping;
if (pm.IsAnonymousType) {
element.SchemaType = ExportAnonymousPrimitiveMapping(pm);
}
else {
element.SchemaTypeName = ExportPrimitiveMapping(pm, ns);
}
}
else if (mapping is StructMapping) {
ExportStructMapping((StructMapping)mapping, ns, element);
}
else if (mapping is MembersMapping)
element.SchemaType = ExportMembersMapping((MembersMapping)mapping, ns);
else if (mapping is SpecialMapping)
ExportSpecialMapping((SpecialMapping)mapping, ns, isAny, element);
else if (mapping is NullableMapping) {
ExportElementMapping(element, ((NullableMapping)mapping).BaseMapping, ns, isAny);
}
else
throw new ArgumentException(Res.GetString(Res.XmlInternalError), "mapping");
}
XmlQualifiedName ExportNonXsdPrimitiveMapping(PrimitiveMapping mapping, string ns) {
XmlSchemaSimpleType type = (XmlSchemaSimpleType)mapping.TypeDesc.DataType;
if (!SchemaContainsItem(type, UrtTypes.Namespace)) {
AddSchemaItem(type, UrtTypes.Namespace, ns);
}
else {
AddSchemaImport(mapping.Namespace, ns);
}
return new XmlQualifiedName(mapping.TypeDesc.DataType.Name, UrtTypes.Namespace);
}
XmlSchemaType ExportSpecialMapping(SpecialMapping mapping, string ns, bool isAny, XmlSchemaElement element) {
switch (mapping.TypeDesc.Kind) {
case TypeKind.Node: {
XmlSchemaComplexType type = new XmlSchemaComplexType();
type.IsMixed = mapping.TypeDesc.IsMixed;
XmlSchemaSequence seq = new XmlSchemaSequence();
XmlSchemaAny any = new XmlSchemaAny();
if (isAny) {
type.AnyAttribute = new XmlSchemaAnyAttribute();
type.IsMixed = true;
any.MaxOccurs = decimal.MaxValue;
}
seq.Items.Add(any);
type.Particle = seq;
if (element != null)
element.SchemaType = type;
return type;
}
case TypeKind.Serializable: {
SerializableMapping serializableMapping = (SerializableMapping)mapping;
if (serializableMapping.IsAny) {
XmlSchemaComplexType type = new XmlSchemaComplexType();
type.IsMixed = mapping.TypeDesc.IsMixed;
XmlSchemaSequence seq = new XmlSchemaSequence();
XmlSchemaAny any = new XmlSchemaAny();
if (isAny) {
type.AnyAttribute = new XmlSchemaAnyAttribute();
type.IsMixed = true;
any.MaxOccurs = decimal.MaxValue;
}
if (serializableMapping.NamespaceList.Length > 0)
any.Namespace = serializableMapping.NamespaceList;
any.ProcessContents = XmlSchemaContentProcessing.Lax;
if (serializableMapping.Schemas != null) {
foreach (XmlSchema schema in serializableMapping.Schemas.Schemas()) {
if (schema.TargetNamespace != XmlSchema.Namespace) {
schemas.Add(schema, true);
AddSchemaImport(schema.TargetNamespace, ns);
}
}
}
seq.Items.Add(any);
type.Particle = seq;
if (element != null)
element.SchemaType = type;
return type;
}
else if (serializableMapping.XsiType != null || serializableMapping.XsdType != null) {
XmlSchemaType type = serializableMapping.XsdType;
// for performance reasons we need to postpone merging of the serializable schemas
foreach (XmlSchema schema in serializableMapping.Schemas.Schemas()) {
if (schema.TargetNamespace != XmlSchema.Namespace) {
schemas.Add(schema, true);
AddSchemaImport(schema.TargetNamespace, ns);
if (!serializableMapping.XsiType.IsEmpty && serializableMapping.XsiType.Namespace == schema.TargetNamespace)
type = (XmlSchemaType)schema.SchemaTypes[serializableMapping.XsiType];
}
}
if (element != null) {
element.SchemaTypeName = serializableMapping.XsiType;
if (element.SchemaTypeName.IsEmpty)
element.SchemaType = type;
}
// check for duplicate top-level elements XmlAttributes
serializableMapping.CheckDuplicateElement(element, ns);
return type;
}
else if (serializableMapping.Schema != null) {
// this is the strongly-typed DataSet
XmlSchemaComplexType type = new XmlSchemaComplexType();
XmlSchemaAny any = new XmlSchemaAny();
XmlSchemaSequence seq = new XmlSchemaSequence();
seq.Items.Add(any);
type.Particle = seq;
string anyNs = serializableMapping.Schema.TargetNamespace;
any.Namespace = anyNs == null ? "" : anyNs;
XmlSchema existingSchema = schemas[anyNs];
if (existingSchema == null) {
schemas.Add(serializableMapping.Schema);
}
else if (existingSchema != serializableMapping.Schema) {
throw new InvalidOperationException(Res.GetString(Res.XmlDuplicateNamespace, anyNs));
}
if (element != null)
element.SchemaType = type;
// check for duplicate top-level elements XmlAttributes
serializableMapping.CheckDuplicateElement(element, ns);
return type;
}
else {
// DataSet
XmlSchemaComplexType type = new XmlSchemaComplexType();
XmlSchemaElement schemaElement = new XmlSchemaElement();
schemaElement.RefName = new XmlQualifiedName("schema", XmlSchema.Namespace);
XmlSchemaSequence seq = new XmlSchemaSequence();
seq.Items.Add(schemaElement);
seq.Items.Add(new XmlSchemaAny());
type.Particle = seq;
AddSchemaImport(XmlSchema.Namespace, ns);
if (element != null)
element.SchemaType = type;
return type;
}
}
default:
throw new ArgumentException(Res.GetString(Res.XmlInternalError), "mapping");
}
}
XmlSchemaType ExportMembersMapping(MembersMapping mapping, string ns) {
XmlSchemaComplexType type = new XmlSchemaComplexType();
ExportTypeMembers(type, mapping.Members, mapping.TypeName, ns, false, false);
if (mapping.XmlnsMember != null) {
AddXmlnsAnnotation(type, mapping.XmlnsMember.Name);
}
return type;
}
XmlSchemaType ExportAnonymousPrimitiveMapping(PrimitiveMapping mapping) {
if (mapping is EnumMapping) {
return ExportEnumMapping((EnumMapping)mapping, null);
}
else {
throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Unsuported anonymous mapping type: " + mapping.ToString()));
}
}
XmlQualifiedName ExportPrimitiveMapping(PrimitiveMapping mapping, string ns) {
XmlQualifiedName qname;
if (mapping is EnumMapping) {
XmlSchemaType type = ExportEnumMapping((EnumMapping)mapping, ns);
qname = new XmlQualifiedName(type.Name, mapping.Namespace);
}
else {
if (mapping.TypeDesc.IsXsdType) {
qname = new XmlQualifiedName(mapping.TypeDesc.DataType.Name, XmlSchema.Namespace);
}
else {
qname = ExportNonXsdPrimitiveMapping(mapping, ns);
}
}
return qname;
}
void ExportArrayMapping(ArrayMapping mapping, string ns, XmlSchemaElement element) {
// some of the items in the linked list differ only by CLR type. We don't need to
// export different schema types for these. Look further down the list for another
// entry with the same elements. If there is one, it will be exported later so
// just return its name now.
ArrayMapping currentMapping = mapping;
while (currentMapping.Next != null) {
currentMapping = currentMapping.Next;
}
XmlSchemaComplexType type = (XmlSchemaComplexType) types[currentMapping];
if (type == null) {
CheckForDuplicateType(currentMapping, currentMapping.Namespace);
type = new XmlSchemaComplexType();
if (!mapping.IsAnonymousType) {
type.Name = mapping.TypeName;
AddSchemaItem(type, mapping.Namespace, ns);
}
if (!currentMapping.IsAnonymousType)
types.Add(currentMapping, type);
XmlSchemaSequence seq = new XmlSchemaSequence();
ExportElementAccessors(seq, mapping.Elements, true, false, mapping.Namespace);
if (seq.Items.Count > 0) {
#if DEBUG
// we can have only one item for the array mapping
if (seq.Items.Count != 1)
throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Type " + mapping.TypeName + " from namespace '" + ns + "' is an invalid array mapping"));
#endif
if (seq.Items[0] is XmlSchemaChoice) {
type.Particle = (XmlSchemaChoice)seq.Items[0];
}
else {
type.Particle = seq;
}
}
}
else {
AddSchemaImport(mapping.Namespace, ns);
}
if (element != null) {
if (mapping.IsAnonymousType) {
element.SchemaType = type;
}
else {
element.SchemaTypeName = new XmlQualifiedName(type.Name, mapping.Namespace);
}
}
}
void ExportElementAccessors(XmlSchemaGroupBase group, ElementAccessor[] accessors, bool repeats, bool valueTypeOptional, string ns) {
if (accessors.Length == 0) return;
if (accessors.Length == 1) {
ExportElementAccessor(group, accessors[0], repeats, valueTypeOptional, ns);
}
else {
XmlSchemaChoice choice = new XmlSchemaChoice();
choice.MaxOccurs = repeats ? decimal.MaxValue : 1;
choice.MinOccurs = repeats ? 0 : 1;
for (int i = 0; i < accessors.Length; i++)
ExportElementAccessor(choice, accessors[i], false, valueTypeOptional, ns);
if (choice.Items.Count > 0) group.Items.Add(choice);
}
}
void ExportAttributeAccessor(XmlSchemaComplexType type, AttributeAccessor accessor, bool valueTypeOptional, string ns) {
if (accessor == null) return;
XmlSchemaObjectCollection attributes;
if (type.ContentModel != null) {
if (type.ContentModel.Content is XmlSchemaComplexContentRestriction)
attributes = ((XmlSchemaComplexContentRestriction)type.ContentModel.Content).Attributes;
else if (type.ContentModel.Content is XmlSchemaComplexContentExtension)
attributes = ((XmlSchemaComplexContentExtension)type.ContentModel.Content).Attributes;
else if (type.ContentModel.Content is XmlSchemaSimpleContentExtension)
attributes = ((XmlSchemaSimpleContentExtension)type.ContentModel.Content).Attributes;
else
throw new InvalidOperationException(Res.GetString(Res.XmlInvalidContent, type.ContentModel.Content.GetType().Name));
}
else {
attributes = type.Attributes;
}
if (accessor.IsSpecialXmlNamespace) {
// add <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
AddSchemaImport(XmlReservedNs.NsXml, ns);
// generate <xsd:attribute ref="xml:lang" use="optional" />
XmlSchemaAttribute attribute = new XmlSchemaAttribute();
attribute.Use = XmlSchemaUse.Optional;
attribute.RefName = new XmlQualifiedName(accessor.Name, XmlReservedNs.NsXml);
attributes.Add(attribute);
}
else if (accessor.Any) {
if (type.ContentModel == null) {
type.AnyAttribute = new XmlSchemaAnyAttribute();
}
else {
XmlSchemaContent content = type.ContentModel.Content;
if (content is XmlSchemaComplexContentExtension) {
XmlSchemaComplexContentExtension extension = (XmlSchemaComplexContentExtension)content;
extension.AnyAttribute = new XmlSchemaAnyAttribute();
}
else if (content is XmlSchemaComplexContentRestriction) {
XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction)content;
restriction.AnyAttribute = new XmlSchemaAnyAttribute();
}
else if (type.ContentModel.Content is XmlSchemaSimpleContentExtension) {
XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension)content;
extension.AnyAttribute = new XmlSchemaAnyAttribute();
}
}
}
else {
XmlSchemaAttribute attribute = new XmlSchemaAttribute();
attribute.Use = XmlSchemaUse.None;
if (!accessor.HasDefault && !valueTypeOptional && accessor.Mapping.TypeDesc.IsValueType) {
attribute.Use = XmlSchemaUse.Required;
}
attribute.Name = accessor.Name;
if (accessor.Namespace == null || accessor.Namespace == ns) {
// determine the form attribute value
XmlSchema schema = schemas[ns];
if (schema == null)
attribute.Form = accessor.Form == attributeFormDefault ? XmlSchemaForm.None : accessor.Form;
else {
attribute.Form = accessor.Form == schema.AttributeFormDefault ? XmlSchemaForm.None : accessor.Form;
}
attributes.Add(attribute);
}
else {
// we are going to add the attribute to the top-level items. "use" attribute should not be set
if (this.attributes[accessor] == null) {
attribute.Use = XmlSchemaUse.None;
attribute.Form = accessor.Form;
AddSchemaItem(attribute, accessor.Namespace, ns);
this.attributes.Add(accessor, accessor);
}
XmlSchemaAttribute refAttribute = new XmlSchemaAttribute();
refAttribute.Use = XmlSchemaUse.None;
refAttribute.RefName = new XmlQualifiedName(accessor.Name, accessor.Namespace);
attributes.Add(refAttribute);
AddSchemaImport(accessor.Namespace, ns);
}
if (accessor.Mapping is PrimitiveMapping) {
PrimitiveMapping pm = (PrimitiveMapping)accessor.Mapping;
if (pm.IsList) {
// create local simple type for the list-like attributes
XmlSchemaSimpleType dataType = new XmlSchemaSimpleType();
XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
if (pm.IsAnonymousType) {
list.ItemType = (XmlSchemaSimpleType)ExportAnonymousPrimitiveMapping(pm);
}
else {
list.ItemTypeName = ExportPrimitiveMapping(pm, accessor.Namespace == null ? ns : accessor.Namespace);
}
dataType.Content = list;
attribute.SchemaType = dataType;
}
else {
if (pm.IsAnonymousType) {
attribute.SchemaType = (XmlSchemaSimpleType)ExportAnonymousPrimitiveMapping(pm);
}
else {
attribute.SchemaTypeName = ExportPrimitiveMapping(pm, accessor.Namespace == null ? ns : accessor.Namespace);
}
}
}
else if (!(accessor.Mapping is SpecialMapping))
throw new InvalidOperationException(Res.GetString(Res.XmlInternalError));
if (accessor.HasDefault) {
attribute.DefaultValue = ExportDefaultValue(accessor.Mapping, accessor.Default);
}
}
}
void ExportElementAccessor(XmlSchemaGroupBase group, ElementAccessor accessor, bool repeats, bool valueTypeOptional, string ns) {
if (accessor.Any && accessor.Name.Length == 0) {
XmlSchemaAny any = new XmlSchemaAny();
any.MinOccurs = 0;
any.MaxOccurs = repeats ? decimal.MaxValue : 1;
if (accessor.Namespace != null && accessor.Namespace.Length > 0 && accessor.Namespace != ns)
any.Namespace = accessor.Namespace;
group.Items.Add(any);
}
else {
XmlSchemaElement element = (XmlSchemaElement)elements[accessor];
int minOccurs = repeats || accessor.HasDefault || (!accessor.IsNullable && !accessor.Mapping.TypeDesc.IsValueType) || valueTypeOptional ? 0 : 1;
decimal maxOccurs = repeats || accessor.IsUnbounded ? decimal.MaxValue : 1;
if (element == null) {
element = new XmlSchemaElement();
element.IsNillable = accessor.IsNullable;
element.Name = accessor.Name;
if (accessor.HasDefault)
element.DefaultValue = ExportDefaultValue(accessor.Mapping, accessor.Default);
if (accessor.IsTopLevelInSchema) {
elements.Add(accessor, element);
element.Form = accessor.Form;
AddSchemaItem(element, accessor.Namespace, ns);
}
else {
element.MinOccurs = minOccurs;
element.MaxOccurs = maxOccurs;
// determine the form attribute value
XmlSchema schema = schemas[ns];
if (schema == null)
element.Form = accessor.Form == elementFormDefault ? XmlSchemaForm.None : accessor.Form;
else {
element.Form = accessor.Form == schema.ElementFormDefault ? XmlSchemaForm.None : accessor.Form;
}
}
ExportElementMapping(element, (TypeMapping)accessor.Mapping, accessor.Namespace, accessor.Any);
}
if (accessor.IsTopLevelInSchema) {
XmlSchemaElement refElement = new XmlSchemaElement();
refElement.RefName = new XmlQualifiedName(accessor.Name, accessor.Namespace);
refElement.MinOccurs = minOccurs;
refElement.MaxOccurs = maxOccurs;
group.Items.Add(refElement);
AddSchemaImport(accessor.Namespace, ns);
}
else {
group.Items.Add(element);
}
}
}
static internal string ExportDefaultValue(TypeMapping mapping, object value) {
if (!(mapping is PrimitiveMapping))
// should throw, but it will be a breaking change;
return null;
if (value == null || value == DBNull.Value)
return null;
if (mapping is EnumMapping) {
EnumMapping em = (EnumMapping)mapping;
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (value.GetType() != typeof(string)) throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, Res.GetString(Res.XmlInvalidDefaultValue, value.ToString(), value.GetType().FullName)));
#endif
// check the validity of the value
ConstantMapping[] c = em.Constants;
if (em.IsFlags) {
string[] names = new string[c.Length];
long[] ids = new long[c.Length];
Hashtable values = new Hashtable();
for (int i = 0; i < c.Length; i++) {
names[i] = c[i].XmlName;
ids[i] = 1 << i;
values.Add(c[i].Name, ids[i]);
}
long val = XmlCustomFormatter.ToEnum((string)value, values, em.TypeName, false);
return val != 0 ? XmlCustomFormatter.FromEnum(val, names, ids, mapping.TypeDesc.FullName) : null;
}
else {
for (int i = 0; i < c.Length; i++) {
if (c[i].Name == (string)value) {
return c[i].XmlName;
}
}
return null; // unknown value
}
}
PrimitiveMapping pm = (PrimitiveMapping)mapping;
if (!pm.TypeDesc.HasCustomFormatter) {
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (pm.TypeDesc.Type == null) {
throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Mapping for " + pm.TypeDesc.Name + " missing type property"));
}
#endif
if (pm.TypeDesc.FormatterName == "String")
return (string)value;
Type formatter = typeof(XmlConvert);
System.Reflection.MethodInfo format = formatter.GetMethod("ToString", new Type[] { pm.TypeDesc.Type });
if (format != null)
return (string)format.Invoke(formatter, new Object[] {value});
}
else {
string defaultValue = XmlCustomFormatter.FromDefaultValue(value, pm.TypeDesc.FormatterName);
if (defaultValue == null)
throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultValue, value.ToString(), pm.TypeDesc.Name));
return defaultValue;
}
throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultValue, value.ToString(), pm.TypeDesc.Name));
}
void ExportRootIfNecessary(TypeScope typeScope) {
if (!needToExportRoot)
return;
foreach (TypeMapping mapping in typeScope.TypeMappings) {
if (mapping is StructMapping && mapping.TypeDesc.IsRoot) {
ExportDerivedMappings((StructMapping) mapping);
}
else if (mapping is ArrayMapping) {
ExportArrayMapping((ArrayMapping) mapping, mapping.Namespace, null);
}
else if (mapping is SerializableMapping) {
ExportSpecialMapping((SerializableMapping)mapping, mapping.Namespace, false, null);
}
}
}
XmlQualifiedName ExportStructMapping(StructMapping mapping, string ns, XmlSchemaElement element) {
if (mapping.TypeDesc.IsRoot) {
needToExportRoot = true;
return XmlQualifiedName.Empty;
}
if (mapping.IsAnonymousType) {
if (references[mapping] != null)
throw new InvalidOperationException(Res.GetString(Res.XmlCircularReference2, mapping.TypeDesc.Name, "AnonymousType", "false"));
references[mapping] = mapping;
}
XmlSchemaComplexType type = (XmlSchemaComplexType)types[mapping];
if (type == null) {
if (!mapping.IncludeInSchema) throw new InvalidOperationException(Res.GetString(Res.XmlCannotIncludeInSchema, mapping.TypeDesc.Name));
CheckForDuplicateType(mapping, mapping.Namespace);
type = new XmlSchemaComplexType();
if (!mapping.IsAnonymousType) {
type.Name = mapping.TypeName;
AddSchemaItem(type, mapping.Namespace, ns);
types.Add(mapping, type);
}
type.IsAbstract = mapping.TypeDesc.IsAbstract;
bool openModel = mapping.IsOpenModel;
if (mapping.BaseMapping != null && mapping.BaseMapping.IncludeInSchema) {
if (mapping.BaseMapping.IsAnonymousType) {
throw new InvalidOperationException(Res.GetString(Res.XmlAnonymousBaseType, mapping.TypeDesc.Name, mapping.BaseMapping.TypeDesc.Name, "AnonymousType", "false"));
}
if (mapping.HasSimpleContent) {
XmlSchemaSimpleContent model = new XmlSchemaSimpleContent();
XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
extension.BaseTypeName = ExportStructMapping(mapping.BaseMapping, mapping.Namespace, null);
model.Content = extension;
type.ContentModel = model;
}
else {
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
extension.BaseTypeName = ExportStructMapping(mapping.BaseMapping, mapping.Namespace, null);
XmlSchemaComplexContent model = new XmlSchemaComplexContent();
model.Content = extension;
model.IsMixed = XmlSchemaImporter.IsMixed((XmlSchemaComplexType)types[mapping.BaseMapping]);
type.ContentModel = model;
}
openModel = false;
}
ExportTypeMembers(type, mapping.Members, mapping.TypeName, mapping.Namespace, mapping.HasSimpleContent, openModel);
ExportDerivedMappings(mapping);
if (mapping.XmlnsMember != null) {
AddXmlnsAnnotation(type, mapping.XmlnsMember.Name);
}
}
else {
AddSchemaImport(mapping.Namespace, ns);
}
if (mapping.IsAnonymousType) {
references[mapping] = null;
if (element != null)
element.SchemaType = type;
return XmlQualifiedName.Empty;
}
else {
XmlQualifiedName qname = new XmlQualifiedName(type.Name, mapping.Namespace);
if (element != null) element.SchemaTypeName = qname;
return qname;
}
}
void ExportTypeMembers(XmlSchemaComplexType type, MemberMapping[] members, string name, string ns, bool hasSimpleContent, bool openModel) {
XmlSchemaGroupBase seq = new XmlSchemaSequence();
TypeMapping textMapping = null;
for (int i = 0; i < members.Length; i++) {
MemberMapping member = members[i];
if (member.Ignore)
continue;
if (member.Text != null) {
if (textMapping != null) {
throw new InvalidOperationException(Res.GetString(Res.XmlIllegalMultipleText, name));
}
textMapping = member.Text.Mapping;
}
if (member.Elements.Length > 0) {
bool repeats = member.TypeDesc.IsArrayLike &&
!(member.Elements.Length == 1 && member.Elements[0].Mapping is ArrayMapping);
bool valueTypeOptional = member.CheckSpecified != SpecifiedAccessor.None || member.CheckShouldPersist;
ExportElementAccessors(seq, member.Elements, repeats, valueTypeOptional, ns);
}
}
if (seq.Items.Count > 0) {
if (type.ContentModel != null) {
if (type.ContentModel.Content is XmlSchemaComplexContentRestriction)
((XmlSchemaComplexContentRestriction)type.ContentModel.Content).Particle = seq;
else if (type.ContentModel.Content is XmlSchemaComplexContentExtension)
((XmlSchemaComplexContentExtension)type.ContentModel.Content).Particle = seq;
else
throw new InvalidOperationException(Res.GetString(Res.XmlInvalidContent, type.ContentModel.Content.GetType().Name));
}
else {
type.Particle = seq;
}
}
if (textMapping != null) {
if (hasSimpleContent) {
if (textMapping is PrimitiveMapping && seq.Items.Count == 0) {
PrimitiveMapping pm = (PrimitiveMapping)textMapping;
if (pm.IsList) {
type.IsMixed = true;
}
else {
if (pm.IsAnonymousType) {
throw new InvalidOperationException(Res.GetString(Res.XmlAnonymousBaseType, textMapping.TypeDesc.Name, pm.TypeDesc.Name, "AnonymousType", "false"));
}
// Create simpleContent
XmlSchemaSimpleContent model = new XmlSchemaSimpleContent();
XmlSchemaSimpleContentExtension ex = new XmlSchemaSimpleContentExtension();
model.Content = ex;
type.ContentModel = model;
ex.BaseTypeName = ExportPrimitiveMapping(pm, ns);
}
}
}
else {
type.IsMixed = true;
}
}
bool anyAttribute = false;
for (int i = 0; i < members.Length; i++) {
AttributeAccessor accessor = members[i].Attribute;
if (accessor != null) {
ExportAttributeAccessor(type, members[i].Attribute, members[i].CheckSpecified != SpecifiedAccessor.None || members[i].CheckShouldPersist, ns);
if (members[i].Attribute.Any)
anyAttribute = true;
}
}
if (openModel && !anyAttribute) {
AttributeAccessor any = new AttributeAccessor();
any.Any = true;
ExportAttributeAccessor(type, any, false, ns);
}
}
void ExportDerivedMappings(StructMapping mapping) {
if (mapping.IsAnonymousType)
return;
for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) {
if (derived.IncludeInSchema) ExportStructMapping(derived, derived.Namespace, null);
}
}
XmlSchemaType ExportEnumMapping(EnumMapping mapping, string ns) {
if (!mapping.IncludeInSchema) throw new InvalidOperationException(Res.GetString(Res.XmlCannotIncludeInSchema, mapping.TypeDesc.Name));
XmlSchemaSimpleType dataType = (XmlSchemaSimpleType)types[mapping];
if (dataType == null) {
CheckForDuplicateType(mapping, mapping.Namespace);
dataType = new XmlSchemaSimpleType();
dataType.Name = mapping.TypeName;
if (!mapping.IsAnonymousType) {
types.Add(mapping, dataType);
AddSchemaItem(dataType, mapping.Namespace, ns);
}
XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
restriction.BaseTypeName = new XmlQualifiedName("string", XmlSchema.Namespace);
for (int i = 0; i < mapping.Constants.Length; i++) {
ConstantMapping constant = mapping.Constants[i];
XmlSchemaEnumerationFacet enumeration = new XmlSchemaEnumerationFacet();
enumeration.Value = constant.XmlName;
restriction.Facets.Add(enumeration);
}
if (!mapping.IsFlags) {
dataType.Content = restriction;
}
else {
XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList();
XmlSchemaSimpleType enumType = new XmlSchemaSimpleType();
enumType.Content = restriction;
list.ItemType = enumType;
dataType.Content = list;
}
}
if (!mapping.IsAnonymousType) {
AddSchemaImport(mapping.Namespace, ns);
}
return dataType;
}
void AddXmlnsAnnotation(XmlSchemaComplexType type, string xmlnsMemberName) {
XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
XmlSchemaAppInfo appinfo = new XmlSchemaAppInfo();
XmlDocument d = new XmlDocument();
XmlElement e = d.CreateElement("keepNamespaceDeclarations");
if (xmlnsMemberName != null)
e.InsertBefore(d.CreateTextNode(xmlnsMemberName), null);
appinfo.Markup = new XmlNode[] {e};
annotation.Items.Add(appinfo);
type.Annotation = annotation;
}
}
}
|