// // System.Runtime.Remoting.MetadataServices.MetaDataCodeGenerator // // Authors: // Lluis Sanchez Gual (lluis@ximian.com) // // (C) 2003 Novell, Inc // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Collections; using System.IO; using System.Xml; using System.Reflection; using System.Runtime.Remoting; using System.Runtime.Remoting.Metadata; namespace System.Runtime.Remoting.MetadataServices { internal class MetaDataCodeGenerator { XmlDocument doc; CodeFile currentFile; XmlNamespaceManager nsManager; Hashtable sudsTypes; public void GenerateCode (bool clientProxy, string outputDirectory, Stream inputStream, ArrayList outCodeStreamList, string proxyUrl, string proxyNamespace) { doc = new XmlDocument (); doc.Load (inputStream); nsManager = new XmlNamespaceManager (doc.NameTable); nsManager.AddNamespace ("wsdl", MetaData.WsdlNamespace); nsManager.AddNamespace ("s", MetaData.SchemaNamespace); nsManager.AddNamespace ("suds", MetaData.SudsNamespace); if (outputDirectory == null) outputDirectory = Directory.GetCurrentDirectory(); CodeFile mainFile = new CodeFile (outputDirectory); currentFile = mainFile; // Suds types sudsTypes = new Hashtable (); XmlNodeList nodes = doc.DocumentElement.SelectNodes ("wsdl:binding/suds:class|wsdl:binding/suds:interface|wsdl:binding/suds:struct", nsManager); foreach (XmlElement node in nodes) sudsTypes [GetTypeQualifiedName (node, node.GetAttribute ("type"))] = node; // Data types nodes = doc.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager); foreach (XmlElement schema in nodes) GenerateSchemaCode (schema); // Services nodes = doc.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager); foreach (XmlElement port in nodes) GeneratePortCode (port); mainFile.Write (); if (mainFile.FileName != null) outCodeStreamList.Add (mainFile.FilePath); } void GeneratePortCode (XmlElement port) { XmlElement binding = GetBinding (port.GetAttribute ("binding")); XmlElement type = null; foreach (XmlNode node in binding) if ((node is XmlElement) && ((XmlElement)node).NamespaceURI == MetaData.SudsNamespace) { type = (XmlElement) node; break; } string rootType = type.GetAttribute ("rootType"); if (rootType == "Delegate") GenerateServiceDelegateCode (port, binding, type); else GenerateServiceClassCode (port, binding, type); } void GenerateServiceDelegateCode (XmlElement port, XmlElement binding, XmlElement type) { string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name"); string portName = GetNameFromQn (binding.GetAttribute ("type")); string name, ns; GetTypeQualifiedName (port, typeName, out name, out ns); currentFile.SetCurrentNamespace (ns); XmlElement oper = (XmlElement) binding.SelectSingleNode ("wsdl:operation[@name='Invoke']", nsManager); if (oper == null) throw new InvalidOperationException ("Invalid delegate schema"); string parsDec; string returnType; GetParameters (oper, portName, "Invoke", out parsDec, out returnType); currentFile.WriteLine ("public delegate " + returnType + " " + name + " (" + parsDec + ");"); currentFile.WriteLine (""); } void GenerateServiceClassCode (XmlElement port, XmlElement binding, XmlElement type) { string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name"); string name, ns; GetTypeQualifiedName (port, typeName, out name, out ns); currentFile.SetCurrentNamespace (ns); string cls = "public " + type.LocalName + " " + name; string baset = type.GetAttribute ("extends"); if (baset != "") cls += ": " + GetTypeQualifiedName (port, baset); // Interfaces XmlNodeList interfaces = type.SelectNodes ("suds:implements",nsManager); if (interfaces.Count == 0) interfaces = type.SelectNodes ("suds:extends",nsManager); foreach (XmlElement interf in interfaces) { string iname = GetTypeQualifiedName (interf, interf.GetAttribute ("type")); if (cls.IndexOf (':') == -1) cls += ": " + iname; else cls += ", " + iname; } currentFile.WriteLine (cls); currentFile.WriteLineInd ("{"); string portName = GetNameFromQn (binding.GetAttribute ("type")); bool isInterface = type.LocalName == "interface"; string vis = isInterface? "":"public "; ArrayList mets = GetMethods (portName, binding); foreach (MethodData met in mets) { if (met.IsProperty) { string prop = vis + met.ReturnType + " "; if (met.Signature != "") prop += "this [" + met.Signature + "]"; else prop += met.Name; if (isInterface) { prop += " { "; if (met.HasGet) prop += "get; "; if (met.HasSet) prop += "set; "; prop += "}"; currentFile.WriteLine (prop); } else { currentFile.WriteLine (prop); currentFile.WriteLineInd ("{"); if (met.HasGet) currentFile.WriteLine ("get { throw new NotImplementedException (); }"); if (met.HasSet) currentFile.WriteLine ("set { throw new NotImplementedException (); }"); currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); } } else { currentFile.WriteLine (vis + met.ReturnType + " " + met.Name + " (" + met.Signature + ")" + (isInterface?";":"")); if (!isInterface) { currentFile.WriteLineInd ("{"); currentFile.WriteLine ("throw new NotImplementedException ();"); currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); } } } currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); } class MethodData { public string ReturnType; public string Signature; public string Name; public bool HasSet; public bool HasGet; public bool IsProperty { get { return HasGet || HasSet; } } } ArrayList GetMethods (string portName, XmlElement binding) { ArrayList mets = new ArrayList (); XmlNodeList nodes = binding.SelectNodes ("wsdl:operation", nsManager); foreach (XmlElement oper in nodes) { MethodData md = new MethodData (); md.Name = oper.GetAttribute ("name"); GetParameters (oper, portName, md.Name, out md.Signature, out md.ReturnType); if (md.Name.StartsWith ("set_") || md.Name.StartsWith ("get_")) { string tmp = ", " + md.Signature; if (tmp.IndexOf (", out ") == -1 && tmp.IndexOf (", ref ") == -1) { bool isSet = md.Name[0]=='s'; md.Name = md.Name.Substring (4); MethodData previousProp = null; foreach (MethodData fmd in mets) if (fmd.Name == md.Name && fmd.IsProperty) previousProp = fmd; if (previousProp != null) { if (isSet) previousProp.HasSet = true; else { previousProp.HasGet = true; previousProp.Signature = md.Signature; } continue; } else { if (isSet) { md.HasSet = true; md.Signature = ""; } else md.HasGet = true; } } } mets.Add (md); } return mets; } void GetParameters (XmlElement oper, string portName, string operName, out string signature, out string returnType) { returnType = null; XmlElement portType = (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:portType[@name='" + portName + "']", nsManager); XmlElement portOper = (XmlElement) portType.SelectSingleNode ("wsdl:operation[@name='" + operName + "']", nsManager); string[] parNames = portOper.GetAttribute ("parameterOrder").Split (' '); XmlElement inPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:input", nsManager); XmlElement inMsg = FindMessageFromPortMessage (inPortMsg); XmlElement outPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:output", nsManager); XmlElement outMsg = FindMessageFromPortMessage (outPortMsg); string[] parameters; if (parNames [0] != "") parameters = new string [parNames.Length]; else parameters = new string [0]; foreach (XmlElement part in inMsg.SelectNodes ("wsdl:part",nsManager)) { int i = Array.IndexOf (parNames, part.GetAttribute ("name")); string type = GetTypeQualifiedName (part, part.GetAttribute ("type")); parameters [i] = type + " " + parNames [i]; } foreach (XmlElement part in outMsg.SelectNodes ("wsdl:part",nsManager)) { string pn = part.GetAttribute ("name"); string type = GetTypeQualifiedName (part, part.GetAttribute ("type")); if (pn == "return") returnType = type; else { int i = Array.IndexOf (parNames, pn); if (parameters [i] != null) parameters [i] = "ref " + parameters [i]; else parameters [i] = "out " + type + " " + pn; } } signature = string.Join (", ", parameters); if (returnType == null) returnType = "void"; } XmlElement FindMessageFromPortMessage (XmlElement portMsg) { string msgName = portMsg.GetAttribute ("message"); msgName = GetNameFromQn (msgName); return (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:message[@name='" + msgName + "']", nsManager); } void GenerateSchemaCode (XmlElement schema) { string ns = schema.GetAttribute ("targetNamespace"); string clrNs = DecodeNamespace (ns); currentFile.SetCurrentNamespace (clrNs); foreach (XmlNode node in schema) { XmlElement elem = node as XmlElement; if (elem == null) continue; if (elem.LocalName == "complexType") GenerateClassCode (ns, elem); else if (elem.LocalName == "simpleType") GenerateEnumCode (ns, elem); } } void GenerateClassCode (string ns, XmlElement elem) { if (elem.SelectSingleNode ("s:complexContent/s:restriction", nsManager) != null) return; string clrNs = DecodeNamespace (ns); string typeName = GetTypeName (elem.GetAttribute ("name"), ns); XmlElement sudsType = (XmlElement) sudsTypes [clrNs + "." + typeName]; string typetype = "class"; if (sudsType != null) typetype = sudsType.LocalName; currentFile.WriteLine ("[Serializable, SoapType (XmlNamespace = @\"" + ns + "\", XmlTypeNamespace = @\"" + ns + "\")]"); string cls = "public " + typetype + " " + typeName; string baseType = elem.GetAttribute ("base"); if (baseType != "") cls += ": " + GetTypeQualifiedName (elem, baseType); bool isSerializable = (sudsType.GetAttribute ("rootType") == "ISerializable"); if (isSerializable) { if (cls.IndexOf (':') == -1) cls += ": "; else cls += ", "; cls += "System.Runtime.Serialization.ISerializable"; } currentFile.WriteLine (cls); currentFile.WriteLineInd ("{"); XmlNodeList elems = elem.GetElementsByTagName ("element", MetaData.SchemaNamespace); foreach (XmlElement elemField in elems) WriteField (elemField); elems = elem.GetElementsByTagName ("attribute", MetaData.SchemaNamespace); foreach (XmlElement elemField in elems) WriteField (elemField); if (isSerializable) { currentFile.WriteLine (""); currentFile.WriteLine ("public " + typeName + " ()"); currentFile.WriteLineInd ("{"); currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); currentFile.WriteLine ("public " + typeName + " (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)"); currentFile.WriteLineInd ("{"); currentFile.WriteLine ("throw new NotImplementedException ();"); currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); currentFile.WriteLine ("public void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)"); currentFile.WriteLineInd ("{"); currentFile.WriteLine ("throw new NotImplementedException ();"); currentFile.WriteLineUni ("}"); } currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); } void WriteField (XmlElement elemField) { bool isAttr = elemField.LocalName == "attribute"; string type = elemField.GetAttribute ("type"); if (isAttr) currentFile.WriteLine ("[SoapField (UseAttribute = true)]"); else if (!IsPrimitive (elemField, type)) currentFile.WriteLine ("[SoapField (Embedded = true)]"); currentFile.WriteLine ("public " + GetTypeQualifiedName (elemField, type) + " " + elemField.GetAttribute ("name") + ";"); } void GenerateEnumCode (string ns, XmlElement elem) { currentFile.WriteLine ("public enum " + GetTypeName (elem.GetAttribute ("name"), ns)); currentFile.WriteLineInd ("{"); XmlNodeList nodes = elem.SelectNodes ("s:restriction/s:enumeration/@value", nsManager); foreach (XmlNode node in nodes) currentFile.WriteLine (node.Value + ","); currentFile.WriteLineUni ("}"); currentFile.WriteLine (""); } bool IsPrimitive (XmlNode node, string qname) { string name = GetTypeQualifiedName (node, qname); return name.IndexOf ('.') == -1; } string GetTypeName (string localName, string ns) { return localName; } void GetTypeQualifiedName (XmlNode node, string qualifiedName, out string name, out string ns) { int i = qualifiedName.IndexOf (':'); if (i == -1) { name = qualifiedName; ns = ""; return; } string prefix = qualifiedName.Substring (0,i); name = qualifiedName.Substring (i+1); ns = node.GetNamespaceOfPrefix (prefix); string arrayType = GetArrayType (node, name, ns); if (arrayType != null) { name = arrayType; ns = ""; } else if (ns != MetaData.SchemaNamespace) { ns = DecodeNamespace (ns); } else { ns = ""; name = GetClrFromXsd (name); } } string GetClrFromXsd (string type) { switch (type) { case "boolean": return "bool"; case "unsignedByte": return "byte"; case "char": return "char"; case "dateTime": return "DateTime"; case "decimal": return "decimal"; case "double": return "double"; case "short": return "short"; case "int": return "int"; case "long": return "long"; case "byte": return "sbyte"; case "float": return "float"; case "unsignedShort": return "ushort"; case "unsignedInt": return "uint"; case "unsignedLong": return "ulong"; case "string": return "string"; case "duration": return "TimeSpan"; case "anyType": return "object"; } throw new InvalidOperationException ("Unknown schema type: " + type); } string GetTypeQualifiedName (XmlNode node, string qualifiedName) { string name, ns; GetTypeQualifiedName (node, qualifiedName, out name, out ns); if (ns != "") return ns + "." + name; else return name; } string GetTypeNamespace (XmlNode node, string qualifiedName) { string name, ns; GetTypeQualifiedName (node, qualifiedName, out name, out ns); return ns; } string GetArrayType (XmlNode node, string name, string ns) { XmlNode anod = doc.SelectSingleNode ("wsdl:definitions/wsdl:types/s:schema[@targetNamespace='" + ns + "']/s:complexType[@name='" + name + "']/s:complexContent/s:restriction/s:attribute/@wsdl:arrayType", nsManager); if (anod == null) return null; string atype = anod.Value; int i = atype.IndexOf ('['); string itemType = GetTypeQualifiedName (node, atype.Substring (0,i)); return itemType + atype.Substring (i); } XmlElement GetBinding (string name) { int i = name.IndexOf (':'); name = name.Substring (i+1); return doc.SelectSingleNode ("wsdl:definitions/wsdl:binding[@name='" + name + "']", nsManager) as XmlElement; } string DecodeNamespace (string xmlNamespace) { string tns, tasm; if (!SoapServices.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace, out tns, out tasm)) tns = xmlNamespace; return tns; } string GetLiteral (object ob) { if (ob == null) return "null"; if (ob is string) return "\"" + ob.ToString().Replace("\"","\"\"") + "\""; if (ob is bool) return ((bool)ob) ? "true" : "false"; if (ob is XmlQualifiedName) { XmlQualifiedName qn = (XmlQualifiedName)ob; return "new XmlQualifiedName (" + GetLiteral(qn.Name) + "," + GetLiteral(qn.Namespace) + ")"; } else return ob.ToString (); } string Params (params string[] pars) { string res = ""; foreach (string p in pars) { if (res != "") res += ", "; res += p; } return res; } string GetNameFromQn (string qn) { int i = qn.IndexOf (':'); if (i == -1) return qn; else return qn.Substring (i+1); } } class CodeFile { public string FileName; public string Directory; public string FilePath; Hashtable namespaces = new Hashtable (); public StringWriter writer; int indent; string currentNamespace; public CodeFile (string directory) { Directory = directory; } public void SetCurrentNamespace (string ns) { writer = namespaces [ns] as StringWriter; if (writer == null) { indent = 0; writer = new StringWriter (); namespaces [ns] = writer; WriteLine ("namespace " + ns); WriteLineInd ("{"); } indent = 1; if (FileName == null) FileName = ns + ".cs"; } public void WriteLineInd (string code) { WriteLine (code); indent++; } public void WriteLineUni (string code) { if (indent > 0) indent--; WriteLine (code); } public void WriteLine (string code) { if (code != "") writer.Write (new String ('\t',indent)); writer.WriteLine (code); } public void Write () { if (FileName == null) return; FilePath = Path.Combine (Directory, FileName); StreamWriter sw = new StreamWriter (FilePath); sw.WriteLine ("using System;"); sw.WriteLine ("using System.Runtime.Remoting.Metadata;"); sw.WriteLine (); foreach (StringWriter nsWriter in namespaces.Values) { sw.Write (nsWriter.ToString ()); sw.WriteLine ("}"); sw.WriteLine (); } sw.Close (); } } }