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;
34 using System.Reflection;
35 using System.Security.Permissions;
36 using System.Web.Compilation;
37 using System.Web.Configuration;
38 using System.Web.Util;
40 namespace System.Web.UI {
43 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 public abstract class TemplateParser : BaseParser
49 string privateBinPath;
50 Hashtable mainAttributes;
51 ArrayList dependencies;
59 RootBuilder rootBuilder;
61 string compilerOptions;
63 bool strictOn = false;
64 bool explicitOn = false;
67 string oc_header, oc_custom, oc_param, oc_controls;
69 OutputCacheLocation oc_location;
72 string partialClassName;
75 int appAssemblyIndex = -1;
77 internal TemplateParser ()
79 imports = new ArrayList ();
80 imports.Add ("System");
81 imports.Add ("System.Collections");
82 imports.Add ("System.Collections.Specialized");
83 imports.Add ("System.Configuration");
84 imports.Add ("System.Text");
85 imports.Add ("System.Text.RegularExpressions");
86 imports.Add ("System.Web");
87 imports.Add ("System.Web.Caching");
88 imports.Add ("System.Resources"); // should perhaps be conditional on App_Global/LocalResources existence?
89 imports.Add ("System.Web.Security");
90 imports.Add ("System.Web.SessionState");
91 imports.Add ("System.Web.UI");
92 imports.Add ("System.Web.UI.WebControls");
93 imports.Add ("System.Web.UI.HtmlControls");
95 assemblies = new ArrayList ();
97 bool addAssembliesInBin = false;
98 foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
99 if (info.Assembly == "*")
100 addAssembliesInBin = true;
102 AddAssemblyByName (info.Assembly);
104 if (addAssembliesInBin)
105 AddAssembliesInBin ();
107 foreach (NamespaceInfo info in PagesConfig.Namespaces) {
108 imports.Add (info.Namespace);
111 foreach (string a in CompilationConfig.Assemblies)
112 AddAssemblyByName (a);
113 if (CompilationConfig.AssembliesInBin)
114 AddAssembliesInBin ();
117 language = CompilationConfig.DefaultLanguage;
120 internal void AddApplicationAssembly ()
122 string location = Context.ApplicationInstance.AssemblyLocation;
123 if (location != typeof (TemplateParser).Assembly.Location) {
124 appAssemblyIndex = assemblies.Add (location);
128 protected abstract Type CompileIntoType ();
130 internal virtual void HandleOptions (object obj)
134 internal static string GetOneKey (Hashtable tbl)
136 foreach (object key in tbl.Keys)
137 return key.ToString ();
142 internal virtual void AddDirective (string directive, Hashtable atts)
144 if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
145 if (mainAttributes != null)
146 ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
148 mainAttributes = atts;
149 ProcessMainAttributes (mainAttributes);
153 int cmp = String.Compare ("Assembly", directive, true);
155 string name = GetString (atts, "Name", null);
156 string src = GetString (atts, "Src", null);
159 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
161 if (name == null && src == null)
162 ThrowParseException ("You gotta specify Src or Name");
164 if (name != null && src != null)
165 ThrowParseException ("Src and Name cannot be used together");
168 AddAssemblyByName (name);
170 GetAssemblyFromSource (src);
176 cmp = String.Compare ("Import", directive, true);
178 string namesp = GetString (atts, "Namespace", null);
180 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
182 if (namesp != null && namesp != "")
187 cmp = String.Compare ("Implements", directive, true);
189 string ifacename = GetString (atts, "Interface", "");
192 ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
194 Type iface = LoadType (ifacename);
196 ThrowParseException ("Cannot find type " + ifacename);
198 if (!iface.IsInterface)
199 ThrowParseException (iface + " is not an interface");
201 AddInterface (iface.FullName);
205 cmp = String.Compare ("OutputCache", directive, true);
209 if (atts ["Duration"] == null)
210 ThrowParseException ("The directive is missing a 'duration' attribute.");
211 if (atts ["VaryByParam"] == null)
212 ThrowParseException ("This directive is missing a 'VaryByParam' " +
213 "attribute, which should be set to \"none\", \"*\", " +
214 "or a list of name/value pairs.");
216 foreach (DictionaryEntry entry in atts) {
217 string key = (string) entry.Key;
218 switch (key.ToLower ()) {
220 oc_duration = Int32.Parse ((string) entry.Value);
222 ThrowParseException ("The 'duration' attribute must be set " +
223 "to a positive integer value");
226 oc_param = (string) entry.Value;
227 if (String.Compare (oc_param, "none") == 0)
231 oc_header = (string) entry.Value;
234 oc_custom = (string) entry.Value;
237 if (!(this is PageParser))
241 oc_location = (OutputCacheLocation) Enum.Parse (
242 typeof (OutputCacheLocation), (string) entry.Value, true);
244 ThrowParseException ("The 'location' attribute is case sensitive and " +
245 "must be one of the following values: Any, Client, " +
246 "Downstream, Server, None, ServerAndClient.");
249 case "varybycontrol":
250 if (this is PageParser)
253 oc_controls = (string) entry.Value;
256 if (this is PageParser)
260 oc_shared = Boolean.Parse ((string) entry.Value);
262 ThrowParseException ("The 'shared' attribute is case sensitive" +
263 " and must be set to 'true' or 'false'.");
267 ThrowParseException ("The '" + key + "' attribute is not " +
268 "supported by the 'Outputcache' directive.");
277 ThrowParseException ("Unknown directive: " + directive);
280 internal Type LoadType (string typeName)
282 // First try loaded assemblies, then try assemblies in Bin directory.
284 bool seenBin = false;
285 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
286 foreach (Assembly ass in assemblies) {
287 type = ass.GetType (typeName);
291 if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
292 AddAssembly (ass, true);
297 AddDependency (ass.Location);
305 if (!Directory.Exists (PrivateBinPath))
308 string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
309 foreach (string s in binDlls) {
310 Assembly binA = Assembly.LoadFrom (s);
311 type = binA.GetType (typeName);
315 AddDependency (binA.Location);
322 void AddAssembliesInBin ()
324 if (!Directory.Exists (PrivateBinPath))
327 string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
328 foreach (string s in binDlls) {
333 internal virtual void AddInterface (string iface)
335 if (interfaces == null)
336 interfaces = new ArrayList ();
338 if (!interfaces.Contains (iface))
339 interfaces.Add (iface);
342 internal virtual void AddImport (string namesp)
345 imports = new ArrayList ();
347 if (!imports.Contains (namesp))
348 imports.Add (namesp);
351 internal virtual void AddSourceDependency (string filename)
353 if (dependencies != null && dependencies.Contains (filename)) {
354 ThrowParseException ("Circular file references are not allowed. File: " + filename);
357 AddDependency (filename);
360 internal virtual void AddDependency (string filename)
365 if (dependencies == null)
366 dependencies = new ArrayList ();
368 if (!dependencies.Contains (filename))
369 dependencies.Add (filename);
372 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
374 if (assembly.Location == "")
378 anames = new Hashtable ();
380 string name = assembly.GetName ().Name;
381 string loc = assembly.Location;
383 if (!assemblies.Contains (loc)) {
384 assemblies.Add (loc);
388 anames [loc] = assembly;
390 if (!assemblies.Contains (name)) {
391 assemblies.Add (name);
394 anames [name] = assembly;
398 internal virtual Assembly AddAssemblyByFileName (string filename)
400 Assembly assembly = null;
401 Exception error = null;
404 assembly = Assembly.LoadFrom (filename);
405 } catch (Exception e) { error = e; }
407 if (assembly == null)
408 ThrowParseException ("Assembly " + filename + " not found", error);
410 AddAssembly (assembly, true);
414 internal virtual Assembly AddAssemblyByName (string name)
417 anames = new Hashtable ();
419 if (anames.Contains (name)) {
420 object o = anames [name];
427 Assembly assembly = null;
428 Exception error = null;
429 if (name.IndexOf (',') != -1) {
431 assembly = Assembly.Load (name);
432 } catch (Exception e) { error = e; }
435 if (assembly == null) {
437 assembly = Assembly.LoadWithPartialName (name);
438 } catch (Exception e) { error = e; }
441 if (assembly == null)
442 ThrowParseException ("Assembly " + name + " not found", error);
444 AddAssembly (assembly, true);
448 internal virtual void ProcessMainAttributes (Hashtable atts)
450 atts.Remove ("Description"); // ignored
452 atts.Remove ("CodeBehind"); // ignored
454 atts.Remove ("AspCompat"); // ignored
456 debug = GetBool (atts, "Debug", true);
457 compilerOptions = GetString (atts, "CompilerOptions", "");
458 language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
459 strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
460 explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
462 string inherits = GetString (atts, "Inherits", null);
464 // In ASP 2, the source file is actually integrated with
465 // the generated file via the use of partial classes. This
466 // means that the code file has to be confirmed, but not
467 // used at this point.
468 src = GetString (atts, "CodeFile", null);
470 if (src != null && inherits != null) {
471 // Make sure the source exists
472 src = UrlUtils.Combine (BaseVirtualDir, src);
473 string realPath = MapPath (src, false);
474 if (!File.Exists (realPath))
475 ThrowParseException ("File " + src + " not found");
477 // Verify that the inherits is a valid identify not a
478 // fully-qualified name.
479 if (!CodeGenerator.IsValidLanguageIndependentIdentifier (inherits))
480 ThrowParseException (String.Format ("'{0}' is not valid for 'inherits'", inherits));
482 // We are going to create a partial class that shares
483 // the same name as the inherits tag, so reset the
484 // name. The base type is changed because it is the
485 // code file's responsibilty to extend the classes
487 partialClassName = inherits;
489 // Add the code file as an option to the
490 // compiler. This lets both files be compiled at once.
491 compilerOptions += " \"" + realPath + "\"";
492 } else if (inherits != null) {
493 // We just set the inherits directly because this is a
494 // Single-Page model.
495 SetBaseType (inherits);
498 string src = GetString (atts, "Src", null);
501 srcAssembly = GetAssemblyFromSource (src);
503 if (inherits != null)
504 SetBaseType (inherits);
506 className = GetString (atts, "ClassName", null);
507 if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
508 ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
512 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
515 internal void SetBaseType (string type)
517 if (type == DefaultBaseTypeName)
521 if (srcAssembly != null)
522 parent = srcAssembly.GetType (type);
525 parent = LoadType (type);
528 ThrowParseException ("Cannot find type " + type);
530 if (!DefaultBaseType.IsAssignableFrom (parent))
531 ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
536 Assembly GetAssemblyFromSource (string vpath)
538 vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
539 string realPath = MapPath (vpath, false);
540 if (!File.Exists (realPath))
541 ThrowParseException ("File " + vpath + " not found");
543 AddSourceDependency (realPath);
545 CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
546 if (result.NativeCompilerReturnValue != 0) {
547 StreamReader reader = new StreamReader (realPath);
548 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
551 AddAssembly (result.CompiledAssembly, true);
552 return result.CompiledAssembly;
555 internal abstract Type DefaultBaseType { get; }
556 internal abstract string DefaultBaseTypeName { get; }
557 internal abstract string DefaultDirectiveName { get; }
559 internal string InputFile
561 get { return inputFile; }
562 set { inputFile = value; }
566 internal bool IsPartial
568 get { return src != null; }
571 internal string PartialClassName
573 get { return partialClassName; }
580 set { text = value; }
583 internal Type BaseType
586 if (baseType == null)
587 baseType = DefaultBaseType;
593 internal string ClassName {
595 if (className != null)
598 className = Path.GetFileName (inputFile).Replace ('.', '_');
599 className = className.Replace ('-', '_');
600 className = className.Replace (' ', '_');
602 if (Char.IsDigit(className[0])) {
603 className = "_" + className;
610 internal string PrivateBinPath {
612 if (privateBinPath != null)
613 return privateBinPath;
615 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
616 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
618 return privateBinPath;
622 internal ArrayList Scripts {
625 scripts = new ArrayList ();
631 internal ArrayList Imports {
632 get { return imports; }
635 internal ArrayList Assemblies {
637 if (appAssemblyIndex != -1) {
638 object o = assemblies [appAssemblyIndex];
639 assemblies.RemoveAt (appAssemblyIndex);
641 appAssemblyIndex = -1;
648 internal ArrayList Interfaces {
649 get { return interfaces; }
652 internal RootBuilder RootBuilder {
653 get { return rootBuilder; }
654 set { rootBuilder = value; }
657 internal ArrayList Dependencies {
658 get { return dependencies; }
659 set { dependencies = value; }
662 internal string CompilerOptions {
663 get { return compilerOptions; }
666 internal string Language {
667 get { return language; }
670 internal bool StrictOn {
671 get { return strictOn; }
674 internal bool ExplicitOn {
675 get { return explicitOn; }
678 internal bool Debug {
679 get { return debug; }
682 internal bool OutputCache {
683 get { return output_cache; }
686 internal int OutputCacheDuration {
687 get { return oc_duration; }
690 internal string OutputCacheVaryByHeader {
691 get { return oc_header; }
694 internal string OutputCacheVaryByCustom {
695 get { return oc_custom; }
698 internal string OutputCacheVaryByControls {
699 get { return oc_controls; }
702 internal bool OutputCacheShared {
703 get { return oc_shared; }
706 internal OutputCacheLocation OutputCacheLocation {
707 get { return oc_location; }
710 internal string OutputCacheVaryByParam {
711 get { return oc_param; }
715 internal PagesSection PagesConfig {
717 return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
721 internal PagesConfiguration PagesConfig {
722 get { return PagesConfiguration.GetInstance (Context); }