ProtocolImporter.cs 15 KB

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