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