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