Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / System.Web / System.Web.Compilation / TemplateBuildProvider.cs
1 //
2 // System.Web.Compilation.TemplateBuildProvider
3 //
4 // Authors:
5 //   Marek Habersack (mhabersack@novell.com)
6 //
7 // (C) 2007 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.Text.RegularExpressions;
39 using System.Web;
40 using System.Web.Hosting;
41 using System.Web.UI;
42 using System.Web.Util;
43
44 namespace System.Web.Compilation
45 {
46         abstract class TemplateBuildProvider : GenericBuildProvider <TemplateParser>
47         {
48                 delegate void ExtractDirectiveDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp);
49                 
50                 static SortedDictionary <string, ExtractDirectiveDependencies> directiveAttributes;
51                 static char[] directiveValueTrimChars = {' ', '\t', '\r', '\n', '"', '\''};
52
53                 SortedDictionary <string, bool> dependencies;
54                 string compilationLanguage;
55                 
56                 internal override string LanguageName {
57                         get {
58                                 if (String.IsNullOrEmpty (compilationLanguage)) {
59                                         ExtractDependencies ();
60                                         if (String.IsNullOrEmpty (compilationLanguage))
61                                                 compilationLanguage = base.LanguageName;
62                                 }
63                                 
64                                 return compilationLanguage;
65                         }
66                 }
67                 
68                 static TemplateBuildProvider ()
69                 {
70                         directiveAttributes = new SortedDictionary <string, ExtractDirectiveDependencies> (StringComparer.InvariantCultureIgnoreCase);
71                         directiveAttributes.Add ("Control", ExtractControlDependencies);
72                         directiveAttributes.Add ("Master", ExtractPageOrMasterDependencies);
73                         directiveAttributes.Add ("MasterType", ExtractPreviousPageTypeOrMasterTypeDependencies);
74                         directiveAttributes.Add ("Page", ExtractPageOrMasterDependencies);
75                         directiveAttributes.Add ("PreviousPageType", ExtractPreviousPageTypeOrMasterTypeDependencies);
76                         directiveAttributes.Add ("Reference", ExtractReferenceDependencies);
77                         directiveAttributes.Add ("Register", ExtractRegisterDependencies);
78                         directiveAttributes.Add ("WebHandler", ExtractLanguage);
79                         directiveAttributes.Add ("WebService", ExtractLanguage);
80                 }
81
82                 static string ExtractDirectiveAttribute (string baseDirectory, string name, CaptureCollection names, CaptureCollection values)
83                 {
84                         return ExtractDirectiveAttribute (baseDirectory, name, names, values, true);
85                 }
86                 
87                 static string ExtractDirectiveAttribute (string baseDirectory, string name, CaptureCollection names, CaptureCollection values, bool isPath)
88                 {
89                         if (names.Count == 0)
90                                 return String.Empty;
91
92                         int index = 0;
93                         int valuesCount = values.Count;
94                         foreach (Capture c in names) {
95                                 if (String.Compare (c.Value, name, StringComparison.OrdinalIgnoreCase) != 0) {
96                                         index++;
97                                         continue;
98                                 }
99                                 
100                                 if (index > valuesCount)
101                                         return String.Empty;
102
103                                 if (isPath) {
104                                         string value = values [index].Value.Trim (directiveValueTrimChars);
105                                         if (String.IsNullOrEmpty (value))
106                                                 return String.Empty;
107                                         
108                                         return new VirtualPath (value, baseDirectory).Absolute;
109                                 } else
110                                         return values [index].Value.Trim ();
111                         }
112
113                         return String.Empty;
114                 }
115
116                 static void ExtractControlDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
117                 {
118                         ExtractLanguage (baseDirectory, names, values, bp);
119                         ExtractCodeBehind (baseDirectory, names, values, bp);
120                 }
121                 
122                 static void ExtractLanguage (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
123                 {
124                         string value = ExtractDirectiveAttribute (baseDirectory, "Language", names, values, false);
125                         if (String.IsNullOrEmpty (value))
126                                 return;
127
128                         bp.compilationLanguage = value;
129
130                         ExtractCodeBehind (baseDirectory, names, values, bp);
131                 }
132                 
133                 static void ExtractPageOrMasterDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
134                 {
135                         ExtractLanguage (baseDirectory, names, values, bp);
136                         string value = ExtractDirectiveAttribute (baseDirectory, "MasterPageFile", names, values);
137                         if (!String.IsNullOrEmpty (value)) {
138                                 if (!bp.dependencies.ContainsKey (value))
139                                         bp.dependencies.Add (value, true);
140                         }
141
142                         ExtractCodeBehind (baseDirectory, names, values, bp);
143                 }
144
145                 static void ExtractCodeBehind (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
146                 {
147                         string[] varray = new string [2];
148
149                         varray [0] = ExtractDirectiveAttribute (baseDirectory, "CodeFile", names, values);
150                         varray [1] = ExtractDirectiveAttribute (baseDirectory, "Src", names, values);
151                         foreach (string value in varray) {
152                                 if (!String.IsNullOrEmpty (value)) {
153                                         if (!bp.dependencies.ContainsKey (value))
154                                                 bp.dependencies.Add (value, true);
155                                 }
156                         }
157                 }
158                 
159                 static void ExtractRegisterDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
160                 {
161                         string src = ExtractDirectiveAttribute (baseDirectory, "Src", names, values);
162                         if (String.IsNullOrEmpty (src))
163                                 return;
164                         
165                         string value = ExtractDirectiveAttribute (baseDirectory, "TagName", names, values);
166                         if (String.IsNullOrEmpty (value))
167                                 return;
168
169                         value = ExtractDirectiveAttribute (baseDirectory, "TagPrefix", names, values);
170                         if (String.IsNullOrEmpty (value))
171                                 return;
172
173                         if (bp.dependencies.ContainsKey (src))
174                                 return;
175
176                         bp.dependencies.Add (src, true);
177                 }
178
179                 static void ExtractPreviousPageTypeOrMasterTypeDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
180                 {
181                         string value = ExtractDirectiveAttribute (baseDirectory, "VirtualPath", names, values);
182                         if (String.IsNullOrEmpty (value))
183                                 return;
184
185                         if (bp.dependencies.ContainsKey (value))
186                                 return;
187
188                         bp.dependencies.Add (value, true);
189                 }
190
191                 static void ExtractReferenceDependencies (string baseDirectory, CaptureCollection names, CaptureCollection values, TemplateBuildProvider bp)
192                 {
193                         string control = ExtractDirectiveAttribute (baseDirectory, "Control", names, values);
194                         string virtualPath = ExtractDirectiveAttribute (baseDirectory, "VirtualPath", names, values);
195                         string page = ExtractDirectiveAttribute (baseDirectory, "Page", names, values);
196                         bool controlEmpty = String.IsNullOrEmpty (control);
197                         bool virtualPathEmpty = String.IsNullOrEmpty (virtualPath);
198                         bool pageEmpty = String.IsNullOrEmpty (page);
199                         
200                         if (controlEmpty && virtualPathEmpty && pageEmpty)
201                                 return;
202
203                         if ((controlEmpty ? 1 : 0) + (virtualPathEmpty ? 1 : 0) + (pageEmpty ? 1 : 0) != 2)
204                                 return;
205                         
206                         string value;
207                         if (!controlEmpty)
208                                 value = control;
209                         else if (!virtualPathEmpty)
210                                 value = virtualPath;
211                         else
212                                 value = page;
213
214                         if (bp.dependencies.ContainsKey (value))
215                                 return;
216
217                         bp.dependencies.Add (value, true);
218                 }
219
220                 IDictionary <string, bool> AddParsedDependencies (IDictionary <string, bool> dict)
221                 {
222                         if (Parsed) {
223                                 List <string> deps = Parser.Dependencies;
224                                 if (deps == null || deps.Count > 0)
225                                         return dict;
226                                 
227                                 if (dict == null) {
228                                         dict = dependencies;
229                                         if (dict == null)
230                                                 dict = dependencies = new SortedDictionary <string, bool> (StringComparer.OrdinalIgnoreCase);
231                                 }
232                                 
233                                 string s;
234                                 foreach (object o in deps) {
235                                         s = o as string;
236                                         if (s == null || dict.ContainsKey (s))
237                                                 continue;
238                                         dict.Add (s, true);
239                                 }
240                         }
241
242                         if (dict == null || dict.Count == 0)
243                                 return null;
244                         
245                         return dict;
246                 }
247                 
248                 internal override IDictionary <string, bool> ExtractDependencies ()
249                 {
250                         if (dependencies != null)
251                                 return AddParsedDependencies (dependencies);
252
253                         string vpath = VirtualPath;
254                         if (String.IsNullOrEmpty (vpath))
255                                 return AddParsedDependencies (null);
256
257                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
258                         if (!vpp.FileExists (vpath))
259                                 return AddParsedDependencies (null);
260                         
261                         VirtualFile vf = vpp.GetFile (vpath);
262                         if (vf == null)
263                                 return AddParsedDependencies (null);
264
265                         string input;
266                         using (Stream st = vf.Open ()) {
267                                 if (st == null || !st.CanRead)
268                                         return AddParsedDependencies (null);
269                                 
270                                 using (StreamReader sr = new StreamReader (st, WebEncoding.FileEncoding)) {
271                                         input = sr.ReadToEnd ();
272                                 }
273                         }
274                                         
275                         if (String.IsNullOrEmpty (input))
276                                 return AddParsedDependencies (null);
277
278                         MatchCollection matches = AspGenerator.DirectiveRegex.Matches (input);
279                         if (matches == null || matches.Count == 0)
280                                 return AddParsedDependencies (null);
281                         
282                         dependencies = new SortedDictionary <string, bool> (StringComparer.InvariantCultureIgnoreCase);
283                         CaptureCollection ccNames;
284                         GroupCollection groups;
285                         string directiveName;
286                         ExtractDirectiveDependencies edd;
287                         string baseDirectory = VirtualPathUtility.GetDirectory (vpath);
288                         
289                         foreach (Match match in matches) {
290                                 groups = match.Groups;
291                                 if (groups.Count < 6)
292                                         continue;
293                                 
294                                 ccNames = groups [3].Captures;
295                                 directiveName = ccNames [0].Value;
296                                 if (!directiveAttributes.TryGetValue (directiveName, out edd))
297                                         continue;
298                                 edd (baseDirectory, ccNames, groups [5].Captures, this);
299                         }
300
301                         return AddParsedDependencies (dependencies);
302                 }
303                 
304                 protected override string GetClassType (BaseCompiler compiler, TemplateParser parser)
305                 {
306                         if (compiler != null)
307                                 return compiler.MainClassType;
308
309                         return null;
310                 }
311                 
312                 protected override ICollection GetParserDependencies (TemplateParser parser)
313                 {
314                         if (parser != null)
315                                 return parser.Dependencies;
316                         
317                         return null;
318                 }
319                 
320                 protected override string GetParserLanguage (TemplateParser parser)
321                 {
322                         if (parser != null)
323                                 return parser.Language;
324
325                         return null;
326                 }
327                 
328                 protected override string GetCodeBehindSource (TemplateParser parser)
329                 {
330                         if (parser != null) {
331                                 string codeBehind = parser.CodeBehindSource;
332                                 if (String.IsNullOrEmpty (codeBehind))
333                                         return null;                            
334
335                                 return parser.CodeBehindSource;
336                         }
337                         
338                         return null;
339                 }
340                 
341                 protected override AspGenerator CreateAspGenerator (TemplateParser parser)
342                 {
343                         if (parser != null)
344                                 return new AspGenerator (parser);
345
346                         return null;
347                 }
348
349                 protected override List <string> GetReferencedAssemblies (TemplateParser parser)
350                 {
351                         if (parser == null)
352                                 return null;
353                         
354                         List <string> asms = parser.Assemblies;
355                         if (asms == null || asms.Count == 0)
356                                 return null;
357
358                         List <string> ret = new List <string> ();                       
359                         foreach (string loc in asms) {
360                                 if (String.IsNullOrEmpty (loc))
361                                         continue;
362
363                                 if (ret.Contains (loc))
364                                         continue;
365
366                                 ret.Add (loc);
367                         }
368
369                         return ret;
370                 }
371         }
372 }