// // System.Runtime.Remoting.MetadataServices.MetaDataExporter // // Authors: // Lluis Sanchez Gual (lluis@ximian.com) // // (C) 2003 Novell, Inc // using System.Collections; using System.IO; using System.Text; using System.Xml; using System.Reflection; using System.Net; using System.Runtime.Remoting; using System.Runtime.Remoting.Metadata; using System.Runtime.Serialization; namespace System.Runtime.Remoting.MetadataServices { internal class MetaDataExporter { public void ExportTypes (ServiceType[] servicetypes, SdlType sdltype, XmlTextWriter tw) { if (sdltype == SdlType.Sdl) // Obsolete, we don't support this throw new NotSupportedException (); if (servicetypes.Length == 0) return; Type maint = servicetypes [0].ObjectType; Hashtable dataTypes = new Hashtable (); ArrayList services = new ArrayList (); FindTypes (servicetypes, dataTypes, services); if (services.Count > 0) maint = ((ServiceType) services[0]).ObjectType; string serviceNs = GetXmlNamespace (maint, null); tw.Formatting = Formatting.Indented; tw.WriteStartElement ("definitions", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", maint.Name); tw.WriteAttributeString ("targetNamespace", serviceNs); tw.WriteAttributeString ("xmlns", MetaData.XmlnsNamespace, MetaData.WsdlNamespace); tw.WriteAttributeString ("xmlns", "tns", MetaData.XmlnsNamespace, serviceNs); tw.WriteAttributeString ("xmlns", "xsd", MetaData.XmlnsNamespace, MetaData.SchemaNamespace); tw.WriteAttributeString ("xmlns", "xsi", MetaData.XmlnsNamespace, MetaData.SchemaInstanceNamespace); tw.WriteAttributeString ("xmlns", "suds", MetaData.XmlnsNamespace, MetaData.SudsNamespace); tw.WriteAttributeString ("xmlns", "wsdl", MetaData.XmlnsNamespace, MetaData.WsdlNamespace); tw.WriteAttributeString ("xmlns", "soapenc", MetaData.XmlnsNamespace, MetaData.SoapEncodingNamespace); tw.WriteAttributeString ("xmlns", "soap", MetaData.XmlnsNamespace, MetaData.SoapNamespace); int nums = 0; foreach (DictionaryEntry entry in dataTypes) { string ns = (string) entry.Key; if (tw.LookupPrefix (ns) != null) continue; tw.WriteAttributeString ("xmlns", "ns"+nums, MetaData.XmlnsNamespace, ns); nums++; } // Schema if (dataTypes.Count > 0) { tw.WriteStartElement ("types", MetaData.WsdlNamespace); foreach (DictionaryEntry entry in dataTypes) { SchemaInfo sinfo = (SchemaInfo) entry.Value; if (sinfo == null || sinfo.Types.Count == 0) continue; tw.WriteStartElement ("s", "schema", MetaData.SchemaNamespace); tw.WriteAttributeString ("targetNamespace", (string) entry.Key); tw.WriteAttributeString ("elementFormDefault", "unqualified"); tw.WriteAttributeString ("attributeFormDefault", "unqualified"); foreach (string ns in sinfo.Imports) { if (ns == (string) entry.Key) continue; tw.WriteStartElement ("import", MetaData.SchemaNamespace); tw.WriteAttributeString ("namespace", ns); tw.WriteEndElement (); } foreach (Type type in sinfo.Types) WriteDataTypeSchema (tw, type); tw.WriteEndElement (); } tw.WriteEndElement (); } // Bindings /* foreach (ServiceType st in servicetypes) WriteServiceBinding (tw, st); */ foreach (ServiceType st in services) WriteServiceBinding (tw, st, dataTypes); // Service element tw.WriteStartElement ("service", MetaData.WsdlNamespace); if (services.Count > 0) tw.WriteAttributeString ("name", GetServiceName (maint)); else tw.WriteAttributeString ("name", "Service"); foreach (ServiceType st in services) { WriteServiceType (tw, st); } tw.WriteEndElement (); // Closing tw.WriteEndElement (); tw.Flush (); } void WriteServiceType (XmlTextWriter tw, ServiceType st) { tw.WriteStartElement ("port", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", GetPortName (st.ObjectType)); tw.WriteAttributeString ("binding", "tns:" + GetBindingName (st.ObjectType)); if (st.Url != null) { tw.WriteStartElement ("soap","address", MetaData.SoapNamespace); tw.WriteAttributeString ("location", st.Url); tw.WriteEndElement (); } tw.WriteEndElement (); } void WriteServiceBinding (XmlTextWriter tw, ServiceType st, Hashtable dataTypes) { Type type = st.ObjectType; string typeName = type.Name; MethodInfo[] mets = type.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); bool isService = IsService (type); // Messages if (isService) { foreach (MethodInfo met in mets) { if (met.DeclaringType.Assembly == typeof(object).Assembly) continue; ParameterInfo[] pars = met.GetParameters (); tw.WriteStartElement ("message", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", typeName + "." + met.Name + "Input"); foreach (ParameterInfo par in pars) { if (!par.ParameterType.IsByRef) WritePart (tw, par.Name, par.ParameterType, type); } tw.WriteEndElement (); // message tw.WriteStartElement ("message", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", typeName + "." + met.Name + "Output"); if (met.ReturnType != typeof(void)) WritePart (tw, "return", met.ReturnType, type); foreach (ParameterInfo par in pars) { if (par.ParameterType.IsByRef || par.IsOut) WritePart (tw, par.Name, par.ParameterType, type); } tw.WriteEndElement (); // message } } // Port type tw.WriteStartElement ("portType", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", typeName + "PortType"); if (isService) { foreach (MethodInfo met in mets) { if (met.DeclaringType.Assembly == typeof(object).Assembly) continue; tw.WriteStartElement ("operation", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name); StringBuilder sb = new StringBuilder (); ParameterInfo[] pars = met.GetParameters (); foreach (ParameterInfo par in pars) { if (sb.Length != 0) sb.Append (" "); sb.Append (par.Name); } tw.WriteAttributeString ("parameterOrder", sb.ToString ()); tw.WriteStartElement ("input", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name + "Request"); tw.WriteAttributeString ("message", "tns:" + typeName + "." + met.Name + "Input"); tw.WriteEndElement (); tw.WriteStartElement ("output", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name + "Response"); tw.WriteAttributeString ("message", "tns:" + typeName + "." + met.Name + "Output"); tw.WriteEndElement (); tw.WriteEndElement (); // operation } } tw.WriteEndElement (); // portType // Binding tw.WriteStartElement ("binding", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", typeName + "Binding"); tw.WriteAttributeString ("type", "tns:" + typeName + "PortType"); tw.WriteStartElement ("soap", "binding", MetaData.SoapNamespace); tw.WriteAttributeString ("style", "rpc"); tw.WriteAttributeString ("transport", "http://schemas.xmlsoap.org/soap/http"); tw.WriteEndElement (); WriteTypeSuds (tw, type); SchemaInfo sinfo = (SchemaInfo) dataTypes [GetXmlNamespace (type,null)]; if (sinfo != null && !sinfo.SudsGenerated) { foreach (Type dt in sinfo.Types) WriteTypeSuds (tw, dt); sinfo.SudsGenerated = true; } if (isService) { foreach (MethodInfo met in mets) { if (met.DeclaringType.Assembly == typeof(object).Assembly) continue; tw.WriteStartElement ("operation", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name); tw.WriteStartElement ("soap", "operation", MetaData.SoapNamespace); tw.WriteAttributeString ("soapAction", GetSoapAction (met)); tw.WriteEndElement (); tw.WriteStartElement ("suds", "method", MetaData.SudsNamespace); tw.WriteAttributeString ("attributes", "public"); tw.WriteEndElement (); tw.WriteStartElement ("input", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name + "Request"); WriteMessageBindingBody (tw, type); tw.WriteEndElement (); tw.WriteStartElement ("output", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", met.Name + "Response"); WriteMessageBindingBody (tw, type); tw.WriteEndElement (); tw.WriteEndElement (); // operation } } tw.WriteEndElement (); // binding } void WriteTypeSuds (XmlTextWriter tw, Type type) { if (type.IsArray || type.IsEnum) { return; } else if (type.IsInterface) { tw.WriteStartElement ("suds", "interface", MetaData.SudsNamespace); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, type, null)); foreach (Type interf in type.GetInterfaces ()) { tw.WriteStartElement ("suds","extends", MetaData.SudsNamespace); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, interf, null)); tw.WriteEndElement (); } } else if (type.IsValueType) { tw.WriteStartElement ("suds", "struct", MetaData.SudsNamespace); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, type, null)); if (type.BaseType != typeof(ValueType)) tw.WriteAttributeString ("extends", GetQualifiedXmlType (tw, type.BaseType, null)); } else { tw.WriteStartElement ("suds", "class", MetaData.SudsNamespace); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, type, null)); if (IsService (type)) { if (type.IsMarshalByRef) tw.WriteAttributeString ("rootType", "MarshalByRefObject"); else tw.WriteAttributeString ("rootType", "Delegate"); if (type.BaseType != typeof(MarshalByRefObject)) tw.WriteAttributeString ("extends", GetQualifiedXmlType (tw, type.BaseType, null)); if (type.IsMarshalByRef) { foreach (Type interf in type.GetInterfaces ()) { tw.WriteStartElement ("suds","implements", MetaData.SudsNamespace); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, interf, null)); tw.WriteEndElement (); } } } else if (typeof(ISerializable).IsAssignableFrom (type)) tw.WriteAttributeString ("rootType", "ISerializable"); } tw.WriteEndElement (); // suds } void WriteMessageBindingBody (XmlTextWriter tw, Type t) { tw.WriteStartElement ("soap", "body", MetaData.SoapNamespace); tw.WriteAttributeString ("use", "encoded"); tw.WriteAttributeString ("encodingStyle", MetaData.SoapEncodingNamespace); tw.WriteAttributeString ("namespace", GetXmlNamespace (t, null)); tw.WriteEndElement (); } void WritePart (XmlTextWriter tw, string name, Type t, Type containerType) { tw.WriteStartElement ("part", MetaData.WsdlNamespace); tw.WriteAttributeString ("name", name); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, t, containerType)); tw.WriteEndElement (); } void WriteDataTypeSchema (XmlTextWriter tw, Type type) { if (type.IsArray) WriteArraySchema (tw, type); else if (type.IsEnum) WriteEnumSchema (tw, type); else WriteClassSchema (tw, type); } void WriteArraySchema (XmlTextWriter tw, Type type) { tw.WriteStartElement ("complexType", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", GetXmlType (type)); tw.WriteStartElement ("complexContent", MetaData.SchemaNamespace); tw.WriteStartElement ("restriction", MetaData.SchemaNamespace); tw.WriteAttributeString ("base", GetQualifiedName (tw, MetaData.SoapEncodingNamespace, "Array")); tw.WriteStartElement ("attribute", MetaData.SchemaNamespace); tw.WriteAttributeString ("ref", GetQualifiedName (tw, MetaData.SoapEncodingNamespace, "arrayType")); string arrayType = ""; while (type.IsArray) { arrayType = arrayType + "[" + new string (',', type.GetArrayRank()-1) + "]"; type = type.GetElementType (); } arrayType = GetQualifiedXmlType (tw, type, null) + arrayType; tw.WriteAttributeString ("wsdl", "arrayType", MetaData.WsdlNamespace, arrayType); tw.WriteEndElement (); // attribute tw.WriteEndElement (); // restriction tw.WriteEndElement (); // complexContent tw.WriteEndElement (); // complexType } void WriteEnumSchema (XmlTextWriter tw, Type type) { tw.WriteStartElement ("simpleType", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", GetXmlType (type)); tw.WriteAttributeString ("suds", "enumType", MetaData.SudsNamespace, GetQualifiedXmlType (tw, type.UnderlyingSystemType, null)); tw.WriteStartElement ("restriction", MetaData.SchemaNamespace); tw.WriteAttributeString ("base", "xsd:string"); foreach (string name in Enum.GetNames (type)) { tw.WriteStartElement ("enumeration", MetaData.SchemaNamespace); tw.WriteAttributeString ("value", name); tw.WriteEndElement (); } tw.WriteEndElement (); // restriction tw.WriteEndElement (); // simpleType } void WriteClassSchema (XmlTextWriter tw, Type type) { tw.WriteStartElement ("element", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", type.Name); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, type, null)); tw.WriteEndElement (); tw.WriteStartElement ("complexType", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", GetXmlType (type)); if (type.BaseType != null && type.BaseType != typeof(object) && type.BaseType != typeof(ValueType)) tw.WriteAttributeString ("base", GetQualifiedXmlType (tw, type.BaseType, null)); FieldInfo[] fields = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); // Element fields bool elemsStart = false; foreach (FieldInfo fi in fields) { SoapFieldAttribute att = (SoapFieldAttribute) InternalRemotingServices.GetCachedSoapAttribute (fi); if (att.UseAttribute) continue; if (!elemsStart) { tw.WriteStartElement ("all", MetaData.SchemaNamespace); elemsStart = true; } tw.WriteStartElement ("element", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", att.XmlElementName); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, fi.FieldType, type)); tw.WriteEndElement (); } if (elemsStart) tw.WriteEndElement (); // all // Attribute fields foreach (FieldInfo fi in fields) { SoapFieldAttribute att = (SoapFieldAttribute) InternalRemotingServices.GetCachedSoapAttribute (fi); if (!att.UseAttribute) continue; tw.WriteStartElement ("attribute", MetaData.SchemaNamespace); tw.WriteAttributeString ("name", att.XmlElementName); tw.WriteAttributeString ("type", GetQualifiedXmlType (tw, fi.FieldType, type)); tw.WriteEndElement (); } tw.WriteEndElement (); // complexType } ArrayList FindServices (ServiceType[] servicetypes) { ArrayList list = new ArrayList (); foreach (ServiceType st in servicetypes) if (IsService (st.ObjectType)) list.Add (st); return list; } string GetSoapAction (MethodInfo mb) { return SoapServices.GetSoapActionFromMethodBase (mb); } string GetXmlNamespace (Type t, Type containerType) { string name, ns; if (t.IsArray) { return GetXmlNamespace (containerType, null); } if (SoapServices.GetXmlTypeForInteropType (t, out name, out ns)) return ns; SoapTypeAttribute att = (SoapTypeAttribute) InternalRemotingServices.GetCachedSoapAttribute (t); return att.XmlNamespace; } string GetQualifiedName (XmlTextWriter tw, string namspace, string localName) { return tw.LookupPrefix (namspace) + ":" + localName; } string GetQualifiedXmlType (XmlTextWriter tw, Type type, Type containerType) { string name, ns; if (type.IsArray) { name = GetXmlType (type); ns = GetXmlNamespace (type, containerType); } else { name = GetXsdType (type); if (name != null) return "xsd:" + name; if (!SoapServices.GetXmlTypeForInteropType (type, out name, out ns)) { SoapTypeAttribute att = (SoapTypeAttribute) InternalRemotingServices.GetCachedSoapAttribute (type); name = att.XmlTypeName; ns = att.XmlNamespace; } } return GetQualifiedName (tw, ns, name); } string GetXmlType (Type type) { if (type.IsArray) { string itemType = GetXmlType (type.GetElementType ()); itemType = "ArrayOf" + char.ToUpper (itemType[0]) + itemType.Substring (1); if (type.GetArrayRank () > 1) itemType += type.GetArrayRank (); return itemType; } else { string name = null, ns; name = GetXsdType (type); if (name != null) return name; if (SoapServices.GetXmlTypeForInteropType (type, out name, out ns)) return name; SoapTypeAttribute att = (SoapTypeAttribute) InternalRemotingServices.GetCachedSoapAttribute (type); return att.XmlTypeName; } } string GetServiceName (Type t) { return t.Name + "Service"; } string GetPortName (Type t) { return t.Name + "Port"; } string GetBindingName (Type t) { return t.Name + "Binding"; } void FindTypes (ServiceType[] servicetypes, Hashtable dataTypes, ArrayList services) { Hashtable types = new Hashtable (); ArrayList mbrTypes = new ArrayList(); foreach (ServiceType st in servicetypes) FindDataTypes (st.ObjectType, null, dataTypes, mbrTypes); foreach (Type mbrType in mbrTypes) { ServiceType stFound = null; foreach (ServiceType st in servicetypes) if (mbrType == st.ObjectType) stFound = st; if (stFound != null) services.Add (stFound); else services.Add (new ServiceType (mbrType)); } } void FindDataTypes (Type t, Type containerType, Hashtable types, ArrayList services) { if (IsSystemType (t)) { string ns = GetXmlNamespace (t, null); types [ns] = null; return; } if (!IsService (t)) { if (!t.IsSerializable) return; string ns = GetXmlNamespace (t, containerType); SchemaInfo sinfo = (SchemaInfo) types [ns]; if (sinfo == null) { sinfo = new SchemaInfo (); types [ns] = sinfo; } if (sinfo.Types.Contains (t)) return; sinfo.Types.Add (t); if (t.IsArray) return; FieldInfo[] fields = t.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (FieldInfo fi in fields) { string fns = GetXmlNamespace (fi.FieldType, t); if (!sinfo.Imports.Contains (fns)) sinfo.Imports.Add (fns); FindDataTypes (fi.FieldType, t, types, services); } } else { if (services.Contains (t)) return; services.Add (t); foreach (MethodInfo met in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { ParameterInfo[] pars = met.GetParameters (); foreach (ParameterInfo par in pars) FindDataTypes (par.ParameterType, t, types, services); FindDataTypes (met.ReturnType, t, types, services); } } } bool IsService (Type t) { return t.IsMarshalByRef || t.IsInterface || typeof(Delegate).IsAssignableFrom (t); } bool IsSystemType (Type t) { return t.FullName.StartsWith ("System.") && !t.IsArray; } static string GetXsdType (Type type) { if (type.IsEnum) return null; switch (Type.GetTypeCode (type)) { case TypeCode.Boolean: return "boolean"; case TypeCode.Byte: return "unsignedByte"; case TypeCode.Char: return "char"; case TypeCode.DateTime: return "dateTime"; case TypeCode.Decimal: return "decimal"; case TypeCode.Double: return "double"; case TypeCode.Int16: return "short"; case TypeCode.Int32: return "int"; case TypeCode.Int64: return "long"; case TypeCode.SByte: return "byte"; case TypeCode.Single: return "float"; case TypeCode.UInt16: return "unsignedShort"; case TypeCode.UInt32: return "unsignedInt"; case TypeCode.UInt64: return "unsignedLong"; case TypeCode.String: return "string"; } if (type == typeof (TimeSpan)) return "duration"; if (type == typeof (object)) return "anyType"; return null; } } class SchemaInfo { public ArrayList Types = new ArrayList (); public ArrayList Imports = new ArrayList (); public bool SudsGenerated; } }