2004-06-03 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / SimpleWebHandlerParser.cs
1 //
2 // System.Web.UI.SimpleWebHandlerParser
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 //
9
10 using System;
11 using System.CodeDom.Compiler;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15 using System.Text;
16 using System.Web;
17 using System.Web.Compilation;
18 using System.Web.Configuration;
19 using System.Web.Util;
20
21 namespace System.Web.UI
22 {
23         public abstract class SimpleWebHandlerParser
24         {
25                 HttpContext context;
26                 string vPath;
27                 string physPath;
28                 string className;
29                 string codeBehind;
30                 bool debug;
31                 string language;
32                 string program;
33                 bool gotDefault;
34                 ArrayList assemblies;
35                 ArrayList dependencies;
36                 Hashtable anames;
37                 string privateBinPath;
38                 string baseDir;
39                 string baseVDir;
40                 CompilationConfiguration compilationConfig;
41
42                 protected SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath)
43                 {
44                         this.context = context;
45                         this.vPath = virtualPath;
46                         this.physPath = physicalPath;
47                         AddDependency (physicalPath);
48
49                         assemblies = new ArrayList ();
50                         string location = Context.ApplicationInstance.AssemblyLocation;
51                         if (location != typeof (TemplateParser).Assembly.Location)
52                                 assemblies.Add (location);
53
54                         assemblies.AddRange (CompilationConfig.Assemblies);
55                         if (CompilationConfig.AssembliesInBin)
56                                 AddAssembliesInBin ();
57
58                         language = CompilationConfig.DefaultLanguage;
59
60                         GetDirectivesAndContent ();
61                 }
62
63                 [MonoTODO]
64                 protected Type GetCompiledTypeFromCache ()
65                 {
66                         return null;
67                 }
68
69                 void GetDirectivesAndContent ()
70                 {
71                         StreamReader reader = new StreamReader (File.OpenRead (physPath));
72                         string line;
73                         bool directiveFound = false;
74                         StringBuilder content = new StringBuilder ();
75
76                         while ((line = reader.ReadLine ()) != null) {
77                                 string trimmed = line.Trim ();
78                                 if (!directiveFound && trimmed == String.Empty)
79                                         continue;
80                                 
81                                 if (trimmed.StartsWith ("<")) {
82                                         ParseDirective (trimmed);
83                                         directiveFound = true;
84                                         continue;
85                                 }
86
87                                 content.Append (line + "\n");
88                                 content.Append (reader.ReadToEnd ());
89                         }
90
91                         if (!gotDefault)
92                                 throw new ParseException (null, "No @WebService directive found");
93                                 
94                         this.program = content.ToString ();
95                         reader.Close ();
96                 }
97
98                 void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes)
99                 {
100                         if (tagtype != System.Web.Compilation.TagType.Directive)
101                                 throw new ParseException (location, "Unexpected tag");
102
103                         if (String.Compare (tagid, DefaultDirectiveName, true) == 0) {
104                                 AddDefaultDirective (location, attributes);
105                         } else if (String.Compare (tagid, "Assembly", true) == 0) {
106                                 AddAssemblyDirective (location, attributes);
107                         } else {
108                                 throw new ParseException (location, "Unexpected directive: " + tagid);
109                         }
110                 }
111
112                 void TextParsed (ILocation location, string text)
113                 {
114                         if (text.Trim () != "")
115                                 throw new ParseException (location, "Text not allowed here");
116                 }
117
118                 void ParseError (ILocation location, string message)
119                 {
120                         throw new ParseException (location, message);
121                 }
122
123                 static string GetAndRemove (Hashtable table, string key)
124                 {
125                         string o = table [key] as string;
126                         table.Remove (key);
127                         return o;
128                 }
129                 
130                 void ParseDirective (string line)
131                 {
132                         AspParser parser = new AspParser (physPath, new StringReader (line));
133                         parser.Error += new ParseErrorHandler (ParseError);
134                         parser.TagParsed += new TagParsedHandler (TagParsed);
135                         parser.TextParsed += new TextParsedHandler (TextParsed);
136
137                         parser.Parse ();
138                 }
139
140                 internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs)
141                 {
142                         if (gotDefault)
143                                 throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive");
144
145                         gotDefault = true;
146                         Hashtable attributes = attrs.GetDictionary (null);
147                         className = GetAndRemove (attributes, "class");
148                         if (className == null)
149                                 throw new ParseException (null, "No Class attribute found.");
150                         
151                         string d = GetAndRemove (attributes, "debug");
152                         if (d != null) {
153                                 debug = (String.Compare (d, "true", true) == 0);
154                                 if (debug == false && String.Compare (d, "false", true) != 0)
155                                         throw new ParseException (null, "Invalid value for Debug attribute");
156                         }
157
158                         language = GetAndRemove (attributes, "language");
159                         if (language == null)
160                                 language = CompilationConfig.DefaultLanguage;
161
162                         codeBehind = GetAndRemove (attributes, "codebehind");
163                         if (attributes.Count > 0)
164                                 throw new ParseException (location, "Unrecognized attribute in " +
165                                                           DefaultDirectiveName + " directive");
166                 }
167
168                 internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs)
169                 {
170                         Hashtable tbl = attrs.GetDictionary (null);
171                         string name = GetAndRemove (tbl, "Name");
172                         string src = GetAndRemove (tbl, "Src");
173                         if (name == null && src == null)
174                                 throw new ParseException (location, "You gotta specify Src or Name");
175
176                         if (name != null && src != null)
177                                 throw new ParseException (location, "Src and Name cannot be used together");
178
179                         if (name != null) {
180                                 AddAssemblyByName (name, location);
181                         } else {
182                                 GetAssemblyFromSource (src, location);
183                         }
184
185                         if (tbl.Count > 0)
186                                 throw new ParseException (location, "Unrecognized attribute in Assembly directive");
187                 }
188
189                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
190                 {
191                         if (anames == null)
192                                 anames = new Hashtable ();
193
194                         string name = assembly.GetName ().Name;
195                         string loc = assembly.Location;
196                         if (fullPath) {
197                                 if (!assemblies.Contains (loc)) {
198                                         assemblies.Add (loc);
199                                 }
200
201                                 anames [name] = loc;
202                                 anames [loc] = assembly;
203                         } else {
204                                 if (!assemblies.Contains (name)) {
205                                         assemblies.Add (name);
206                                 }
207
208                                 anames [name] = assembly;
209                         }
210                 }
211
212                 internal virtual Assembly AddAssemblyByName (string name, ILocation location)
213                 {
214                         if (anames == null)
215                                 anames = new Hashtable ();
216
217                         if (anames.Contains (name)) {
218                                 object o = anames [name];
219                                 if (o is string)
220                                         o = anames [o];
221
222                                 return (Assembly) o;
223                         }
224
225                         bool fullpath = true;
226                         Assembly assembly = LoadAssemblyFromBin (name);
227                         if (assembly != null) {
228                                 AddAssembly (assembly, fullpath);
229                                 return assembly;
230                         }
231
232                         try {
233                                 assembly = Assembly.LoadWithPartialName (name);
234                                 string loc = assembly.Location;
235                                 fullpath = (Path.GetDirectoryName (loc) == PrivateBinPath);
236                         } catch (Exception e) {
237                                 throw new ParseException (location, "Assembly " + name + " not found", e);
238                         }
239
240                         AddAssembly (assembly, fullpath);
241                         return assembly;
242                 }
243
244                 void AddAssembliesInBin ()
245                 {
246                         if (!Directory.Exists (PrivateBinPath))
247                                 return;
248
249                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
250                         foreach (string dll in binDlls) {
251                                 try {
252                                         Assembly assembly = Assembly.LoadFrom (dll);
253                                         AddAssembly (assembly, true);
254                                 } catch (Exception e) {
255                                         throw new Exception ("Error while loading " + dll, e);
256                                 }
257                         }
258                 }
259
260                 Assembly LoadAssemblyFromBin (string name)
261                 {
262                         Assembly assembly;
263                         if (!Directory.Exists (PrivateBinPath))
264                                 return null;
265
266                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
267                         foreach (string dll in binDlls) {
268                                 string fn = Path.GetFileName (dll);
269                                 fn = Path.ChangeExtension (fn, null);
270                                 if (fn != name)
271                                         continue;
272
273                                 assembly = Assembly.LoadFrom (dll);
274                                 return assembly;
275                         }
276
277                         return null;
278                 }
279
280                 Assembly GetAssemblyFromSource (string vpath, ILocation location)
281                 {
282                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
283                         string realPath = context.Request.MapPath (vpath);
284                         if (!File.Exists (realPath))
285                                 throw new ParseException (location, "File " + vpath + " not found");
286
287                         AddDependency (realPath);
288
289                         CompilerResults result = CachingCompiler.Compile (realPath, realPath, assemblies);
290                         if (result.NativeCompilerReturnValue != 0) {
291                                 StreamReader reader = new StreamReader (realPath);
292                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
293                         }
294
295                         AddAssembly (result.CompiledAssembly, true);
296                         return result.CompiledAssembly;
297                 }
298                 
299                 internal Type GetTypeFromBin (string typeName)
300                 {
301                         if (!Directory.Exists (PrivateBinPath))
302                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
303
304                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
305                         Type result = null;
306                         foreach (string dll in binDlls) {
307                                 Assembly assembly = Assembly.LoadFrom (dll);
308                                 Type type = assembly.GetType (typeName, false);
309                                 if (type != null) {
310                                         if (result != null) 
311                                                 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
312
313                                         result = type;
314                                 } 
315                         }
316
317                         if (result == null)
318                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
319
320                         return result;
321                 }
322                 
323                 internal virtual void AddDependency (string filename)
324                 {
325                         if (dependencies == null)
326                                 dependencies = new ArrayList ();
327
328                         if (!dependencies.Contains (filename))
329                                 dependencies.Add (filename);
330                 }
331                 
332                 // Properties
333                 protected abstract string DefaultDirectiveName { get; }
334
335                 internal HttpContext Context {
336                         get { return context; }
337                 }
338
339                 internal string VirtualPath {
340                         get { return vPath; }
341                 }
342
343                 internal string PhysicalPath {
344                         get { return physPath; }
345                 }
346
347                 internal string ClassName {
348                         get { return className; }
349                 }
350
351                 internal string CodeBehind {
352                         get { return codeBehind; }
353                 }
354
355                 internal bool Debug {
356                         get { return debug; }
357                 }
358
359                 internal string Language {
360                         get { return language; }
361                 }
362
363                 internal string Program {
364                         get { return program; }
365                 }
366
367                 internal ArrayList Assemblies {
368                         get { return assemblies; }
369                 }
370
371                 internal ArrayList Dependencies {
372                         get { return dependencies; }
373                 }
374
375                 internal string PrivateBinPath {
376                         get {
377                                 if (privateBinPath != null)
378                                         return privateBinPath;
379
380                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
381                                 privateBinPath = setup.PrivateBinPath;
382                                         
383                                 if (!Path.IsPathRooted (privateBinPath)) {
384                                         string appbase = setup.ApplicationBase;
385                                         if (appbase.StartsWith ("file://")) {
386                                                 appbase = appbase.Substring (7);
387                                                 if (Path.DirectorySeparatorChar != '/')
388                                                         appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
389                                         }
390                                         privateBinPath = Path.Combine (appbase, privateBinPath);
391                                 }
392
393                                 return privateBinPath;
394                         }
395                 }
396
397                 internal string BaseDir {
398                         get {
399                                 if (baseDir == null)
400                                         baseDir = context.Request.MapPath (BaseVirtualDir);
401
402                                 return baseDir;
403                         }
404                 }
405
406                 internal virtual string BaseVirtualDir {
407                         get {
408                                 if (baseVDir == null)
409                                         baseVDir = UrlUtils.GetDirectory (context.Request.FilePath);
410
411                                 return baseVDir;
412                         }
413                 }
414
415                 internal CompilationConfiguration CompilationConfig {
416                         get {
417                                 if (compilationConfig == null)
418                                         compilationConfig = CompilationConfiguration.GetInstance (context);
419
420                                 return compilationConfig;
421                         }
422                 }
423         }
424 }
425