System.Web.Configuration.(AdapterDictionary,AuthenticationMode,MachineKeyValidation...
[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 //      Marek Habersack (mhabersack@novell.com)
8 //
9 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.CodeDom.Compiler;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.ComponentModel;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Security.Permissions;
40 using System.Text;
41 using System.Threading;
42 using System.Web.Compilation;
43 using System.Web.Hosting;
44 using System.Web.Configuration;
45 using System.Web.Util;
46
47 namespace System.Web.UI {
48         internal class ServerSideScript
49         {
50                 public readonly string Script;
51                 public readonly ILocation Location;
52                 
53                 public ServerSideScript (string script, ILocation location)
54                 {
55                         Script = script;
56                         Location = location;
57                 }
58         }
59         
60         // CAS
61         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
62         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
63         public abstract class TemplateParser : BaseParser
64         {
65                 [Flags]
66                 internal enum OutputCacheParsedParams
67                 {
68                         Location               = 0x0001,
69                         CacheProfile           = 0x0002,
70                         NoStore                = 0x0004,
71                         SqlDependency          = 0x0008,
72                         VaryByCustom           = 0x0010,
73                         VaryByHeader           = 0x0020,
74                         VaryByControl          = 0x0040,
75                         VaryByContentEncodings = 0x0080
76                 }
77                 
78                 string inputFile;
79                 string text;
80                 IDictionary mainAttributes;
81                 List <string> dependencies;
82                 List <string> assemblies;
83                 IDictionary anames;
84                 string[] binDirAssemblies;
85                 Dictionary <string, bool> namespacesCache;
86                 Dictionary <string, bool> imports;
87                 List <string> interfaces;
88                 List <ServerSideScript> scripts;
89                 Type baseType;
90                 bool baseTypeIsGlobal = true;
91                 string className;
92                 RootBuilder rootBuilder;
93                 bool debug;
94                 string compilerOptions;
95                 string language;
96                 bool implicitLanguage;
97                 bool strictOn ;
98                 bool explicitOn;
99                 bool linePragmasOn = true;
100                 bool output_cache;
101                 int oc_duration;
102                 string oc_header, oc_custom, oc_param, oc_controls;
103                 string oc_content_encodings, oc_cacheprofile, oc_sqldependency;
104                 bool oc_nostore;
105                 OutputCacheParsedParams oc_parsed_params = 0;
106                 bool oc_shared;
107                 OutputCacheLocation oc_location;
108
109                 // Kludge needed to support pre-parsing of the main directive (see
110                 // AspNetGenerator.GetRootBuilderType)
111                 internal int allowedMainDirectives = 0;
112                 
113                 byte[] md5checksum;
114                 string src;
115                 bool srcIsLegacy;
116                 string partialClassName;
117                 string codeFileBaseClass;
118                 string metaResourceKey;
119                 Type codeFileBaseClassType;
120                 Type pageParserFilterType;
121                 PageParserFilter pageParserFilter;
122                 
123                 List <UnknownAttributeDescriptor> unknownMainAttributes;
124                 Stack <string> includeDirs;
125                 List <string> registeredTagNames;
126                 ILocation directiveLocation;
127                 
128                 int appAssemblyIndex = -1;
129
130                 internal TemplateParser ()
131                 {
132                         imports = new Dictionary <string, bool> (StringComparer.Ordinal);
133                         LoadConfigDefaults ();
134                         assemblies = new List <string> ();
135                         CompilationSection compConfig = CompilationConfig;
136                         foreach (AssemblyInfo info in compConfig.Assemblies) {
137                                 if (info.Assembly != "*")
138                                         AddAssemblyByName (info.Assembly);
139                         }
140
141                         language = compConfig.DefaultLanguage;
142                         implicitLanguage = true;
143                 }
144
145                 internal virtual void LoadConfigDefaults ()
146                 {
147                         AddNamespaces (imports);
148                         debug = CompilationConfig.Debug;
149                 }
150                 
151                 internal void AddApplicationAssembly ()
152                 {
153                         if (Context.ApplicationInstance == null)
154                                 return; // this may happen if we have Global.asax and have
155                                         // controls registered from Web.Config
156                         string location = Context.ApplicationInstance.AssemblyLocation;
157                         if (location != typeof (TemplateParser).Assembly.Location) {
158                                  assemblies.Add (location);
159                                  appAssemblyIndex = assemblies.Count - 1;
160                         }
161                 }
162
163                 internal abstract Type CompileIntoType ();
164
165                 internal void AddControl (Type type, IDictionary attributes)
166                 {
167                         AspGenerator generator = AspGenerator;
168                         if (generator == null)
169                                 return;
170                         generator.AddControl (type, attributes);
171                 }
172                 
173                 void AddNamespaces (Dictionary <string, bool> imports)
174                 {
175                         if (BuildManager.HaveResources)
176                                 imports.Add ("System.Resources", true);
177                         
178                         PagesSection pages = PagesConfig;
179                         if (pages == null)
180                                 return;
181
182                         NamespaceCollection namespaces = pages.Namespaces;
183                         if (namespaces == null || namespaces.Count == 0)
184                                 return;
185                         
186                         foreach (NamespaceInfo nsi in namespaces) {
187                                 string ns = nsi.Namespace;
188                                 if (imports.ContainsKey (ns))
189                                         continue;
190                                 
191                                 imports.Add (ns, true);
192                         }
193                 }
194                 
195                 internal void RegisterCustomControl (string tagPrefix, string tagName, string src)
196                 {
197                         string realpath = null;
198                         bool fileExists = false;
199                         VirtualFile vf = null;
200                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
201                         VirtualPath vp = new VirtualPath (src, BaseVirtualDir);
202                         string vpAbsolute = vpp.CombineVirtualPaths (VirtualPath.Absolute, vp.Absolute);
203                         
204                         if (vpp.FileExists (vpAbsolute)) {
205                                 fileExists = true;
206                                 vf = vpp.GetFile (vpAbsolute);
207                                 if (vf != null)
208                                         realpath = MapPath (vf.VirtualPath);
209                         }
210
211                         if (!fileExists)
212                                 ThrowParseFileNotFound (src);
213
214                         if (String.Compare (realpath, inputFile, StringComparison.Ordinal) == 0)
215                                 return;
216                         
217                         string vpath = vf.VirtualPath;
218                         
219                         try {
220                                 RegisterTagName (tagPrefix + ":" + tagName);
221                                 RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, vpath);
222                                 AddDependency (vpath);
223                         } catch (ParseException pe) {
224                                 if (this is UserControlParser)
225                                         throw new ParseException (Location, pe.Message, pe);
226                                 throw;
227                         }
228                 }
229
230                 internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
231                 {
232                         AddImport (ns);
233                         Assembly ass = null;
234                         
235                         if (assembly != null && assembly.Length > 0)
236                                 ass = AddAssemblyByName (assembly);
237                         
238                         RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
239                 }
240
241                 internal virtual void HandleOptions (object obj)
242                 {
243                 }
244
245                 internal static string GetOneKey (IDictionary tbl)
246                 {
247                         foreach (object key in tbl.Keys)
248                                 return key.ToString ();
249
250                         return null;
251                 }
252                 
253                 internal virtual void AddDirective (string directive, IDictionary atts)
254                 {
255                         var pageParserFilter = PageParserFilter;
256                         if (String.Compare (directive, DefaultDirectiveName, true, Helpers.InvariantCulture) == 0) {
257                                 bool allowMainDirective = allowedMainDirectives > 0;
258                                 
259                                 if (mainAttributes != null && !allowMainDirective)
260                                         ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
261
262                                 allowedMainDirectives--;
263                                 if (mainAttributes != null)
264                                         return;
265                                 
266                                 if (pageParserFilter != null)
267                                         pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
268                                 
269                                 mainAttributes = atts;
270                                 ProcessMainAttributes (mainAttributes);
271                                 return;
272                         } else if (pageParserFilter != null)
273                                 pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
274                                 
275                         int cmp = String.Compare ("Assembly", directive, true, Helpers.InvariantCulture);
276                         if (cmp == 0) {
277                                 string name = GetString (atts, "Name", null);
278                                 string src = GetString (atts, "Src", null);
279
280                                 if (atts.Count > 0)
281                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
282
283                                 if (name == null && src == null)
284                                         ThrowParseException ("You gotta specify Src or Name");
285                                         
286                                 if (name != null && src != null)
287                                         ThrowParseException ("Src and Name cannot be used together");
288
289                                 if (name != null) {
290                                         AddAssemblyByName (name);
291                                 } else {
292                                         GetAssemblyFromSource (src);
293                                 }
294
295                                 return;
296                         }
297
298                         cmp = String.Compare ("Import", directive, true, Helpers.InvariantCulture);
299                         if (cmp == 0) {
300                                 string namesp = GetString (atts, "Namespace", null);
301                                 if (atts.Count > 0)
302                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
303                                 
304                                 AddImport (namesp);
305                                 return;
306                         }
307
308                         cmp = String.Compare ("Implements", directive, true, Helpers.InvariantCulture);
309                         if (cmp == 0) {
310                                 string ifacename = GetString (atts, "Interface", "");
311
312                                 if (atts.Count > 0)
313                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
314                                 
315                                 Type iface = LoadType (ifacename);
316                                 if (iface == null)
317                                         ThrowParseException ("Cannot find type " + ifacename);
318
319                                 if (!iface.IsInterface)
320                                         ThrowParseException (iface + " is not an interface");
321
322                                 AddInterface (iface.FullName);
323                                 return;
324                         }
325
326                         cmp = String.Compare ("OutputCache", directive, true, Helpers.InvariantCulture);
327                         if (cmp == 0) {
328                                 HttpResponse response = HttpContext.Current.Response;
329                                 if (response != null)
330                                         response.Cache.SetValidUntilExpires (true);
331                                 
332                                 output_cache = true;
333                                 ProcessOutputCacheAttributes (atts);
334                                 return;
335                         }
336
337                         ThrowParseException ("Unknown directive: " + directive);
338                 }
339
340                 internal virtual void ProcessOutputCacheAttributes (IDictionary atts)
341                 {
342                         if (atts ["Duration"] == null)
343                                 ThrowParseException ("The directive is missing a 'duration' attribute.");
344                         if (atts ["VaryByParam"] == null && atts ["VaryByControl"] == null)
345                                 ThrowParseException ("This directive is missing 'VaryByParam' " +
346                                                      "or 'VaryByControl' attribute, which should be set to \"none\", \"*\", " +
347                                                      "or a list of name/value pairs.");
348
349                         foreach (DictionaryEntry entry in atts) {
350                                 string key = (string) entry.Key;
351                                 if (key == null)
352                                         continue;
353                                         
354                                 switch (key.ToLower (Helpers.InvariantCulture)) {
355                                         case "duration":
356                                                 oc_duration = Int32.Parse ((string) entry.Value);
357                                                 if (oc_duration < 1)
358                                                         ThrowParseException ("The 'duration' attribute must be set " +
359                                                                              "to a positive integer value");
360                                                 break;
361
362                                         case "sqldependency":
363                                                 oc_sqldependency = (string) entry.Value;
364                                                 break;
365                                                         
366                                         case "nostore":
367                                                 try {
368                                                         oc_nostore = Boolean.Parse ((string) entry.Value);
369                                                         oc_parsed_params |= OutputCacheParsedParams.NoStore;
370                                                 } catch {
371                                                         ThrowParseException ("The 'NoStore' attribute is case sensitive" +
372                                                                              " and must be set to 'true' or 'false'.");
373                                                 }
374                                                 break;
375
376                                         case "cacheprofile":
377                                                 oc_cacheprofile = (string) entry.Value;
378                                                 oc_parsed_params |= OutputCacheParsedParams.CacheProfile;
379                                                 break;
380                                                         
381                                         case "varybycontentencodings":
382                                                 oc_content_encodings = (string) entry.Value;
383                                                 oc_parsed_params |= OutputCacheParsedParams.VaryByContentEncodings;
384                                                 break;
385
386                                         case "varybyparam":
387                                                 oc_param = (string) entry.Value;
388                                                 if (String.Compare (oc_param, "none", true, Helpers.InvariantCulture) == 0)
389                                                         oc_param = null;
390                                                 break;
391                                         case "varybyheader":
392                                                 oc_header = (string) entry.Value;
393                                                 oc_parsed_params |= OutputCacheParsedParams.VaryByHeader;
394                                                 break;
395                                         case "varybycustom":
396                                                 oc_custom = (string) entry.Value;
397                                                 oc_parsed_params |= OutputCacheParsedParams.VaryByCustom;
398                                                 break;
399                                         case "location":
400                                                 if (!(this is PageParser))
401                                                         goto default;
402                                                 
403                                                 try {
404                                                         oc_location = (OutputCacheLocation) Enum.Parse (
405                                                                 typeof (OutputCacheLocation), (string) entry.Value, true);
406                                                         oc_parsed_params |= OutputCacheParsedParams.Location;
407                                                 } catch {
408                                                         ThrowParseException ("The 'location' attribute is case sensitive and " +
409                                                                              "must be one of the following values: Any, Client, " +
410                                                                              "Downstream, Server, None, ServerAndClient.");
411                                                 }
412                                                 break;
413                                         case "varybycontrol":
414                                                 oc_controls = (string) entry.Value;
415                                                 oc_parsed_params |= OutputCacheParsedParams.VaryByControl;
416                                                 break;
417                                         case "shared":
418                                                 if (this is PageParser)
419                                                         goto default;
420
421                                                 try {
422                                                         oc_shared = Boolean.Parse ((string) entry.Value);
423                                                 } catch {
424                                                         ThrowParseException ("The 'shared' attribute is case sensitive" +
425                                                                              " and must be set to 'true' or 'false'.");
426                                                 }
427                                                 break;
428                                         default:
429                                                 ThrowParseException ("The '" + key + "' attribute is not " +
430                                                                      "supported by the 'Outputcache' directive.");
431                                                 break;
432                                 }
433                                         
434                         }
435                 }
436                 
437                 internal Type LoadType (string typeName)
438                 {
439                         Type type = HttpApplication.LoadType (typeName);
440                         if (type == null)
441                                 return null;
442                         Assembly asm = type.Assembly;
443                         string location = asm.Location;
444                         
445                         string dirname = Path.GetDirectoryName (location);
446                         bool doAddAssembly = true;
447                         if (dirname == HttpApplication.BinDirectory)
448                                 doAddAssembly = false;
449
450                         if (doAddAssembly)
451                                 AddAssembly (asm, true);
452
453                         return type;
454                 }
455
456                 internal virtual void AddInterface (string iface)
457                 {
458                         if (interfaces == null)
459                                 interfaces = new List <string> ();
460
461                         if (!interfaces.Contains (iface))
462                                 interfaces.Add (iface);
463                 }
464                 
465                 internal virtual void AddImport (string namesp)
466                 {
467                         if (namesp == null || namesp.Length == 0)
468                                 return;
469                         
470                         if (imports == null)
471                                 imports = new Dictionary <string, bool> (StringComparer.Ordinal);
472                         
473                         if (imports.ContainsKey (namesp))
474                                 return;
475                         
476                         imports.Add (namesp, true);
477                         AddAssemblyForNamespace (namesp);
478                 }
479
480                 void AddAssemblyForNamespace (string namesp)
481                 {
482                         if (binDirAssemblies == null)
483                                 binDirAssemblies = HttpApplication.BinDirectoryAssemblies;
484                         if (binDirAssemblies.Length == 0)
485                                 return;
486
487                         if (namespacesCache == null)
488                                 namespacesCache = new Dictionary <string, bool> ();
489                         else if (namespacesCache.ContainsKey (namesp))
490                                 return;
491                         
492                         foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ())
493                                 if (FindNamespaceInAssembly (asm, namesp))
494                                         return;
495                         
496                         IList tla = BuildManager.TopLevelAssemblies;
497                         if (tla != null && tla.Count > 0) {
498                                 foreach (Assembly asm in tla) {
499                                         if (FindNamespaceInAssembly (asm, namesp))
500                                                 return;
501                                 }
502                         }
503
504                         Assembly a;
505                         foreach (string s in binDirAssemblies) {
506                                 a = Assembly.LoadFrom (s);
507                                 if (FindNamespaceInAssembly (a, namesp))
508                                         return;
509                         }
510                 }
511
512                 bool FindNamespaceInAssembly (Assembly asm, string namesp)
513                 {
514                         Type[] asmTypes;
515
516                         try {
517                                 asmTypes = asm.GetTypes ();
518                         } catch (ReflectionTypeLoadException) {
519                                 // ignore
520                                 return false;
521                         }
522                         
523                         foreach (Type type in asmTypes) {
524                                 if (String.Compare (type.Namespace, namesp, StringComparison.Ordinal) == 0) {
525                                         namespacesCache.Add (namesp, true);
526                                         AddAssembly (asm, true);
527                                         return true;
528                                 }
529                         }
530
531                         return false;
532                 }
533                 
534                 internal virtual void AddSourceDependency (string filename)
535                 {
536                         if (dependencies != null && dependencies.Contains (filename))
537                                 ThrowParseException ("Circular file references are not allowed. File: " + filename);
538
539                         AddDependency (filename);
540                 }
541
542                 internal virtual void AddDependency (string filename)
543                 {
544                         AddDependency (filename, true);
545                 }
546                 
547                 internal virtual void AddDependency (string filename, bool combinePaths)
548                 {
549                         if (String.IsNullOrEmpty (filename))
550                                 return;
551
552                         if (dependencies == null)
553                                 dependencies = new List <string> ();
554
555                         if (combinePaths)
556                                 filename = HostingEnvironment.VirtualPathProvider.CombineVirtualPaths (VirtualPath.Absolute, filename);
557
558                         if (!dependencies.Contains (filename))
559                                 dependencies.Add (filename);
560                 }
561                 
562                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
563                 {
564                         if (assembly == null || assembly.Location == String.Empty)
565                                 return;
566
567                         if (anames == null)
568                                 anames = new Dictionary <string, object> ();
569
570                         string name = assembly.GetName ().Name;
571                         string loc = assembly.Location;
572                         if (fullPath) {
573                                 if (!assemblies.Contains (loc)) {
574                                         assemblies.Add (loc);
575                                 }
576
577                                 anames [name] = loc;
578                                 anames [loc] = assembly;
579                         } else {
580                                 if (!assemblies.Contains (name)) {
581                                         assemblies.Add (name);
582                                 }
583
584                                 anames [name] = assembly;
585                         }
586                 }
587
588                 internal virtual Assembly AddAssemblyByFileName (string filename)
589                 {
590                         Assembly assembly = null;
591                         Exception error = null;
592
593                         try {
594                                 assembly = Assembly.LoadFrom (filename);
595                         } catch (Exception e) { error = e; }
596
597                         if (assembly == null)
598                                 ThrowParseException ("Assembly " + filename + " not found", error);
599
600                         AddAssembly (assembly, true);
601                         return assembly;
602                 }
603
604                 internal virtual Assembly AddAssemblyByName (string name)
605                 {
606                         if (anames == null)
607                                 anames = new Dictionary <string, object> ();
608
609                         if (anames.Contains (name)) {
610                                 object o = anames [name];
611                                 if (o is string)
612                                         o = anames [o];
613
614                                 return (Assembly) o;
615                         }
616
617                         Assembly assembly = null;
618                         Exception error = null;
619                         try {
620                                 assembly = Assembly.Load (name);
621                         } catch (Exception e) { error = e; }
622
623                         if (assembly == null) {
624                                 try {
625                                         assembly = Assembly.LoadWithPartialName (name);
626                                 } catch (Exception e) { error = e; }
627                         }
628                         
629                         if (assembly == null)
630                                 ThrowParseException ("Assembly " + name + " not found", error);
631
632                         AddAssembly (assembly, true);
633                         return assembly;
634                 }
635                 
636                 internal virtual void ProcessMainAttributes (IDictionary atts)
637                 {
638                         directiveLocation = new System.Web.Compilation.Location (Location);
639                         CompilationSection compConfig;
640
641                         compConfig = CompilationConfig;
642                         
643                         atts.Remove ("Description"); // ignored
644                         atts.Remove ("CodeBehind");  // ignored
645                         atts.Remove ("AspCompat"); // ignored
646                         
647                         debug = GetBool (atts, "Debug", compConfig.Debug);
648                         compilerOptions = GetString (atts, "CompilerOptions", String.Empty);
649                         language = GetString (atts, "Language", "");
650                         if (language.Length != 0)
651                                 implicitLanguage = false;
652                         else
653                                 language = compConfig.DefaultLanguage;
654                         
655                         strictOn = GetBool (atts, "Strict", compConfig.Strict);
656                         explicitOn = GetBool (atts, "Explicit", compConfig.Explicit);
657                         if (atts.Contains ("LinePragmas"))
658                                 linePragmasOn = GetBool (atts, "LinePragmas", true);
659
660                         string inherits = GetString (atts, "Inherits", null);
661                         string srcRealPath = null;
662                         
663                         // In ASP 2+, the source file is actually integrated with
664                         // the generated file via the use of partial classes. This
665                         // means that the code file has to be confirmed, but not
666                         // used at this point.
667                         src = GetString (atts, "CodeFile", null);
668                         codeFileBaseClass = GetString (atts, "CodeFileBaseClass", null);
669
670                         if (src == null && codeFileBaseClass != null)
671                                 ThrowParseException ("The 'CodeFileBaseClass' attribute cannot be used without a 'CodeFile' attribute");
672
673                         string legacySrc = GetString (atts, "Src", null);
674                         var vpp = HostingEnvironment.VirtualPathProvider;
675                         if (legacySrc != null) {
676                                 legacySrc = vpp.CombineVirtualPaths (BaseVirtualDir, legacySrc);
677                                 GetAssemblyFromSource (legacySrc);
678
679                                 if (src == null) {
680                                         src = legacySrc;
681                                         legacySrc = MapPath (legacySrc, false);
682                                         srcRealPath = legacySrc;
683                                         if (!File.Exists (srcRealPath))
684                                                 ThrowParseException ("File " + src + " not found");
685                                         
686                                         srcIsLegacy = true;
687                                 } else 
688                                         legacySrc = MapPath (legacySrc, false);                         
689
690                                 AddDependency (legacySrc, false);
691                         }
692                         
693                         if (!srcIsLegacy && src != null && inherits != null) {
694                                 // Make sure the source exists
695                                 src = vpp.CombineVirtualPaths (BaseVirtualDir, src);
696                                 srcRealPath = MapPath (src, false);
697
698                                 if (!vpp.FileExists (src))
699                                         ThrowParseException ("File " + src + " not found");
700
701                                 // We are going to create a partial class that shares
702                                 // the same name as the inherits tag, so reset the
703                                 // name. The base type is changed because it is the
704                                 // code file's responsibilty to extend the classes
705                                 // needed.
706                                 partialClassName = inherits;
707
708                                 // Add the code file as an option to the
709                                 // compiler. This lets both files be compiled at once.
710                                 compilerOptions += " \"" + srcRealPath + "\"";
711
712                                 if (codeFileBaseClass != null) {
713                                         try {
714                                                 codeFileBaseClassType = LoadType (codeFileBaseClass);
715                                         } catch (Exception) {
716                                         }
717
718                                         if (codeFileBaseClassType == null)
719                                                 ThrowParseException ("Could not load type '{0}'", codeFileBaseClass);
720                                 }
721                         } else if (inherits != null) {
722                                 // We just set the inherits directly because this is a
723                                 // Single-Page model.
724                                 SetBaseType (inherits);
725                         }
726
727                         if (src != null) {
728                                 if (VirtualPathUtility.IsAbsolute (src))
729                                         src = VirtualPathUtility.ToAppRelative (src);
730                                 AddDependency (src, false);
731                         }
732                         
733                         className = GetString (atts, "ClassName", null);
734                         if (className != null) {
735                                 string [] identifiers = className.Split ('.');
736                                 for (int i = 0; i < identifiers.Length; i++)
737                                         if (!CodeGenerator.IsValidLanguageIndependentIdentifier (identifiers [i]))
738                                                 ThrowParseException (String.Format ("'{0}' is not a valid "
739                                                         + "value for attribute 'classname'.", className));
740                         }
741
742                         if (this is TemplateControlParser)
743                                 metaResourceKey = GetString (atts, "meta:resourcekey", null);
744                         
745                         if (inherits != null && (this is PageParser || this is UserControlParser) && atts.Count > 0) {
746                                 if (unknownMainAttributes == null)
747                                         unknownMainAttributes = new List <UnknownAttributeDescriptor> ();
748                                 string key, val;
749                                 
750                                 foreach (DictionaryEntry de in atts) {
751                                         key = de.Key as string;
752                                         val = de.Value as string;
753                                         
754                                         if (String.IsNullOrEmpty (key) || String.IsNullOrEmpty (val))
755                                                 continue;
756                                         CheckUnknownAttribute (key, val, inherits);
757                                 }
758                                 return;
759                         }
760
761                         if (atts.Count > 0)
762                                 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
763                 }
764
765                 void RegisterTagName (string tagName)
766                 {
767                         if (registeredTagNames == null)
768                                 registeredTagNames = new List <string> ();
769
770                         if (registeredTagNames.Contains (tagName))
771                                 return;
772
773                         registeredTagNames.Add (tagName);
774                 }
775                 
776                 void CheckUnknownAttribute (string name, string val, string inherits)
777                 {
778                         MemberInfo mi = null;
779                         bool missing = false;
780                         string memberName = name.Trim ().ToLower (Helpers.InvariantCulture);
781                         Type parent = codeFileBaseClassType;
782
783                         if (parent == null)
784                                 parent = baseType;
785                         
786                         try {
787                                 MemberInfo[] infos = parent.GetMember (memberName,
788                                                                        MemberTypes.Field | MemberTypes.Property,
789                                                                        BindingFlags.Public | BindingFlags.Instance |
790                                                                        BindingFlags.IgnoreCase | BindingFlags.Static);
791                                 if (infos.Length != 0) {
792                                         // prefer public properties to public methods (it's what MS.NET does)
793                                         foreach (MemberInfo tmp in infos) {
794                                                 if (tmp is PropertyInfo) {
795                                                         mi = tmp;
796                                                         break;
797                                                 }
798                                         }
799                                         if (mi == null)
800                                                 mi = infos [0];
801                                 } else
802                                         missing = true;
803                         } catch (Exception) {
804                                 missing = true;
805                         }
806                         if (missing)
807                                 ThrowParseException (
808                                         "Error parsing attribute '{0}': Type '{1}' does not have a public property named '{0}'",
809                                         memberName, inherits);
810                         
811                         Type memberType = null;
812                         if (mi is PropertyInfo) {
813                                 PropertyInfo pi = mi as PropertyInfo;
814                                 
815                                 if (!pi.CanWrite)
816                                         ThrowParseException (
817                                                 "Error parsing attribute '{0}': The '{0}' property is read-only and cannot be set.",
818                                                 memberName);
819                                 memberType = pi.PropertyType;
820                         } else if (mi is FieldInfo) {
821                                 memberType = ((FieldInfo)mi).FieldType;
822                         } else
823                                 ThrowParseException ("Could not determine member the kind of '{0}' in base type '{1}",
824                                                      memberName, inherits);
825                         TypeConverter converter = TypeDescriptor.GetConverter (memberType);
826                         bool convertible = true;
827                         object value = null;
828                         
829                         if (converter == null || !converter.CanConvertFrom (typeof (string)))
830                                 convertible = false;
831
832                         if (convertible) {
833                                 try {
834                                         value = converter.ConvertFromInvariantString (val);
835                                 } catch (Exception) {
836                                         convertible = false;
837                                 }
838                         }
839
840                         if (!convertible)
841                                 ThrowParseException ("Error parsing attribute '{0}': Cannot create an object of type '{1}' from its string representation '{2}' for the '{3}' property.",
842                                                      memberName, memberType, val, mi.Name);
843                         
844                         UnknownAttributeDescriptor desc = new UnknownAttributeDescriptor (mi, value);
845                         unknownMainAttributes.Add (desc);
846                 }
847                 
848                 internal void SetBaseType (string type)
849                 {
850                         Type parent;                    
851                         if (type == null || type == DefaultBaseTypeName)
852                                 parent = DefaultBaseType;
853                         else
854                                 parent = null;
855
856                         if (parent == null) {
857                                 parent = LoadType (type);
858
859                                 if (parent == null)
860                                         ThrowParseException ("Cannot find type " + type);
861
862                                 if (!DefaultBaseType.IsAssignableFrom (parent))
863                                         ThrowParseException ("The parent type '" + type + "' does not derive from " + DefaultBaseType);
864                         }
865
866                         var pageParserFilter = PageParserFilter;
867                         if (pageParserFilter != null && !pageParserFilter.AllowBaseType (parent))
868                                 throw new HttpException ("Base type '" + parent + "' is not allowed.");
869                         
870                         baseType = parent;
871                 }
872
873                 internal void SetLanguage (string language)
874                 {
875                         this.language = language;
876                         implicitLanguage = false;
877                 }
878
879                 internal void PushIncludeDir (string dir)
880                 {
881                         if (includeDirs == null)
882                                 includeDirs = new Stack <string> (1);
883
884                         includeDirs.Push (dir);
885                 }
886
887                 internal string PopIncludeDir ()
888                 {
889                         if (includeDirs == null || includeDirs.Count == 0)
890                                 return null;
891
892                         return includeDirs.Pop () as string;
893                 }
894                 
895                 Assembly GetAssemblyFromSource (string vpath)
896                 {                       
897                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
898                         string realPath = MapPath (vpath, false);
899                         if (!File.Exists (realPath))
900                                 ThrowParseException ("File " + vpath + " not found");
901
902                         AddSourceDependency (vpath);
903                         
904                         CompilerResults result;
905                         string tmp;
906                         CompilerParameters parameters;
907                         CodeDomProvider provider = BaseCompiler.CreateProvider (HttpContext.Current, language, out parameters, out tmp);
908                         if (provider == null)
909                                 throw new HttpException ("Cannot find provider for language '" + language + "'.");
910                         
911                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
912                         abuilder.CompilerOptions = parameters;
913                         abuilder.AddAssemblyReference (BuildManager.GetReferencedAssemblies () as List <Assembly>);
914                         abuilder.AddCodeFile (realPath);
915                         result = abuilder.BuildAssembly (new VirtualPath (vpath));
916
917                         if (result.NativeCompilerReturnValue != 0) {
918                                 using (StreamReader reader = new StreamReader (realPath)) {
919                                         throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
920                                 }
921                         }
922
923                         AddAssembly (result.CompiledAssembly, true);
924                         return result.CompiledAssembly;
925                 }               
926
927                 internal abstract string DefaultBaseTypeName { get; }
928                 internal abstract string DefaultDirectiveName { get; }
929
930                 internal bool LinePragmasOn {
931                         get { return linePragmasOn; }
932                 }
933                 
934                 internal byte[] MD5Checksum {
935                         get { return md5checksum; }
936                         set { md5checksum = value; }
937                 }
938
939                 internal PageParserFilter PageParserFilter {
940                         get {
941                                 if (pageParserFilter != null)
942                                         return pageParserFilter;
943
944                                 Type t = PageParserFilterType;
945                                 if (t == null)
946                                         return null;
947                                 
948                                 pageParserFilter = Activator.CreateInstance (t) as PageParserFilter;
949                                 pageParserFilter.Initialize (this);
950
951                                 return pageParserFilter;
952                         }
953                 }
954                 
955                 internal Type PageParserFilterType {
956                         get {
957                                 if (pageParserFilterType == null) {
958                                         pageParserFilterType = PageParser.DefaultPageParserFilterType;
959                                         if (pageParserFilterType != null)
960                                                 return pageParserFilterType;
961                                         string typeName = PagesConfig.PageParserFilterType;
962                                         if (String.IsNullOrEmpty (typeName))
963                                                 return null;
964                                         
965                                         pageParserFilterType = HttpApplication.LoadType (typeName, true);
966                                 }
967                                 
968                                 return pageParserFilterType;
969                         }
970                 }
971                 internal virtual
972                 Type DefaultBaseType {
973                         get {
974                                 Type type = Type.GetType (DefaultBaseTypeName, true);
975
976                                 return type;
977                         }
978                 }
979                 
980                 internal ILocation DirectiveLocation {
981                         get { return directiveLocation; }
982                 }
983                 
984                 internal string ParserDir {
985                         get {
986                                 if (includeDirs == null || includeDirs.Count == 0)
987                                         return BaseDir;
988
989                                 return includeDirs.Peek () as string;
990                         }
991                 }
992                 
993                 internal string InputFile
994                 {
995                         get { return inputFile; }
996                         set { inputFile = value; }
997                 }
998
999                 internal bool IsPartial {
1000                         get { return (!srcIsLegacy && src != null); }
1001                 }
1002
1003                 internal string CodeBehindSource {
1004                         get {
1005                                 if (srcIsLegacy)
1006                                         return null;
1007                                 
1008                                 return src;
1009                         }
1010                 }
1011                         
1012                 internal string PartialClassName {
1013                         get { return partialClassName; }
1014                 }
1015
1016                 internal string CodeFileBaseClass {
1017                         get { return codeFileBaseClass; }
1018                 }
1019
1020                 internal string MetaResourceKey {
1021                         get { return metaResourceKey; }
1022                 }
1023                 
1024                 internal Type CodeFileBaseClassType
1025                 {
1026                         get { return codeFileBaseClassType; }
1027                 }
1028                 
1029                 internal List <UnknownAttributeDescriptor> UnknownMainAttributes
1030                 {
1031                         get { return unknownMainAttributes; }
1032                 }
1033
1034                 internal string Text {
1035                         get { return text; }
1036                         set { text = value; }
1037                 }
1038
1039                 internal Type BaseType {
1040                         get {
1041                                 if (baseType == null)
1042                                         SetBaseType (DefaultBaseTypeName);
1043                                 
1044                                 return baseType;
1045                         }
1046                 }
1047                 
1048                 internal bool BaseTypeIsGlobal {
1049                         get { return baseTypeIsGlobal; }
1050                         set { baseTypeIsGlobal = value; }
1051                 }
1052
1053                 static long autoClassCounter = 0;
1054
1055                 internal string EncodeIdentifier (string value)
1056                 {
1057                         if (value == null || value.Length == 0 || CodeGenerator.IsValidLanguageIndependentIdentifier (value))
1058                                 return value;
1059
1060                         StringBuilder ret = new StringBuilder ();
1061
1062                         char ch = value [0];
1063                         switch (Char.GetUnicodeCategory (ch)) {
1064                                 case UnicodeCategory.LetterNumber:
1065                                 case UnicodeCategory.LowercaseLetter:
1066                                 case UnicodeCategory.TitlecaseLetter:
1067                                 case UnicodeCategory.UppercaseLetter:
1068                                 case UnicodeCategory.OtherLetter:
1069                                 case UnicodeCategory.ModifierLetter:
1070                                 case UnicodeCategory.ConnectorPunctuation:
1071                                         ret.Append (ch);
1072                                         break;
1073
1074                                 case UnicodeCategory.DecimalDigitNumber:
1075                                         ret.Append ('_');
1076                                         ret.Append (ch);
1077                                         break;
1078                                         
1079                                 default:
1080                                         ret.Append ('_');
1081                                         break;
1082                         }
1083
1084                         for (int i = 1; i < value.Length; i++) {
1085                                 ch = value [i];
1086                                 switch (Char.GetUnicodeCategory (ch)) {
1087                                         case UnicodeCategory.LetterNumber:
1088                                         case UnicodeCategory.LowercaseLetter:
1089                                         case UnicodeCategory.TitlecaseLetter:
1090                                         case UnicodeCategory.UppercaseLetter:
1091                                         case UnicodeCategory.OtherLetter:
1092                                         case UnicodeCategory.ModifierLetter:
1093                                         case UnicodeCategory.ConnectorPunctuation:
1094                                         case UnicodeCategory.DecimalDigitNumber:
1095                                         case UnicodeCategory.NonSpacingMark:
1096                                         case UnicodeCategory.SpacingCombiningMark:
1097                                         case UnicodeCategory.Format:
1098                                                 ret.Append (ch);
1099                                                 break;
1100                                                 
1101                                         default:
1102                                                 ret.Append ('_');
1103                                                 break;
1104                                 }
1105                         }
1106
1107                         return ret.ToString ();
1108                 }
1109                 
1110                 internal string ClassName {
1111                         get {
1112                                 if (className != null)
1113                                         return className;
1114
1115                                 string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
1116                                 string inFile;
1117                                 
1118                                 if (String.IsNullOrEmpty (inputFile)) {
1119                                         inFile = null;
1120                                         using (StreamReader sr = Reader as StreamReader) {
1121                                                 if (sr != null) {
1122                                                         FileStream fr = sr.BaseStream as FileStream;
1123                                                         if (fr != null)
1124                                                                 inFile = fr.Name;
1125                                                 }
1126                                         }
1127                                 } else
1128                                         inFile = inputFile;
1129
1130                                 if (String.IsNullOrEmpty (inFile)) {
1131                                         // generate a unique class name
1132                                         long suffix;
1133                                         suffix = Interlocked.Increment (ref autoClassCounter);
1134                                         className = String.Format ("autoclass_nosource_{0:x}", suffix);
1135                                         return className;
1136                                 }
1137                                 
1138                                 if (StrUtils.StartsWith (inFile, physPath))
1139                                         className = inputFile.Substring (physPath.Length).ToLower (Helpers.InvariantCulture);
1140                                 else
1141                                         className = Path.GetFileName (inputFile);
1142                                 className = EncodeIdentifier (className);
1143                                 return className;
1144                         }
1145                 }
1146
1147                 internal List <ServerSideScript> Scripts {
1148                         get {
1149                                 if (scripts == null)
1150                                         scripts = new List <ServerSideScript> ();
1151
1152                                 return scripts;
1153                         }
1154                 }
1155
1156                 internal Dictionary <string, bool> Imports {
1157                         get { return imports; }
1158                 }
1159
1160                 internal List <string> Interfaces {
1161                         get { return interfaces; }
1162                 }
1163                 
1164                 internal List <string> Assemblies {
1165                         get {
1166                                 if (appAssemblyIndex != -1) {
1167                                         string o = assemblies [appAssemblyIndex];
1168                                         assemblies.RemoveAt (appAssemblyIndex);
1169                                         assemblies.Add (o);
1170                                         appAssemblyIndex = -1;
1171                                 }
1172
1173                                 return assemblies;
1174                         }
1175                 }
1176
1177                 internal RootBuilder RootBuilder {
1178                         get {
1179                                 if (rootBuilder != null)
1180                                         return rootBuilder;
1181                                 AspGenerator generator = AspGenerator;
1182                                 if (generator != null)
1183                                         rootBuilder = generator.RootBuilder;
1184
1185                                 return rootBuilder;
1186                         }
1187                         set { rootBuilder = value; }
1188                 }
1189
1190                 internal List <string> Dependencies {
1191                         get { return dependencies; }
1192                         set { dependencies = value; }
1193                 }
1194
1195                 internal string CompilerOptions {
1196                         get { return compilerOptions; }
1197                 }
1198
1199                 internal string Language {
1200                         get { return language; }
1201                 }
1202
1203                 internal bool ImplicitLanguage {
1204                         get { return implicitLanguage; }
1205                 }
1206                 
1207                 internal bool StrictOn {
1208                         get { return strictOn; }
1209                 }
1210
1211                 internal bool ExplicitOn {
1212                         get { return explicitOn; }
1213                 }
1214                 
1215                 internal bool Debug {
1216                         get { return debug; }
1217                 }
1218
1219                 internal bool OutputCache {
1220                         get { return output_cache; }
1221                 }
1222
1223                 internal int OutputCacheDuration {
1224                         get { return oc_duration; }
1225                 }
1226
1227                 internal OutputCacheParsedParams OutputCacheParsedParameters {
1228                         get { return oc_parsed_params; }
1229                 }
1230
1231                 internal string OutputCacheSqlDependency {
1232                         get { return oc_sqldependency; }
1233                 }
1234                 
1235                 internal string OutputCacheCacheProfile {
1236                         get { return oc_cacheprofile; }
1237                 }
1238                 
1239                 internal string OutputCacheVaryByContentEncodings {
1240                         get { return oc_content_encodings; }
1241                 }
1242
1243                 internal bool OutputCacheNoStore {
1244                         get { return oc_nostore; }
1245                 }
1246                 
1247                 internal virtual TextReader Reader {
1248                         get { return null; }
1249                         set { /* no-op */ }
1250                 }
1251                 
1252                 internal string OutputCacheVaryByHeader {
1253                         get { return oc_header; }
1254                 }
1255
1256                 internal string OutputCacheVaryByCustom {
1257                         get { return oc_custom; }
1258                 }
1259
1260                 internal string OutputCacheVaryByControls {
1261                         get { return oc_controls; }
1262                 }
1263                 
1264                 internal bool OutputCacheShared {
1265                         get { return oc_shared; }
1266                 }
1267                 
1268                 internal OutputCacheLocation OutputCacheLocation {
1269                         get { return oc_location; }
1270                 }
1271
1272                 internal string OutputCacheVaryByParam {
1273                         get { return oc_param; }
1274                 }
1275
1276                 internal List <string> RegisteredTagNames {
1277                         get { return registeredTagNames; }
1278                 }
1279                 
1280                 internal PagesSection PagesConfig {
1281                         get { return GetConfigSection <PagesSection> ("system.web/pages") as PagesSection; }
1282                 }
1283
1284                 internal AspGenerator AspGenerator {
1285                         get;
1286                         set;
1287                 }
1288         }
1289 }