(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
blobd21312dd433e881d7a2fd8ec78379e8fcbd333d1
1 //
2 // System.Web.Compilation.AspGenerator
3 //
4 // Authors:
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 //
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.
30 using System;
31 using System.Collections;
32 using System.CodeDom.Compiler;
33 using System.IO;
34 using System.Text;
35 using System.Web.Caching;
36 using System.Web.UI;
37 using System.Web.UI.HtmlControls;
38 using System.Web.Util;
40 namespace System.Web.Compilation
42 class BuilderLocation
44 public ControlBuilder Builder;
45 public ILocation Location;
47 public BuilderLocation (ControlBuilder builder, ILocation location)
49 this.Builder = builder;
50 this.Location = location;
54 class BuilderLocationStack : Stack
56 public override void Push (object o)
58 if (!(o is BuilderLocation))
59 throw new InvalidOperationException ();
61 base.Push (o);
64 public virtual void Push (ControlBuilder builder, ILocation location)
66 BuilderLocation bl = new BuilderLocation (builder, location);
67 Push (bl);
70 public new BuilderLocation Peek ()
72 return (BuilderLocation) base.Peek ();
75 public new BuilderLocation Pop ()
77 return (BuilderLocation) base.Pop ();
80 public ControlBuilder Builder {
81 get { return Peek ().Builder; }
85 class ParserStack
87 Hashtable files;
88 Stack parsers;
89 AspParser current;
91 public ParserStack ()
93 files = new Hashtable (); // may be this should be case sensitive for windows
94 parsers = new Stack ();
97 public bool Push (AspParser parser)
99 if (files.Contains (parser.Filename))
100 return false;
102 files [parser.Filename] = true;
103 parsers.Push (parser);
104 current = parser;
105 return true;
108 public AspParser Pop ()
110 if (parsers.Count == 0)
111 return null;
113 files.Remove (current.Filename);
114 AspParser result = (AspParser) parsers.Pop ();
115 if (parsers.Count > 0)
116 current = (AspParser) parsers.Peek ();
117 else
118 current = null;
120 return result;
123 public AspParser Parser {
124 get { return current; }
127 public string Filename {
128 get { return current.Filename; }
132 class TagStack
134 Stack tags;
136 public TagStack ()
138 tags = new Stack ();
141 public void Push (string tagid)
143 tags.Push (tagid);
146 public string Pop ()
148 if (tags.Count == 0)
149 return null;
151 return (string) tags.Pop ();
154 public bool CompareTo (string tagid)
156 if (tags.Count == 0)
157 return false;
159 return 0 == String.Compare (tagid, (string) tags.Peek (), true);
162 public int Count {
163 get { return tags.Count; }
166 public string Current {
167 get { return (string) tags.Peek (); }
171 class AspGenerator
173 ParserStack pstack;
174 BuilderLocationStack stack;
175 TemplateParser tparser;
176 StringBuilder text;
177 RootBuilder rootBuilder;
178 bool inScript, javascript;
179 ILocation location;
180 bool isApplication;
181 StringBuilder tagInnerText = new StringBuilder ();
182 static Hashtable emptyHash = new Hashtable ();
183 bool inForm;
184 TagStack formTags;
186 public AspGenerator (TemplateParser tparser)
188 this.tparser = tparser;
189 tparser.AddDependency (tparser.InputFile);
190 text = new StringBuilder ();
191 stack = new BuilderLocationStack ();
192 rootBuilder = new RootBuilder (tparser);
193 stack.Push (rootBuilder, null);
194 tparser.RootBuilder = rootBuilder;
195 pstack = new ParserStack ();
198 public AspParser Parser {
199 get { return pstack.Parser; }
202 public string Filename {
203 get { return pstack.Filename; }
206 BaseCompiler GetCompilerFromType ()
208 Type type = tparser.GetType ();
209 if (type == typeof (PageParser))
210 return new PageCompiler ((PageParser) tparser);
212 if (type == typeof (ApplicationFileParser))
213 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
215 if (type == typeof (UserControlParser))
216 return new UserControlCompiler ((UserControlParser) tparser);
218 throw new Exception ("Got type: " + type);
221 void InitParser (string filename)
223 StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
224 AspParser parser = new AspParser (filename, reader);
225 reader.Close ();
226 parser.Error += new ParseErrorHandler (ParseError);
227 parser.TagParsed += new TagParsedHandler (TagParsed);
228 parser.TextParsed += new TextParsedHandler (TextParsed);
229 if (!pstack.Push (parser))
230 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
231 tparser.AddDependency (filename);
234 void DoParse ()
236 pstack.Parser.Parse ();
237 if (text.Length > 0)
238 FlushText ();
240 pstack.Pop ();
243 public Type GetCompiledType ()
245 Type type = (Type) HttpRuntime.Cache.Get ("@@Type" + tparser.InputFile);
246 if (type != null) {
247 return type;
250 isApplication = tparser.DefaultDirectiveName == "application";
251 InitParser (Path.GetFullPath (tparser.InputFile));
253 DoParse ();
254 #if DEBUG
255 PrintTree (rootBuilder, 0);
256 #endif
258 if (stack.Count > 1)
259 throw new ParseException (stack.Builder.location,
260 "Expecting </" + stack.Builder.TagName + ">" + stack.Builder);
262 BaseCompiler compiler = GetCompilerFromType ();
264 type = compiler.GetCompiledType ();
265 CacheDependency cd = new CacheDependency ((string[])
266 tparser.Dependencies.ToArray (typeof (string)));
268 HttpRuntime.Cache.Insert ("@@Type" + tparser.InputFile, type, cd);
269 return type;
272 #if DEBUG
273 static void PrintTree (ControlBuilder builder, int indent)
275 if (builder == null)
276 return;
278 string i = new string ('\t', indent);
279 Console.Write (i);
280 Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
281 builder, builder.ID, builder.ControlType, builder.parentBuilder);
283 if (builder.Children != null)
284 foreach (object o in builder.Children) {
285 if (o is ControlBuilder)
286 PrintTree ((ControlBuilder) o, indent++);
289 #endif
291 static void PrintLocation (ILocation loc)
293 Console.WriteLine ("\tFile name: " + loc.Filename);
294 Console.WriteLine ("\tBegin line: " + loc.BeginLine);
295 Console.WriteLine ("\tEnd line: " + loc.EndLine);
296 Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
297 Console.WriteLine ("\tEnd column: " + loc.EndColumn);
298 Console.WriteLine ("\tPlainText: " + loc.PlainText);
299 Console.WriteLine ();
302 void ParseError (ILocation location, string message)
304 throw new ParseException (location, message);
307 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
309 this.location = new Location (location);
310 if (tparser != null)
311 tparser.Location = location;
313 if (text.Length != 0)
314 FlushText ();
316 if (0 == String.Compare (tagid, "script", true)) {
317 if (ProcessScript (tagtype, attributes))
318 return;
321 switch (tagtype) {
322 case TagType.Directive:
323 if (tagid == "")
324 tagid = tparser.DefaultDirectiveName;
326 tparser.AddDirective (tagid, attributes.GetDictionary (null));
327 break;
328 case TagType.Tag:
329 if (ProcessTag (tagid, attributes, tagtype))
330 break;
332 if (inForm) {
333 stack.Builder.EnsureOtherTags ();
334 stack.Builder.OtherTags.Add (tagid);
337 TextParsed (location, location.PlainText);
338 break;
339 case TagType.Close:
340 bool notServer = (inForm && TryRemoveTag (tagid, stack.Builder.OtherTags));
341 if (!notServer && CloseControl (tagid))
342 break;
344 TextParsed (location, location.PlainText);
345 break;
346 case TagType.SelfClosing:
347 int count = stack.Count;
348 if (!ProcessTag (tagid, attributes, tagtype)) {
349 TextParsed (location, location.PlainText);
350 } else if (stack.Count != count) {
351 CloseControl (tagid);
353 break;
354 case TagType.DataBinding:
355 goto case TagType.CodeRender;
356 case TagType.CodeRenderExpression:
357 goto case TagType.CodeRender;
358 case TagType.CodeRender:
359 if (isApplication)
360 throw new ParseException (location, "Invalid content for application file.");
362 ProcessCode (tagtype, tagid, location);
363 break;
364 case TagType.Include:
365 if (isApplication)
366 throw new ParseException (location, "Invalid content for application file.");
368 string file = attributes ["virtual"] as string;
369 bool isvirtual = (file != null);
370 if (!isvirtual)
371 file = attributes ["file"] as string;
373 if (isvirtual) {
374 file = tparser.MapPath (file);
375 } else {
376 file = GetIncludeFilePath (tparser.BaseDir, file);
379 InitParser (file);
380 DoParse ();
381 break;
382 default:
383 break;
385 //PrintLocation (location);
388 static bool TryRemoveTag (string tagid, ArrayList otags)
390 if (otags == null || otags.Count == 0)
391 return false;
393 int idx = otags.Count - 1;
394 string otagid = (string) otags [idx];
395 if (0 != String.Compare (tagid, otagid, true))
396 return false;
398 otags.RemoveAt (idx);
399 return true;
402 static string GetIncludeFilePath (string basedir, string filename)
404 if (Path.DirectorySeparatorChar == '/')
405 filename = filename.Replace ("\\", "/");
407 return Path.GetFullPath (Path.Combine (basedir, filename));
410 void TextParsed (ILocation location, string text)
412 if (text.IndexOf ("<%") != -1 && !inScript) {
413 if (this.text.Length > 0)
414 FlushText ();
415 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
416 r.AddChildren ();
417 return;
420 this.text.Append (text);
421 //PrintLocation (location);
424 void FlushText ()
426 string t = text.ToString ();
427 text.Length = 0;
428 if (inScript) {
429 // TODO: store location
430 tparser.Scripts.Add (t);
431 return;
434 if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
435 throw new ParseException (location, "Content not valid for application file.");
437 ControlBuilder current = stack.Builder;
438 current.AppendLiteralString (t);
439 if (current.NeedsTagInnerText ()) {
440 tagInnerText.Append (t);
444 bool ProcessTag (string tagid, TagAttributes atts, TagType tagtype)
446 if ((atts == null || !atts.IsRunAtServer ()) && String.Compare (tagid, "tbody", true) == 0) {
447 // MS completely ignores tbody or, if runat="server", fails when compiling
448 if (stack.Count > 0)
449 return stack.Builder.ChildrenAsProperties;
451 return false;
454 if (isApplication) {
455 if (String.Compare (tagid, "object", true) != 0)
456 throw new ParseException (location, "Invalid tag for application file.");
459 ControlBuilder parent = stack.Builder;
460 ControlBuilder builder = null;
461 Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
462 if (stack.Count > 1) {
463 try {
464 builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
465 } catch (TypeLoadException e) {
466 throw new ParseException (Location, "Type not found.", e);
467 } catch (Exception e) {
468 throw new ParseException (Location, e.Message, e);
472 if (builder == null && atts != null && atts.IsRunAtServer ()) {
473 string id = htable ["id"] as string;
474 if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
475 throw new ParseException (Location, "'" + id + "' is not a valid identifier");
477 try {
478 builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
479 } catch (TypeLoadException e) {
480 throw new ParseException (Location, "Type not found.", e);
481 } catch (Exception e) {
482 throw new ParseException (Location, e.Message, e);
486 if (builder == null)
487 return false;
489 builder.location = location;
490 builder.ID = htable ["id"] as string;
491 if (typeof (HtmlForm).IsAssignableFrom (builder.ControlType)) {
492 if (inForm)
493 throw new ParseException (location, "Only one <form> allowed.");
495 inForm = true;
496 formTags = new TagStack ();
499 if (builder.HasBody () && !(builder is ObjectTagBuilder)) {
500 if (builder is TemplateBuilder) {
501 // push the id list
503 stack.Push (builder, location);
504 } else {
505 if (!isApplication && builder is ObjectTagBuilder) {
506 ObjectTagBuilder ot = (ObjectTagBuilder) builder;
507 if (ot.Scope != null && ot.Scope != "")
508 throw new ParseException (location, "Scope not allowed here");
510 if (tagtype == TagType.Tag) {
511 stack.Push (builder, location);
512 return true;
516 parent.AppendSubBuilder (builder);
517 builder.CloseControl ();
520 return true;
523 bool ProcessScript (TagType tagtype, TagAttributes attributes)
525 if (tagtype != TagType.Close) {
526 if (attributes != null && attributes.IsRunAtServer ()) {
527 CheckLanguage ((string) attributes ["language"]);
528 if (tagtype == TagType.Tag) {
529 Parser.VerbatimID = "script";
530 inScript = true;
531 } //else if (tagtype == TagType.SelfClosing)
532 // load script file here
534 return true;
535 } else {
536 if (tagtype != TagType.SelfClosing) {
537 Parser.VerbatimID = "script";
538 javascript = true;
540 TextParsed (location, location.PlainText);
541 return true;
545 bool result;
546 if (inScript) {
547 result = inScript;
548 inScript = false;
549 } else {
550 result = javascript;
551 javascript = false;
552 TextParsed (location, location.PlainText);
555 return result;
558 bool CloseControl (string tagid)
560 ControlBuilder current = stack.Builder;
561 if (String.Compare (tagid, "tbody", true) == 0) {
562 if (!current.ChildrenAsProperties) {
563 try {
564 TextParsed (location, location.PlainText);
565 FlushText ();
566 } catch {}
568 return true;
571 string btag = current.TagName;
572 if (0 != String.Compare (tagid, btag, true))
573 return false;
575 // if (current is TemplateBuilder)
576 // pop from the id list
577 if (current.NeedsTagInnerText ()) {
578 try {
579 current.SetTagInnerText (tagInnerText.ToString ());
580 } catch (Exception e) {
581 throw new ParseException (current.location, e.Message, e);
584 tagInnerText.Length = 0;
587 if (typeof (HtmlForm).IsAssignableFrom (current.ControlType)) {
588 inForm = false;
591 current.CloseControl ();
592 stack.Pop ();
593 stack.Builder.AppendSubBuilder (current);
594 return true;
597 bool ProcessCode (TagType tagtype, string code, ILocation location)
599 ControlBuilder b = null;
600 if (tagtype == TagType.CodeRender)
601 b = new CodeRenderBuilder (code, false, location);
602 else if (tagtype == TagType.CodeRenderExpression)
603 b = new CodeRenderBuilder (code, true, location);
604 else if (tagtype == TagType.DataBinding)
605 b = new DataBindingBuilder (code, location);
606 else
607 throw new HttpException ("Should never happen");
609 stack.Builder.AppendSubBuilder (b);
610 return true;
613 public ILocation Location {
614 get { return location; }
617 void CheckLanguage (string lang)
619 if (lang == null || lang == "")
620 return;
622 if (String.Compare (lang, tparser.Language, true) != 0) {
623 throw new ParseException (Location,
624 String.Format ("Trying to mix language '{0}' and '{1}'.",
625 tparser.Language, lang));
629 // Used to get CodeRender tags in attribute values
630 class CodeRenderParser
632 string str;
633 ControlBuilder builder;
635 public CodeRenderParser (string str, ControlBuilder builder)
637 this.str = str;
638 this.builder = builder;
641 public void AddChildren ()
643 int index = str.IndexOf ("<%");
644 if (index > 0) {
645 TextParsed (null, str.Substring (0, index));
646 str = str.Substring (index);
649 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
650 parser.Error += new ParseErrorHandler (ParseError);
651 parser.TagParsed += new TagParsedHandler (TagParsed);
652 parser.TextParsed += new TextParsedHandler (TextParsed);
653 parser.Parse ();
656 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
658 if (tagtype == TagType.CodeRender)
659 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
660 else if (tagtype == TagType.CodeRenderExpression)
661 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
662 else if (tagtype == TagType.DataBinding)
663 builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
664 else
665 builder.AppendLiteralString (location.PlainText);
668 void TextParsed (ILocation location, string text)
670 builder.AppendLiteralString (text);
673 void ParseError (ILocation location, string message)
675 throw new ParseException (location, message);