2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="XmlSchemaExporter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
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 ;
}
}
}