Add this for backwards compatibility
[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.IO;
34 using System.Reflection;
35 using System.Security.Permissions;
36 using System.Web.Compilation;
37 using System.Web.Configuration;
38 using System.Web.Util;
39
40 namespace System.Web.UI {
41
42         // CAS
43         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45         public abstract class TemplateParser : BaseParser
46         {
47                 string inputFile;
48                 string text;
49                 string privateBinPath;
50                 Hashtable mainAttributes;
51                 ArrayList dependencies;
52                 ArrayList assemblies;
53                 Hashtable anames;
54                 ArrayList imports;
55                 ArrayList interfaces;
56                 ArrayList scripts;
57                 Type baseType;
58                 string className;
59                 RootBuilder rootBuilder;
60                 bool debug;
61                 string compilerOptions;
62                 string language;
63                 bool strictOn = false;
64                 bool explicitOn = false;
65                 bool output_cache;
66                 int oc_duration;
67                 string oc_header, oc_custom, oc_param, oc_controls;
68                 bool oc_shared;
69                 OutputCacheLocation oc_location;
70 #if NET_2_0
71                 string src;
72                 string partialClassName;
73 #endif
74                 Assembly srcAssembly;
75                 int appAssemblyIndex = -1;
76
77                 [MonoTODO ("deal with AddAssembliesInBin in the 2.0 case")]
78                 internal TemplateParser ()
79                 {
80                         imports = new ArrayList ();
81                         imports.Add ("System");
82                         imports.Add ("System.Collections");
83                         imports.Add ("System.Collections.Specialized");
84                         imports.Add ("System.Configuration");
85                         imports.Add ("System.Text");
86                         imports.Add ("System.Text.RegularExpressions");
87                         imports.Add ("System.Web");
88                         imports.Add ("System.Web.Caching");
89                         imports.Add ("System.Web.Security");
90                         imports.Add ("System.Web.SessionState");
91                         imports.Add ("System.Web.UI");
92                         imports.Add ("System.Web.UI.WebControls");
93                         imports.Add ("System.Web.UI.HtmlControls");
94
95                         assemblies = new ArrayList ();
96 #if CONFIGURATION_2_0
97                         foreach (AssemblyInfo info in CompilationConfig.Assemblies)
98                                 AddAssemblyByName (info.Assembly);
99 #else
100                         foreach (string a in CompilationConfig.Assemblies)
101                                 AddAssemblyByName (a);
102                         if (CompilationConfig.AssembliesInBin)
103                                 AddAssembliesInBin ();
104 #endif
105
106                         language = CompilationConfig.DefaultLanguage;
107                 }
108
109                 internal void AddApplicationAssembly ()
110                 {
111                         string location = Context.ApplicationInstance.AssemblyLocation;
112                         if (location != typeof (TemplateParser).Assembly.Location) {
113                                 appAssemblyIndex = assemblies.Add (location);
114                         }
115                 }
116
117                 protected abstract Type CompileIntoType ();
118
119                 internal virtual void HandleOptions (object obj)
120                 {
121                 }
122
123                 internal static string GetOneKey (Hashtable tbl)
124                 {
125                         foreach (object key in tbl.Keys)
126                                 return key.ToString ();
127
128                         return null;
129                 }
130                 
131                 internal virtual void AddDirective (string directive, Hashtable atts)
132                 {
133                         if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
134                                 if (mainAttributes != null)
135                                         ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
136
137                                 mainAttributes = atts;
138                                 ProcessMainAttributes (mainAttributes);
139                                 return;
140                         }
141
142                         int cmp = String.Compare ("Assembly", directive, true);
143                         if (cmp == 0) {
144                                 string name = GetString (atts, "Name", null);
145                                 string src = GetString (atts, "Src", null);
146
147                                 if (atts.Count > 0)
148                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
149
150                                 if (name == null && src == null)
151                                         ThrowParseException ("You gotta specify Src or Name");
152                                         
153                                 if (name != null && src != null)
154                                         ThrowParseException ("Src and Name cannot be used together");
155
156                                 if (name != null) {
157                                         AddAssemblyByName (name);
158                                 } else {
159                                         GetAssemblyFromSource (src);
160                                 }
161
162                                 return;
163                         }
164
165                         cmp = String.Compare ("Import", directive, true);
166                         if (cmp == 0) {
167                                 string namesp = GetString (atts, "Namespace", null);
168                                 if (atts.Count > 0)
169                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
170                                 
171                                 if (namesp != null && namesp != "")
172                                         AddImport (namesp);
173                                 return;
174                         }
175
176                         cmp = String.Compare ("Implements", directive, true);
177                         if (cmp == 0) {
178                                 string ifacename = GetString (atts, "Interface", "");
179
180                                 if (atts.Count > 0)
181                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
182                                 
183                                 Type iface = LoadType (ifacename);
184                                 if (iface == null)
185                                         ThrowParseException ("Cannot find type " + ifacename);
186
187                                 if (!iface.IsInterface)
188                                         ThrowParseException (iface + " is not an interface");
189
190                                 AddInterface (iface.FullName);
191                                 return;
192                         }
193
194                         cmp = String.Compare ("OutputCache", directive, true);
195                         if (cmp == 0) {
196                                 output_cache = true;
197                                 
198                                 if (atts ["Duration"] == null)
199                                         ThrowParseException ("The directive is missing a 'duration' attribute.");
200                                 if (atts ["VaryByParam"] == null)
201                                         ThrowParseException ("This directive is missing a 'VaryByParam' " +
202                                                         "attribute, which should be set to \"none\", \"*\", " +
203                                                         "or a list of name/value pairs.");
204
205                                 foreach (DictionaryEntry entry in atts) {
206                                         string key = (string) entry.Key;
207                                         switch (key.ToLower ()) {
208                                         case "duration":
209                                                 oc_duration = Int32.Parse ((string) entry.Value);
210                                                 if (oc_duration < 1)
211                                                         ThrowParseException ("The 'duration' attribute must be set " +
212                                                                         "to a positive integer value");
213                                                 break;
214                                         case "varybyparam":
215                                                 oc_param = (string) entry.Value;
216                                                 if (String.Compare (oc_param, "none") == 0)
217                                                         oc_param = null;
218                                                 break;
219                                         case "varybyheader":
220                                                 oc_header = (string) entry.Value;
221                                                 break;
222                                         case "varybycustom":
223                                                 oc_custom = (string) entry.Value;
224                                                 break;
225                                         case "location":
226                                                 if (!(this is PageParser))
227                                                         goto default;
228
229                                                 try {
230                                                         oc_location = (OutputCacheLocation) Enum.Parse (
231                                                                 typeof (OutputCacheLocation), (string) entry.Value, true);
232                                                 } catch {
233                                                         ThrowParseException ("The 'location' attribute is case sensitive and " +
234                                                                         "must be one of the following values: Any, Client, " +
235                                                                         "Downstream, Server, None, ServerAndClient.");
236                                                 }
237                                                 break;
238                                         case "varybycontrol":
239                                                 if (this is PageParser)
240                                                         goto default;
241
242                                                 oc_controls = (string) entry.Value;
243                                                 break;
244                                         case "shared":
245                                                 if (this is PageParser)
246                                                         goto default;
247
248                                                 try {
249                                                         oc_shared = Boolean.Parse ((string) entry.Value);
250                                                 } catch {
251                                                         ThrowParseException ("The 'shared' attribute is case sensitive" +
252                                                                         " and must be set to 'true' or 'false'.");
253                                                 }
254                                                 break;
255                                         default:
256                                                 ThrowParseException ("The '" + key + "' attribute is not " +
257                                                                 "supported by the 'Outputcache' directive.");
258                                                 break;
259                                         }
260                                         
261                                 }
262                                 
263                                 return;
264                         }
265
266                         ThrowParseException ("Unknown directive: " + directive);
267                 }
268
269                 internal Type LoadType (string typeName)
270                 {
271                         // First try loaded assemblies, then try assemblies in Bin directory.
272                         Type type = null;
273                         bool seenBin = false;
274                         Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
275                         foreach (Assembly ass in assemblies) {
276                                 type = ass.GetType (typeName);
277                                 if (type == null)
278                                         continue;
279
280                                 if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
281                                         AddAssembly (ass, true);
282                                 } else {
283                                         seenBin = true;
284                                 }
285
286                                 AddDependency (ass.Location);
287                                 return type;
288                         }
289
290                         if (seenBin)
291                                 return null;
292
293                         // Load from bin
294                         if (!Directory.Exists (PrivateBinPath))
295                                 return null;
296
297                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
298                         foreach (string s in binDlls) {
299                                 Assembly binA = Assembly.LoadFrom (s);
300                                 type = binA.GetType (typeName);
301                                 if (type == null)
302                                         continue;
303
304                                 AddDependency (binA.Location);
305                                 return type;
306                         }
307
308                         return null;
309                 }
310
311                 void AddAssembliesInBin ()
312                 {
313                         if (!Directory.Exists (PrivateBinPath))
314                                 return;
315
316                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
317                         foreach (string s in binDlls) {
318                                 assemblies.Add (s);
319                         }
320                 }
321
322                 internal virtual void AddInterface (string iface)
323                 {
324                         if (interfaces == null)
325                                 interfaces = new ArrayList ();
326
327                         if (!interfaces.Contains (iface))
328                                 interfaces.Add (iface);
329                 }
330                 
331                 internal virtual void AddImport (string namesp)
332                 {
333                         if (imports == null)
334                                 imports = new ArrayList ();
335
336                         if (!imports.Contains (namesp))
337                                 imports.Add (namesp);
338                 }
339
340                 internal virtual void AddSourceDependency (string filename)
341                 {
342                         if (dependencies != null && dependencies.Contains (filename)) {
343                                 ThrowParseException ("Circular file references are not allowed. File: " + filename);
344                         }
345
346                         AddDependency (filename);
347                 }
348
349                 internal virtual void AddDependency (string filename)
350                 {
351                         if (filename == "")
352                                 return;
353
354                         if (dependencies == null)
355                                 dependencies = new ArrayList ();
356
357                         if (!dependencies.Contains (filename))
358                                 dependencies.Add (filename);
359                 }
360                 
361                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
362                 {
363                         if (assembly.Location == "")
364                                 return;
365
366                         if (anames == null)
367                                 anames = new Hashtable ();
368
369                         string name = assembly.GetName ().Name;
370                         string loc = assembly.Location;
371                         if (fullPath) {
372                                 if (!assemblies.Contains (loc)) {
373                                         assemblies.Add (loc);
374                                 }
375
376                                 anames [name] = loc;
377                                 anames [loc] = assembly;
378                         } else {
379                                 if (!assemblies.Contains (name)) {
380                                         assemblies.Add (name);
381                                 }
382
383                                 anames [name] = assembly;
384                         }
385                 }
386
387                 internal virtual Assembly AddAssemblyByName (string name)
388                 {
389                         if (anames == null)
390                                 anames = new Hashtable ();
391
392                         if (anames.Contains (name)) {
393                                 object o = anames [name];
394                                 if (o is string)
395                                         o = anames [o];
396
397                                 return (Assembly) o;
398                         }
399
400                         Assembly assembly = null;
401                         Exception error = null;
402                         if (name.IndexOf (',') != -1) {
403                                 try {
404                                         assembly = Assembly.Load (name);
405                                 } catch (Exception e) { error = e; }
406                         }
407
408                         if (assembly == null) {
409                                 try {
410                                         assembly = Assembly.LoadWithPartialName (name);
411                                 } catch (Exception e) { error = e; }
412                         }
413
414                         if (assembly == null)
415                                 ThrowParseException ("Assembly " + name + " not found", error);
416
417                         AddAssembly (assembly, true);
418                         return assembly;
419                 }
420
421                 internal virtual void ProcessMainAttributes (Hashtable atts)
422                 {
423                         atts.Remove ("Description"); // ignored
424 #if NET_1_1
425                         atts.Remove ("CodeBehind");  // ignored
426 #endif
427                         atts.Remove ("AspCompat"); // ignored
428
429                         debug = GetBool (atts, "Debug", true);
430                         compilerOptions = GetString (atts, "CompilerOptions", "");
431                         language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
432                         strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
433                         explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
434
435                         string inherits = GetString (atts, "Inherits", null);
436 #if NET_2_0
437                         // In ASP 2, the source file is actually integrated with
438                         // the generated file via the use of partial classes. This
439                         // means that the code file has to be confirmed, but not
440                         // used at this point.
441                         src = GetString (atts, "CodeFile", null);
442
443                         if (src != null && inherits != null) {
444                                 // Make sure the source exists
445                                 src = UrlUtils.Combine (BaseVirtualDir, src);
446                                 string realPath = MapPath (src, false);
447                                 if (!File.Exists (realPath))
448                                         ThrowParseException ("File " + src + " not found");
449
450                                 // Verify that the inherits is a valid identify not a
451                                 // fully-qualified name.
452                                 if (!CodeGenerator.IsValidLanguageIndependentIdentifier (inherits))
453                                         ThrowParseException (String.Format ("'{0}' is not valid for 'inherits'", inherits));
454
455                                 // We are going to create a partial class that shares
456                                 // the same name as the inherits tag, so reset the
457                                 // name. The base type is changed because it is the
458                                 // code file's responsibilty to extend the classes
459                                 // needed.
460                                 partialClassName = inherits;
461
462                                 // Add the code file as an option to the
463                                 // compiler. This lets both files be compiled at once.
464                                 compilerOptions += " " + realPath;
465                         } else if (inherits != null) {
466                                 // We just set the inherits directly because this is a
467                                 // Single-Page model.
468                                 SetBaseType (inherits);
469                         }
470 #else
471                         string src = GetString (atts, "Src", null);
472
473                         if (src != null)
474                                 srcAssembly = GetAssemblyFromSource (src);
475
476                         if (inherits != null)
477                                 SetBaseType (inherits);
478
479                         className = GetString (atts, "ClassName", null);
480                         if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
481                                 ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
482 #endif
483
484                         if (atts.Count > 0)
485                                 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
486                 }
487
488                 internal void SetBaseType (string type)
489                 {
490                         if (type == DefaultBaseTypeName)
491                                 return;
492
493                         Type parent = null;
494                         if (srcAssembly != null)
495                                 parent = srcAssembly.GetType (type);
496
497                         if (parent == null)
498                                 parent = LoadType (type);
499
500                         if (parent == null)
501                                 ThrowParseException ("Cannot find type " + type);
502
503                         if (!DefaultBaseType.IsAssignableFrom (parent))
504                                 ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
505
506                         baseType = parent;
507                 }
508
509                 Assembly GetAssemblyFromSource (string vpath)
510                 {
511                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
512                         string realPath = MapPath (vpath, false);
513                         if (!File.Exists (realPath))
514                                 ThrowParseException ("File " + vpath + " not found");
515
516                         AddSourceDependency (realPath);
517
518                         CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
519                         if (result.NativeCompilerReturnValue != 0) {
520                                 StreamReader reader = new StreamReader (realPath);
521                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
522                         }
523
524                         AddAssembly (result.CompiledAssembly, true);
525                         return result.CompiledAssembly;
526                 }
527                 
528                 internal abstract Type DefaultBaseType { get; }
529                 internal abstract string DefaultBaseTypeName { get; }
530                 internal abstract string DefaultDirectiveName { get; }
531
532                 internal string InputFile
533                 {
534                         get { return inputFile; }
535                         set { inputFile = value; }
536                 }
537
538 #if NET_2_0
539                 internal bool IsPartial
540                 {
541                         get { return src != null; }
542                 }
543
544                 internal string PartialClassName
545                 {
546                         get { return partialClassName; }
547                 }
548 #endif
549
550                 internal string Text
551                 {
552                         get { return text; }
553                         set { text = value; }
554                 }
555
556                 internal Type BaseType
557                 {
558                         get {
559                                 if (baseType == null)
560                                         baseType = DefaultBaseType;
561
562                                 return baseType;
563                         }
564                 }
565                 
566                 internal string ClassName {
567                         get {
568                                 if (className != null)
569                                         return className;
570
571                                 className = Path.GetFileName (inputFile).Replace ('.', '_');
572                                 className = className.Replace ('-', '_'); 
573                                 className = className.Replace (' ', '_');
574
575                                 if (Char.IsDigit(className[0])) {
576                                         className = "_" + className;
577                                 }
578
579                                 return className;
580                         }
581                 }
582
583                 internal string PrivateBinPath {
584                         get {
585                                 if (privateBinPath != null)
586                                         return privateBinPath;
587
588                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
589                                 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
590
591                                 return privateBinPath;
592                         }
593                 }
594
595                 internal ArrayList Scripts {
596                         get {
597                                 if (scripts == null)
598                                         scripts = new ArrayList ();
599
600                                 return scripts;
601                         }
602                 }
603
604                 internal ArrayList Imports {
605                         get { return imports; }
606                 }
607
608                 internal ArrayList Assemblies {
609                         get {
610                                 if (appAssemblyIndex != -1) {
611                                         object o = assemblies [appAssemblyIndex];
612                                         assemblies.RemoveAt (appAssemblyIndex);
613                                         assemblies.Add (o);
614                                         appAssemblyIndex = -1;
615                                 }
616
617                                 return assemblies;
618                         }
619                 }
620
621                 internal ArrayList Interfaces {
622                         get { return interfaces; }
623                 }
624
625                 internal RootBuilder RootBuilder {
626                         get { return rootBuilder; }
627                         set { rootBuilder = value; }
628                 }
629
630                 internal ArrayList Dependencies {
631                         get { return dependencies; }
632                         set { dependencies = value; }
633                 }
634
635                 internal string CompilerOptions {
636                         get { return compilerOptions; }
637                 }
638
639                 internal string Language {
640                         get { return language; }
641                 }
642
643                 internal bool StrictOn {
644                         get { return strictOn; }
645                 }
646
647                 internal bool ExplicitOn {
648                         get { return explicitOn; }
649                 }
650                 
651                 internal bool Debug {
652                         get { return debug; }
653                 }
654
655                 internal bool OutputCache {
656                         get { return output_cache; }
657                 }
658
659                 internal int OutputCacheDuration {
660                         get { return oc_duration; }
661                 }
662
663                 internal string OutputCacheVaryByHeader {
664                         get { return oc_header; }
665                 }
666
667                 internal string OutputCacheVaryByCustom {
668                         get { return oc_custom; }
669                 }
670
671                 internal string OutputCacheVaryByControls {
672                         get { return oc_controls; }
673                 }
674                 
675                 internal bool OutputCacheShared {
676                         get { return oc_shared; }
677                 }
678                 
679                 internal OutputCacheLocation OutputCacheLocation {
680                         get { return oc_location; }
681                 }
682
683                 internal string OutputCacheVaryByParam {
684                         get { return oc_param; }
685                 }
686
687 #if CONFIGURATION_2_0
688                 internal PagesSection PagesConfig {
689                         get {
690                                 return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
691                         }
692                 }
693 #else
694                 internal PagesConfiguration PagesConfig {
695                         get { return PagesConfiguration.GetInstance (Context); }
696                 }
697 #endif
698         }
699 }
700