2 // System.Web.UI.TemplateParser
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.CodeDom.Compiler;
32 using System.Collections;
33 using System.Globalization;
35 using System.Reflection;
36 using System.Security.Permissions;
37 using System.Web.Compilation;
38 using System.Web.Configuration;
39 using System.Web.Util;
41 namespace System.Web.UI {
44 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46 public abstract class TemplateParser : BaseParser
50 string privateBinPath;
51 Hashtable mainAttributes;
52 ArrayList dependencies;
60 RootBuilder rootBuilder;
62 string compilerOptions;
64 bool strictOn = false;
65 bool explicitOn = false;
68 string oc_header, oc_custom, oc_param, oc_controls;
70 OutputCacheLocation oc_location;
71 CultureInfo invariantCulture = CultureInfo.InvariantCulture;
74 string partialClassName;
77 int appAssemblyIndex = -1;
79 internal TemplateParser ()
81 imports = new ArrayList ();
83 AddNamespaces (imports);
85 imports.Add ("System");
86 imports.Add ("System.Collections");
87 imports.Add ("System.Collections.Specialized");
88 imports.Add ("System.Configuration");
89 imports.Add ("System.Text");
90 imports.Add ("System.Text.RegularExpressions");
91 imports.Add ("System.Web");
92 imports.Add ("System.Web.Caching");
93 imports.Add ("System.Web.Security");
94 imports.Add ("System.Web.SessionState");
95 imports.Add ("System.Web.UI");
96 imports.Add ("System.Web.UI.WebControls");
97 imports.Add ("System.Web.UI.HtmlControls");
100 assemblies = new ArrayList ();
102 bool addAssembliesInBin = false;
103 foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
104 if (info.Assembly == "*")
105 addAssembliesInBin = true;
107 AddAssemblyByName (info.Assembly);
109 if (addAssembliesInBin)
110 AddAssembliesInBin ();
112 foreach (NamespaceInfo info in PagesConfig.Namespaces) {
113 imports.Add (info.Namespace);
116 foreach (string a in CompilationConfig.Assemblies)
117 AddAssemblyByName (a);
118 if (CompilationConfig.AssembliesInBin)
119 AddAssembliesInBin ();
122 language = CompilationConfig.DefaultLanguage;
125 internal void AddApplicationAssembly ()
127 string location = Context.ApplicationInstance.AssemblyLocation;
128 if (location != typeof (TemplateParser).Assembly.Location) {
129 appAssemblyIndex = assemblies.Add (location);
133 protected abstract Type CompileIntoType ();
136 void AddNamespaces (ArrayList imports)
138 if (BuildManager.HaveResources)
139 imports.Add ("System.Resources");
141 PagesSection pages = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
145 NamespaceCollection namespaces = pages.Namespaces;
146 if (namespaces == null || namespaces.Count == 0)
149 foreach (NamespaceInfo nsi in namespaces)
150 imports.Add (nsi.Namespace);
153 internal void RegisterConfigControls ()
155 PagesSection pages = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
159 TagPrefixCollection controls = pages.Controls;
160 if (controls == null || controls.Count == 0)
163 foreach (TagPrefixInfo tpi in controls) {
164 if (!String.IsNullOrEmpty (tpi.TagName))
165 RegisterCustomControl (tpi.TagPrefix, tpi.TagName, tpi.Source);
166 else if (!String.IsNullOrEmpty (tpi.Namespace))
167 RegisterNamespace (tpi.TagPrefix, tpi.Namespace, tpi.Assembly);
172 internal void RegisterCustomControl (string tagPrefix, string tagName, string src)
174 string realpath = MapPath (src);
175 if (String.Compare (realpath, inputFile, false, invariantCulture) == 0)
178 if (!File.Exists (realpath))
179 throw new ParseException (Location, "Could not find file \"" + realpath + "\".");
180 string vpath = UrlUtils.Combine (BaseVirtualDir, src);
182 AddDependency (realpath);
184 ArrayList other_deps = new ArrayList ();
185 type = UserControlParser.GetCompiledType (vpath, realpath, other_deps, Context);
186 foreach (string s in other_deps) {
189 } catch (ParseException pe) {
190 if (this is UserControlParser)
191 throw new ParseException (Location, pe.Message, pe);
195 AddAssembly (type.Assembly, true);
196 RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, type);
199 internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
202 Assembly ass = AddAssemblyByName (assembly);
203 AddDependency (ass.Location);
204 RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
207 internal virtual void HandleOptions (object obj)
211 internal static string GetOneKey (Hashtable tbl)
213 foreach (object key in tbl.Keys)
214 return key.ToString ();
219 internal virtual void AddDirective (string directive, Hashtable atts)
221 if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
222 if (mainAttributes != null)
223 ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
225 mainAttributes = atts;
226 ProcessMainAttributes (mainAttributes);
230 int cmp = String.Compare ("Assembly", directive, true);
232 string name = GetString (atts, "Name", null);
233 string src = GetString (atts, "Src", null);
236 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
238 if (name == null && src == null)
239 ThrowParseException ("You gotta specify Src or Name");
241 if (name != null && src != null)
242 ThrowParseException ("Src and Name cannot be used together");
245 AddAssemblyByName (name);
247 GetAssemblyFromSource (src);
253 cmp = String.Compare ("Import", directive, true);
255 string namesp = GetString (atts, "Namespace", null);
257 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
259 if (namesp != null && namesp != "")
264 cmp = String.Compare ("Implements", directive, true);
266 string ifacename = GetString (atts, "Interface", "");
269 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
271 Type iface = LoadType (ifacename);
273 ThrowParseException ("Cannot find type " + ifacename);
275 if (!iface.IsInterface)
276 ThrowParseException (iface + " is not an interface");
278 AddInterface (iface.FullName);
282 cmp = String.Compare ("OutputCache", directive, true);
286 if (atts ["Duration"] == null)
287 ThrowParseException ("The directive is missing a 'duration' attribute.");
288 if (atts ["VaryByParam"] == null)
289 ThrowParseException ("This directive is missing a 'VaryByParam' " +
290 "attribute, which should be set to \"none\", \"*\", " +
291 "or a list of name/value pairs.");
293 foreach (DictionaryEntry entry in atts) {
294 string key = (string) entry.Key;
295 switch (key.ToLower ()) {
297 oc_duration = Int32.Parse ((string) entry.Value);
299 ThrowParseException ("The 'duration' attribute must be set " +
300 "to a positive integer value");
303 oc_param = (string) entry.Value;
304 if (String.Compare (oc_param, "none") == 0)
308 oc_header = (string) entry.Value;
311 oc_custom = (string) entry.Value;
314 if (!(this is PageParser))
318 oc_location = (OutputCacheLocation) Enum.Parse (
319 typeof (OutputCacheLocation), (string) entry.Value, true);
321 ThrowParseException ("The 'location' attribute is case sensitive and " +
322 "must be one of the following values: Any, Client, " +
323 "Downstream, Server, None, ServerAndClient.");
326 case "varybycontrol":
327 if (this is PageParser)
330 oc_controls = (string) entry.Value;
333 if (this is PageParser)
337 oc_shared = Boolean.Parse ((string) entry.Value);
339 ThrowParseException ("The 'shared' attribute is case sensitive" +
340 " and must be set to 'true' or 'false'.");
344 ThrowParseException ("The '" + key + "' attribute is not " +
345 "supported by the 'Outputcache' directive.");
354 ThrowParseException ("Unknown directive: " + directive);
357 internal Type LoadType (string typeName)
359 // First try loaded assemblies, then try assemblies in Bin directory.
361 bool seenBin = false;
362 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
363 foreach (Assembly ass in assemblies) {
364 type = ass.GetType (typeName);
368 if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
369 AddAssembly (ass, true);
374 AddDependency (ass.Location);
382 if (!Directory.Exists (PrivateBinPath))
385 string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
386 foreach (string s in binDlls) {
387 Assembly binA = Assembly.LoadFrom (s);
388 type = binA.GetType (typeName);
392 AddDependency (binA.Location);
399 void AddAssembliesInBin ()
401 if (!Directory.Exists (PrivateBinPath))
404 string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
405 foreach (string s in binDlls) {
410 internal virtual void AddInterface (string iface)
412 if (interfaces == null)
413 interfaces = new ArrayList ();
415 if (!interfaces.Contains (iface))
416 interfaces.Add (iface);
419 internal virtual void AddImport (string namesp)
422 imports = new ArrayList ();
424 if (!imports.Contains (namesp))
425 imports.Add (namesp);
428 internal virtual void AddSourceDependency (string filename)
430 if (dependencies != null && dependencies.Contains (filename)) {
431 ThrowParseException ("Circular file references are not allowed. File: " + filename);
434 AddDependency (filename);
437 internal virtual void AddDependency (string filename)
442 if (dependencies == null)
443 dependencies = new ArrayList ();
445 if (!dependencies.Contains (filename))
446 dependencies.Add (filename);
449 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
451 if (assembly.Location == "")
455 anames = new Hashtable ();
457 string name = assembly.GetName ().Name;
458 string loc = assembly.Location;
460 if (!assemblies.Contains (loc)) {
461 assemblies.Add (loc);
465 anames [loc] = assembly;
467 if (!assemblies.Contains (name)) {
468 assemblies.Add (name);
471 anames [name] = assembly;
475 internal virtual Assembly AddAssemblyByFileName (string filename)
477 Assembly assembly = null;
478 Exception error = null;
481 assembly = Assembly.LoadFrom (filename);
482 } catch (Exception e) { error = e; }
484 if (assembly == null)
485 ThrowParseException ("Assembly " + filename + " not found", error);
487 AddAssembly (assembly, true);
491 internal virtual Assembly AddAssemblyByName (string name)
494 anames = new Hashtable ();
496 if (anames.Contains (name)) {
497 object o = anames [name];
504 Assembly assembly = null;
505 Exception error = null;
506 if (name.IndexOf (',') != -1) {
508 assembly = Assembly.Load (name);
509 } catch (Exception e) { error = e; }
512 if (assembly == null) {
514 assembly = Assembly.LoadWithPartialName (name);
515 } catch (Exception e) { error = e; }
518 if (assembly == null)
519 ThrowParseException ("Assembly " + name + " not found", error);
521 AddAssembly (assembly, true);
525 internal virtual void ProcessMainAttributes (Hashtable atts)
527 atts.Remove ("Description"); // ignored
529 atts.Remove ("CodeBehind"); // ignored
531 atts.Remove ("AspCompat"); // ignored
533 debug = GetBool (atts, "Debug", true);
534 compilerOptions = GetString (atts, "CompilerOptions", "");
535 language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
536 strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
537 explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
539 string inherits = GetString (atts, "Inherits", null);
541 // In ASP 2, the source file is actually integrated with
542 // the generated file via the use of partial classes. This
543 // means that the code file has to be confirmed, but not
544 // used at this point.
545 src = GetString (atts, "CodeFile", null);
547 if (src != null && inherits != null) {
548 // Make sure the source exists
549 src = UrlUtils.Combine (BaseVirtualDir, src);
550 string realPath = MapPath (src, false);
551 if (!File.Exists (realPath))
552 ThrowParseException ("File " + src + " not found");
554 // Verify that the inherits is a valid identify not a
555 // fully-qualified name.
556 if (!CodeGenerator.IsValidLanguageIndependentIdentifier (inherits))
557 ThrowParseException (String.Format ("'{0}' is not valid for 'inherits'", inherits));
559 // We are going to create a partial class that shares
560 // the same name as the inherits tag, so reset the
561 // name. The base type is changed because it is the
562 // code file's responsibilty to extend the classes
564 partialClassName = inherits;
566 // Add the code file as an option to the
567 // compiler. This lets both files be compiled at once.
568 compilerOptions += " \"" + realPath + "\"";
569 } else if (inherits != null) {
570 // We just set the inherits directly because this is a
571 // Single-Page model.
572 SetBaseType (inherits);
575 string src = GetString (atts, "Src", null);
578 srcAssembly = GetAssemblyFromSource (src);
580 if (inherits != null)
581 SetBaseType (inherits);
583 className = GetString (atts, "ClassName", null);
584 if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
585 ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
589 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
592 internal void SetBaseType (string type)
594 if (type == DefaultBaseTypeName)
598 if (srcAssembly != null)
599 parent = srcAssembly.GetType (type);
602 parent = LoadType (type);
605 ThrowParseException ("Cannot find type " + type);
607 if (!DefaultBaseType.IsAssignableFrom (parent))
608 ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
613 Assembly GetAssemblyFromSource (string vpath)
615 vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
616 string realPath = MapPath (vpath, false);
617 if (!File.Exists (realPath))
618 ThrowParseException ("File " + vpath + " not found");
620 AddSourceDependency (realPath);
622 CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
623 if (result.NativeCompilerReturnValue != 0) {
624 StreamReader reader = new StreamReader (realPath);
625 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
628 AddAssembly (result.CompiledAssembly, true);
629 return result.CompiledAssembly;
632 internal abstract Type DefaultBaseType { get; }
633 internal abstract string DefaultBaseTypeName { get; }
634 internal abstract string DefaultDirectiveName { get; }
636 internal string InputFile
638 get { return inputFile; }
639 set { inputFile = value; }
643 internal bool IsPartial
645 get { return src != null; }
648 internal string PartialClassName
650 get { return partialClassName; }
657 set { text = value; }
660 internal Type BaseType
663 if (baseType == null)
664 baseType = DefaultBaseType;
670 internal string ClassName {
672 if (className != null)
675 className = Path.GetFileName (inputFile).Replace ('.', '_');
676 className = className.Replace ('-', '_');
677 className = className.Replace (' ', '_');
679 if (Char.IsDigit(className[0])) {
680 className = "_" + className;
687 internal string PrivateBinPath {
689 if (privateBinPath != null)
690 return privateBinPath;
692 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
693 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
695 return privateBinPath;
699 internal ArrayList Scripts {
702 scripts = new ArrayList ();
708 internal ArrayList Imports {
709 get { return imports; }
712 internal ArrayList Assemblies {
714 if (appAssemblyIndex != -1) {
715 object o = assemblies [appAssemblyIndex];
716 assemblies.RemoveAt (appAssemblyIndex);
718 appAssemblyIndex = -1;
725 internal ArrayList Interfaces {
726 get { return interfaces; }
729 internal RootBuilder RootBuilder {
730 get { return rootBuilder; }
731 set { rootBuilder = value; }
734 internal ArrayList Dependencies {
735 get { return dependencies; }
736 set { dependencies = value; }
739 internal string CompilerOptions {
740 get { return compilerOptions; }
743 internal string Language {
744 get { return language; }
747 internal bool StrictOn {
748 get { return strictOn; }
751 internal bool ExplicitOn {
752 get { return explicitOn; }
755 internal bool Debug {
756 get { return debug; }
759 internal bool OutputCache {
760 get { return output_cache; }
763 internal int OutputCacheDuration {
764 get { return oc_duration; }
767 internal string OutputCacheVaryByHeader {
768 get { return oc_header; }
771 internal string OutputCacheVaryByCustom {
772 get { return oc_custom; }
775 internal string OutputCacheVaryByControls {
776 get { return oc_controls; }
779 internal bool OutputCacheShared {
780 get { return oc_shared; }
783 internal OutputCacheLocation OutputCacheLocation {
784 get { return oc_location; }
787 internal string OutputCacheVaryByParam {
788 get { return oc_param; }
792 internal PagesSection PagesConfig {
794 return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
798 internal PagesConfiguration PagesConfig {
799 get { return PagesConfiguration.GetInstance (Context); }