* SchemaTypes.cs: Changed DataSet type for a more generic XmlSerializable.
[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 using System;
11 using System.CodeDom.Compiler;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15 using System.Web;
16 using System.Web.Compilation;
17 using System.Web.Util;
18
19 namespace System.Web.UI
20 {
21         public abstract class TemplateParser : BaseParser
22         {
23                 string inputFile;
24                 string text;
25                 string privateBinPath;
26                 Hashtable mainAttributes;
27                 ArrayList dependencies;
28                 ArrayList assemblies;
29                 Hashtable anames;
30                 ArrayList imports;
31                 ArrayList interfaces;
32                 ArrayList scripts;
33                 Type baseType;
34                 string className;
35                 RootBuilder rootBuilder;
36                 bool debug;
37                 string compilerOptions;
38                 string language;
39
40                 public TemplateParser ()
41                 {
42                         imports = new ArrayList ();
43                         imports.Add ("System");
44                         imports.Add ("System.Collections");
45                         imports.Add ("System.Collections.Specialized");
46                         imports.Add ("System.Configuration");
47                         imports.Add ("System.Text");
48                         imports.Add ("System.Text.RegularExpressions");
49                         imports.Add ("System.Web");
50                         imports.Add ("System.Web.Caching");
51                         imports.Add ("System.Web.Security");
52                         imports.Add ("System.Web.SessionState");
53                         imports.Add ("System.Web.UI");
54                         imports.Add ("System.Web.UI.WebControls");
55                         imports.Add ("System.Web.UI.HtmlControls");
56
57                         assemblies = new ArrayList ();
58                         assemblies.Add ("System.dll");
59                         assemblies.Add ("System.Drawing.dll");
60                         assemblies.Add ("System.Data.dll");
61                         assemblies.Add ("System.Web.dll");
62                         assemblies.Add ("System.Xml.dll");
63                         AddAssembliesInBin ();
64                 }
65
66                 protected abstract Type CompileIntoType ();
67
68                 protected virtual void HandleOptions (object obj)
69                 {
70                 }
71
72                 internal static string GetOneKey (Hashtable tbl)
73                 {
74                         foreach (object key in tbl.Keys)
75                                 return key.ToString ();
76
77                         return null;
78                 }
79                 
80                 internal virtual void AddDirective (string directive, Hashtable atts)
81                 {
82                         if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
83                                 if (mainAttributes != null)
84                                         ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
85
86                                 mainAttributes = atts;
87                                 ProcessMainAttributes (mainAttributes);
88                                 return;
89                         }
90
91                         int cmp = String.Compare ("Assembly", directive, true);
92                         if (cmp == 0) {
93                                 string name = GetString (atts, "Name", null);
94                                 string src = GetString (atts, "Src", null);
95
96                                 if (atts.Count > 0)
97                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
98
99                                 if (name == null && src == null)
100                                         ThrowParseException ("You gotta specify Src or Name");
101                                         
102                                 if (name != null && src != null)
103                                         ThrowParseException ("Src and Name cannot be used together");
104
105                                 if (name != null) {
106                                         AddAssemblyByName (name);
107                                 } else {
108                                         GetAssemblyFromSource (src);
109                                 }
110
111                                 return;
112                         }
113
114                         cmp = String.Compare ("Import", directive, true);
115                         if (cmp == 0) {
116                                 string namesp = GetString (atts, "Namespace", null);
117                                 if (atts.Count > 0)
118                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
119                                 
120                                 if (namesp != null && namesp != "")
121                                         AddImport (namesp);
122                                 return;
123                         }
124
125                         cmp = String.Compare ("Implements", directive, true);
126                         if (cmp == 0) {
127                                 string ifacename = GetString (atts, "Interface", "");
128
129                                 if (atts.Count > 0)
130                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
131                                 
132                                 Type iface = LoadType (ifacename);
133                                 if (iface == null)
134                                         ThrowParseException ("Cannot find type " + ifacename);
135
136                                 if (!iface.IsInterface)
137                                         ThrowParseException (iface + " is not an interface");
138
139                                 AddInterface (iface.FullName);
140                                 return;
141                         }
142
143                         ThrowParseException ("Unknown directive: " + directive);
144                 }
145                 
146                 internal Type LoadType (string typeName)
147                 {
148                         // First try loaded assemblies, then try assemblies in Bin directory.
149                         // By now i do this 'by hand' but may be this is a runtime/gac task.
150                         Type type = null;
151                         Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
152                         foreach (Assembly ass in assemblies) {
153                                 type = ass.GetType (typeName);
154                                 if (type != null) {
155                                         AddAssembly (ass, false);
156                                         AddDependency (ass.Location);
157                                         return type;
158                                 }
159                         }
160
161                         return null;
162                 }
163
164                 void AddAssembliesInBin ()
165                 {
166                         if (!Directory.Exists (PrivateBinPath))
167                                 return;
168
169                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
170                         foreach (string dll in binDlls) {
171                                 Assembly assembly = Assembly.LoadFrom (dll);
172                                 AddAssembly (assembly, true);
173                         }
174                 }
175
176                 Assembly LoadAssemblyFromBin (string name)
177                 {
178                         Assembly assembly;
179                         if (!Directory.Exists (PrivateBinPath))
180                                 return null;
181
182                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
183                         foreach (string dll in binDlls) {
184                                 string fn = Path.GetFileName (dll);
185                                 fn = Path.ChangeExtension (fn, null);
186                                 if (fn != name)
187                                         continue;
188
189                                 assembly = Assembly.LoadFrom (dll);
190                                 return assembly;
191                         }
192
193                         return null;
194                 }
195
196                 internal virtual void AddInterface (string iface)
197                 {
198                         if (interfaces == null)
199                                 interfaces = new ArrayList ();
200
201                         if (!interfaces.Contains (iface))
202                                 interfaces.Add (iface);
203                 }
204                 
205                 internal virtual void AddImport (string namesp)
206                 {
207                         if (imports == null)
208                                 imports = new ArrayList ();
209
210                         if (!imports.Contains (namesp))
211                                 imports.Add (namesp);
212                 }
213                 
214                 internal virtual void AddDependency (string filename)
215                 {
216                         if (dependencies == null)
217                                 dependencies = new ArrayList ();
218
219                         if (!dependencies.Contains (filename))
220                                 dependencies.Add (filename);
221                 }
222                 
223                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
224                 {
225                         if (anames == null)
226                                 anames = new Hashtable ();
227
228                         string name = assembly.GetName ().Name;
229                         string loc = assembly.Location;
230                         if (fullPath) {
231                                 if (!assemblies.Contains (loc)) {
232                                         assemblies.Add (loc);
233                                 }
234
235                                 anames [name] = loc;
236                                 anames [loc] = assembly;
237                         } else {
238                                 if (!assemblies.Contains (name)) {
239                                         assemblies.Add (name);
240                                 }
241
242                                 anames [name] = assembly;
243                         }
244                 }
245
246                 internal virtual Assembly AddAssemblyByName (string name)
247                 {
248                         if (anames == null)
249                                 anames = new Hashtable ();
250
251                         if (anames.Contains (name)) {
252                                 object o = anames [name];
253                                 if (o is string)
254                                         o = anames [o];
255
256                                 return (Assembly) o;
257                         }
258
259                         bool fullpath = true;
260                         Assembly assembly = LoadAssemblyFromBin (name);
261                         if (assembly != null) {
262                                 AddAssembly (assembly, fullpath);
263                                 return assembly;
264                         }
265
266                         try {
267                                 assembly = Assembly.Load (name);
268                                 string loc = assembly.Location;
269                                 fullpath = (Path.GetDirectoryName (loc) == PrivateBinPath);
270                         } catch (Exception e) {
271                                 ThrowParseException ("Assembly " + name + " not found", e);
272                         }
273
274                         AddAssembly (assembly, fullpath);
275                         return assembly;
276                 }
277
278                 internal virtual void ProcessMainAttributes (Hashtable atts)
279                 {
280                         atts.Remove ("Description"); // ignored
281                         atts.Remove ("CodeBehind");  // ignored
282
283                         debug = GetBool (atts, "Debug", true);
284                         compilerOptions = GetString (atts, "CompilerOptions", null);
285                         language = GetString (atts, "Language", "C#");
286                         if (String.Compare (language, "c#", true) != 0)
287                                 ThrowParseException ("Only C# supported.");
288
289                         string src = GetString (atts, "Src", null);
290                         Assembly srcAssembly = null;
291                         if (src != null)
292                                 srcAssembly = GetAssemblyFromSource (src);
293
294                         string inherits = GetString (atts, "Inherits", null);
295                         if (inherits != null) {
296                                 Type parent;
297                                 if (srcAssembly != null)
298                                         parent = srcAssembly.GetType (inherits);
299                                 else
300                                         parent = LoadType (inherits);
301
302                                 if (parent == null)
303                                         ThrowParseException ("Cannot find type " + inherits);
304
305                                 if (!DefaultBaseType.IsAssignableFrom (parent))
306                                         ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
307
308                                 baseType = parent;
309                         }
310
311                         className = GetString (atts, "ClassName", null);
312                         if (atts.Count > 0)
313                                 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
314                 }
315
316                 Assembly GetAssemblyFromSource (string vpath)
317                 {
318                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
319                         string realPath = MapPath (vpath, false);
320                         if (!File.Exists (realPath))
321                                 ThrowParseException ("File " + vpath + " not found");
322
323                         AddDependency (realPath);
324
325                         CompilerResults result = CachingCompiler.Compile (realPath, assemblies);
326                         if (result.NativeCompilerReturnValue != 0) {
327                                 StringWriter writer = new StringWriter();
328                                 StreamReader reader = new StreamReader (realPath);
329                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
330                         }
331
332                         AddAssembly (result.CompiledAssembly, true);
333                         return result.CompiledAssembly;
334                 }
335                 
336                 protected abstract Type DefaultBaseType { get; }
337
338                 protected internal abstract string DefaultDirectiveName { get; }
339
340                 internal string InputFile
341                 {
342                         get { return inputFile; }
343                         set { inputFile = value; }
344                 }
345
346                 internal string Text
347                 {
348                         get { return text; }
349                         set { text = value; }
350                 }
351
352                 internal Type BaseType
353                 {
354                         get {
355                                 if (baseType == null)
356                                         baseType = DefaultBaseType;
357
358                                 return baseType;
359                         }
360                 }
361                 
362                 internal string ClassName {
363                         get {
364                                 if (className != null)
365                                         return className;
366
367                                 className = Path.GetFileName (inputFile).Replace ('.', '_');
368                                 className = className.Replace ('-', '_'); 
369                                 className = className.Replace (' ', '_');
370                                 return className;
371                         }
372                 }
373
374                 internal string PrivateBinPath {
375                         get {
376                                 if (privateBinPath != null)
377                                         return privateBinPath;
378
379                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
380                                 privateBinPath = setup.PrivateBinPath;
381                                         
382                                 if (!Path.IsPathRooted (privateBinPath)) {
383                                         string appbase = setup.ApplicationBase;
384                                         if (appbase.StartsWith ("file://")) {
385                                                 appbase = appbase.Substring (7);
386                                                 if (Path.DirectorySeparatorChar != '/')
387                                                         appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
388                                         }
389                                         privateBinPath = Path.Combine (appbase, privateBinPath);
390                                 }
391
392                                 return privateBinPath;
393                         }
394                 }
395
396                 internal ArrayList Scripts {
397                         get {
398                                 if (scripts == null)
399                                         scripts = new ArrayList ();
400
401                                 return scripts;
402                         }
403                 }
404
405                 internal ArrayList Imports {
406                         get { return imports; }
407                 }
408
409                 internal ArrayList Assemblies {
410                         get { return assemblies; }
411                 }
412
413                 internal ArrayList Interfaces {
414                         get { return interfaces; }
415                 }
416
417                 internal RootBuilder RootBuilder {
418                         get { return rootBuilder; }
419                         set { rootBuilder = value; }
420                 }
421
422                 internal ArrayList Dependencies {
423                         get { return dependencies; }
424                 }
425
426                 internal string CompilerOptions {
427                         get { return compilerOptions; }
428                 }
429
430                 internal string Language {
431                         get { return language; }
432                 }
433
434                 internal bool Debug {
435                         get { return debug; }
436                 }
437         }
438 }
439