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