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