MetaDataCodeGenerator.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. //
  2. // System.Runtime.Remoting.MetadataServices.MetaDataCodeGenerator
  3. //
  4. // Authors:
  5. // Lluis Sanchez Gual ([email protected])
  6. //
  7. // (C) 2003 Novell, Inc
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.Collections;
  30. using System.IO;
  31. using System.Xml;
  32. using System.Reflection;
  33. using System.Runtime.Remoting;
  34. using System.Runtime.Remoting.Metadata;
  35. namespace System.Runtime.Remoting.MetadataServices
  36. {
  37. internal class MetaDataCodeGenerator
  38. {
  39. XmlDocument doc;
  40. CodeFile currentFile;
  41. XmlNamespaceManager nsManager;
  42. Hashtable sudsTypes;
  43. public void GenerateCode (bool clientProxy, string outputDirectory, Stream inputStream,
  44. ArrayList outCodeStreamList, string proxyUrl, string proxyNamespace)
  45. {
  46. doc = new XmlDocument ();
  47. doc.Load (inputStream);
  48. nsManager = new XmlNamespaceManager (doc.NameTable);
  49. nsManager.AddNamespace ("wsdl", MetaData.WsdlNamespace);
  50. nsManager.AddNamespace ("s", MetaData.SchemaNamespace);
  51. nsManager.AddNamespace ("suds", MetaData.SudsNamespace);
  52. if (outputDirectory == null) outputDirectory = Directory.GetCurrentDirectory();
  53. CodeFile mainFile = new CodeFile (outputDirectory);
  54. CodeFile interopFile = new CodeFile (outputDirectory);
  55. currentFile = mainFile;
  56. // Suds types
  57. sudsTypes = new Hashtable ();
  58. XmlNodeList nodes = doc.DocumentElement.SelectNodes ("wsdl:binding/suds:class|wsdl:binding/suds:interface|wsdl:binding/suds:struct", nsManager);
  59. foreach (XmlElement node in nodes)
  60. sudsTypes [GetTypeQualifiedName (node, node.GetAttribute ("type"))] = node;
  61. // Data types
  62. nodes = doc.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager);
  63. foreach (XmlElement schema in nodes)
  64. GenerateSchemaCode (schema);
  65. // Services
  66. nodes = doc.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager);
  67. foreach (XmlElement port in nodes)
  68. GeneratePortCode (port);
  69. mainFile.Write ();
  70. if (mainFile.FileName != null)
  71. outCodeStreamList.Add (mainFile.FilePath);
  72. }
  73. void GeneratePortCode (XmlElement port)
  74. {
  75. XmlElement binding = GetBinding (port.GetAttribute ("binding"));
  76. XmlElement type = null;
  77. foreach (XmlNode node in binding)
  78. if ((node is XmlElement) && ((XmlElement)node).NamespaceURI == MetaData.SudsNamespace)
  79. { type = (XmlElement) node; break; }
  80. string rootType = type.GetAttribute ("rootType");
  81. if (rootType == "Delegate")
  82. GenerateServiceDelegateCode (port, binding, type);
  83. else
  84. GenerateServiceClassCode (port, binding, type);
  85. }
  86. void GenerateServiceDelegateCode (XmlElement port, XmlElement binding, XmlElement type)
  87. {
  88. string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name");
  89. string portName = GetNameFromQn (binding.GetAttribute ("type"));
  90. string name, ns;
  91. GetTypeQualifiedName (port, typeName, out name, out ns);
  92. currentFile.SetCurrentNamespace (ns);
  93. XmlElement oper = (XmlElement) binding.SelectSingleNode ("wsdl:operation[@name='Invoke']", nsManager);
  94. if (oper == null) throw new InvalidOperationException ("Invalid delegate schema");
  95. string parsDec;
  96. string returnType;
  97. GetParameters (oper, portName, "Invoke", out parsDec, out returnType);
  98. currentFile.WriteLine ("public delegate " + returnType + " " + name + " (" + parsDec + ");");
  99. currentFile.WriteLine ("");
  100. }
  101. void GenerateServiceClassCode (XmlElement port, XmlElement binding, XmlElement type)
  102. {
  103. string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name");
  104. string name, ns;
  105. GetTypeQualifiedName (port, typeName, out name, out ns);
  106. currentFile.SetCurrentNamespace (ns);
  107. string cls = "public " + type.LocalName + " " + name;
  108. string baset = type.GetAttribute ("extends");
  109. if (baset != "") cls += ": " + GetTypeQualifiedName (port, baset);
  110. // Interfaces
  111. XmlNodeList interfaces = type.SelectNodes ("suds:implements",nsManager);
  112. if (interfaces.Count == 0) interfaces = type.SelectNodes ("suds:extends",nsManager);
  113. foreach (XmlElement interf in interfaces)
  114. {
  115. string iname = GetTypeQualifiedName (interf, interf.GetAttribute ("type"));
  116. if (cls.IndexOf (':') == -1) cls += ": " + iname;
  117. else cls += ", " + iname;
  118. }
  119. currentFile.WriteLine (cls);
  120. currentFile.WriteLineInd ("{");
  121. string portName = GetNameFromQn (binding.GetAttribute ("type"));
  122. bool isInterface = type.LocalName == "interface";
  123. string vis = isInterface? "":"public ";
  124. ArrayList mets = GetMethods (portName, binding);
  125. foreach (MethodData met in mets)
  126. {
  127. if (met.IsProperty)
  128. {
  129. string prop = vis + met.ReturnType + " ";
  130. if (met.Signature != "") prop += "this [" + met.Signature + "]";
  131. else prop += met.Name;
  132. if (isInterface)
  133. {
  134. prop += " { ";
  135. if (met.HasGet) prop += "get; ";
  136. if (met.HasSet) prop += "set; ";
  137. prop += "}";
  138. currentFile.WriteLine (prop);
  139. }
  140. else
  141. {
  142. currentFile.WriteLine (prop);
  143. currentFile.WriteLineInd ("{");
  144. if (met.HasGet) currentFile.WriteLine ("get { throw new NotImplementedException (); }");
  145. if (met.HasSet) currentFile.WriteLine ("set { throw new NotImplementedException (); }");
  146. currentFile.WriteLineUni ("}");
  147. currentFile.WriteLine ("");
  148. }
  149. }
  150. else
  151. {
  152. currentFile.WriteLine (vis + met.ReturnType + " " + met.Name + " (" + met.Signature + ")" + (isInterface?";":""));
  153. if (!isInterface)
  154. {
  155. currentFile.WriteLineInd ("{");
  156. currentFile.WriteLine ("throw new NotImplementedException ();");
  157. currentFile.WriteLineUni ("}");
  158. currentFile.WriteLine ("");
  159. }
  160. }
  161. }
  162. currentFile.WriteLineUni ("}");
  163. currentFile.WriteLine ("");
  164. }
  165. class MethodData
  166. {
  167. public string ReturnType;
  168. public string Signature;
  169. public string Name;
  170. public bool HasSet;
  171. public bool HasGet;
  172. public bool IsProperty { get { return HasGet || HasSet; } }
  173. }
  174. ArrayList GetMethods (string portName, XmlElement binding)
  175. {
  176. ArrayList mets = new ArrayList ();
  177. XmlNodeList nodes = binding.SelectNodes ("wsdl:operation", nsManager);
  178. foreach (XmlElement oper in nodes)
  179. {
  180. MethodData md = new MethodData ();
  181. md.Name = oper.GetAttribute ("name");
  182. GetParameters (oper, portName, md.Name, out md.Signature, out md.ReturnType);
  183. if (md.Name.StartsWith ("set_") || md.Name.StartsWith ("get_"))
  184. {
  185. string tmp = ", " + md.Signature;
  186. if (tmp.IndexOf (", out ") == -1 && tmp.IndexOf (", ref ") == -1)
  187. {
  188. bool isSet = md.Name[0]=='s';
  189. md.Name = md.Name.Substring (4);
  190. MethodData previousProp = null;
  191. foreach (MethodData fmd in mets)
  192. if (fmd.Name == md.Name && fmd.IsProperty)
  193. previousProp = fmd;
  194. if (previousProp != null) {
  195. if (isSet) previousProp.HasSet = true;
  196. else { previousProp.HasGet = true; previousProp.Signature = md.Signature; }
  197. continue;
  198. }
  199. else {
  200. if (isSet) { md.HasSet = true; md.Signature = ""; }
  201. else md.HasGet = true;
  202. }
  203. }
  204. }
  205. mets.Add (md);
  206. }
  207. return mets;
  208. }
  209. void GetParameters (XmlElement oper, string portName, string operName, out string signature, out string returnType)
  210. {
  211. returnType = null;
  212. XmlElement portType = (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:portType[@name='" + portName + "']", nsManager);
  213. XmlElement portOper = (XmlElement) portType.SelectSingleNode ("wsdl:operation[@name='" + operName + "']", nsManager);
  214. string[] parNames = portOper.GetAttribute ("parameterOrder").Split (' ');
  215. XmlElement inPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:input", nsManager);
  216. XmlElement inMsg = FindMessageFromPortMessage (inPortMsg);
  217. XmlElement outPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:output", nsManager);
  218. XmlElement outMsg = FindMessageFromPortMessage (outPortMsg);
  219. string[] parameters;
  220. if (parNames [0] != "") parameters = new string [parNames.Length];
  221. else parameters = new string [0];
  222. foreach (XmlElement part in inMsg.SelectNodes ("wsdl:part",nsManager))
  223. {
  224. int i = Array.IndexOf (parNames, part.GetAttribute ("name"));
  225. string type = GetTypeQualifiedName (part, part.GetAttribute ("type"));
  226. parameters [i] = type + " " + parNames [i];
  227. }
  228. foreach (XmlElement part in outMsg.SelectNodes ("wsdl:part",nsManager))
  229. {
  230. string pn = part.GetAttribute ("name");
  231. string type = GetTypeQualifiedName (part, part.GetAttribute ("type"));
  232. if (pn == "return")
  233. returnType = type;
  234. else {
  235. int i = Array.IndexOf (parNames, pn);
  236. if (parameters [i] != null) parameters [i] = "ref " + parameters [i];
  237. else parameters [i] = "out " + type + " " + pn;
  238. }
  239. }
  240. signature = string.Join (", ", parameters);
  241. if (returnType == null) returnType = "void";
  242. }
  243. XmlElement FindMessageFromPortMessage (XmlElement portMsg)
  244. {
  245. string msgName = portMsg.GetAttribute ("message");
  246. msgName = GetNameFromQn (msgName);
  247. return (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:message[@name='" + msgName + "']", nsManager);
  248. }
  249. void GenerateSchemaCode (XmlElement schema)
  250. {
  251. string ns = schema.GetAttribute ("targetNamespace");
  252. string clrNs = DecodeNamespace (ns);
  253. currentFile.SetCurrentNamespace (clrNs);
  254. foreach (XmlNode node in schema)
  255. {
  256. XmlElement elem = node as XmlElement;
  257. if (elem == null) continue;
  258. if (elem.LocalName == "complexType")
  259. GenerateClassCode (ns, elem);
  260. else if (elem.LocalName == "simpleType")
  261. GenerateEnumCode (ns, elem);
  262. }
  263. }
  264. void GenerateClassCode (string ns, XmlElement elem)
  265. {
  266. if (elem.SelectSingleNode ("s:complexContent/s:restriction", nsManager) != null) return;
  267. string clrNs = DecodeNamespace (ns);
  268. string typeName = GetTypeName (elem.GetAttribute ("name"), ns);
  269. XmlElement sudsType = (XmlElement) sudsTypes [clrNs + "." + typeName];
  270. string typetype = "class";
  271. if (sudsType != null) typetype = sudsType.LocalName;
  272. currentFile.WriteLine ("[Serializable, SoapType (XmlNamespace = @\"" + ns + "\", XmlTypeNamespace = @\"" + ns + "\")]");
  273. string cls = "public " + typetype + " " + typeName;
  274. string baseType = elem.GetAttribute ("base");
  275. if (baseType != "") cls += ": " + GetTypeQualifiedName (elem, baseType);
  276. bool isSerializable = (sudsType.GetAttribute ("rootType") == "ISerializable");
  277. if (isSerializable)
  278. {
  279. if (cls.IndexOf (':') == -1) cls += ": ";
  280. else cls += ", ";
  281. cls += "System.Runtime.Serialization.ISerializable";
  282. }
  283. currentFile.WriteLine (cls);
  284. currentFile.WriteLineInd ("{");
  285. XmlNodeList elems = elem.GetElementsByTagName ("element", MetaData.SchemaNamespace);
  286. foreach (XmlElement elemField in elems)
  287. WriteField (elemField);
  288. elems = elem.GetElementsByTagName ("attribute", MetaData.SchemaNamespace);
  289. foreach (XmlElement elemField in elems)
  290. WriteField (elemField);
  291. if (isSerializable)
  292. {
  293. currentFile.WriteLine ("");
  294. currentFile.WriteLine ("public " + typeName + " ()");
  295. currentFile.WriteLineInd ("{");
  296. currentFile.WriteLineUni ("}");
  297. currentFile.WriteLine ("");
  298. currentFile.WriteLine ("public " + typeName + " (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
  299. currentFile.WriteLineInd ("{");
  300. currentFile.WriteLine ("throw new NotImplementedException ();");
  301. currentFile.WriteLineUni ("}");
  302. currentFile.WriteLine ("");
  303. currentFile.WriteLine ("public void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
  304. currentFile.WriteLineInd ("{");
  305. currentFile.WriteLine ("throw new NotImplementedException ();");
  306. currentFile.WriteLineUni ("}");
  307. }
  308. currentFile.WriteLineUni ("}");
  309. currentFile.WriteLine ("");
  310. }
  311. void WriteField (XmlElement elemField)
  312. {
  313. bool isAttr = elemField.LocalName == "attribute";
  314. string type = elemField.GetAttribute ("type");
  315. if (isAttr)
  316. currentFile.WriteLine ("[SoapField (UseAttribute = true)]");
  317. else if (!IsPrimitive (elemField, type))
  318. currentFile.WriteLine ("[SoapField (Embedded = true)]");
  319. currentFile.WriteLine ("public " + GetTypeQualifiedName (elemField, type) + " " + elemField.GetAttribute ("name") + ";");
  320. }
  321. void GenerateEnumCode (string ns, XmlElement elem)
  322. {
  323. currentFile.WriteLine ("public enum " + GetTypeName (elem.GetAttribute ("name"), ns));
  324. currentFile.WriteLineInd ("{");
  325. XmlNodeList nodes = elem.SelectNodes ("s:restriction/s:enumeration/@value", nsManager);
  326. foreach (XmlNode node in nodes)
  327. currentFile.WriteLine (node.Value + ",");
  328. currentFile.WriteLineUni ("}");
  329. currentFile.WriteLine ("");
  330. }
  331. bool IsPrimitive (XmlNode node, string qname)
  332. {
  333. string name = GetTypeQualifiedName (node, qname);
  334. return name.IndexOf ('.') == -1;
  335. }
  336. string GetTypeName (string localName, string ns)
  337. {
  338. return localName;
  339. }
  340. void GetTypeQualifiedName (XmlNode node, string qualifiedName, out string name, out string ns)
  341. {
  342. int i = qualifiedName.IndexOf (':');
  343. if (i == -1)
  344. {
  345. name = qualifiedName;
  346. ns = "";
  347. return;
  348. }
  349. string prefix = qualifiedName.Substring (0,i);
  350. name = qualifiedName.Substring (i+1);
  351. ns = node.GetNamespaceOfPrefix (prefix);
  352. string arrayType = GetArrayType (node, name, ns);
  353. if (arrayType != null) {
  354. name = arrayType;
  355. ns = "";
  356. }
  357. else if (ns != MetaData.SchemaNamespace) {
  358. ns = DecodeNamespace (ns);
  359. }
  360. else {
  361. ns = "";
  362. name = GetClrFromXsd (name);
  363. }
  364. }
  365. string GetClrFromXsd (string type)
  366. {
  367. switch (type)
  368. {
  369. case "boolean": return "bool";
  370. case "unsignedByte": return "byte";
  371. case "char": return "char";
  372. case "dateTime": return "DateTime";
  373. case "decimal": return "decimal";
  374. case "double": return "double";
  375. case "short": return "short";
  376. case "int": return "int";
  377. case "long": return "long";
  378. case "byte": return "sbyte";
  379. case "float": return "float";
  380. case "unsignedShort": return "ushort";
  381. case "unsignedInt": return "uint";
  382. case "unsignedLong": return "ulong";
  383. case "string": return "string";
  384. case "duration": return "TimeSpan";
  385. case "anyType": return "object";
  386. }
  387. throw new InvalidOperationException ("Unknown schema type: " + type);
  388. }
  389. string GetTypeQualifiedName (XmlNode node, string qualifiedName)
  390. {
  391. string name, ns;
  392. GetTypeQualifiedName (node, qualifiedName, out name, out ns);
  393. if (ns != "") return ns + "." + name;
  394. else return name;
  395. }
  396. string GetTypeNamespace (XmlNode node, string qualifiedName)
  397. {
  398. string name, ns;
  399. GetTypeQualifiedName (node, qualifiedName, out name, out ns);
  400. return ns;
  401. }
  402. string GetArrayType (XmlNode node, string name, string ns)
  403. {
  404. 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);
  405. if (anod == null) return null;
  406. string atype = anod.Value;
  407. int i = atype.IndexOf ('[');
  408. string itemType = GetTypeQualifiedName (node, atype.Substring (0,i));
  409. return itemType + atype.Substring (i);
  410. }
  411. XmlElement GetBinding (string name)
  412. {
  413. int i = name.IndexOf (':');
  414. name = name.Substring (i+1);
  415. return doc.SelectSingleNode ("wsdl:definitions/wsdl:binding[@name='" + name + "']", nsManager) as XmlElement;
  416. }
  417. string DecodeNamespace (string xmlNamespace)
  418. {
  419. string tns, tasm;
  420. if (!SoapServices.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace, out tns, out tasm))
  421. tns = xmlNamespace;
  422. return tns;
  423. }
  424. string GetLiteral (object ob)
  425. {
  426. if (ob == null) return "null";
  427. if (ob is string) return "\"" + ob.ToString().Replace("\"","\"\"") + "\"";
  428. if (ob is bool) return ((bool)ob) ? "true" : "false";
  429. if (ob is XmlQualifiedName) {
  430. XmlQualifiedName qn = (XmlQualifiedName)ob;
  431. return "new XmlQualifiedName (" + GetLiteral(qn.Name) + "," + GetLiteral(qn.Namespace) + ")";
  432. }
  433. else return ob.ToString ();
  434. }
  435. string Params (params string[] pars)
  436. {
  437. string res = "";
  438. foreach (string p in pars)
  439. {
  440. if (res != "") res += ", ";
  441. res += p;
  442. }
  443. return res;
  444. }
  445. string GetNameFromQn (string qn)
  446. {
  447. int i = qn.IndexOf (':');
  448. if (i == -1) return qn;
  449. else return qn.Substring (i+1);
  450. }
  451. }
  452. class CodeFile
  453. {
  454. public string FileName;
  455. public string Directory;
  456. public string FilePath;
  457. Hashtable namespaces = new Hashtable ();
  458. public StringWriter writer;
  459. int indent;
  460. string currentNamespace;
  461. public CodeFile (string directory)
  462. {
  463. Directory = directory;
  464. }
  465. public void SetCurrentNamespace (string ns)
  466. {
  467. writer = namespaces [ns] as StringWriter;
  468. if (writer == null)
  469. {
  470. indent = 0;
  471. writer = new StringWriter ();
  472. namespaces [ns] = writer;
  473. WriteLine ("namespace " + ns);
  474. WriteLineInd ("{");
  475. }
  476. indent = 1;
  477. if (FileName == null)
  478. FileName = ns + ".cs";
  479. }
  480. public void WriteLineInd (string code)
  481. {
  482. WriteLine (code);
  483. indent++;
  484. }
  485. public void WriteLineUni (string code)
  486. {
  487. if (indent > 0) indent--;
  488. WriteLine (code);
  489. }
  490. public void WriteLine (string code)
  491. {
  492. if (code != "") writer.Write (new String ('\t',indent));
  493. writer.WriteLine (code);
  494. }
  495. public void Write ()
  496. {
  497. if (FileName == null) return;
  498. FilePath = Path.Combine (Directory, FileName);
  499. StreamWriter sw = new StreamWriter (FilePath);
  500. sw.WriteLine ("using System;");
  501. sw.WriteLine ("using System.Runtime.Remoting.Metadata;");
  502. sw.WriteLine ();
  503. foreach (StringWriter nsWriter in namespaces.Values)
  504. {
  505. sw.Write (nsWriter.ToString ());
  506. sw.WriteLine ("}");
  507. sw.WriteLine ();
  508. }
  509. sw.Close ();
  510. }
  511. }
  512. }