Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Script.Services / LogicalTypeInfo.cs
1 //
2 // LogicalTypeInfo.cs
3 //
4 // Author:
5 //   Konstantin Triger <kostat@mainsoft.com>
6 //   Atsushi Enomoto <atsushi@ximian.com>
7 //
8 // (C) 2007 Mainsoft, Inc.  http://www.mainsoft.com
9 // Copyright (C) 2009 Novell, Inc. http://novell.com
10 //
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections.Generic;
34 using System.Collections.Specialized;
35 using System.Text;
36 using System.Web.Services;
37 using System.Reflection;
38 using System.Collections;
39 using System.Web.Script.Serialization;
40 using System.IO;
41 using System.Xml.Serialization;
42 using System.Xml;
43 #if NET_3_5
44 using System.ServiceModel;
45 using System.ServiceModel.Description;
46 #endif
47
48 namespace System.Web.Script.Services
49 {
50         internal sealed class JsonResult
51         {
52                 public readonly object d;
53                 public JsonResult (object result) {
54                         d = result;
55                 }
56         }
57
58         internal abstract class LogicalTypeInfo
59         {
60                 public static LogicalTypeInfo CreateTypeInfo (Type t, string filePath)
61                 {
62 #if NET_3_5
63                         if (t.GetCustomAttributes (typeof (ServiceContractAttribute), false).Length > 0)
64                                 return new WcfLogicalTypeInfo (t, filePath);
65                         else
66 #endif
67                                 return new AsmxLogicalTypeInfo (t, filePath);
68                 }
69
70                 internal abstract class LogicalMethodInfo
71                 {
72                         readonly MethodInfo _methodInfo;
73                         internal readonly ParameterInfo [] _params;
74                         internal readonly Dictionary<string, int> _paramMap;
75                         LogicalTypeInfo _typeInfo;
76
77                         protected LogicalMethodInfo (LogicalTypeInfo typeInfo, MethodInfo method)
78                         {
79                                 _methodInfo = method;
80                                 _params = MethodInfo.GetParameters ();
81                                 _typeInfo = typeInfo;
82
83                                 if (HasParameters) {
84                                         _paramMap = new Dictionary<string, int> (_params.Length, StringComparer.Ordinal);
85                                         for (int i = 0; i < _params.Length; i++)
86                                                 _paramMap.Add(_params[i].Name, i);
87                                 }
88
89                         }
90
91                         public abstract bool UseHttpGet { get; }
92                         public abstract bool EnableSession { get; }
93                         public abstract ResponseFormat ResponseFormat { get; }
94                         public abstract string MethodName { get; }
95                         public MethodInfo MethodInfo { get { return _methodInfo; } }
96                         public bool HasParameters { get { return _params != null && _params.Length > 0; } }
97                         public IEnumerable<Type> GetParameterTypes () {
98                                 if (HasParameters)
99                                         for (int i = 0; i < _params.Length; i++)
100                                                 yield return _params [i].ParameterType;
101
102                                 yield return MethodInfo.ReturnType;
103                         }
104
105                         public void GenerateMethod (StringBuilder proxy, bool isPrototype, bool isPage) {
106                                 string ns;
107                                 string service;// = isPage ? "PageMethods" : MethodInfo.DeclaringType.FullName;
108
109                                 _typeInfo.GetNamespaceAndServiceName (MethodInfo.DeclaringType, isPage, out ns, out service);
110                                 string useHttpGet = UseHttpGet ? "true" : "false";
111                                 string paramMap = GenerateParameters (true);
112                                 string paramList = GenerateParameters (false);
113
114                                 if (isPrototype){
115                                         proxy.AppendFormat (
116 @"
117 {1}:function({4}succeededCallback, failedCallback, userContext) {{
118 return this._invoke({0}.get_path(), '{1}',{2},{{{3}}},succeededCallback,failedCallback,userContext); }}",
119                                         service, MethodName, useHttpGet, paramMap, paramList);
120                                 }
121                                 else
122                                         proxy.AppendFormat (
123 @"
124 {0}.{1}= function({2}onSuccess,onFailed,userContext) {{{0}._staticInstance.{1}({2}onSuccess,onFailed,userContext); }}",
125                                         service, MethodName, paramList);
126                         }
127
128                         string GenerateParameters (bool isMap) {
129                                 if (!HasParameters)
130                                         return null;
131
132                                 StringBuilder builder = new StringBuilder ();
133
134                                 for (int i = 0; i < _params.Length; i++) {
135                                         builder.AppendFormat (isMap ? "{0}:{0}" : "{0}", _params [i].Name);
136                                         builder.Append (',');
137                                 }
138
139                                 if (isMap)
140                                         builder.Length--;
141
142                                 return builder.ToString ();
143                         }
144
145                         public abstract void Invoke (HttpRequest request, HttpResponse response);
146                 }
147
148 #if !TARGET_J2EE
149                 static Hashtable _type_to_logical_type = Hashtable.Synchronized (new Hashtable ());
150 #else
151                 const string type_to_logical_type_key = "System.Web.Script.Services.LogicalTypeInfo";
152                 static Hashtable _type_to_logical_type {
153                         get {
154                                 Hashtable hash = (Hashtable) AppDomain.CurrentDomain.GetData (type_to_logical_type_key);
155
156                                 if (hash != null)
157                                         return hash;
158
159                                 AppDomain.CurrentDomain.SetData (type_to_logical_type_key, Hashtable.Synchronized (new Hashtable ()));
160
161
162                                 return (Hashtable) AppDomain.CurrentDomain.GetData (type_to_logical_type_key);
163                         }
164                 }
165 #endif
166
167                 static internal LogicalTypeInfo GetLogicalTypeInfo (Type t, string filePath) {
168                         Hashtable type_to_manager = _type_to_logical_type;
169                         LogicalTypeInfo tm = (LogicalTypeInfo) type_to_manager [t];
170
171                         if (tm != null)
172                                 return tm;
173
174                         tm = CreateTypeInfo (t, filePath);
175                         type_to_manager [t] = tm;
176
177                         return tm;
178                 }
179
180                 protected static string EnsureNamespaceRegistered (string ns, string name, StringBuilder proxy, List<string> registeredNamespaces) {
181                         if (String.IsNullOrEmpty (ns))
182                                 return "var " + name;
183
184                         if (!registeredNamespaces.Contains (ns)) {
185                                 registeredNamespaces.Add (ns);
186                                 proxy.AppendFormat (
187 @"
188 Type.registerNamespace('{0}');",
189                                                                    ns);
190                         }
191                         return name;
192                 }
193
194                 protected virtual void GetNamespaceAndServiceName (Type type, bool isPage, out string ns, out string service)
195                 {
196                         ns = isPage ? String.Empty : type.Namespace;
197                         service = isPage ? "PageMethods" : type.FullName;
198                 }
199
200                 // instance members
201
202                 internal readonly Type _type;
203                 readonly string _proxy;
204                 internal readonly Hashtable _methodMap;
205
206                 protected LogicalTypeInfo (Type t, string filePath)
207                 {
208                         _type = t;
209                         bool isPage = _type.IsSubclassOf (typeof (System.Web.UI.Page));
210
211                         var logicalMethods = GetLogicalMethods (isPage);
212                         //_logicalMethods = (LogicalMethodInfo []) list.ToArray (typeof (LogicalMethodInfo));
213
214                         _methodMap = new Hashtable (logicalMethods.Count);
215                         for (int i = 0; i < logicalMethods.Count; i++)
216                                 _methodMap.Add (logicalMethods [i].MethodName, logicalMethods [i]);
217
218                         string ns;
219                         string service;
220                         GetNamespaceAndServiceName (t, isPage, out ns, out service);
221                         
222                         StringBuilder proxy = new StringBuilder ();
223                         List<string> registeredNamespaces = new List<string> ();
224                         string scriptTypeDeclaration = EnsureNamespaceRegistered (ns, service, proxy, registeredNamespaces);
225                         proxy.AppendFormat (
226 @"
227 " + scriptTypeDeclaration + @"=function() {{
228 {0}.initializeBase(this);
229 this._timeout = 0;
230 this._userContext = null;
231 this._succeeded = null;
232 this._failed = null;
233 }}
234 {0}.prototype={{",
235                         service);
236
237                         for (int i = 0; i < logicalMethods.Count; i++) {
238                                 if (i > 0)
239                                         proxy.Append (',');
240                                 logicalMethods [i].GenerateMethod (proxy, true, isPage);
241                         }
242
243                         proxy.AppendFormat (
244 @"}}
245 {0}.registerClass('{0}',Sys.Net.WebServiceProxy);
246 {0}._staticInstance = new {0}();
247 {0}.set_path = function(value) {{ {0}._staticInstance.set_path(value); }}
248 {0}.get_path = function() {{ return {0}._staticInstance.get_path(); }}
249 {0}.set_timeout = function(value) {{ {0}._staticInstance.set_timeout(value); }}
250 {0}.get_timeout = function() {{ return {0}._staticInstance.get_timeout(); }}
251 {0}.set_defaultUserContext = function(value) {{ {0}._staticInstance.set_defaultUserContext(value); }}
252 {0}.get_defaultUserContext = function() {{ return {0}._staticInstance.get_defaultUserContext(); }}
253 {0}.set_defaultSucceededCallback = function(value) {{ {0}._staticInstance.set_defaultSucceededCallback(value); }}
254 {0}.get_defaultSucceededCallback = function() {{ return {0}._staticInstance.get_defaultSucceededCallback(); }}
255 {0}.set_defaultFailedCallback = function(value) {{ {0}._staticInstance.set_defaultFailedCallback(value); }}
256 {0}.get_defaultFailedCallback = function() {{ return {0}._staticInstance.get_defaultFailedCallback(); }}
257 {0}.set_path(""{1}"");",
258                         service, filePath);
259
260                         for (int i = 0; i < logicalMethods.Count; i++)
261                                 logicalMethods [i].GenerateMethod (proxy, false, isPage);
262
263                         GenerateTypeRegistrationScript (proxy, registeredNamespaces);
264
265                         proxy.AppendLine ();
266                         _proxy = proxy.ToString ();
267                 }
268
269                 protected IEnumerable<MemberInfo> GetGenerateScriptTypes () {
270                         foreach (LogicalMethodInfo lmi in _methodMap.Values)
271                                 yield return lmi.MethodInfo;
272
273                         yield return _type;
274                 }
275
276                 protected static void GenerateTypeRegistrationScript (StringBuilder proxy, Type scriptType, string scriptTypeId, List<string> registeredNamespaces) {
277                         string className = scriptType.FullName.Replace ('+', '_');
278                         string ns = scriptType.Namespace;
279                         string scriptTypeDeclaration = EnsureNamespaceRegistered (ns, className, proxy, registeredNamespaces);
280                         proxy.AppendFormat (
281 @"
282 if (typeof({0}) === 'undefined') {{", className);
283                         if (scriptType.IsEnum) {
284                                 proxy.AppendFormat (
285 @"
286 {0} = function() {{ throw Error.invalidOperation(); }}
287 {0}.prototype = {1}
288 {0}.registerEnum('{0}', {2});",
289                                 className,
290                                 // This method is also used for WCF, but for enum this should work ...
291                                 AsmxLogicalTypeInfo.JSSerializer.Serialize(GetEnumPrototypeDictionary (scriptType)),
292                                 Attribute.GetCustomAttribute (scriptType, typeof (FlagsAttribute)) != null ? "true" : "false");
293                                 
294                         }
295                         else {
296                                 string typeId = String.IsNullOrEmpty (scriptTypeId) ? scriptType.FullName : scriptTypeId;
297                                 proxy.AppendFormat (
298 @"
299 " + scriptTypeDeclaration + @"=gtc(""{1}"");
300 {0}.registerClass('{0}');",
301                                 className, typeId);
302                         }
303                         proxy.Append ('}');
304                 }
305
306                 static IDictionary <string, object> GetEnumPrototypeDictionary (Type type)
307                 {
308                         var ret = new Dictionary <string, object> ();
309                         string [] names = Enum.GetNames (type);
310                         Array values = Enum.GetValues (type);
311                         for (int i = 0; i < names.Length; i++)
312                                 ret.Add (names [i], values.GetValue (i));
313
314                         return ret;
315                 }
316
317                 static readonly Type typeOfIEnumerable = typeof (IEnumerable);
318                 static readonly Type typeOfIDictionary = typeof (IDictionary);
319
320                 protected static bool ShouldGenerateScript (Type type, bool throwIfNot) {
321                         if (type.IsEnum)
322                                 return true;
323
324                         if (Type.GetTypeCode (type) != TypeCode.Object)
325                                 return false;
326
327                         if (type == typeof (void))
328                                 return false;
329
330                         if (typeOfIEnumerable.IsAssignableFrom (type) ||
331                                 typeOfIDictionary.IsAssignableFrom (type) ||
332                                 type.IsAbstract || type.IsInterface) {
333                                 if (throwIfNot)
334                                         ThrowOnIncorrectGenerateScriptAttribute ();
335                                 return false;
336                         }
337
338                         // LAMESPEC: MS never create proxies for GenericTypes
339                         //&& type.GetGenericTypeDefinition ().GetGenericArguments ().Length > 1
340                         if (type.IsGenericType)
341                                 return false;
342
343                         ConstructorInfo ci = type.GetConstructor (Type.EmptyTypes);
344                         if (ci == null || !ci.IsPublic) {
345                                 if (throwIfNot)
346                                         ThrowOnIncorrectGenerateScriptAttribute ();
347                                 return false;
348                         }
349
350                         return true;
351                 }
352
353                 static void ThrowOnIncorrectGenerateScriptAttribute () {
354                         throw new InvalidOperationException (
355                                 "Using the GenerateScriptTypes attribute is not supported for types in the following categories: primitive types; DateTime; generic types taking more than one parameter; types implementing IEnumerable or IDictionary; interfaces; Abstract classes; classes without a public default constructor.");
356                 }
357                 
358                 protected abstract void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces);
359
360                 protected abstract List<LogicalMethodInfo> GetLogicalMethods (bool isPage);
361
362                 public string Proxy { get { return _proxy; } }
363
364                 public LogicalMethodInfo this [string method] {
365                         get { return (LogicalMethodInfo) _methodMap [method]; }
366                 }
367         }
368
369         internal sealed class AsmxLogicalTypeInfo : LogicalTypeInfo
370         {
371                 #region LogicalMethodInfo
372
373                 public sealed class AsmxLogicalMethodInfo : LogicalTypeInfo.LogicalMethodInfo
374                 {
375                         readonly LogicalTypeInfo _typeInfo;
376
377                         readonly WebMethodAttribute _wma;
378
379                         readonly ScriptMethodAttribute _sma;
380
381                         readonly XmlSerializer _xmlSer;
382
383                         public AsmxLogicalMethodInfo (LogicalTypeInfo typeInfo, MethodInfo method)
384                                 : base (typeInfo, method)
385                         {
386                                 _typeInfo = typeInfo;
387
388                                 _wma = (WebMethodAttribute) Attribute.GetCustomAttribute (method, typeof (WebMethodAttribute));
389
390                                 _sma = (ScriptMethodAttribute) Attribute.GetCustomAttribute (method, typeof (ScriptMethodAttribute));
391                                 if (_sma == null)
392                                         _sma = ScriptMethodAttribute.Default;
393
394                                 if (ScriptMethod.ResponseFormat == ResponseFormat.Xml
395                                         && MethodInfo.ReturnType != typeof (void)) {
396                                         Type retType = MethodInfo.ReturnType;
397                                         if (Type.GetTypeCode (retType) != TypeCode.String || ScriptMethod.XmlSerializeString)
398                                                 _xmlSer = new XmlSerializer (retType);
399                                 }
400                         }
401
402                         IDictionary<string,object> BuildInvokeParameters (HttpRequest request)
403                         {
404                                 return "GET".Equals (request.RequestType, StringComparison.OrdinalIgnoreCase) ?
405                                         GetNameValueCollectionDictionary (request.QueryString) :
406                                         (IDictionary<string, object>) JavaScriptSerializer.DefaultSerializer.DeserializeObjectInternal (new StreamReader (request.InputStream, request.ContentEncoding));
407                         }
408
409                         IDictionary <string, object> GetNameValueCollectionDictionary (NameValueCollection nvc)
410                         {
411                                 var ret = new Dictionary <string, object> ();
412
413                                 for (int i = nvc.Count - 1; i >= 0; i--)
414                                         ret.Add (nvc.GetKey (i), nvc.Get (i));
415
416                                 return ret;
417                         }
418
419                         public override void Invoke (HttpRequest request, HttpResponse response) {
420                                 var writer = response.Output;
421                                 IDictionary<string, object> @params = BuildInvokeParameters (request);
422
423                                 object [] pp = null;
424                                 if (HasParameters) {
425                                         Type ptype;
426                                         int i;
427                                         object value;
428                                         pp = new object [_params.Length];
429
430                                         foreach (KeyValuePair<string, object> pair in @params) {
431                                                 if (!_paramMap.TryGetValue (pair.Key, out i))
432                                                         continue;
433
434                                                 value = pair.Value;
435                                                 ptype = _params [i].ParameterType;
436                                                 if (ptype == typeof (System.Object))
437                                                         pp [i] = value;
438                                                 else
439                                                         pp [i] = AsmxLogicalTypeInfo.JSSerializer.ConvertToType (value, ptype);
440                                         }
441                                 }
442
443                                 object target = MethodInfo.IsStatic ? null : Activator.CreateInstance (_typeInfo._type);
444                                 object result = MethodInfo.Invoke (target, pp);
445                                 if (_xmlSer != null) {
446                                         XmlTextWriter xwriter = new XmlTextWriter (writer);
447                                         xwriter.Formatting = Formatting.None;
448                                         _xmlSer.Serialize (xwriter, result);
449                                 }
450                                 else
451                                 {
452                                         result = new JsonResult (result);
453                                         AsmxLogicalTypeInfo.JSSerializer.Serialize (result, writer);
454                                 }
455                         }
456
457                         public override string MethodName { get { return String.IsNullOrEmpty (WebMethod.MessageName) ? MethodInfo.Name : WebMethod.MessageName; } }
458
459                         public ScriptMethodAttribute ScriptMethod { get { return _sma; } }
460                         public WebMethodAttribute WebMethod { get { return _wma; } }
461                         public override bool UseHttpGet { get { return ScriptMethod.UseHttpGet; } }
462                         public override bool EnableSession { get { return WebMethod.EnableSession; } }
463                         public override ResponseFormat ResponseFormat { get { return ScriptMethod.ResponseFormat; } }
464                 }
465
466                 #endregion
467
468                 //readonly LogicalMethodInfo [] _logicalMethods;
469                 internal static readonly JavaScriptSerializer JSSerializer = new JavaScriptSerializer (null, true);
470
471                 protected override List<LogicalMethodInfo> GetLogicalMethods (bool isPage)
472                 {
473                         BindingFlags bindingAttr = isPage ? (BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
474                         MethodInfo [] all_type_methods = _type.GetMethods (bindingAttr);
475                         List<LogicalMethodInfo> logicalMethods = new List<LogicalMethodInfo> (all_type_methods.Length);
476                         foreach (MethodInfo mi in all_type_methods) {
477                                 if (mi.IsPublic && 
478                                         mi.GetCustomAttributes (typeof (WebMethodAttribute), false).Length > 0)
479                                         logicalMethods.Add (new AsmxLogicalMethodInfo (this, mi));
480                                 else {
481                                         foreach (Type ifaceType in _type.GetInterfaces ()) {
482                                                 if (ifaceType.GetCustomAttributes (typeof (WebServiceBindingAttribute), false).Length > 0) {
483                                                         MethodInfo found = FindInInterface (ifaceType, mi);
484                                                         if (found != null) {
485                                                                 if (found.GetCustomAttributes (typeof (WebMethodAttribute), false).Length > 0)
486                                                                         logicalMethods.Add (new AsmxLogicalMethodInfo (this, found));
487
488                                                                 break;
489                                                         }
490                                                 }
491                                         }
492                                 }
493                         }
494                         return logicalMethods;
495                 }
496
497                 internal AsmxLogicalTypeInfo (Type t, string filePath) 
498                         : base (t, filePath)
499                 {
500                 }
501
502                 IEnumerable<GenerateScriptTypeAttribute> GetGenerateScriptTypeAttributes () {
503                         Hashtable generatedTypes = new Hashtable ();
504
505                         foreach (MemberInfo mi in GetGenerateScriptTypes ()) {
506                                 GenerateScriptTypeAttribute [] gstas = (GenerateScriptTypeAttribute []) mi.GetCustomAttributes (typeof (GenerateScriptTypeAttribute), true);
507                                 if (gstas == null || gstas.Length == 0)
508                                         continue;
509
510                                 for (int i = 0; i < gstas.Length; i++) {
511                                         if (!generatedTypes.Contains (gstas [i].Type)) {
512                                                 if (ShouldGenerateScript (gstas [i].Type, true)) {
513                                                         generatedTypes [gstas [i].Type] = gstas [i].Type;
514                                                         yield return gstas [i];
515                                                 }
516                                         }
517                                 }
518                         }
519
520                         foreach (LogicalMethodInfo lmi in _methodMap.Values) {
521                                 foreach (Type t in lmi.GetParameterTypes ()) {
522                                         Type param = GetTypeToGenerate (t);
523                                         if (!generatedTypes.Contains (param)) {
524                                                 if (ShouldGenerateScript (param, false)) {
525                                                         generatedTypes [param] = param;
526                                                         yield return new GenerateScriptTypeAttribute (param);
527                                                 }
528                                         }
529                                 }
530                         }
531                 }
532
533                 static Type GetTypeToGenerate (Type type) {
534                         if (type.IsArray)
535                                 return type.GetElementType ();
536                         if (type.IsGenericType) {
537                                 while (type.IsGenericType && type.GetGenericArguments ().Length == 1)
538                                         type = type.GetGenericArguments () [0];
539                                 return type;
540                         }
541                         return type;
542                 }
543
544                 static MethodInfo FindInInterface (Type ifaceType, MethodInfo method) {
545                         int nameStartIndex = 0;
546                         if (method.IsPrivate) {
547                                 nameStartIndex = method.Name.LastIndexOf ('.');
548                                 if (nameStartIndex < 0)
549                                         nameStartIndex = 0;
550                                 else {
551                                         if (String.CompareOrdinal (
552                                                 ifaceType.FullName.Replace ('+', '.'), 0, method.Name, 0, nameStartIndex) != 0)
553                                                 return null;
554
555                                         nameStartIndex++;
556                                 }
557                         }
558                         foreach (MethodInfo mi in ifaceType.GetMembers ()) {
559                                 if (method.ReturnType == mi.ReturnType &&
560                                         String.CompareOrdinal (method.Name, nameStartIndex, mi.Name, 0, mi.Name.Length) == 0) {
561                                         ParameterInfo [] rpi = method.GetParameters ();
562                                         ParameterInfo [] lpi = mi.GetParameters ();
563                                         if (rpi.Length == lpi.Length) {
564                                                 bool match = true;
565                                                 for (int i = 0; i < rpi.Length; i++) {
566                                                         if (rpi [i].ParameterType != lpi [i].ParameterType) {
567                                                                 match = false;
568                                                                 break;
569                                                         }
570                                                 }
571
572                                                 if (match)
573                                                         return mi;
574                                         }
575                                 }
576                         }
577
578                         return null;
579                 }
580
581                 protected override void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces)
582                 {
583                         bool gtc = false;
584
585                         foreach (GenerateScriptTypeAttribute gsta in GetGenerateScriptTypeAttributes ()) {
586                                 if (!gtc && !gsta.Type.IsEnum) {
587                                         proxy.Append (
588 @"
589 var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;");
590                                         gtc = true;
591                                 }
592                                 GenerateTypeRegistrationScript (proxy, gsta.Type, gsta.ScriptTypeId, registeredNamespaces);
593                         }
594                 }
595         }
596
597 #if NET_3_5
598         internal class WcfLogicalTypeInfo : LogicalTypeInfo
599         {
600                 ContractDescription cd;
601
602                 public WcfLogicalTypeInfo (Type type, string filePath)
603                         : base (type, filePath)
604                 {
605                 }
606
607                 ContractDescription Contract {
608                         get {
609                                 if (cd == null)
610                                         cd = ContractDescription.GetContract (_type);
611                                 return cd;
612                         }
613                 }
614
615                 IEnumerable<KeyValuePair<Type,string>> GetDataContractTypeInfos ()
616                 {
617                         foreach (var od in Contract.Operations) {
618                                 foreach (var md in od.Messages) {
619                                         foreach (var pd in md.Body.Parts) {
620                                                 if (ShouldGenerateScript (pd.Type, false))
621                                                         yield return new KeyValuePair<Type,string> (pd.Type, null);
622                                         }
623                                         if (md.Body.ReturnValue != null && ShouldGenerateScript (md.Body.ReturnValue.Type, false))
624                                                 yield return new KeyValuePair<Type,string> (md.Body.ReturnValue.Type, null);
625                                 }
626                         }
627                         yield break;
628                 }
629
630                 protected override void GetNamespaceAndServiceName (Type type, bool isPage, out string ns, out string service)
631                 {
632                         string name = type.Namespace;
633                         int dot = name.LastIndexOf ('.');
634                         if (dot > -1)
635                                 name = name.Substring (dot + 1);
636                         ns = name;
637                         service = name + "." + type.Name;
638                 }
639
640                 protected override void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces)
641                 {
642                         bool gtc = false;
643
644                         foreach (KeyValuePair<Type,string> pair in GetDataContractTypeInfos ()) {
645                                 if (!gtc && !pair.Key.IsEnum) {
646                                         proxy.Append (
647 @"
648 var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;");
649                                         gtc = true;
650                                 }
651                                 GenerateTypeRegistrationScript (proxy, pair.Key, pair.Value, registeredNamespaces);
652                         }
653                 }
654
655                 protected override List<LogicalMethodInfo> GetLogicalMethods (bool isPage)
656                 {
657                         if (isPage)
658                                 throw new NotSupportedException ();
659
660                         var l = new List<LogicalMethodInfo> ();
661                         foreach (var od in Contract.Operations)
662                                 l.Add (new WcfLogicalMethodInfo (this, od));
663                         return l;
664                 }
665
666                 internal class WcfLogicalMethodInfo : LogicalMethodInfo
667                 {
668                         OperationDescription od;
669
670                         public WcfLogicalMethodInfo (LogicalTypeInfo typeInfo, OperationDescription od)
671                                 : base (typeInfo, od.SyncMethod)
672                         {
673                                 this.od = od;
674                         }
675
676                         public override bool UseHttpGet { get { return true; } } // always
677
678                         // FIXME: could this be enabled?
679                         public override bool EnableSession {
680                                 get { return false; }
681                         }
682
683                         public override ResponseFormat ResponseFormat {
684                                 get { return ResponseFormat.Json; } // always
685                         }
686
687                         public override string MethodName {
688                                 get { return od.Name; }
689                         }
690
691                         public override void Invoke (HttpRequest request, HttpResponse response)
692                         {
693                                 // invocation is done in WCF part.
694                                 throw new NotSupportedException ();
695                         }
696                 }
697         }
698 #endif
699 }