From de50f78581f7bab5b577fb0edb5ac6a3abbc2a3c Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Tue, 10 May 2011 17:40:35 +0900 Subject: [PATCH] Update XsdDataContractImporter.CanImport and implement ImportXmlType. --- .../XsdDataContractImporter.cs | 254 +++++++++++++++++- .../Test/Resources/Schemas/ns34.xsd | 19 ++ .../Test/Resources/Schemas/ns34_2.xsd | 21 ++ .../XsdDataContractImporterTest.cs | 59 +++- 4 files changed, 341 insertions(+), 12 deletions(-) create mode 100644 mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34.xsd create mode 100644 mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34_2.xsd diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XsdDataContractImporter.cs b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XsdDataContractImporter.cs index b05b2bd22f0..90654b04dd5 100644 --- a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XsdDataContractImporter.cs +++ b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XsdDataContractImporter.cs @@ -37,9 +37,11 @@ using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; +using QName = System.Xml.XmlQualifiedName; + namespace System.Runtime.Serialization { - [MonoTODO ("Support ImportXmlType option; support arrays; CanImport is not up to date with Import")] + [MonoTODO ("support arrays")] public class XsdDataContractImporter { static readonly XmlQualifiedName qname_anytype = new XmlQualifiedName ("anyType", XmlSchema.Namespace); @@ -71,6 +73,65 @@ namespace System.Runtime.Serialization } } + void GenerateXmlType (XmlQualifiedName qname) + { + var cns = GetCodeNamespace (qname.Namespace); + var td = new CodeTypeDeclaration () { + Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns), + TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public }; + cns.Types.Add (td); + td.BaseTypes.Add (new CodeTypeReference (typeof (IXmlSerializable))); + + var thisNodes = new CodePropertyReferenceExpression (new CodeThisReferenceExpression (), "Nodes"); // property this.Nodes + var xmlSerializableServices = new CodeTypeReferenceExpression (typeof (XmlSerializableServices)); // static XmlSerializableServices. + var qnameType = new CodeTypeReference (typeof (XmlQualifiedName)); + + // XmlQualifiedName qname = new XmlQualifiedName ({qname.Name}, {qname.Namespace}); + td.Members.Add (new CodeMemberField () { Name = "qname", Type = qnameType, InitExpression = new CodeObjectCreateExpression (qnameType, new CodePrimitiveExpression (qname.Name), new CodePrimitiveExpression (qname.Namespace)) }); + + // public XmlNode[] Nodes { get; set; } + td.Members.Add (new CodeMemberProperty () { Name = "Nodes", Type = new CodeTypeReference (typeof (XmlNode [])), Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, HasGet = true, HasSet = true }); + + // public void ReadXml(XmlReader reader) { + var read = new CodeMemberMethod () { Name = "ReadXml", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final }; + read.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlReader)), "reader")); + // this.Nodes = XmlSerializableServices.ReadXml(reader); + read.Statements.Add ( + new CodeAssignStatement (thisNodes, + new CodeMethodInvokeExpression ( + new CodeMethodReferenceExpression (xmlSerializableServices, "ReadXml"), + new CodeArgumentReferenceExpression ("reader")))); + // } + td.Members.Add (read); + + // public void WriteXml(XmlWriter writer) { + var write = new CodeMemberMethod () { Name = "WriteXml",Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final }; + write.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlWriter)), "writer")); + // XmlSerializableServices.WriteXml(writer, this.Nodes); + write.Statements.Add ( + new CodeMethodInvokeExpression ( + new CodeMethodReferenceExpression (xmlSerializableServices, "WriteXml"), + new CodeArgumentReferenceExpression ("writer"), + thisNodes)); + // } + td.Members.Add (write); + + // public XmlSchema GetSchema () { return null; } + var getSchema = new CodeMemberMethod () { Name = "GetSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, ReturnType = new CodeTypeReference (typeof (XmlSchema)) }; + getSchema.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (null))); + td.Members.Add (getSchema); + + // public static XmlQualifiedName ExportSchema (XmlSchemaSet schemas) { + var export = new CodeMemberMethod () { Name = "ExportSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final | MemberAttributes.Static, ReturnType = qnameType }; + export.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlSchemaSet)), "schemas")); + // XmlSerializableServices.AddDefaultSchema (schemas); + export.Statements.Add (new CodeMethodInvokeExpression (xmlSerializableServices, "AddDefaultSchema", new CodeArgumentReferenceExpression ("schemas"))); + // return qname; + export.Statements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "qname"))); + // } + td.Members.Add (export); + } + // CanImport public bool CanImport (XmlSchemaSet schemas) @@ -113,23 +174,190 @@ namespace System.Runtime.Serialization if (!schemas.IsCompiled) schemas.Compile (); + if (IsPredefinedType (typeName)) + return true; // while it just ignores... + if (!schemas.GlobalTypes.Contains (typeName)) throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName)); - return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaComplexType); + return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType); } public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element) { if (schemas == null) throw new ArgumentNullException ("schemas"); + if (element == null) + throw new ArgumentNullException ("element"); if (!schemas.IsCompiled) schemas.Compile (); - return CanImport (schemas, element.ElementSchemaType as XmlSchemaComplexType); + return CanImport (schemas, element.ElementSchemaType as XmlSchemaType); + } + + +#if true // new + bool CanImport (XmlSchemaSet schemas, XmlSchemaType type) + { + if (IsPredefinedType (type.QualifiedName)) + return true; + + var st = type as XmlSchemaSimpleType; + if (st != null) { + return CanImportSimpleType (schemas, st); + } else { + var ct = (XmlSchemaComplexType) type; + var sc = ct.ContentModel as XmlSchemaSimpleContent; + if (sc != null) { + if (sc.Content is XmlSchemaSimpleContentExtension) + return false; + } + if (!CanImportComplexType (schemas, ct)) + return false; + return true; + } } + bool CanImportSimpleType (XmlSchemaSet schemas, XmlSchemaSimpleType type) + { + var scl = type.Content as XmlSchemaSimpleTypeList; + if (scl != null) { + if (scl.ItemType == null) + return false; + var itemType = scl.ItemType as XmlSchemaSimpleType; + var ir = itemType.Content as XmlSchemaSimpleTypeRestriction; + if (ir == null) + return false; + return true; // as enum + } + var scr = type.Content as XmlSchemaSimpleTypeRestriction; + if (scr != null) + return true; // as enum + + return false; + } + + bool CanImportComplexType (XmlSchemaSet schemas, XmlSchemaComplexType type) + { + foreach (XmlSchemaAttribute att in type.AttributeUses.Values) + if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace) + return false; + + CodeTypeReference baseClrType = null; + var particle = type.Particle; + if (type.ContentModel != null) { + var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction; + if (xsscr != null) { + if (xsscr.BaseType != null) { + if (!CanImport (schemas, xsscr.BaseType)) + return false; + } else { + if (!CanImport (schemas, xsscr.BaseTypeName)) + return false; + } + // The above will result in an error, but make sure to show we don't support it. + return false; + } + var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension; + if (xscce != null) { + if (!CanImport (schemas, xscce.BaseTypeName)) + return false; + baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false); + + var baseInfo = GetTypeInfo (xscce.BaseTypeName, false); + particle = xscce.Particle; + } + var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction; + if (xsccr != null) + return false; + } + + var seq = particle as XmlSchemaSequence; + if (seq == null && particle != null) + return false; + + if (seq != null) { + + if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) { + + // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check. + + } else { + + foreach (var child in seq.Items) + if (!(child is XmlSchemaElement)) + return false; + + bool isDictionary = false; + if (type.Annotation != null) { + foreach (var ann in type.Annotation.Items) { + var ai = ann as XmlSchemaAppInfo; + if (ai != null && ai.Markup != null && + ai.Markup.Length > 0 && + ai.Markup [0].NodeType == XmlNodeType.Element && + ai.Markup [0].LocalName == "IsDictionary" && + ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace) + isDictionary = true; + } + } + + if (seq.Items.Count == 1) { + var pt = (XmlSchemaParticle) seq.Items [0]; + var xe = pt as XmlSchemaElement; + if (pt.MaxOccursString == "unbounded") { + // import as a collection contract. + if (pt is XmlSchemaAny) { + } else if (isDictionary) { + var kvt = xe.ElementSchemaType as XmlSchemaComplexType; + var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null; + var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null; + var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null; + if (k == null || v == null) + return false; + if (!CanImport (schemas, k.ElementSchemaType)) + return false; + if (!CanImport (schemas, v.ElementSchemaType)) + return false; + + return true; + } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace && + IsPredefinedType (xe.ElementSchemaType.QualifiedName)) { + // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead. + return true; + } + else + if (!CanImport (schemas, xe.ElementSchemaType)) + return false; + return true; + } + } + if (isDictionary) + return false; + + // import as a (normal) contract. + var elems = new List (); + foreach (XmlSchemaElement xe in seq.Items) { + if (xe.MaxOccurs != 1) + return false; + + if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name)) + return false; + + elems.Add (xe); + } + foreach (var xe in elems) { + // import property type in prior. + if (!CanImport (schemas, xe.ElementSchemaType.QualifiedName)) + return false; + } + + } // if (seq contains only an xs:any) + } // if (seq != 0) + + return true; + } +#else bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type) { if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported. @@ -148,6 +376,7 @@ namespace System.Runtime.Serialization return true; } +#endif // Import @@ -210,6 +439,12 @@ namespace System.Runtime.Serialization break; } + if (!CanImport (schemas, element) && Options != null && Options.ImportXmlType) { + var qn = element.QualifiedName; + GenerateXmlType (qn); + return qn; + } + // FIXME: use element to fill nillable and arrays. var qname = element.SchemaType != null ? element.QualifiedName : element.ElementSchemaType.QualifiedName; Import (schemas, element.ElementSchemaType, qname); @@ -218,6 +453,10 @@ namespace System.Runtime.Serialization void Import (XmlSchemaSet schemas, XmlSchemaType type) { + if (!CanImport (schemas, type) && Options != null && Options.ImportXmlType) { + GenerateXmlType (type.QualifiedName); + return; + } Import (schemas, type, type.QualifiedName); } @@ -247,7 +486,7 @@ namespace System.Runtime.Serialization { CodeNamespace cns = null; CodeTypeReference clrRef; - cns = GetCodeNamespace (qname); + cns = GetCodeNamespace (qname.Namespace); clrRef = new CodeTypeReference (cns.Name.Length > 0 ? cns.Name + "." + qname.Name : qname.Name); var td = new CodeTypeDeclaration () { @@ -393,7 +632,6 @@ namespace System.Runtime.Serialization } // Returns false if it should remove the imported type. - // FIXME: also support ImportXmlType bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname) { foreach (XmlSchemaAttribute att in type.AttributeUses.Values) @@ -560,11 +798,11 @@ namespace System.Runtime.Serialization return false; } - CodeNamespace GetCodeNamespace (XmlQualifiedName name) + CodeNamespace GetCodeNamespace (string xmlns) { string ns = null; - if (Options == null || !Options.Namespaces.TryGetValue (name.Namespace, out ns)) - ns = GetCodeNamespaceFromXmlns (name.Namespace); + if (Options == null || !Options.Namespaces.TryGetValue (xmlns, out ns)) + ns = GetCodeNamespaceFromXmlns (xmlns); foreach (CodeNamespace cns in CodeCompileUnit.Namespaces) if (cns.Name == ns) diff --git a/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34.xsd b/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34.xsd new file mode 100644 index 00000000000..f8de5b318c9 --- /dev/null +++ b/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34_2.xsd b/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34_2.xsd new file mode 100644 index 00000000000..3e90f6b3846 --- /dev/null +++ b/mcs/class/System.Runtime.Serialization/Test/Resources/Schemas/ns34_2.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XsdDataContractImporterTest.cs b/mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XsdDataContractImporterTest.cs index b20fedd4103..e5423fc0826 100644 --- a/mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XsdDataContractImporterTest.cs +++ b/mcs/class/System.Runtime.Serialization/Test/System.Runtime.Serialization/XsdDataContractImporterTest.cs @@ -45,6 +45,8 @@ using System.Xml.Serialization; using Microsoft.CSharp; using NUnit.Framework; +using QName = System.Xml.XmlQualifiedName; + namespace MonoTests.System.Runtime.Serialization { [TestFixture] @@ -165,7 +167,6 @@ namespace MonoTests.System.Runtime.Serialization [Test] [ExpectedException (typeof (ArgumentNullException))] - [Category ("NotWorking")] public void CanImportNullTest1 () { XsdDataContractImporter xsdi = GetImporter (); @@ -173,7 +174,6 @@ namespace MonoTests.System.Runtime.Serialization } [Test] - [Category ("NotWorking")] [ExpectedException (typeof (ArgumentNullException))] public void CanImportNullTest2 () { @@ -182,7 +182,6 @@ namespace MonoTests.System.Runtime.Serialization } [Test] - [Category ("NotWorking")] [ExpectedException (typeof (ArgumentNullException))] public void CanImportNullTest3 () { @@ -410,9 +409,16 @@ namespace MonoTests.System.Runtime.Serialization } CodeCompileUnit DoImport (params string [] schemaFiles) + { + return DoImport (false, schemaFiles); + } + + CodeCompileUnit DoImport (bool xmlType, params string [] schemaFiles) { var ccu = new CodeCompileUnit (); var xdi = new XsdDataContractImporter (ccu); + if (xmlType) + xdi.Options = new ImportOptions () { ImportXmlType = true }; var xss = new XmlSchemaSet (); foreach (var schemaFile in schemaFiles) xss.Add (null, schemaFile); @@ -421,6 +427,13 @@ namespace MonoTests.System.Runtime.Serialization return ccu; } + string GenerateCode (CodeCompileUnit ccu) + { + var sw = new StringWriter (); + new CSharpCodeProvider ().GenerateCodeFromCompileUnit (ccu, sw, null); + return sw.ToString (); + } + // FIXME: this set of tests need further assertion in each test case. Right now it just checks if things import or fail just fine. [Test] @@ -429,18 +442,37 @@ namespace MonoTests.System.Runtime.Serialization DoImport ("Test/Resources/Schemas/ns0.xsd"); } + [Test] + public void ImportTestX0_2 () + { + var ccu = DoImport (true, "Test/Resources/Schemas/ns0.xsd"); + Assert.IsTrue (GenerateCode (ccu).IndexOf ("class") < 0, "#1"); + } + [Test] public void ImportTestX1 () { DoImport ("Test/Resources/Schemas/ns1.xsd"); } + [Test] + public void ImportTestX1_2 () + { + Assert.AreEqual (GenerateCode (DoImport ("Test/Resources/Schemas/ns1.xsd")), GenerateCode (DoImport (true, "Test/Resources/Schemas/ns1.xsd")), "#1"); + } + [Test] public void ImportTestX2 () { DoImport ("Test/Resources/Schemas/ns2.xsd"); } + [Test] + public void ImportTestX2_2 () + { + Assert.AreEqual (GenerateCode (DoImport ("Test/Resources/Schemas/ns2.xsd")), GenerateCode (DoImport (true, "Test/Resources/Schemas/ns2.xsd")), "#1"); + } + [Test] [ExpectedException (typeof (InvalidDataContractException))] public void ImportTestX3 () @@ -448,6 +480,19 @@ namespace MonoTests.System.Runtime.Serialization DoImport ("Test/Resources/Schemas/ns3.xsd"); } + [Test] + [Category ("NotWorking")] + public void ImportTestX3_2 () + { + var ccu = DoImport (true, "Test/Resources/Schemas/ns3.xsd"); + var code = GenerateCode (ccu); + Assert.IsTrue (code.IndexOf ("class T2") > 0, "#1"); + Assert.IsTrue (code.IndexOf ("IXmlSerializable") > 0, "#2"); + Assert.IsTrue (code.IndexOf ("WriteXml") > 0, "#3"); + Assert.IsTrue (code.IndexOf ("XmlRootAttribute(ElementName=\"E2\", Namespace=\"urn:bar\"") > 0, "#4"); + Assert.IsTrue (code.IndexOf ("XmlSchemaProviderAttribute(\"ExportSchema\")") > 0, "#5"); + } + [Test] [ExpectedException (typeof (InvalidDataContractException))] public void ImportTestX4 () @@ -641,7 +686,13 @@ namespace MonoTests.System.Runtime.Serialization [Test] public void ImportTestX33 () { - DoImport ("Test/Resources/Schemas/ns32.xsd"); + DoImport ("Test/Resources/Schemas/ns33.xsd"); + } + + [Test] + public void ImportTestX34 () + { + DoImport (true, "Test/Resources/Schemas/ns34.xsd", "Test/Resources/Schemas/ns34_2.xsd"); } /* Helper methods */ -- 2.25.1