ProtocolImporter.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. //
  2. // System.Web.Services.Description.ProtocolImporter.cs
  3. //
  4. // Author:
  5. // Tim Coleman ([email protected])
  6. // Lluis Sanchez Gual ([email protected])
  7. //
  8. // Copyright (C) Tim Coleman, 2002
  9. //
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.CodeDom;
  32. using System.Web.Services;
  33. using System.Web.Services.Protocols;
  34. using System.Xml.Serialization;
  35. using System.Xml;
  36. using System.Xml.Schema;
  37. using System.Collections;
  38. using System.Configuration;
  39. namespace System.Web.Services.Description {
  40. public abstract class ProtocolImporter {
  41. #region Fields
  42. Binding binding;
  43. string className;
  44. CodeIdentifiers classNames;
  45. CodeNamespace codeNamespace;
  46. CodeCompileUnit codeCompileUnit;
  47. CodeTypeDeclaration codeTypeDeclaration;
  48. Message inputMessage;
  49. string methodName;
  50. Operation operation;
  51. OperationBinding operationBinding;
  52. Message outputMessage;
  53. Port port;
  54. PortType portType;
  55. string protocolName;
  56. Service service;
  57. ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;
  58. ServiceDescriptionImporter descriptionImporter;
  59. ImportInfo iinfo;
  60. XmlSchemas xmlSchemas;
  61. XmlSchemas soapSchemas;
  62. #endregion // Fields
  63. #region Constructors
  64. protected ProtocolImporter ()
  65. {
  66. }
  67. #endregion // Constructors
  68. #region Properties
  69. [MonoTODO]
  70. public XmlSchemas AbstractSchemas {
  71. get { return descriptionImporter.Schemas; }
  72. }
  73. public Binding Binding {
  74. get { return binding; }
  75. }
  76. public string ClassName {
  77. get { return className; }
  78. }
  79. public CodeIdentifiers ClassNames {
  80. get { return classNames; }
  81. }
  82. public CodeNamespace CodeNamespace {
  83. get { return codeNamespace; }
  84. }
  85. public CodeTypeDeclaration CodeTypeDeclaration {
  86. get { return codeTypeDeclaration; }
  87. }
  88. [MonoTODO]
  89. public XmlSchemas ConcreteSchemas {
  90. get { return descriptionImporter.Schemas; }
  91. }
  92. public Message InputMessage {
  93. get { return inputMessage; }
  94. }
  95. public string MethodName {
  96. get { return methodName; }
  97. }
  98. public Operation Operation {
  99. get { return operation; }
  100. }
  101. public OperationBinding OperationBinding {
  102. get { return operationBinding; }
  103. }
  104. public Message OutputMessage {
  105. get { return outputMessage; }
  106. }
  107. public Port Port {
  108. get { return port; }
  109. }
  110. public PortType PortType {
  111. get { return portType; }
  112. }
  113. public abstract string ProtocolName {
  114. get;
  115. }
  116. public XmlSchemas Schemas {
  117. get { return descriptionImporter.Schemas; }
  118. }
  119. public Service Service {
  120. get { return service; }
  121. }
  122. public ServiceDescriptionCollection ServiceDescriptions {
  123. get { return descriptionImporter.ServiceDescriptions; }
  124. }
  125. public ServiceDescriptionImportStyle Style {
  126. get { return descriptionImporter.Style; }
  127. }
  128. public ServiceDescriptionImportWarnings Warnings {
  129. get { return warnings; }
  130. set { warnings = value; }
  131. }
  132. internal ImportInfo ImportInfo
  133. {
  134. get { return iinfo; }
  135. }
  136. internal XmlSchemas LiteralSchemas
  137. {
  138. get { return xmlSchemas; }
  139. }
  140. internal XmlSchemas EncodedSchemas
  141. {
  142. get { return soapSchemas; }
  143. }
  144. #endregion // Properties
  145. #region Methods
  146. internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
  147. {
  148. this.descriptionImporter = descriptionImporter;
  149. this.classNames = new CodeIdentifiers();;
  150. this.codeNamespace = codeNamespace;
  151. this.codeCompileUnit = codeCompileUnit;
  152. warnings = (ServiceDescriptionImportWarnings) 0;
  153. bool found = false;
  154. ClasifySchemas (importInfo);
  155. BeginNamespace ();
  156. foreach (ImportInfo info in importInfo)
  157. {
  158. foreach (Service service in info.ServiceDescription.Services)
  159. {
  160. this.service = service;
  161. int bindingCount = 0;
  162. foreach (Port port in service.Ports)
  163. {
  164. binding = ServiceDescriptions.GetBinding (port.Binding);
  165. if (IsBindingSupported ()) bindingCount ++;
  166. }
  167. foreach (Port port in service.Ports)
  168. {
  169. this.iinfo = info;
  170. this.port = port;
  171. binding = ServiceDescriptions.GetBinding (port.Binding);
  172. if (!IsBindingSupported ()) continue;
  173. found = true;
  174. ImportPortBinding (bindingCount > 1);
  175. }
  176. }
  177. }
  178. EndNamespace ();
  179. if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
  180. return true;
  181. }
  182. void ImportPortBinding (bool multipleBindings)
  183. {
  184. if (multipleBindings) className = port.Name;
  185. else className = service.Name;
  186. className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
  187. className = className.Replace ("_x0020_", ""); // MS.NET seems to do this
  188. try
  189. {
  190. portType = ServiceDescriptions.GetPortType (binding.Type);
  191. if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
  192. CodeTypeDeclaration codeClass = BeginClass ();
  193. codeTypeDeclaration = codeClass;
  194. AddCodeType (codeClass, port.Documentation);
  195. codeClass.Attributes = MemberAttributes.Public;
  196. if (service.Documentation != null && service.Documentation != "")
  197. AddComments (codeClass, service.Documentation);
  198. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
  199. AddCustomAttribute (codeClass, att, true);
  200. att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
  201. att.Arguments.Add (GetArg ("code"));
  202. AddCustomAttribute (codeClass, att, true);
  203. if (binding.Operations.Count == 0) {
  204. warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
  205. return;
  206. }
  207. foreach (OperationBinding oper in binding.Operations)
  208. {
  209. operationBinding = oper;
  210. operation = FindPortOperation ();
  211. if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
  212. foreach (OperationMessage omsg in operation.Messages)
  213. {
  214. Message msg = ServiceDescriptions.GetMessage (omsg.Message);
  215. if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
  216. if (omsg is OperationInput)
  217. inputMessage = msg;
  218. else
  219. outputMessage = msg;
  220. }
  221. CodeMemberMethod method = GenerateMethod ();
  222. if (method != null)
  223. {
  224. methodName = method.Name;
  225. if (operation.Documentation != null && operation.Documentation != "")
  226. AddComments (method, operation.Documentation);
  227. }
  228. }
  229. EndClass ();
  230. }
  231. catch (InvalidOperationException ex)
  232. {
  233. warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
  234. UnsupportedBindingWarning (ex.Message);
  235. }
  236. }
  237. Operation FindPortOperation ()
  238. {
  239. string inMessage = null;
  240. string outMessage = null;
  241. int numMsg = 1;
  242. if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
  243. inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
  244. if (operationBinding.Output != null) {
  245. outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
  246. numMsg++;
  247. }
  248. string operName = operationBinding.Name;
  249. Operation foundOper = null;
  250. foreach (Operation oper in PortType.Operations)
  251. {
  252. if (oper.Name == operName)
  253. {
  254. int hits = 0;
  255. foreach (OperationMessage omsg in oper.Messages)
  256. {
  257. if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
  258. if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
  259. }
  260. if (hits == numMsg) return oper;
  261. foundOper = oper;
  262. }
  263. }
  264. return foundOper;
  265. }
  266. string GetOperMessageName (OperationMessage msg, string operName)
  267. {
  268. if (msg.Name == null) return operName;
  269. else return msg.Name;
  270. }
  271. internal string GetServiceUrl (string location)
  272. {
  273. if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
  274. return location;
  275. else
  276. {
  277. string url;
  278. if (Style == ServiceDescriptionImportStyle.Server) throw new InvalidOperationException ("Cannot set appSettingUrlKey if Style is Server");
  279. url = ConfigurationSettings.AppSettings [ImportInfo.AppSettingUrlKey];
  280. if (ImportInfo.AppSettingBaseUrl != null && ImportInfo.AppSettingBaseUrl != string.Empty)
  281. url += "/" + ImportInfo.AppSettingBaseUrl + "/" + location;
  282. return url;
  283. }
  284. }
  285. void ClasifySchemas (ArrayList importInfo)
  286. {
  287. // I don't like this, but I could not find any other way of clasifying
  288. // schemas between encoded and literal.
  289. xmlSchemas = new XmlSchemas ();
  290. soapSchemas = new XmlSchemas ();
  291. foreach (ImportInfo info in importInfo)
  292. {
  293. foreach (Service service in info.ServiceDescription.Services)
  294. {
  295. foreach (Port port in service.Ports)
  296. {
  297. this.iinfo = info;
  298. this.port = port;
  299. binding = ServiceDescriptions.GetBinding (port.Binding);
  300. if (binding == null) continue;
  301. portType = ServiceDescriptions.GetPortType (binding.Type);
  302. if (portType == null) continue;
  303. foreach (OperationBinding oper in binding.Operations)
  304. {
  305. operationBinding = oper;
  306. operation = FindPortOperation ();
  307. if (operation == null) continue;
  308. foreach (OperationMessage omsg in operation.Messages)
  309. {
  310. Message msg = ServiceDescriptions.GetMessage (omsg.Message);
  311. if (msg == null) continue;
  312. if (omsg is OperationInput)
  313. inputMessage = msg;
  314. else
  315. outputMessage = msg;
  316. }
  317. if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
  318. AddMessageSchema (soapSchemas, oper.Input, inputMessage);
  319. else
  320. AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
  321. if (oper.Output != null) {
  322. if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
  323. AddMessageSchema (soapSchemas, oper.Output, outputMessage);
  324. else
  325. AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
  326. }
  327. }
  328. }
  329. }
  330. }
  331. XmlSchemas defaultList = xmlSchemas;
  332. if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
  333. defaultList = soapSchemas;
  334. // Schemas not referenced by any message
  335. foreach (XmlSchema sc in Schemas)
  336. {
  337. if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
  338. if (ImportsEncodedNamespace (sc))
  339. soapSchemas.Add (sc);
  340. else
  341. defaultList.Add (sc);
  342. }
  343. }
  344. }
  345. void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
  346. {
  347. foreach (MessagePart part in msg.Parts)
  348. {
  349. if (part.Element != XmlQualifiedName.Empty)
  350. AddIncludingSchema (schemas, part.Element.Namespace);
  351. else if (part.Type != XmlQualifiedName.Empty)
  352. AddIncludingSchema (schemas, part.Type.Namespace);
  353. }
  354. SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
  355. if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
  356. }
  357. void AddIncludingSchema (XmlSchemas list, string ns)
  358. {
  359. XmlSchema sc = Schemas [ns];
  360. if (sc == null || list.Contains (sc)) return;
  361. list.Add (sc);
  362. foreach (XmlSchemaObject ob in sc.Includes)
  363. {
  364. XmlSchemaImport import = ob as XmlSchemaImport;
  365. if (import != null) AddIncludingSchema (list, import.Namespace);
  366. }
  367. }
  368. SoapBindingUse GetMessageEncoding (MessageBinding mb)
  369. {
  370. SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
  371. if (sbb == null)
  372. {
  373. if (mb is InputBinding) return SoapBindingUse.Encoded;
  374. else return SoapBindingUse.Literal;
  375. }
  376. else
  377. if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
  378. else
  379. return SoapBindingUse.Literal;
  380. }
  381. bool ImportsEncodedNamespace (XmlSchema sc)
  382. {
  383. foreach (XmlSchemaObject ob in sc.Includes)
  384. {
  385. XmlSchemaImport import = ob as XmlSchemaImport;
  386. if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
  387. }
  388. return false;
  389. }
  390. [MonoTODO]
  391. public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions)
  392. {
  393. throw new NotImplementedException ();
  394. }
  395. protected abstract CodeTypeDeclaration BeginClass ();
  396. protected virtual void BeginNamespace ()
  397. {
  398. }
  399. protected virtual void EndClass ()
  400. {
  401. }
  402. protected virtual void EndNamespace ()
  403. {
  404. }
  405. protected abstract CodeMemberMethod GenerateMethod ();
  406. protected abstract bool IsBindingSupported ();
  407. protected abstract bool IsOperationFlowSupported (OperationFlow flow);
  408. [MonoTODO]
  409. public Exception OperationBindingSyntaxException (string text)
  410. {
  411. throw new NotImplementedException ();
  412. }
  413. [MonoTODO]
  414. public Exception OperationSyntaxException (string text)
  415. {
  416. throw new NotImplementedException ();
  417. }
  418. public void UnsupportedBindingWarning (string text)
  419. {
  420. warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
  421. AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
  422. }
  423. public void UnsupportedOperationBindingWarning (string text)
  424. {
  425. warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
  426. AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
  427. }
  428. public void UnsupportedOperationWarning (string text)
  429. {
  430. warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
  431. AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
  432. }
  433. void AddGlobalComments (string comments)
  434. {
  435. codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
  436. }
  437. void AddComments (CodeTypeMember member, string comments)
  438. {
  439. if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
  440. else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
  441. }
  442. void AddCodeType (CodeTypeDeclaration type, string comments)
  443. {
  444. AddComments (type, comments);
  445. codeNamespace.Types.Add (type);
  446. }
  447. internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
  448. {
  449. if (att.Arguments.Count == 0 && !addIfNoParams) return;
  450. if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
  451. ctm.CustomAttributes.Add (att);
  452. }
  453. internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
  454. {
  455. if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
  456. ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
  457. }
  458. internal CodeAttributeArgument GetArg (string name, object value)
  459. {
  460. return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
  461. }
  462. internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
  463. {
  464. return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
  465. }
  466. internal CodeAttributeArgument GetArg (object value)
  467. {
  468. return new CodeAttributeArgument (new CodePrimitiveExpression(value));
  469. }
  470. internal CodeAttributeArgument GetTypeArg (string name, string typeName)
  471. {
  472. return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
  473. }
  474. #endregion
  475. }
  476. }