2008-02-08 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateParser.cs
1 //
2 // System.Web.UI.TemplateParser
3 //
4 // Authors:
5 //      Duncan Mak (duncan@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 using System.CodeDom.Compiler;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Globalization;
35 using System.IO;
36 using System.Reflection;
37 using System.Security.Permissions;
38 using System.Web.Compilation;
39 using System.Web.Configuration;
40 using System.Web.Util;
41
42 #if NET_2_0
43 using System.Collections.Generic;
44 #endif
45
46 namespace System.Web.UI {
47         internal class ServerSideScript
48         {
49                 public readonly string Script;
50                 public readonly ILocation Location;
51                 
52                 public ServerSideScript (string script, ILocation location)
53                 {
54                         Script = script;
55                         Location = location;
56                 }
57         }
58         
59         // CAS
60         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
61         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
62         public abstract class TemplateParser : BaseParser
63         {
64                 string inputFile;
65                 string text;
66                 string privateBinPath;
67                 Hashtable mainAttributes;
68                 ArrayList dependencies;
69                 ArrayList assemblies;
70                 Hashtable anames;
71                 ArrayList imports;
72                 ArrayList interfaces;
73                 ArrayList scripts;
74                 Type baseType;
75                 bool baseTypeIsGlobal = true;
76                 string className;
77                 RootBuilder rootBuilder;
78                 bool debug;
79                 string compilerOptions;
80                 string language;
81                 bool implicitLanguage;
82                 bool strictOn = false;
83                 bool explicitOn = false;
84                 bool linePragmasOn = false;
85                 bool output_cache;
86                 int oc_duration;
87                 string oc_header, oc_custom, oc_param, oc_controls;
88 #if NET_2_0
89                 string oc_content_encodings;
90 #endif
91                 bool oc_shared;
92                 OutputCacheLocation oc_location;
93                 CultureInfo invariantCulture = CultureInfo.InvariantCulture;
94 #if NET_2_0
95                 string src;
96                 bool srcIsLegacy;
97                 string partialClassName;
98                 string codeFileBaseClass;
99                 string metaResourceKey;
100                 Type codeFileBaseClassType;
101                 List <UnknownAttributeDescriptor> unknownMainAttributes;
102 #endif
103                 ILocation directiveLocation;
104                 
105                 Assembly srcAssembly;
106                 int appAssemblyIndex = -1;
107
108                 internal TemplateParser ()
109                 {
110                         LoadConfigDefaults ();
111                         
112                         imports = new ArrayList ();
113 #if NET_2_0
114                         AddNamespaces (imports);
115 #else
116                         imports.Add ("System");
117                         imports.Add ("System.Collections");
118                         imports.Add ("System.Collections.Specialized");
119                         imports.Add ("System.Configuration");
120                         imports.Add ("System.Text");
121                         imports.Add ("System.Text.RegularExpressions");
122                         imports.Add ("System.Web");
123                         imports.Add ("System.Web.Caching");
124                         imports.Add ("System.Web.Security");
125                         imports.Add ("System.Web.SessionState");
126                         imports.Add ("System.Web.UI");
127                         imports.Add ("System.Web.UI.WebControls");
128                         imports.Add ("System.Web.UI.HtmlControls");
129 #endif
130
131                         assemblies = new ArrayList ();
132 #if NET_2_0
133                         CompilationSection compConfig = CompilationConfig;
134                         
135                         bool addAssembliesInBin = false;
136                         foreach (AssemblyInfo info in compConfig.Assemblies) {
137                                 if (info.Assembly == "*")
138                                         addAssembliesInBin = true;
139                                 else
140                                         AddAssemblyByName (info.Assembly);
141                         }
142                         if (addAssembliesInBin)
143                                 AddAssembliesInBin ();
144
145                         foreach (NamespaceInfo info in PagesConfig.Namespaces) {
146                                 imports.Add (info.Namespace);
147                         }
148 #else
149                         CompilationConfiguration compConfig = CompilationConfig;
150                         
151                         foreach (string a in compConfig.Assemblies)
152                                 AddAssemblyByName (a);
153                         if (compConfig.AssembliesInBin)
154                                 AddAssembliesInBin ();
155 #endif
156
157                         language = compConfig.DefaultLanguage;
158                         implicitLanguage = true;
159                 }
160
161                 internal virtual void LoadConfigDefaults ()
162                 {
163                         debug = CompilationConfig.Debug;
164                 }
165                 
166                 internal void AddApplicationAssembly ()
167                 {
168                         if (Context.ApplicationInstance == null)
169                                 return; // this may happen if we have Global.asax and have
170                                         // controls registered from Web.Config
171                         string location = Context.ApplicationInstance.AssemblyLocation;
172                         if (location != typeof (TemplateParser).Assembly.Location) {
173                                 appAssemblyIndex = assemblies.Add (location);
174                         }
175                 }
176
177                 protected abstract Type CompileIntoType ();
178
179 #if NET_2_0
180                 void AddNamespaces (ArrayList imports)
181                 {
182                         if (BuildManager.HaveResources)
183                                 imports.Add ("System.Resources");
184                         
185                         PagesSection pages = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
186                         if (pages == null)
187                                 return;
188
189                         NamespaceCollection namespaces = pages.Namespaces;
190                         if (namespaces == null || namespaces.Count == 0)
191                                 return;
192
193                         foreach (NamespaceInfo nsi in namespaces)
194                                 imports.Add (nsi.Namespace);
195                 }
196 #endif
197                 
198                 internal void RegisterCustomControl (string tagPrefix, string tagName, string src)
199                 {
200                         string realpath = MapPath (src);
201                         if (String.Compare (realpath, inputFile, false, invariantCulture) == 0)
202                                 return;
203                         
204                         if (!File.Exists (realpath))
205                                 throw new ParseException (Location, "Could not find file \"" + realpath + "\".");
206                         string vpath = VirtualPathUtility.Combine (BaseVirtualDir, src);
207                         if (VirtualPathUtility.IsAbsolute (vpath))
208                                 vpath = VirtualPathUtility.ToAppRelative (vpath);
209                         
210                         Type type = null;
211                         AddDependency (vpath);
212                         try {
213 #if NET_2_0
214                                 type = BuildManager.GetCompiledType (vpath);
215 #else
216                                 ArrayList other_deps = new ArrayList ();
217                                 type = UserControlParser.GetCompiledType (vpath, realpath, other_deps, Context);
218                                 foreach (string s in other_deps)
219                                         AddDependency (s);
220 #endif
221                         } catch (ParseException pe) {
222                                 if (this is UserControlParser)
223                                         throw new ParseException (Location, pe.Message, pe);
224                                 throw;
225                         }
226
227                         AddAssembly (type.Assembly, true);
228                         RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, type);
229                 }
230
231                 internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
232                 {
233                         AddImport (ns);
234                         Assembly ass = null;
235                         
236                         if (assembly != null && assembly.Length > 0)
237                                 ass = AddAssemblyByName (assembly);
238                         
239                         RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
240                 }
241
242                 internal virtual void HandleOptions (object obj)
243                 {
244                 }
245
246                 internal static string GetOneKey (Hashtable tbl)
247                 {
248                         foreach (object key in tbl.Keys)
249                                 return key.ToString ();
250
251                         return null;
252                 }
253                 
254                 internal virtual void AddDirective (string directive, Hashtable atts)
255                 {
256                         if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
257                                 if (mainAttributes != null)
258                                         ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
259
260                                 mainAttributes = atts;
261                                 ProcessMainAttributes (mainAttributes);
262                                 return;
263                         }
264
265                         int cmp = String.Compare ("Assembly", directive, true);
266                         if (cmp == 0) {
267                                 string name = GetString (atts, "Name", null);
268                                 string src = GetString (atts, "Src", null);
269
270                                 if (atts.Count > 0)
271                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
272
273                                 if (name == null && src == null)
274                                         ThrowParseException ("You gotta specify Src or Name");
275                                         
276                                 if (name != null && src != null)
277                                         ThrowParseException ("Src and Name cannot be used together");
278
279                                 if (name != null) {
280                                         AddAssemblyByName (name);
281                                 } else {
282                                         GetAssemblyFromSource (src);
283                                 }
284
285                                 return;
286                         }
287
288                         cmp = String.Compare ("Import", directive, true);
289                         if (cmp == 0) {
290                                 string namesp = GetString (atts, "Namespace", null);
291                                 if (atts.Count > 0)
292                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
293                                 
294                                 if (namesp != null && namesp != "")
295                                         AddImport (namesp);
296                                 return;
297                         }
298
299                         cmp = String.Compare ("Implements", directive, true);
300                         if (cmp == 0) {
301                                 string ifacename = GetString (atts, "Interface", "");
302
303                                 if (atts.Count > 0)
304                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
305                                 
306                                 Type iface = LoadType (ifacename);
307                                 if (iface == null)
308                                         ThrowParseException ("Cannot find type " + ifacename);
309
310                                 if (!iface.IsInterface)
311                                         ThrowParseException (iface + " is not an interface");
312
313                                 AddInterface (iface.FullName);
314                                 return;
315                         }
316
317                         cmp = String.Compare ("OutputCache", directive, true);
318                         if (cmp == 0) {
319                                 HttpResponse response = HttpContext.Current.Response;
320                                 if (response != null)
321                                         response.Cache.SetValidUntilExpires (true);
322                                 
323                                 output_cache = true;
324                                 
325                                 if (atts ["Duration"] == null)
326                                         ThrowParseException ("The directive is missing a 'duration' attribute.");
327                                 if (atts ["VaryByParam"] == null && atts ["VaryByControl"] == null)
328                                         ThrowParseException ("This directive is missing 'VaryByParam' " +
329                                                         "or 'VaryByControl' attribute, which should be set to \"none\", \"*\", " +
330                                                         "or a list of name/value pairs.");
331
332                                 foreach (DictionaryEntry entry in atts) {
333                                         string key = (string) entry.Key;
334                                         switch (key.ToLower ()) {
335                                                 case "duration":
336                                                         oc_duration = Int32.Parse ((string) entry.Value);
337                                                         if (oc_duration < 1)
338                                                                 ThrowParseException ("The 'duration' attribute must be set " +
339                                                                                      "to a positive integer value");
340                                                         break;
341 #if NET_2_0
342                                                 case "varybycontentencodings":
343                                                         oc_content_encodings = (string) entry.Value;
344                                                         break;
345 #endif
346                                                 case "varybyparam":
347                                                         oc_param = (string) entry.Value;
348                                                         if (String.Compare (oc_param, "none") == 0)
349                                                                 oc_param = null;
350                                                         break;
351                                                 case "varybyheader":
352                                                         oc_header = (string) entry.Value;
353                                                         break;
354                                                 case "varybycustom":
355                                                         oc_custom = (string) entry.Value;
356                                                         break;
357                                                 case "location":
358                                                         if (!(this is PageParser))
359                                                                 goto default;
360                                                 
361                                                         try {
362                                                                 oc_location = (OutputCacheLocation) Enum.Parse (
363                                                                         typeof (OutputCacheLocation), (string) entry.Value, true);
364                                                         } catch {
365                                                                 ThrowParseException ("The 'location' attribute is case sensitive and " +
366                                                                                      "must be one of the following values: Any, Client, " +
367                                                                                      "Downstream, Server, None, ServerAndClient.");
368                                                         }
369                                                         break;
370                                                 case "varybycontrol":
371 #if ONLY_1_1
372                                                         if (this is PageParser)
373                                                                 goto default;
374 #endif
375                                                         oc_controls = (string) entry.Value;
376                                                         break;
377                                                 case "shared":
378                                                         if (this is PageParser)
379                                                                 goto default;
380
381                                                         try {
382                                                                 oc_shared = Boolean.Parse ((string) entry.Value);
383                                                         } catch {
384                                                                 ThrowParseException ("The 'shared' attribute is case sensitive" +
385                                                                                      " and must be set to 'true' or 'false'.");
386                                                         }
387                                                         break;
388                                                 default:
389                                                         ThrowParseException ("The '" + key + "' attribute is not " +
390                                                                              "supported by the 'Outputcache' directive.");
391                                                         break;
392                                         }
393                                         
394                                 }
395                                 
396                                 return;
397                         }
398
399                         ThrowParseException ("Unknown directive: " + directive);
400                 }
401
402                 internal Type LoadType (string typeName)
403                 {
404                         Type type = HttpApplication.LoadType (typeName);
405                         if (type == null)
406                                 return null;
407                         Assembly asm = type.Assembly;
408                         string location = asm.Location;
409                         
410                         string dirname = Path.GetDirectoryName (location);
411                         bool doAddAssembly = true;
412                         foreach (string dir in HttpApplication.BinDirectories) {
413                                 if (dirname == dir) {
414                                         doAddAssembly = false;
415                                         break;
416                                 }
417                         }
418
419                         if (doAddAssembly)
420                                 AddAssembly (asm, true);
421
422                         return type;
423                 }
424
425                 void AddAssembliesInBin ()
426                 {
427                         foreach (string s in HttpApplication.BinDirectoryAssemblies)
428                                 assemblies.Add (s);
429                 }
430                 
431                 internal virtual void AddInterface (string iface)
432                 {
433                         if (interfaces == null)
434                                 interfaces = new ArrayList ();
435
436                         if (!interfaces.Contains (iface))
437                                 interfaces.Add (iface);
438                 }
439                 
440                 internal virtual void AddImport (string namesp)
441                 {
442                         if (imports == null)
443                                 imports = new ArrayList ();
444
445                         if (!imports.Contains (namesp))
446                                 imports.Add (namesp);
447                 }
448
449                 internal virtual void AddSourceDependency (string filename)
450                 {
451                         if (dependencies != null && dependencies.Contains (filename))
452                                 ThrowParseException ("Circular file references are not allowed. File: " + filename);
453
454                         AddDependency (filename);
455                 }
456
457                 internal virtual void AddDependency (string filename)
458                 {
459                         if (filename == "")
460                                 return;
461
462                         if (dependencies == null)
463                                 dependencies = new ArrayList ();
464
465                         if (!dependencies.Contains (filename))
466                                 dependencies.Add (filename);
467                 }
468                 
469                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
470                 {
471                         if (assembly.Location == "")
472                                 return;
473
474                         if (anames == null)
475                                 anames = new Hashtable ();
476
477                         string name = assembly.GetName ().Name;
478                         string loc = assembly.Location;
479                         if (fullPath) {
480                                 if (!assemblies.Contains (loc)) {
481                                         assemblies.Add (loc);
482                                 }
483
484                                 anames [name] = loc;
485                                 anames [loc] = assembly;
486                         } else {
487                                 if (!assemblies.Contains (name)) {
488                                         assemblies.Add (name);
489                                 }
490
491                                 anames [name] = assembly;
492                         }
493                 }
494
495                 internal virtual Assembly AddAssemblyByFileName (string filename)
496                 {
497                         Assembly assembly = null;
498                         Exception error = null;
499
500                         try {
501                                 assembly = Assembly.LoadFrom (filename);
502                         } catch (Exception e) { error = e; }
503
504                         if (assembly == null)
505                                 ThrowParseException ("Assembly " + filename + " not found", error);
506
507                         AddAssembly (assembly, true);
508                         return assembly;
509                 }
510
511                 internal virtual Assembly AddAssemblyByName (string name)
512                 {
513                         if (anames == null)
514                                 anames = new Hashtable ();
515
516                         if (anames.Contains (name)) {
517                                 object o = anames [name];
518                                 if (o is string)
519                                         o = anames [o];
520
521                                 return (Assembly) o;
522                         }
523
524                         Assembly assembly = null;
525                         Exception error = null;
526                         try {
527                                 assembly = Assembly.Load (name);
528                         } catch (Exception e) { error = e; }
529
530                         if (assembly == null) {
531                                 try {
532                                         assembly = Assembly.LoadWithPartialName (name);
533                                 } catch (Exception e) { error = e; }
534                         }
535                         
536                         if (assembly == null)
537                                 ThrowParseException ("Assembly " + name + " not found", error);
538
539                         AddAssembly (assembly, true);
540                         return assembly;
541                 }
542                 
543                 internal virtual void ProcessMainAttributes (Hashtable atts)
544                 {
545                         directiveLocation = new System.Web.Compilation.Location (Location);
546                         
547 #if NET_2_0
548                         CompilationSection compConfig;
549 #else
550                         CompilationConfiguration compConfig;
551 #endif
552
553                         compConfig = CompilationConfig;
554                         
555                         atts.Remove ("Description"); // ignored
556 #if NET_1_1
557                         atts.Remove ("CodeBehind");  // ignored
558 #endif
559                         atts.Remove ("AspCompat"); // ignored
560                         
561                         debug = GetBool (atts, "Debug", compConfig.Debug);
562                         compilerOptions = GetString (atts, "CompilerOptions", "");
563                         language = GetString (atts, "Language", "");
564                         if (language.Length != 0)
565                                 implicitLanguage = false;
566                         else
567                                 language = compConfig.DefaultLanguage;
568                         
569                         strictOn = GetBool (atts, "Strict", compConfig.Strict);
570                         explicitOn = GetBool (atts, "Explicit", compConfig.Explicit);
571                         linePragmasOn = GetBool (atts, "LinePragmas", false);
572                         
573                         string inherits = GetString (atts, "Inherits", null);
574                         string srcRealPath = null;
575                         
576 #if NET_2_0
577                         // In ASP 2, the source file is actually integrated with
578                         // the generated file via the use of partial classes. This
579                         // means that the code file has to be confirmed, but not
580                         // used at this point.
581                         src = GetString (atts, "CodeFile", null);
582                         codeFileBaseClass = GetString (atts, "CodeFileBaseClass", null);
583
584                         if (src == null && codeFileBaseClass != null)
585                                 ThrowParseException ("The 'CodeFileBaseClass' attribute cannot be used without a 'CodeFile' attribute");
586
587                         string legacySrc = GetString (atts, "Src", null);
588                         if (legacySrc != null) {
589                                 legacySrc = UrlUtils.Combine (BaseVirtualDir, legacySrc);
590                                 GetAssemblyFromSource (legacySrc);
591
592                                 if (src == null) {
593                                         src = legacySrc;
594                                         legacySrc = MapPath (legacySrc, false);
595                                         srcRealPath = legacySrc;
596                                         if (!File.Exists (srcRealPath))
597                                                 ThrowParseException ("File " + src + " not found");
598                                         
599                                         srcIsLegacy = true;
600                                 } else 
601                                         legacySrc = MapPath (legacySrc, false);                         
602
603                                 AddDependency (legacySrc);
604                         }
605                         
606                         if (!srcIsLegacy && src != null && inherits != null) {
607                                 // Make sure the source exists
608                                 src = UrlUtils.Combine (BaseVirtualDir, src);
609                                 srcRealPath = MapPath (src, false);
610                                 
611                                 if (!File.Exists (srcRealPath))
612                                         ThrowParseException ("File " + src + " not found");
613
614                                 // We are going to create a partial class that shares
615                                 // the same name as the inherits tag, so reset the
616                                 // name. The base type is changed because it is the
617                                 // code file's responsibilty to extend the classes
618                                 // needed.
619                                 partialClassName = inherits;
620
621                                 // Add the code file as an option to the
622                                 // compiler. This lets both files be compiled at once.
623                                 compilerOptions += " \"" + srcRealPath + "\"";
624
625                                 if (codeFileBaseClass != null) {
626                                         try {
627                                                 codeFileBaseClassType = LoadType (codeFileBaseClass);
628                                         } catch (Exception) {
629                                         }
630
631                                         if (codeFileBaseClassType == null)
632                                                 ThrowParseException ("Could not load type '{0}'", codeFileBaseClass);
633                                 }
634                         } else if (inherits != null) {
635                                 // We just set the inherits directly because this is a
636                                 // Single-Page model.
637                                 SetBaseType (inherits);
638                         }
639 #else
640                         string src = GetString (atts, "Src", null);
641
642                         if (src != null) {
643                                 srcRealPath = MapPath (src, false);
644                                 srcAssembly = GetAssemblyFromSource (src);
645                         }
646                         
647                         if (inherits != null)
648                                 SetBaseType (inherits);
649 #endif
650                         if (src != null) {
651                                 if (VirtualPathUtility.IsAbsolute (src))
652                                         src = VirtualPathUtility.ToAppRelative (src);
653                                 AddDependency (src);
654                         }
655                         
656                         className = GetString (atts, "ClassName", null);
657                         if (className != null) {
658 #if NET_2_0
659                                 string [] identifiers = className.Split ('.');
660                                 for (int i = 0; i < identifiers.Length; i++)
661                                         if (!CodeGenerator.IsValidLanguageIndependentIdentifier (identifiers [i]))
662                                                 ThrowParseException (String.Format ("'{0}' is not a valid "
663                                                         + "value for attribute 'classname'.", className));
664 #else
665                                 if (!CodeGenerator.IsValidLanguageIndependentIdentifier (className))
666                                         ThrowParseException (String.Format ("'{0}' is not a valid "
667                                                 + "value for attribute 'classname'.", className));
668 #endif
669                         }
670
671 #if NET_2_0
672                         if (this is TemplateControlParser)
673                                 metaResourceKey = GetString (atts, "meta:resourcekey", null);
674                         
675                         if (inherits != null && (this is PageParser || this is UserControlParser) && atts.Count > 0) {
676                                 if (unknownMainAttributes == null)
677                                         unknownMainAttributes = new List <UnknownAttributeDescriptor> ();
678                                 string key, val;
679                                 
680                                 foreach (DictionaryEntry de in atts) {
681                                         key = de.Key as string;
682                                         val = de.Value as string;
683                                         
684                                         if (String.IsNullOrEmpty (key) || String.IsNullOrEmpty (val))
685                                                 continue;
686                                         CheckUnknownAttribute (key, val, inherits);
687                                 }
688                                 return;
689                         }
690 #endif
691                         if (atts.Count > 0)
692                                 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
693                 }
694
695 #if NET_2_0
696                 void CheckUnknownAttribute (string name, string val, string inherits)
697                 {
698                         MemberInfo mi = null;
699                         bool missing = false;
700                         string memberName = name.Trim ().ToLower (CultureInfo.InvariantCulture);
701                         Type parent = codeFileBaseClassType;
702
703                         if (parent == null)
704                                 parent = baseType;
705                         
706                         try {
707                                 MemberInfo[] infos = parent.GetMember (memberName,
708                                                                        MemberTypes.Field | MemberTypes.Property,
709                                                                        BindingFlags.Public | BindingFlags.Instance |
710                                                                        BindingFlags.IgnoreCase | BindingFlags.Static);
711                                 if (infos.Length != 0) {
712                                         // prefer public properties to public methods (it's what MS.NET does)
713                                         foreach (MemberInfo tmp in infos) {
714                                                 if (tmp is PropertyInfo) {
715                                                         mi = tmp;
716                                                         break;
717                                                 }
718                                         }
719                                         if (mi == null)
720                                                 mi = infos [0];
721                                 } else
722                                         missing = true;
723                         } catch (Exception) {
724                                 missing = true;
725                         }
726                         if (missing)
727                                 ThrowParseException (
728                                         "Error parsing attribute '{0}': Type '{1}' does not have a public property named '{0}'",
729                                         memberName, inherits);
730                         
731                         Type memberType = null;
732                         if (mi is PropertyInfo) {
733                                 PropertyInfo pi = mi as PropertyInfo;
734                                 
735                                 if (!pi.CanWrite)
736                                         ThrowParseException (
737                                                 "Error parsing attribute '{0}': The '{0}' property is read-only and cannot be set.",
738                                                 memberName);
739                                 memberType = pi.PropertyType;
740                         } else if (mi is FieldInfo) {
741                                 memberType = ((FieldInfo)mi).FieldType;
742                         } else
743                                 ThrowParseException ("Could not determine member the kind of '{0}' in base type '{1}",
744                                                      memberName, inherits);
745                         TypeConverter converter = TypeDescriptor.GetConverter (memberType);
746                         bool convertible = true;
747                         object value = null;
748                         
749                         if (converter == null || !converter.CanConvertFrom (typeof (string)))
750                                 convertible = false;
751
752                         if (convertible) {
753                                 try {
754                                         value = converter.ConvertFromInvariantString (val);
755                                 } catch (Exception) {
756                                         convertible = false;
757                                 }
758                         }
759
760                         if (!convertible)
761                                 ThrowParseException ("Error parsing attribute '{0}': Cannot create an object of type '{1}' from its string representation '{2}' for the '{3}' property.",
762                                                      memberName, memberType, val, mi.Name);
763                         
764                         UnknownAttributeDescriptor desc = new UnknownAttributeDescriptor (mi, value);
765                         unknownMainAttributes.Add (desc);
766                 }
767 #endif
768                 
769                 internal void SetBaseType (string type)
770                 {
771                         if (type == null || type == DefaultBaseTypeName) {
772                                 baseType = DefaultBaseType;
773                                 return;
774                         }
775                         
776                         Type parent = null;
777                         if (srcAssembly != null)
778                                 parent = srcAssembly.GetType (type);
779
780                         if (parent == null)
781                                 parent = LoadType (type);
782
783                         if (parent == null)
784                                 ThrowParseException ("Cannot find type " + type);
785
786                         if (!DefaultBaseType.IsAssignableFrom (parent))
787                                 ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
788
789                         baseType = parent;
790                 }
791
792                 internal void SetLanguage (string language)
793                 {
794                         this.language = language;
795                         implicitLanguage = false;
796                 }
797
798                 Assembly GetAssemblyFromSource (string vpath)
799                 {                       
800                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
801                         string realPath = MapPath (vpath, false);
802                         if (!File.Exists (realPath))
803                                 ThrowParseException ("File " + vpath + " not found");
804
805                         AddSourceDependency (vpath);
806                         
807                         CompilerResults result;
808
809 #if NET_2_0
810                         string tmp;
811                         CompilerParameters parameters;
812                         CodeDomProvider provider = BaseCompiler.CreateProvider (HttpContext.Current, language, out parameters, out tmp);
813                         if (provider == null)
814                                 throw new HttpException ("Cannot find provider for language '" + language + "'.");
815                         
816                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
817                         abuilder.CompilerOptions = parameters;
818                         abuilder.AddAssemblyReference (BuildManager.GetReferencedAssemblies () as List <Assembly>);
819                         abuilder.AddCodeFile (realPath);
820                         result = abuilder.BuildAssembly (vpath);
821 #else
822                         result = CachingCompiler.Compile (language, realPath, realPath, assemblies, Debug);
823 #endif
824                         if (result.NativeCompilerReturnValue != 0) {
825                                 StreamReader reader = new StreamReader (realPath);
826                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
827                         }
828
829                         AddAssembly (result.CompiledAssembly, true);
830                         return result.CompiledAssembly;
831                 }               
832
833                 internal abstract string DefaultBaseTypeName { get; }
834                 internal abstract string DefaultDirectiveName { get; }
835
836                 internal Type DefaultBaseType {
837                         get {
838                                 Type type = Type.GetType (DefaultBaseTypeName, true);
839
840                                 return type;
841                         }
842                 }
843                 
844                 internal ILocation DirectiveLocation {
845                         get { return directiveLocation; }
846                 }
847                 
848                 internal string InputFile
849                 {
850                         get { return inputFile; }
851                         set { inputFile = value; }
852                 }
853
854 #if NET_2_0
855                 internal bool IsPartial {
856                         get { return (!srcIsLegacy && src != null); }
857                 }
858
859                 internal string CodeBehindSource {
860                         get {
861                                 if (srcIsLegacy)
862                                         return null;
863                                 
864                                 return src;
865                         }
866                 }
867                         
868                 internal string PartialClassName {
869                         get { return partialClassName; }
870                 }
871
872                 internal string CodeFileBaseClass {
873                         get { return codeFileBaseClass; }
874                 }
875
876                 internal string MetaResourceKey {
877                         get { return metaResourceKey; }
878                 }
879                 
880                 internal Type CodeFileBaseClassType
881                 {
882                         get { return codeFileBaseClassType; }
883                 }
884                 
885                 internal List <UnknownAttributeDescriptor> UnknownMainAttributes
886                 {
887                         get { return unknownMainAttributes; }
888                 }
889 #endif
890
891                 internal string Text {
892                         get { return text; }
893                         set { text = value; }
894                 }
895
896                 internal Type BaseType {
897                         get {
898                                 if (baseType == null)
899                                         SetBaseType (DefaultBaseTypeName);
900                                 
901                                 return baseType;
902                         }
903                 }
904                 
905                 internal bool BaseTypeIsGlobal {
906                         get { return baseTypeIsGlobal; }
907                         set { baseTypeIsGlobal = value; }
908                 }
909                 
910                 internal string ClassName {
911                         get {
912                                 if (className != null)
913                                         return className;
914
915 #if NET_2_0
916                                 string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
917                                 string inFile;
918                                 
919                                 if (String.IsNullOrEmpty (inputFile)) {
920                                         inFile = null;
921                                         StreamReader sr = Reader as StreamReader;
922
923                                         if (sr != null) {
924                                                 FileStream fr = sr.BaseStream as FileStream;
925                                                 if (fr != null)
926                                                         inFile = fr.Name;
927                                         }
928                                 } else
929                                         inFile = inputFile;
930
931                                 if (String.IsNullOrEmpty (inFile))
932                                         throw new HttpException ("Unable to determine class name - no input file found.");
933                                 
934                                 if (StrUtils.StartsWith (inFile, physPath)) {
935                                         className = inputFile.Substring (physPath.Length).ToLower (CultureInfo.InvariantCulture);
936                                         className = className.Replace ('.', '_');
937                                         className = className.Replace ('/', '_').Replace ('\\', '_');
938                                 } else
939 #endif
940                                 className = Path.GetFileName (inputFile).Replace ('.', '_');
941                                 className = className.Replace ('-', '_'); 
942                                 className = className.Replace (' ', '_');
943
944                                 if (Char.IsDigit(className[0])) {
945                                         className = "_" + className;
946                                 }
947
948                                 return className;
949                         }
950                 }
951
952                 internal ArrayList Scripts {
953                         get {
954                                 if (scripts == null)
955                                         scripts = new ArrayList ();
956
957                                 return scripts;
958                         }
959                 }
960
961                 internal ArrayList Imports {
962                         get { return imports; }
963                 }
964
965                 internal ArrayList Assemblies {
966                         get {
967                                 if (appAssemblyIndex != -1) {
968                                         object o = assemblies [appAssemblyIndex];
969                                         assemblies.RemoveAt (appAssemblyIndex);
970                                         assemblies.Add (o);
971                                         appAssemblyIndex = -1;
972                                 }
973
974                                 return assemblies;
975                         }
976                 }
977
978                 internal ArrayList Interfaces {
979                         get { return interfaces; }
980                 }
981
982                 internal RootBuilder RootBuilder {
983                         get { return rootBuilder; }
984                         set { rootBuilder = value; }
985                 }
986
987                 internal ArrayList Dependencies {
988                         get { return dependencies; }
989                         set { dependencies = value; }
990                 }
991
992                 internal string CompilerOptions {
993                         get { return compilerOptions; }
994                 }
995
996                 internal string Language {
997                         get { return language; }
998                 }
999
1000                 internal bool ImplicitLanguage {
1001                         get { return implicitLanguage; }
1002                 }
1003                 
1004                 internal bool StrictOn {
1005                         get { return strictOn; }
1006                 }
1007
1008                 internal bool ExplicitOn {
1009                         get { return explicitOn; }
1010                 }
1011                 
1012                 internal bool Debug {
1013                         get { return debug; }
1014                 }
1015
1016                 internal bool OutputCache {
1017                         get { return output_cache; }
1018                 }
1019
1020                 internal int OutputCacheDuration {
1021                         get { return oc_duration; }
1022                 }
1023
1024 #if NET_2_0
1025                 internal string OutputCacheVaryByContentEncodings {
1026                         get { return oc_content_encodings; }
1027                 }
1028
1029                 internal virtual TextReader Reader {
1030                         get { return null; }
1031                         set { /* no-op */ }
1032                 }
1033 #endif
1034                 
1035                 internal string OutputCacheVaryByHeader {
1036                         get { return oc_header; }
1037                 }
1038
1039                 internal string OutputCacheVaryByCustom {
1040                         get { return oc_custom; }
1041                 }
1042
1043                 internal string OutputCacheVaryByControls {
1044                         get { return oc_controls; }
1045                 }
1046                 
1047                 internal bool OutputCacheShared {
1048                         get { return oc_shared; }
1049                 }
1050                 
1051                 internal OutputCacheLocation OutputCacheLocation {
1052                         get { return oc_location; }
1053                 }
1054
1055                 internal string OutputCacheVaryByParam {
1056                         get { return oc_param; }
1057                 }
1058
1059 #if NET_2_0
1060                 internal PagesSection PagesConfig {
1061                         get {
1062                                 return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
1063                         }
1064                 }
1065 #else
1066                 internal PagesConfiguration PagesConfig {
1067                         get { return PagesConfiguration.GetInstance (Context); }
1068                 }
1069 #endif
1070         }
1071 }
1072