[asp.net] Use faster string comparison in caches in System.Web.Compilation
[mono.git] / mcs / class / System.Web / System.Web.Compilation / GenericBuildProvider.cs
1 //
2 // System.Web.Compilation.GenericBuildProvider
3 //
4 // Authors:
5 //   Marek Habersack (mhabersack@novell.com)
6 //
7 // (C) 2008 Novell, Inc
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.CodeDom;
33 using System.CodeDom.Compiler;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.IO;
37 using System.Reflection;
38 using System.Web;
39 using System.Web.UI;
40
41 namespace System.Web.Compilation
42 {
43         internal abstract class GenericBuildProvider <TParser> : BuildProvider
44         {
45                 TParser _parser;
46                 CompilerType _compilerType;
47                 BaseCompiler _compiler;
48                 TextReader _reader;
49                 bool _parsed;
50                 bool _codeGenerated;
51
52                 protected bool Parsed {
53                         get { return _parsed; }
54                 }
55                 
56                 protected abstract TParser CreateParser (VirtualPath virtualPath, string physicalPath, TextReader reader, HttpContext context);
57                 protected abstract TParser CreateParser (VirtualPath virtualPath, string physicalPath, HttpContext context);
58                 protected abstract BaseCompiler CreateCompiler (TParser parser);
59                 protected abstract string GetParserLanguage (TParser parser);
60                 protected abstract ICollection GetParserDependencies (TParser parser);
61                 protected abstract string GetCodeBehindSource (TParser parser);
62                 protected abstract string GetClassType (BaseCompiler compiler, TParser parser);
63                 protected abstract AspGenerator CreateAspGenerator (TParser parser);
64                 protected abstract List <string> GetReferencedAssemblies (TParser parser);
65
66                 protected virtual string MapPath (VirtualPath virtualPath)
67                 {
68                         HttpContext ctx = HttpContext.Current;
69                         HttpRequest req = ctx != null ? ctx.Request : null;
70
71                         if (req != null)
72                                 return req.MapPath (VirtualPath);
73                         else
74                                 return null;
75                 }
76                 
77                 protected virtual TParser Parse ()
78                 {
79                         TParser parser = Parser;
80                         
81                         if (_parsed)
82                                 return parser;
83
84                         if (!IsDirectoryBuilder) {
85                                 AspGenerator generator = CreateAspGenerator (parser);
86                                 if (_reader != null)
87                                         generator.Parse (_reader, MapPath (VirtualPathInternal), true);
88                                 else
89                                         generator.Parse ();
90                         }
91                         
92                         _parsed = true;
93                         return parser;
94                 }
95
96                 protected virtual void OverrideAssemblyPrefix (TParser parser, AssemblyBuilder assemblyBuilder)
97                 {
98                 }
99
100                 internal override void GenerateCode ()
101                 {
102                         TParser parser = Parse ();
103                         _compiler = CreateCompiler (parser);
104                         if (NeedsConstructType)
105                                 _compiler.ConstructType ();
106                         _codeGenerated = true;
107                 }
108                 
109                 protected virtual void GenerateCode (AssemblyBuilder assemblyBuilder, TParser parser, BaseCompiler compiler)
110                 {                               
111                         CodeCompileUnit unit = _compiler.CompileUnit;
112                         if (unit == null)
113                                 throw new HttpException ("Unable to generate source code.");
114                                 
115                         assemblyBuilder.AddCodeCompileUnit (this, unit);
116                 }
117                 
118                 public override void GenerateCode (AssemblyBuilder assemblyBuilder)
119                 {
120                         if (!_codeGenerated)
121                                 GenerateCode ();
122                         
123                         TParser parser = Parse ();
124                         OverrideAssemblyPrefix (parser, assemblyBuilder);
125                         
126                         string codeBehindSource = GetCodeBehindSource (parser);
127                         if (codeBehindSource != null)
128                                 assemblyBuilder.AddCodeFile (codeBehindSource, this, true);
129
130                         List <string> refasms = GetReferencedAssemblies (parser);
131                         if (refasms != null && refasms.Count > 0) {
132                                 foreach (string loc in refasms)
133                                         assemblyBuilder.AddAssemblyReference (loc);
134                         }
135                         
136                         GenerateCode (assemblyBuilder, parser, _compiler);
137                 }
138
139                 protected virtual Type LoadTypeFromBin (BaseCompiler compiler, TParser parser)
140                 {
141                         return null;
142                 }
143                 
144                 public override Type GetGeneratedType (CompilerResults results)
145                 {
146                         if (NeedsLoadFromBin && _compiler != null)
147                                 return LoadTypeFromBin (_compiler, Parser);
148                         
149                         // This is not called if compilation failed.
150                         // Returning null makes the caller throw an InvalidCastException
151                         Assembly assembly = results != null ? results.CompiledAssembly : null;
152                         if (assembly == null)
153                                 return null;
154                         
155                         return assembly.GetType (GetClassType (_compiler, Parser));
156                 }
157
158                 // This is intended to be used by builders which may need to do special processing
159                 // on the virtualPath before actually opening the reader.
160                 protected virtual TextReader SpecialOpenReader (VirtualPath virtualPath, out string physicalPath)
161                 {
162                         physicalPath = null;
163                         return OpenReader (virtualPath.Original);
164                 }
165                 
166                 // FIXME: figure this out.
167                 public override ICollection VirtualPathDependencies {
168                         get {
169                                 TParser parser = Parser;
170                                 return GetParserDependencies (parser);
171                         }
172                 }
173
174                 internal override string LanguageName {
175                         get {
176                                 TParser parser = Parse ();
177                                 if (parser != null)
178                                         return GetParserLanguage (parser);
179                                 return base.LanguageName;
180                         }
181                 }
182                 
183                 public override CompilerType CodeCompilerType {
184                         get {
185                                 if (_compilerType == null)
186                                         _compilerType = GetDefaultCompilerTypeForLanguage (LanguageName);
187
188                                 return _compilerType;
189                         }
190                 }
191
192                 public TParser Parser {
193                         get {
194                                 if (_parser == null) {
195                                         VirtualPath vp = VirtualPathInternal;
196                                         if (vp == null)
197                                                 throw new HttpException ("VirtualPath not set, cannot instantiate parser.");
198                                         
199                                         if (!IsDirectoryBuilder) {
200                                                 string physicalPath;
201                                                 _reader = SpecialOpenReader (vp, out physicalPath);
202                                                 _parser = CreateParser (vp, physicalPath, _reader, HttpContext.Current);
203                                         } else
204                                                 _parser = CreateParser (vp, null,  HttpContext.Current);
205                                         
206                                         if (_parser == null)
207                                                 throw new HttpException ("Unable to create type parser.");
208                                 }
209                                 
210                                 return _parser;
211                         }
212                 }
213
214                 protected virtual bool IsDirectoryBuilder {
215                         get { return false; }
216                 }
217
218                 protected virtual bool NeedsConstructType {
219                         get { return true; }
220                 }
221
222                 protected virtual bool NeedsLoadFromBin {
223                         get { return false; }
224                 }
225                 
226                 internal override CodeCompileUnit CodeUnit {
227                         get {
228                                 if (!_codeGenerated)
229                                         GenerateCode ();
230                                 return _compiler.CompileUnit;
231                         }
232                 }
233         }
234 }