2010-06-05 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspComponentFoundry.cs
index c05c719fd66ef6100662c052245a13af1f54b803..cc3268e383301e078c72bfc23d178c0f2da628c1 100644 (file)
 //
 using System;
 using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Reflection;
+using System.Web;
+using System.Web.Configuration;
+using System.Web.UI;
+using System.Web.Util;
 
 namespace System.Web.Compilation
 {
-       internal class AspComponentFoundry
+       class AspComponentFoundry
        {
-               private Hashtable foundries;
-
+               Hashtable foundries;
+               Dictionary <string, AspComponent> components;
+               Dictionary <string, AspComponent> Components {
+                       get {
+                               if (components == null)
+                                       components = new Dictionary <string, AspComponent> (StringComparer.OrdinalIgnoreCase);
+                               return components;
+                       }
+               }
+               
                public AspComponentFoundry ()
                {
-#if NET_2_0
                        foundries = new Hashtable (StringComparer.InvariantCultureIgnoreCase);
-#else
-                       foundries = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
-                                                  CaseInsensitiveComparer.DefaultInvariant);
-#endif
-
                        Assembly sw = typeof (AspComponentFoundry).Assembly;
                        RegisterFoundry ("asp", sw, "System.Web.UI.WebControls");
                        RegisterFoundry ("", "object", typeof (System.Web.UI.ObjectTag));
+                       RegisterConfigControls ();
                }
 
-               public Type GetComponentType (string foundryName, string tag)
+               public AspComponent GetComponent (string tagName)
                {
-                       Foundry foundry = foundries [foundryName] as Foundry;
-                       if (foundry == null)
+                       if (tagName == null || tagName.Length == 0)
+                               return null;
+                       
+                       if (components != null) {
+                               AspComponent ret;
+                               if (components.TryGetValue (tagName, out ret))
+                                       return ret;
+                       }
+
+                       string foundryName, tag;
+                       int colon = tagName.IndexOf (':');
+                       if (colon > -1) {
+                               if (colon == 0)
+                                       throw new Exception ("Empty TagPrefix is not valid.");
+                               if (colon + 1 == tagName.Length)
+                                       return null;
+                               foundryName = tagName.Substring (0, colon);
+                               tag = tagName.Substring (colon + 1);
+                       } else {
+                               foundryName = String.Empty;
+                               tag = tagName;
+                       }
+                       
+                       object o = foundries [foundryName];                     
+                       if (o == null)
+                               return null;
+
+                       Foundry foundry = o as Foundry;
+                       if (foundry != null)
+                               return CreateComponent (foundry, tagName, foundryName, tag);
+                       
+                       ArrayList af = o as ArrayList;
+                       if (af == null)
                                return null;
 
-                       return foundry.GetType (tag);
+                       AspComponent component = null;
+                       Exception e = null;
+                       foreach (Foundry f in af) {
+                               try {
+                                       component = CreateComponent (f, tagName, foundryName, tag);
+                                       if (component != null)
+                                               return component;
+                               } catch (Exception ex) {
+                                       e = ex;
+                               }
+                       }
+
+                       if (e != null)
+                               throw e;
+                       
+                       return null;
                }
 
+               AspComponent CreateComponent (Foundry foundry, string tagName, string prefix, string tag)
+               {
+                       string source, ns;
+                       Type type;
+
+                       type = foundry.GetType (tag, out source, out ns);
+                       if (type == null)
+                               return null;
+                       
+                       AspComponent ret = new AspComponent (type, ns, prefix, source, foundry.FromConfig);
+                       Dictionary <string, AspComponent> components = Components;
+                       components.Add (tagName, ret);
+                       return ret;
+               }
+               
+               public void RegisterFoundry (string foundryName, Assembly assembly, string nameSpace)
+               {
+                       RegisterFoundry (foundryName, assembly, nameSpace, false);
+               }
+               
                public void RegisterFoundry (string foundryName,
-                                               Assembly assembly,
-                                               string nameSpace)
+                                            Assembly assembly,
+                                            string nameSpace,
+                                            bool fromConfig)
                {
                        AssemblyFoundry foundry = new AssemblyFoundry (assembly, nameSpace);
-                       InternalRegister (foundryName, foundry);
+                       foundry.FromConfig = fromConfig;
+                       InternalRegister (foundryName, foundry, fromConfig);
                }
 
+               public void RegisterFoundry (string foundryName, string tagName, Type type)
+               {
+                       RegisterFoundry (foundryName, tagName, type, false);
+               }
+               
                public void RegisterFoundry (string foundryName,
-                                               string tagName,
-                                               Type type)
+                                            string tagName,
+                                            Type type,
+                                            bool fromConfig)
                {
                        TagNameFoundry foundry = new TagNameFoundry (tagName, type);
-                       InternalRegister (foundryName, foundry);
+                       foundry.FromConfig = fromConfig;
+                       InternalRegister (foundryName, foundry, fromConfig);
+               }
+
+               public void RegisterFoundry (string foundryName, string tagName, string source)
+               {
+                       RegisterFoundry (foundryName, tagName, source, false);
+               }
+               
+               public void RegisterFoundry (string foundryName,
+                                            string tagName,
+                                            string source,
+                                            bool fromConfig)
+               {
+                       TagNameFoundry foundry = new TagNameFoundry (tagName, source);
+                       foundry.FromConfig = fromConfig;
+                       InternalRegister (foundryName, foundry, fromConfig);
                }
 
-               void InternalRegister (string foundryName, Foundry foundry)
+               public void RegisterAssemblyFoundry (string foundryName,
+                                                    string assemblyName,
+                                                    string nameSpace,
+                                                    bool fromConfig)
+               {
+                       AssemblyFoundry foundry = new AssemblyFoundry (assemblyName, nameSpace);
+                       foundry.FromConfig = fromConfig;
+                       InternalRegister (foundryName, foundry, fromConfig);
+               }               
+
+               void RegisterConfigControls ()
+               {
+                       PagesSection pages = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+                       if (pages == null)
+                               return;
+
+                       TagPrefixCollection controls = pages.Controls;
+                       if (controls == null || controls.Count == 0)
+                               return;
+                       
+                       IList appCode = BuildManager.CodeAssemblies;
+                       bool haveCodeAssemblies = appCode != null && appCode.Count > 0;
+                       Assembly asm;
+                       foreach (TagPrefixInfo tpi in controls) {
+                               if (!String.IsNullOrEmpty (tpi.TagName))
+                                       RegisterFoundry (tpi.TagPrefix, tpi.TagName, tpi.Source, true);
+                               else if (String.IsNullOrEmpty (tpi.Assembly)) {
+                                       if (haveCodeAssemblies) {
+                                               foreach (object o in appCode) {
+                                                       asm = o as Assembly;
+                                                       if (asm == null)
+                                                               continue;
+                                                       RegisterFoundry (tpi.TagPrefix, asm, tpi.Namespace, true);
+                                               }
+                                       }
+                               } else if (!String.IsNullOrEmpty (tpi.Namespace))
+                                       RegisterAssemblyFoundry (tpi.TagPrefix,
+                                                                tpi.Assembly,
+                                                                tpi.Namespace,
+                                                                true);
+                       }
+               }
+               
+               void InternalRegister (string foundryName, Foundry foundry, bool fromConfig)
                {
                        object f = foundries [foundryName];
+                       Foundry newFoundry = null;
+                       
                        if (f is CompoundFoundry) {
                                ((CompoundFoundry) f).Add (foundry);
-                       } else if (f == null || (f is AssemblyFoundry && foundry is AssemblyFoundry)) {
-                               // If more than 1 namespace/assembly specified, the last one is used.
-                               foundries [foundryName] = foundry;
+                               return;
+                       } else if (f == null || f is ArrayList || (f is AssemblyFoundry && foundry is AssemblyFoundry)) {
+                               newFoundry = foundry;
                        } else if (f != null) {
                                CompoundFoundry compound = new CompoundFoundry (foundryName);
                                compound.Add ((Foundry) f);
                                compound.Add (foundry);
-                               foundries [foundryName] = compound;
+                               newFoundry = foundry;
+                               newFoundry.FromConfig = fromConfig;
+                       }
+
+                       if (newFoundry == null)
+                               return;
+
+                       if (f == null) {
+                               foundries [foundryName] = newFoundry;
+                               return;
                        }
+
+                       ArrayList af = f as ArrayList;
+                       if (af == null) {
+                               af = new ArrayList (2);
+                               af.Add (f);
+                               foundries [foundryName] = af;
+                       }
+
+                       if (newFoundry is AssemblyFoundry) {
+                               object o;
+                               for (int i = 0; i < af.Count; i++) {
+                                       o = af [i];
+                                       if (o is AssemblyFoundry) {
+                                               af.Insert (i, newFoundry);
+                                               return;
+                                       }
+                               }
+                               af.Add (newFoundry);
+                       } else
+                               af.Insert (0, newFoundry);
                }
 
                public bool LookupFoundry (string foundryName)
@@ -100,7 +273,14 @@ namespace System.Web.Compilation
 
                abstract class Foundry
                {
-                       public abstract Type GetType (string componentName);
+                       bool _fromConfig;
+
+                       public bool FromConfig {
+                               get { return _fromConfig; }
+                               set { _fromConfig = value; }
+                       }
+                       
+                       public abstract Type GetType (string componentName, out string source, out string ns);
                }
                
 
@@ -108,21 +288,64 @@ namespace System.Web.Compilation
                {
                        string tagName;
                        Type type;
+                       string source;
 
+                       public bool FromWebConfig {
+                               get { return source != null; }
+                       }
+                       
+                       public TagNameFoundry (string tagName, string source)
+                       {
+                               this.tagName = tagName;
+                               this.source = source;
+                       }
+                       
                        public TagNameFoundry (string tagName, Type type)
                        {
                                this.tagName = tagName;
                                this.type = type;
                        }
 
-                       public override Type GetType (string componentName)
+                       public override Type GetType (string componentName, out string source, out string ns)
                        {
-                               if (0 != String.Compare (componentName, tagName, true))
+                               source = null;
+                               ns = null;
+                               if (0 != String.Compare (componentName, tagName, true, Helpers.InvariantCulture))
                                        return null;
+
+                               source = this.source;
+                               return LoadType ();
+                       }
+
+                       Type LoadType ()
+                       {
+                               if (type != null)
+                                       return type;
+
+                               HttpContext context = HttpContext.Current;
+                               string vpath;
+                               string realpath;
+                               
+                               if (VirtualPathUtility.IsAppRelative (source)) {
+                                       vpath = source;
+                                       realpath = context.Request.MapPath (source);
+                               } else {
+                                       vpath = VirtualPathUtility.ToAppRelative (source);
+                                       realpath = source;
+                               }
                                
+                               if ((type = CachingCompiler.GetTypeFromCache (realpath)) != null)
+                                       return type;
+                               
+                               ArrayList other_deps = new ArrayList ();
+                               type = BuildManager.GetCompiledType (vpath);
+                               if (type != null) {
+                                       AspGenerator.AddTypeToCache (other_deps, realpath, type);
+                                       BuildManager.AddToReferencedAssemblies (type.Assembly);
+                               }
                                return type;
                        }
-
+                       
                        public string TagName {
                                get { return tagName; }
                        }
@@ -132,16 +355,84 @@ namespace System.Web.Compilation
                {
                        string nameSpace;
                        Assembly assembly;
-
+                       string assemblyName;
+                       Dictionary <string, Assembly> assemblyCache;
+                       
                        public AssemblyFoundry (Assembly assembly, string nameSpace)
                        {
                                this.assembly = assembly;
                                this.nameSpace = nameSpace;
+
+                               if (assembly != null)
+                                       this.assemblyName = assembly.FullName;
+                               else
+                                       this.assemblyName = null;
+                       }
+
+                       public AssemblyFoundry (string assemblyName, string nameSpace)
+                       {
+                               this.assembly = null;
+                               this.nameSpace = nameSpace;
+                               this.assemblyName = assemblyName;
+                       }
+                       
+                       public override Type GetType (string componentName, out string source, out string ns)
+                       {
+                               source = null;
+                               ns = nameSpace;
+
+                               if (assembly == null && assemblyName != null)
+                                       assembly = GetAssemblyByName (assemblyName, true);
+
+                               string typeName = String.Concat (nameSpace, ".", componentName);
+                               if (assembly != null)
+                                       return assembly.GetType (typeName, false, true);
+
+                               IList tla = BuildManager.TopLevelAssemblies;
+                               if (tla != null && tla.Count > 0) {
+                                       Type ret = null;
+                                       foreach (Assembly asm in tla) {
+                                               if (asm == null)
+                                                       continue;
+                                               ret = asm.GetType (typeName, false, true);
+                                               if (ret != null)
+                                                       return ret;
+                                       }
+                               }
+
+                               return null;
                        }
 
-                       public override Type GetType (string componentName)
+                       Assembly GetAssemblyByName (string name, bool throwOnMissing)
                        {
-                               return assembly.GetType (nameSpace + "." + componentName, true, true);
+                               if (assemblyCache == null)
+                                       assemblyCache = new Dictionary <string, Assembly> ();
+                               
+                               if (assemblyCache.ContainsKey (name))
+                                       return assemblyCache [name];
+                               
+                               Assembly assembly = null;
+                               Exception error = null;
+                               if (name.IndexOf (',') != -1) {
+                                       try {
+                                               assembly = Assembly.Load (name);
+                                       } catch (Exception e) { error = e; }
+                               }
+
+                               if (assembly == null) {
+                                       try {
+                                               assembly = Assembly.LoadWithPartialName (name);
+                                       } catch (Exception e) { error = e; }
+                               }
+                       
+                               if (assembly == null)
+                                       if (throwOnMissing)
+                                               throw new HttpException ("Assembly " + name + " not found", error);
+                                       else
+                                               return null;
+
+                               assemblyCache.Add (name, assembly);
+                               return assembly;
                        }
                }
 
@@ -154,12 +445,7 @@ namespace System.Web.Compilation
                        public CompoundFoundry (string tagPrefix)
                        {
                                this.tagPrefix = tagPrefix;
-#if NET_2_0
                                tagnames = new Hashtable (StringComparer.InvariantCultureIgnoreCase);
-#else
-                               tagnames = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
-                                                         CaseInsensitiveComparer.DefaultInvariant);
-#endif
                        }
 
                        public void Add (Foundry foundry)
@@ -172,31 +458,34 @@ namespace System.Web.Compilation
                                TagNameFoundry tn = (TagNameFoundry) foundry;
                                string tagName = tn.TagName;
                                if (tagnames.Contains (tagName)) {
+                                       if (tn.FromWebConfig)
+                                               return;
+
                                        string msg = String.Format ("{0}:{1} already registered.", tagPrefix, tagName);
                                        throw new ApplicationException (msg);
                                }
                                tagnames.Add (tagName, foundry);
                        }
 
-                       public override Type GetType (string componentName)
+                       public override Type GetType (string componentName, out string source, out string ns)
                        {
+                               source = null;
+                               ns = null;
                                Type type = null;
                                Foundry foundry = tagnames [componentName] as Foundry;
                                if (foundry != null)
-                                       return foundry.GetType (componentName);
+                                       return foundry.GetType (componentName, out source, out ns);
 
                                if (assemblyFoundry != null) {
                                        try {
-                                               type = assemblyFoundry.GetType (componentName);
+                                               type = assemblyFoundry.GetType (componentName, out source, out ns);
                                                return type;
                                        } catch { }
                                }
 
-                               string msg = String.Format ("Type {0} not registered for prefix {1}",
-                                                                    componentName, tagPrefix);
+                               string msg = String.Format ("Type {0} not registered for prefix {1}", componentName, tagPrefix);
                                throw new ApplicationException (msg);
                        }
                }
        }
 }
-