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