MetaDataCodeGenerator.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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. currentFile = mainFile;
  55. // Suds types
  56. sudsTypes = new Hashtable ();
  57. XmlNodeList nodes = doc.DocumentElement.SelectNodes ("wsdl:binding/suds:class|wsdl:binding/suds:interface|wsdl:binding/suds:struct", nsManager);
  58. foreach (XmlElement node in nodes)
  59. sudsTypes [GetTypeQualifiedName (node, node.GetAttribute ("type"))] = node;
  60. // Data types
  61. nodes = doc.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager);
  62. foreach (XmlElement schema in nodes)
  63. GenerateSchemaCode (schema);
  64. // Services
  65. nodes = doc.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager);
  66. foreach (XmlElement port in nodes)
  67. GeneratePortCode (port);
  68. mainFile.Write ();
  69. if (mainFile.FileName != null)
  70. outCodeStreamList.Add (mainFile.FilePath);
  71. }
  72. void GeneratePortCode (XmlElement port)
  73. {
  74. XmlElement binding = GetBinding (port.GetAttribute ("binding"));
  75. XmlElement type = null;
  76. foreach (XmlNode node in binding)
  77. if ((node is XmlElement) && ((XmlElement)node).NamespaceURI == MetaData.SudsNamespace)
  78. { type = (XmlElement) node; break; }
  79. string rootType = type.GetAttribute ("rootType");
  80. if (rootType == "Delegate")
  81. GenerateServiceDelegateCode (port, binding, type);
  82. else
  83. GenerateServiceClassCode (port, binding, type);
  84. }
  85. void GenerateServiceDelegateCode (XmlElement port, XmlElement binding, XmlElement type)
  86. {
  87. string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name");
  88. string portName = GetNameFromQn (binding.GetAttribute ("type"));
  89. string name, ns;
  90. GetTypeQualifiedName (port, typeName, out name, out ns);
  91. currentFile.SetCurrentNamespace (ns);
  92. XmlElement oper = (XmlElement) binding.SelectSingleNode ("wsdl:operation[@name='Invoke']", nsManager);
  93. if (oper == null) throw new InvalidOperationException ("Invalid delegate schema");
  94. string parsDec;
  95. string returnType;
  96. GetParameters (oper, portName, "Invoke", out parsDec, out returnType);
  97. currentFile.WriteLine ("public delegate " + returnType + " " + name + " (" + parsDec + ");");
  98. currentFile.WriteLine ("");
  99. }
  100. void GenerateServiceClassCode (XmlElement port, XmlElement binding, XmlElement type)
  101. {
  102. string typeName = (type != null) ? type.GetAttribute ("type") : port.GetAttribute ("name");
  103. string name, ns;
  104. GetTypeQualifiedName (port, typeName, out name, out ns);
  105. currentFile.SetCurrentNamespace (ns);
  106. string cls = "public " + type.LocalName + " " + name;
  107. string baset = type.GetAttribute ("extends");
  108. if (baset != "") cls += ": " + GetTypeQualifiedName (port, baset);
  109. // Interfaces
  110. XmlNodeList interfaces = type.SelectNodes ("suds:implements",nsManager);
  111. if (interfaces.Count == 0) interfaces = type.SelectNodes ("suds:extends",nsManager);
  112. foreach (XmlElement interf in interfaces)
  113. {
  114. string iname = GetTypeQualifiedName (interf, interf.GetAttribute ("type"));
  115. if (cls.IndexOf (':') == -1) cls += ": " + iname;
  116. else cls += ", " + iname;
  117. }
  118. currentFile.WriteLine (cls);
  119. currentFile.WriteLineInd ("{");
  120. string portName = GetNameFromQn (binding.GetAttribute ("type"));
  121. bool isInterface = type.LocalName == "interface";
  122. string vis = isInterface? "":"public ";
  123. ArrayList mets = GetMethods (portName, binding);
  124. foreach (MethodData met in mets)
  125. {
  126. if (met.IsProperty)
  127. {
  128. string prop = vis + met.ReturnType + " ";
  129. if (met.Signature != "") prop += "this [" + met.Signature + "]";
  130. else prop += met.Name;
  131. if (isInterface)
  132. {
  133. prop += " { ";
  134. if (met.HasGet) prop += "get; ";
  135. if (met.HasSet) prop += "set; ";
  136. prop += "}";
  137. currentFile.WriteLine (prop);
  138. }
  139. else
  140. {
  141. currentFile.WriteLine (prop);
  142. currentFile.WriteLineInd ("{");
  143. if (met.HasGet) currentFile.WriteLine ("get { throw new NotImplementedException (); }");
  144. if (met.HasSet) currentFile.WriteLine ("set { throw new NotImplementedException (); }");
  145. currentFile.WriteLineUni ("}");
  146. currentFile.WriteLine ("");
  147. }
  148. }
  149. else
  150. {
  151. currentFile.WriteLine (vis + met.ReturnType + " " + met.Name + " (" + met.Signature + ")" + (isInterface?";":""));
  152. if (!isInterface)
  153. {
  154. currentFile.WriteLineInd ("{");
  155. currentFile.WriteLine ("throw new NotImplementedException ();");
  156. currentFile.WriteLineUni ("}");
  157. currentFile.WriteLine ("");
  158. }
  159. }
  160. }
  161. currentFile.WriteLineUni ("}");
  162. currentFile.WriteLine ("");
  163. }
  164. class MethodData
  165. {
  166. public string ReturnType;
  167. public string Signature;
  168. public string Name;
  169. public bool HasSet;
  170. public bool HasGet;
  171. public bool IsProperty { get { return HasGet || HasSet; } }
  172. }
  173. ArrayList GetMethods (string portName, XmlElement binding)
  174. {
  175. ArrayList mets = new ArrayList ();
  176. XmlNodeList nodes = binding.SelectNodes ("wsdl:operation", nsManager);
  177. foreach (XmlElement oper in nodes)
  178. {
  179. MethodData md = new MethodData ();
  180. md.Name = oper.GetAttribute ("name");
  181. GetParameters (oper, portName, md.Name, out md.Signature, out md.ReturnType);
  182. if (md.Name.StartsWith ("set_") || md.Name.StartsWith ("get_"))
  183. {
  184. string tmp = ", " + md.Signature;
  185. if (tmp.IndexOf (", out ") == -1 && tmp.IndexOf (", ref ") == -1)
  186. {
  187. bool isSet = md.Name[0]=='s';
  188. md.Name = md.Name.Substring (4);
  189. MethodData previousProp = null;
  190. foreach (MethodData fmd in mets)
  191. if (fmd.Name == md.Name && fmd.IsProperty)
  192. previousProp = fmd;
  193. if (previousProp != null) {
  194. if (isSet) previousProp.HasSet = true;
  195. else { previousProp.HasGet = true; previousProp.Signature = md.Signature; }
  196. continue;
  197. }
  198. else {
  199. if (isSet) { md.HasSet = true; md.Signature = ""; }
  200. else md.HasGet = true;
  201. }
  202. }
  203. }
  204. mets.Add (md);
  205. }
  206. return mets;
  207. }
  208. void GetParameters (XmlElement oper, string portName, string operName, out string signature, out string returnType)
  209. {
  210. returnType = null;
  211. XmlElement portType = (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:portType[@name='" + portName + "']", nsManager);
  212. XmlElement portOper = (XmlElement) portType.SelectSingleNode ("wsdl:operation[@name='" + operName + "']", nsManager);
  213. string[] parNames = portOper.GetAttribute ("parameterOrder").Split (' ');
  214. XmlElement inPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:input", nsManager);
  215. XmlElement inMsg = FindMessageFromPortMessage (inPortMsg);
  216. XmlElement outPortMsg = (XmlElement) portOper.SelectSingleNode ("wsdl:output", nsManager);
  217. XmlElement outMsg = FindMessageFromPortMessage (outPortMsg);
  218. string[] parameters;
  219. if (parNames [0] != "") parameters = new string [parNames.Length];
  220. else parameters = new string [0];
  221. foreach (XmlElement part in inMsg.SelectNodes ("wsdl:part",nsManager))
  222. {
  223. int i = Array.IndexOf (parNames, part.GetAttribute ("name"));
  224. string type = GetTypeQualifiedName (part, part.GetAttribute ("type"));
  225. parameters [i] = type + " " + parNames [i];
  226. }
  227. foreach (XmlElement part in outMsg.SelectNodes ("wsdl:part",nsManager))
  228. {
  229. string pn = part.GetAttribute ("name");
  230. string type = GetTypeQualifiedName (part, part.GetAttribute ("type"));
  231. if (pn == "return")
  232. returnType = type;
  233. else {
  234. int i = Array.IndexOf (parNames, pn);
  235. if (parameters [i] != null) parameters [i] = "ref " + parameters [i];
  236. else parameters [i] = "out " + type + " " + pn;
  237. }
  238. }
  239. signature = string.Join (", ", parameters);
  240. if (returnType == null) returnType = "void";
  241. }
  242. XmlElement FindMessageFromPortMessage (XmlElement portMsg)
  243. {
  244. string msgName = portMsg.GetAttribute ("message");
  245. msgName = GetNameFromQn (msgName);
  246. return (XmlElement) doc.SelectSingleNode ("wsdl:definitions/wsdl:message[@name='" + msgName + "']", nsManager);
  247. }
  248. void GenerateSchemaCode (XmlElement schema)
  249. {
  250. string ns = schema.GetAttribute ("targetNamespace");
  251. string clrNs = DecodeNamespace (ns);
  252. currentFile.SetCurrentNamespace (clrNs);
  253. foreach (XmlNode node in schema)
  254. {
  255. XmlElement elem = node as XmlElement;
  256. if (elem == null) continue;
  257. if (elem.LocalName == "complexType")
  258. GenerateClassCode (ns, elem);
  259. else if (elem.LocalName == "simpleType")
  260. GenerateEnumCode (ns, elem);
  261. }
  262. }
  263. void GenerateClassCode (string ns, XmlElement elem)
  264. {
  265. if (elem.SelectSingleNode ("s:complexContent/s:restriction", nsManager) != null) return;
  266. string clrNs = DecodeNamespace (ns);
  267. string typeName = GetTypeName (elem.GetAttribute ("name"), ns);
  268. XmlElement sudsType = (XmlElement) sudsTypes [clrNs + "." + typeName];
  269. string typetype = "class";
  270. if (sudsType != null) typetype = sudsType.LocalName;
  271. currentFile.WriteLine ("[Serializable, SoapType (XmlNamespace = @\"" + ns + "\", XmlTypeNamespace = @\"" + ns + "\")]");
  272. string cls = "public " + typetype + " " + typeName;
  273. string baseType = elem.GetAttribute ("base");
  274. if (baseType != "") cls += ": " + GetTypeQualifiedName (elem, baseType);
  275. bool isSerializable = (sudsType.GetAttribute ("rootType") == "ISerializable");
  276. if (isSerializable)
  277. {
  278. if (cls.IndexOf (':') == -1) cls += ": ";
  279. else cls += ", ";
  280. cls += "System.Runtime.Serialization.ISerializable";
  281. }
  282. currentFile.WriteLine (cls);
  283. currentFile.WriteLineInd ("{");
  284. XmlNodeList elems = elem.GetElementsByTagName ("element", MetaData.SchemaNamespace);
  285. foreach (XmlElement elemField in elems)
  286. WriteField (elemField);
  287. elems = elem.GetElementsByTagName ("attribute", MetaData.SchemaNamespace);
  288. foreach (XmlElement elemField in elems)
  289. WriteField (elemField);
  290. if (isSerializable)
  291. {
  292. currentFile.WriteLine ("");
  293. currentFile.WriteLine ("public " + typeName + " ()");
  294. currentFile.WriteLineInd ("{");
  295. currentFile.WriteLineUni ("}");
  296. currentFile.WriteLine ("");
  297. currentFile.WriteLine ("public " + typeName + " (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
  298. currentFile.WriteLineInd ("{");
  299. currentFile.WriteLine ("throw new NotImplementedException ();");
  300. currentFile.WriteLineUni ("}");
  301. currentFile.WriteLine ("");
  302. currentFile.WriteLine ("public void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
  303. currentFile.WriteLineInd ("{");
  304. currentFile.WriteLine ("throw new NotImplementedException ();");
  305. currentFile.WriteLineUni ("}");
  306. }
  307. currentFile.WriteLineUni ("}");
  308. currentFile.WriteLine ("");
  309. }
  310. void WriteField (XmlElement elemField)
  311. {
  312. bool isAttr = elemField.LocalName == "attribute";
  313. string type = elemField.GetAttribute ("type");
  314. if (isAttr)
  315. currentFile.WriteLine ("[SoapField (UseAttribute = true)]");
  316. else if (!IsPrimitive (elemField, type))
  317. currentFile.WriteLine ("[SoapField (Embedded = true)]");
  318. currentFile.WriteLine ("public " + GetTypeQualifiedName (elemField, type) + " " + elemField.GetAttribute ("name") + ";");
  319. }
  320. void GenerateEnumCode (string ns, XmlElement elem)
  321. {
  322. currentFile.WriteLine ("public enum " + GetTypeName (elem.GetAttribute ("name"), ns));
  323. currentFile.WriteLineInd ("{");
  324. XmlNodeList nodes = elem.SelectNodes ("s:restriction/s:enumeration/@value", nsManager);
  325. foreach (XmlNode node in nodes)
  326. currentFile.WriteLine (node.Value + ",");
  327. currentFile.WriteLineUni ("}");
  328. currentFile.WriteLine ("");
  329. }
  330. bool IsPrimitive (XmlNode node, string qname)
  331. {
  332. string name = GetTypeQualifiedName (node, qname);
  333. return name.IndexOf ('.') == -1;
  334. }
  335. string GetTypeName (string localName, string ns)
  336. {
  337. return localName;
  338. }
  339. void GetTypeQualifiedName (XmlNode node, string qualifiedName, out string name, out string ns)
  340. {
  341. int i = qualifiedName.IndexOf (':');
  342. if (i == -1)
  343. {
  344. name = qualifiedName;
  345. ns = "";
  346. return;
  347. }
  348. string prefix = qualifiedName.Substring (0,i);
  349. name = qualifiedName.Substring (i+1);
  350. ns = node.GetNamespaceOfPrefix (prefix);
  351. string arrayType = GetArrayType (node, name, ns);
  352. if (arrayType != null) {
  353. name = arrayType;
  354. ns = "";
  355. }
  356. else if (ns != MetaData.SchemaNamespace) {
  357. ns = DecodeNamespace (ns);
  358. }
  359. else {
  360. ns = "";
  361. name = GetClrFromXsd (name);
  362. }
  363. }
  364. string GetClrFromXsd (string type)
  365. {
  366. switch (type)
  367. {
  368. case "boolean": return "bool";
  369. case "unsignedByte": return "byte";
  370. case "char": return "char";
  371. case "dateTime": return "DateTime";
  372. case "decimal": return "decimal";
  373. case "double": return "double";
  374. case "short": return "short";
  375. case "int": return "int";
  376. case "long": return "long";
  377. case "byte": return "sbyte";
  378. case "float": return "float";
  379. case "unsignedShort": return "ushort";
  380. case "unsignedInt": return "uint";
  381. case "unsignedLong": return "ulong";
  382. case "string": return "string";
  383. case "duration": return "TimeSpan";
  384. case "anyType": return "object";
  385. }
  386. throw new InvalidOperationException ("Unknown schema type: " + type);
  387. }
  388. string GetTypeQualifiedName (XmlNode node, string qualifiedName)
  389. {
  390. string name, ns;
  391. GetTypeQualifiedName (node, qualifiedName, out name, out ns);
  392. if (ns != "") return ns + "." + name;
  393. else return name;
  394. }
  395. string GetTypeNamespace (XmlNode node, string qualifiedName)
  396. {
  397. string name, ns;
  398. GetTypeQualifiedName (node, qualifiedName, out name, out ns);
  399. return ns;
  400. }
  401. string GetArrayType (XmlNode node, string name, string ns)
  402. {
  403. 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);
  404. if (anod == null) return null;
  405. string atype = anod.Value;
  406. int i = atype.IndexOf ('[');
  407. string itemType = GetTypeQualifiedName (node, atype.Substring (0,i));
  408. return itemType + atype.Substring (i);
  409. }
  410. XmlElement GetBinding (string name)
  411. {
  412. int i = name.IndexOf (':');
  413. name = name.Substring (i+1);
  414. return doc.SelectSingleNode ("wsdl:definitions/wsdl:binding[@name='" + name + "']", nsManager) as XmlElement;
  415. }
  416. string DecodeNamespace (string xmlNamespace)
  417. {
  418. string tns, tasm;
  419. if (!SoapServices.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace, out tns, out tasm))
  420. tns = xmlNamespace;
  421. return tns;
  422. }
  423. string GetLiteral (object ob)
  424. {
  425. if (ob == null) return "null";
  426. if (ob is string) return "\"" + ob.ToString().Replace("\"","\"\"") + "\"";
  427. if (ob is bool) return ((bool)ob) ? "true" : "false";
  428. if (ob is XmlQualifiedName) {
  429. XmlQualifiedName qn = (XmlQualifiedName)ob;
  430. return "new XmlQualifiedName (" + GetLiteral(qn.Name) + "," + GetLiteral(qn.Namespace) + ")";
  431. }
  432. else return ob.ToString ();
  433. }
  434. string Params (params string[] pars)
  435. {
  436. string res = "";
  437. foreach (string p in pars)
  438. {
  439. if (res != "") res += ", ";
  440. res += p;
  441. }
  442. return res;
  443. }
  444. string GetNameFromQn (string qn)
  445. {
  446. int i = qn.IndexOf (':');
  447. if (i == -1) return qn;
  448. else return qn.Substring (i+1);
  449. }
  450. }
  451. class CodeFile
  452. {
  453. public string FileName;
  454. public string Directory;
  455. public string FilePath;
  456. Hashtable namespaces = new Hashtable ();
  457. public StringWriter writer;
  458. int indent;
  459. string currentNamespace;
  460. public CodeFile (string directory)
  461. {
  462. Directory = directory;
  463. }
  464. public void SetCurrentNamespace (string ns)
  465. {
  466. writer = namespaces [ns] as StringWriter;
  467. if (writer == null)
  468. {
  469. indent = 0;
  470. writer = new StringWriter ();
  471. namespaces [ns] = writer;
  472. WriteLine ("namespace " + ns);
  473. WriteLineInd ("{");
  474. }
  475. indent = 1;
  476. if (FileName == null)
  477. FileName = ns + ".cs";
  478. }
  479. public void WriteLineInd (string code)
  480. {
  481. WriteLine (code);
  482. indent++;
  483. }
  484. public void WriteLineUni (string code)
  485. {
  486. if (indent > 0) indent--;
  487. WriteLine (code);
  488. }
  489. public void WriteLine (string code)
  490. {
  491. if (code != "") writer.Write (new String ('\t',indent));
  492. writer.WriteLine (code);
  493. }
  494. public void Write ()
  495. {
  496. if (FileName == null) return;
  497. FilePath = Path.Combine (Directory, FileName);
  498. StreamWriter sw = new StreamWriter (FilePath);
  499. sw.WriteLine ("using System;");
  500. sw.WriteLine ("using System.Runtime.Remoting.Metadata;");
  501. sw.WriteLine ();
  502. foreach (StringWriter nsWriter in namespaces.Values)
  503. {
  504. sw.Write (nsWriter.ToString ());
  505. sw.WriteLine ("}");
  506. sw.WriteLine ();
  507. }
  508. sw.Close ();
  509. }
  510. }
  511. }