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