New tests, update
[mono.git] / mcs / class / System.Web / System.Web.Compilation / TemplateControlCompiler.cs
index 7e8fddc52efd0f72c7377350db5e2f32f68f2d2f..421e0da03050f8f15e16a3d6148090509b048a67 100644 (file)
@@ -483,7 +483,7 @@ namespace System.Web.Compilation
                }
 
                void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound, bool isExpression)
-               {
+               {                       
                        CodeMemberMethod method = builder.method;
                        bool isWritable = IsWritablePropertyOrField (member);
                        if (isDataBound && isWritable) {
@@ -692,7 +692,7 @@ namespace System.Web.Compilation
 
                        Type t;
                        try {
-                               t = System.Type.GetType (builderType, true);
+                               t = HttpApplication.LoadType (builderType, true);
                        } catch (Exception e) {
                                throw new HttpException (
                                        String.Format ("Failed to load expression builder type `{0}'", builderType), e);
@@ -756,9 +756,13 @@ namespace System.Web.Compilation
        
                        if (StrUtils.StartsWith (inputFile, physPath))
                                inputFile = parser.InputFile.Substring (physPath.Length - 1);
-                        else
+                       else
                                return;
-                       
+
+                       char dsc = System.IO.Path.DirectorySeparatorChar;
+                       if (dsc != '/')
+                               inputFile = inputFile.Replace (dsc, '/');
+
                        object obj = HttpContext.GetLocalResourceObject (inputFile, resname);
                        if (obj == null)
                                return;
@@ -1269,8 +1273,22 @@ namespace System.Web.Compilation
                                bool doCheck = false;
                                
 #if NET_2_0
-                               TemplateBuilder pb = builder.parentBuilder as TemplateBuilder;
-                               if (pb == null || pb.TemplateInstance != TemplateInstance.Single)
+                               bool singleInstance = false;
+                               ControlBuilder pb = builder.parentBuilder;
+                               TemplateBuilder tpb;
+                               while (pb != null) {
+                                       tpb = pb as TemplateBuilder;
+                                       if (tpb == null) {
+                                               pb = pb.parentBuilder;
+                                               continue;
+                                       }
+                                       
+                                       if (tpb.TemplateInstance == TemplateInstance.Single)
+                                               singleInstance = true;
+                                       break;
+                               }
+                               
+                               if (!singleInstance)
 #endif
                                        builder.ID = builder.GetNextID (null);
 #if NET_2_0
@@ -1545,33 +1563,131 @@ namespace System.Web.Compilation
                        return str;
                }
 #endif
-    
-               CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member)
-               {                       
+
+               TypeConverter GetConverterForMember (MemberInfo member)
+               {
+                       TypeConverterAttribute tca = null;
+                       object[] attrs;
+                       
 #if NET_2_0
-                       bool wasNullable = false;
+                       attrs = member.GetCustomAttributes (typeof (TypeConverterAttribute), true);
+                       if (attrs.Length > 0)
+                               tca = attrs [0] as TypeConverterAttribute;
+#else
+                       attrs = member.GetCustomAttributes (true);
                        
+                       foreach (object attr in attrs) {
+                               tca = attr as TypeConverterAttribute;
+                               
+                               if (tca != null)
+                                       break;
+                       }
+#endif
+
+                       if (tca == null)
+                               return null;
+
+                       string typeName = tca.ConverterTypeName;
+                       if (typeName == null || typeName.Length == 0)
+                               return null;
+
+                       Type t = null;
+                       try {
+                               t = HttpApplication.LoadType (typeName);
+                       } catch (Exception) {
+                               // ignore
+                       }
+
+                       if (t == null)
+                               return null;
+
+                       return (TypeConverter) Activator.CreateInstance (t);
+               }
+               
+               CodeExpression CreateNullableExpression (Type type, CodeExpression inst, bool nullable)
+               {
+#if NET_2_0
+                       if (!nullable)
+                               return inst;
+                       
+                       return new CodeObjectCreateExpression (
+                               type,
+                               new CodeExpression[] {inst}
+                       );
+#else
+                       return inst;
+#endif
+               }
+
+               bool SafeCanConvertFrom (Type type, TypeConverter cvt)
+               {
+                       try {
+                               return cvt.CanConvertFrom (type);
+                       } catch (NotImplementedException) {
+                               return false;
+                       }
+               }
+
+               bool SafeCanConvertTo (Type type, TypeConverter cvt)
+               {
+                       try {
+                               return cvt.CanConvertTo (type);
+                       } catch (NotImplementedException) {
+                               return false;
+                       }
+               }
+               
+               CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member)
+               {
+                       TypeConverter cvt = GetConverterForMember (member);
+                       if (cvt != null && !SafeCanConvertFrom (typeof (string), cvt))
+                               cvt = null;
+
+                       object convertedFromAttr = null;
+                       bool preConverted = false;
+                       if (cvt != null && str != null) {
+                               convertedFromAttr = cvt.ConvertFromInvariantString (str);
+                               if (convertedFromAttr != null) {
+                                       type = convertedFromAttr.GetType ();
+                                       preConverted = true;
+                               }
+                       }
+
+                       bool wasNullable = false;
+                       Type originalType = type;
+#if NET_2_0
                        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
                                Type[] types = type.GetGenericArguments();
+                               originalType = type;
                                type = types[0]; // we're interested only in the first type here
                                wasNullable = true;
                        }
 #endif
-
+                       
                        if (type == typeof (string)) {
+                               if (preConverted)
+                                       return CreateNullableExpression (originalType,
+                                                                        new CodePrimitiveExpression ((string) convertedFromAttr),
+                                                                        wasNullable);
+                               
 #if NET_2_0
                                object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true);
                                if (urlAttr.Length != 0)
                                        str = HandleUrlProperty (str, member);
 #endif
-                               return new CodePrimitiveExpression (str);
+                               return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
                        }
 
                        if (type == typeof (bool)) {
+                               if (preConverted)
+                                       return CreateNullableExpression (originalType,
+                                                                        new CodePrimitiveExpression ((bool) convertedFromAttr),
+                                                                        wasNullable);
+                               
                                if (str == null || str == "" || InvariantCompareNoCase (str, "true"))
-                                       return new CodePrimitiveExpression (true);
+                                       return CreateNullableExpression (originalType, new CodePrimitiveExpression (true), wasNullable);
                                else if (InvariantCompareNoCase (str, "false"))
-                                       return new CodePrimitiveExpression (false);
+                                       return CreateNullableExpression (originalType, new CodePrimitiveExpression (false), wasNullable);
 #if NET_2_0
                                else if (wasNullable && InvariantCompareNoCase(str, "null"))
                                        return new CodePrimitiveExpression (null);
@@ -1580,72 +1696,86 @@ namespace System.Web.Compilation
                                        throw new ParseException (currentLocation,
                                                        "Value '" + str  + "' is not a valid boolean.");
                        }
-
+                       
                        if (str == null)
                                return new CodePrimitiveExpression (null);
 
                        if (type.IsPrimitive)
-                               return new CodePrimitiveExpression (Convert.ChangeType (str, type, CultureInfo.InvariantCulture));
+                               return CreateNullableExpression (originalType,
+                                                                new CodePrimitiveExpression (
+                                                                        Convert.ChangeType (preConverted ? convertedFromAttr : str,
+                                                                                            type, CultureInfo.InvariantCulture)),
+                                                                wasNullable);
 
                        if (type == typeof (string [])) {
-                               string [] subs = str.Split (',');
+                               string [] subs;
+
+                               if (preConverted)
+                                       subs = (string[])convertedFromAttr;
+                               else
+                                       subs = str.Split (',');
                                CodeArrayCreateExpression expr = new CodeArrayCreateExpression ();
                                expr.CreateType = new CodeTypeReference (typeof (string));
-                               foreach (string v in subs) {
+                               foreach (string v in subs)
                                        expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ()));
-                               }
 
-                               return expr;
+                               return CreateNullableExpression (originalType, expr, wasNullable);
                        }
-
-                       if (type == typeof (Color)){
-                               if (colorConverter == null)
-                                       colorConverter = TypeDescriptor.GetConverter (typeof (Color));
-
-                               if (str.Trim().Length == 0) {
-                                       CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color));
-                                       return new CodeFieldReferenceExpression (ft, "Empty");
-                               }
-                               
+                       
+                       if (type == typeof (Color)) {
                                Color c;
-                               try {
-                                       if (str.IndexOf (',') == -1) {
-                                               c = (Color) colorConverter.ConvertFromString (str);
-                                       } else {
-                                               int [] argb = new int [4];
-                                               argb [0] = 255;
-
-                                               string [] parts = str.Split (',');
-                                               int length = parts.Length;
-                                               if (length < 3)
-                                                       throw new Exception ();
-
-                                               int basei = (length == 4) ? 0 : 1;
-                                               for (int i = length - 1; i >= 0; i--) {
-                                                       argb [basei + i] = (int) Byte.Parse (parts [i]);
-                                               }
-                                               c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
-                                       }
-                               } catch (Exception e){
-                                       // Hack: "LightGrey" is accepted, but only for ASP.NET, as the
-                                       // TypeConverter for Color fails to ConvertFromString.
-                                       // Hence this hack...
-                                       if (InvariantCompareNoCase ("LightGrey", str)) {
-                                               c = Color.LightGray;
-                                       } else {
-                                               throw new ParseException (currentLocation,
-                                                       "Color " + str + " is not a valid color.", e);
+                               
+                               if (!preConverted) {
+                                       if (colorConverter == null)
+                                               colorConverter = TypeDescriptor.GetConverter (typeof (Color));
+                               
+                                       if (str.Trim().Length == 0) {
+                                               CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color));
+                                               return CreateNullableExpression (originalType,
+                                                                                new CodeFieldReferenceExpression (ft, "Empty"),
+                                                                                wasNullable);
                                        }
-                               }
 
-                               if (c.IsKnownColor){
+                                       try {
+                                               if (str.IndexOf (',') == -1) {
+                                                       c = (Color) colorConverter.ConvertFromString (str);
+                                               } else {
+                                                       int [] argb = new int [4];
+                                                       argb [0] = 255;
+
+                                                       string [] parts = str.Split (',');
+                                                       int length = parts.Length;
+                                                       if (length < 3)
+                                                               throw new Exception ();
+
+                                                       int basei = (length == 4) ? 0 : 1;
+                                                       for (int i = length - 1; i >= 0; i--) {
+                                                               argb [basei + i] = (int) Byte.Parse (parts [i]);
+                                                       }
+                                                       c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
+                                               }
+                                       } catch (Exception e) {
+                                               // Hack: "LightGrey" is accepted, but only for ASP.NET, as the
+                                               // TypeConverter for Color fails to ConvertFromString.
+                                               // Hence this hack...
+                                               if (InvariantCompareNoCase ("LightGrey", str)) {
+                                                       c = Color.LightGray;
+                                               } else {
+                                                       throw new ParseException (currentLocation,
+                                                                                 "Color " + str + " is not a valid color.", e);
+                                               }
+                                       }
+                               } else
+                                       c = (Color)convertedFromAttr;
+                               
+                               if (c.IsKnownColor) {
                                        CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
                                        if (c.IsSystemColor)
                                                type = typeof (SystemColors);
 
                                        expr.TargetObject = new CodeTypeReferenceExpression (type);
                                        expr.FieldName = c.Name;
-                                       return expr;
+                                       return CreateNullableExpression (originalType, expr, wasNullable);
                                } else {
                                        CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
                                        m.TargetObject = new CodeTypeReferenceExpression (type);
@@ -1655,22 +1785,29 @@ namespace System.Web.Compilation
                                        invoke.Parameters.Add (new CodePrimitiveExpression (c.R));
                                        invoke.Parameters.Add (new CodePrimitiveExpression (c.G));
                                        invoke.Parameters.Add (new CodePrimitiveExpression (c.B));
-                                       return invoke;
+                                       return CreateNullableExpression (originalType, invoke, wasNullable);
                                }
                        }
 
-                       TypeConverter converter = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter;
-                       
-                       if (converter != null && converter.CanConvertFrom (typeof (string))) {
-                               object value = converter.ConvertFromInvariantString (str);
+                       TypeConverter converter = preConverted ? cvt :
+                               wasNullable ? TypeDescriptor.GetConverter (type) :
+                               TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter;
+
+                       if (preConverted || (converter != null && SafeCanConvertFrom (typeof (string), converter))) {
+                               object value = preConverted ? convertedFromAttr : converter.ConvertFromInvariantString (str);
 
-                               if (converter.CanConvertTo (typeof (InstanceDescriptor))) {
+                               if (SafeCanConvertTo (typeof (InstanceDescriptor), converter)) {
                                        InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
-                                       return GenerateInstance (idesc, true);
+                                       if (wasNullable)
+                                               return CreateNullableExpression (originalType, GenerateInstance (idesc, true),
+                                                                                wasNullable);
+                                       
+                                       return new CodeCastExpression (type, GenerateInstance (idesc, true));
                                }
 
                                CodeExpression exp = GenerateObjectInstance (value, false);
-                               if (exp != null) return exp;
+                               if (exp != null)
+                                       return CreateNullableExpression (originalType, exp, wasNullable);
                                
                                CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
                                m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor));
@@ -1682,12 +1819,15 @@ namespace System.Web.Compilation
                                invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom");
                                invoke.Parameters.Add (new CodePrimitiveExpression (str));
 
-                               return new CodeCastExpression (tref, invoke);
+                               if (wasNullable)
+                                       return CreateNullableExpression (originalType, invoke, wasNullable);
+                               
+                               return new CodeCastExpression (type, invoke);
                        }
 
                        Console.WriteLine ("Unknown type: " + type + " value: " + str);
-
-                       return new CodePrimitiveExpression (str);
+                       
+                       return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
                }
                
                CodeExpression GenerateInstance (InstanceDescriptor idesc, bool throwOnError)
@@ -1724,11 +1864,17 @@ namespace System.Web.Compilation
                {
                        if (value == null)
                                return new CodePrimitiveExpression (null);
+
+                       if (value is System.Type) {
+                               CodeTypeReference tref = new CodeTypeReference (value.ToString ());
+                               return new CodeTypeOfExpression (tref);
+                       }
                        
-                       Type t = value.GetType();
+                       Type t = value.GetType ();
+
                        if (t.IsPrimitive || value is string)
                                return new CodePrimitiveExpression (value);
-                               
+                       
                        if (t.IsArray) {
                                Array ar = (Array) value;
                                CodeExpression[] items = new CodeExpression [ar.Length];