2 // System.Web.Compilation.AspGenerator
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
10 using System.Collections;
11 using System.CodeDom.Compiler;
14 using System.Web.Caching;
16 using System.Web.Util;
18 namespace System.Web.Compilation
22 public ControlBuilder Builder;
23 public ILocation Location;
25 public BuilderLocation (ControlBuilder builder, ILocation location)
27 this.Builder = builder;
28 this.Location = location;
32 class BuilderLocationStack : Stack
34 public override void Push (object o)
36 if (!(o is BuilderLocation))
37 throw new InvalidOperationException ();
42 public virtual void Push (ControlBuilder builder, ILocation location)
44 BuilderLocation bl = new BuilderLocation (builder, location);
48 public new BuilderLocation Peek ()
50 return (BuilderLocation) base.Peek ();
53 public new BuilderLocation Pop ()
55 return (BuilderLocation) base.Pop ();
58 public ControlBuilder Builder {
59 get { return Peek ().Builder; }
71 files = new Hashtable (); // may be this should be case sensitive for windows
72 parsers = new Stack ();
75 public bool Push (AspParser parser)
77 if (files.Contains (parser.Filename))
80 files [parser.Filename] = true;
81 parsers.Push (parser);
86 public AspParser Pop ()
88 if (parsers.Count == 0)
91 files.Remove (current.Filename);
92 AspParser result = (AspParser) parsers.Pop ();
93 if (parsers.Count > 0)
94 current = (AspParser) parsers.Peek ();
101 public AspParser Parser {
102 get { return current; }
105 public string Filename {
106 get { return current.Filename; }
113 BuilderLocationStack stack;
114 TemplateParser tparser;
116 RootBuilder rootBuilder;
117 bool inScript, javascript;
120 static Hashtable emptyHash = new Hashtable ();
122 public AspGenerator (TemplateParser tparser)
124 this.tparser = tparser;
125 tparser.AddDependency (tparser.InputFile);
126 text = new StringBuilder ();
127 stack = new BuilderLocationStack ();
128 rootBuilder = new RootBuilder (tparser);
129 stack.Push (rootBuilder, null);
130 tparser.RootBuilder = rootBuilder;
131 pstack = new ParserStack ();
134 public AspParser Parser {
135 get { return pstack.Parser; }
138 public string Filename {
139 get { return pstack.Filename; }
142 BaseCompiler GetCompilerFromType ()
144 Type type = tparser.GetType ();
145 if (type == typeof (PageParser))
146 return new PageCompiler ((PageParser) tparser);
148 if (type == typeof (ApplicationFileParser))
149 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
151 if (type == typeof (UserControlParser))
152 return new UserControlCompiler ((UserControlParser) tparser);
154 throw new Exception ("Got type: " + type);
157 void InitParser (string filename)
159 StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
160 AspParser parser = new AspParser (filename, reader);
162 parser.Error += new ParseErrorHandler (ParseError);
163 parser.TagParsed += new TagParsedHandler (TagParsed);
164 parser.TextParsed += new TextParsedHandler (TextParsed);
165 if (!pstack.Push (parser))
166 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
167 tparser.AddDependency (filename);
172 pstack.Parser.Parse ();
179 public Type GetCompiledType ()
181 Type type = (Type) HttpRuntime.Cache.Get (tparser.InputFile);
186 isApplication = tparser.DefaultDirectiveName == "application";
187 InitParser (Path.GetFullPath (tparser.InputFile));
191 PrintTree (rootBuilder, 0);
195 throw new ParseException (stack.Builder.location,
196 "Expecting </" + stack.Builder.TagName + ">" + stack.Builder);
198 BaseCompiler compiler = GetCompilerFromType ();
200 type = compiler.GetCompiledType ();
201 CacheDependency cd = new CacheDependency ((string[])
202 tparser.Dependencies.ToArray (typeof (string)));
204 HttpRuntime.Cache.Insert (tparser.InputFile, type, cd);
209 static void PrintTree (ControlBuilder builder, int indent)
214 string i = new string ('\t', indent);
216 Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
217 builder, builder.ID, builder.ControlType, builder.parentBuilder);
219 if (builder.Children != null)
220 foreach (object o in builder.Children) {
221 if (o is ControlBuilder)
222 PrintTree ((ControlBuilder) o, indent++);
227 static void PrintLocation (ILocation loc)
229 Console.WriteLine ("\tFile name: " + loc.Filename);
230 Console.WriteLine ("\tBegin line: " + loc.BeginLine);
231 Console.WriteLine ("\tEnd line: " + loc.EndLine);
232 Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
233 Console.WriteLine ("\tEnd column: " + loc.EndColumn);
234 Console.WriteLine ("\tPlainText: " + loc.PlainText);
235 Console.WriteLine ();
238 void ParseError (ILocation location, string message)
240 throw new ParseException (location, message);
243 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
245 this.location = new Location (location);
247 tparser.Location = location;
249 if (text.Length != 0)
252 if (0 == String.Compare (tagid, "script", true)) {
253 if (ProcessScript (tagtype, attributes))
258 case TagType.Directive:
260 tagid = tparser.DefaultDirectiveName;
262 tparser.AddDirective (tagid, attributes.GetDictionary (null));
265 if (!ProcessTag (tagid, attributes, tagtype))
266 TextParsed (location, location.PlainText);
269 if (!CloseControl (tagid))
270 TextParsed (location, location.PlainText);
272 case TagType.SelfClosing:
273 int count = stack.Count;
274 if (!ProcessTag (tagid, attributes, tagtype)) {
275 TextParsed (location, location.PlainText);
276 } else if (stack.Count != count) {
277 CloseControl (tagid);
280 case TagType.DataBinding:
281 goto case TagType.CodeRender;
282 case TagType.CodeRenderExpression:
283 goto case TagType.CodeRender;
284 case TagType.CodeRender:
286 throw new ParseException (location, "Invalid content for application file.");
288 ProcessCode (tagtype, tagid, location);
290 case TagType.Include:
292 throw new ParseException (location, "Invalid content for application file.");
294 string file = attributes ["virtual"] as string;
295 bool isvirtual = (file != null);
297 file = attributes ["file"] as string;
300 file = tparser.MapPath (file);
301 } else if (!Path.IsPathRooted (file)) {
302 file = UrlUtils.Combine (tparser.BaseVirtualDir, file);
311 //PrintLocation (location);
314 void TextParsed (ILocation location, string text)
316 if (text.IndexOf ("<%") != -1 && !inScript) {
317 if (this.text.Length > 0)
319 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
324 this.text.Append (text);
325 //PrintLocation (location);
330 string t = text.ToString ();
333 // TODO: store location
334 tparser.Scripts.Add (t);
338 if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
339 throw new ParseException (location, "Content not valid for application file.");
341 stack.Builder.AppendLiteralString (t);
344 bool ProcessTag (string tagid, TagAttributes atts, TagType tagtype)
346 if ((atts == null || !atts.IsRunAtServer ()) && String.Compare (tagid, "tbody", true) == 0) {
347 // MS completely ignores tbody or, if runat="server", fails when compiling
349 return stack.Builder.ChildrenAsProperties;
355 if (String.Compare (tagid, "object", true) != 0)
356 throw new ParseException (location, "Invalid tag for application file.");
359 ControlBuilder parent = stack.Builder;
360 ControlBuilder builder = null;
361 BuilderLocation bl = null;
362 Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
363 if (stack.Count > 1) {
365 builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
366 } catch (TypeLoadException e) {
367 throw new ParseException (Location, "Type not found.", e);
368 } catch (Exception e) {
369 throw new ParseException (Location, e.Message, e);
373 if (builder == null && atts != null && atts.IsRunAtServer ()) {
374 string id = htable ["id"] as string;
375 if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
376 throw new ParseException (Location, "'" + id + "' is not a valid identifier");
379 builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
380 } catch (TypeLoadException e) {
381 throw new ParseException (Location, "Type not found.", e);
382 } catch (Exception e) {
383 throw new ParseException (Location, e.Message, e);
390 builder.location = location;
391 builder.ID = htable ["id"] as string;
392 if (builder.HasBody ()) {
393 if (builder is TemplateBuilder) {
396 stack.Push (builder, location);
398 if (!isApplication && builder is ObjectTagBuilder) {
399 ObjectTagBuilder ot = (ObjectTagBuilder) builder;
400 if (ot.Scope != null && ot.Scope != "")
401 throw new ParseException (location, "Scope not allowed here");
403 if (tagtype == TagType.Tag) {
404 stack.Push (builder, location);
409 parent.AppendSubBuilder (builder);
410 builder.CloseControl ();
416 bool ProcessScript (TagType tagtype, TagAttributes attributes)
418 if (tagtype != TagType.Close) {
419 if (attributes != null && attributes.IsRunAtServer ()) {
420 CheckLanguage ((string) attributes ["language"]);
421 if (tagtype == TagType.Tag) {
422 Parser.VerbatimID = "script";
424 } //else if (tagtype == TagType.SelfClosing)
425 // load script file here
429 Parser.VerbatimID = "script";
431 TextParsed (location, location.PlainText);
443 TextParsed (location, location.PlainText);
449 bool CloseControl (string tagid)
451 ControlBuilder current = stack.Builder;
452 if (String.Compare (tagid, "tbody", true) == 0) {
453 if (!current.ChildrenAsProperties) {
455 TextParsed (location, location.PlainText);
462 string btag = current.TagName;
463 if (0 != String.Compare (tagid, btag, true))
466 // if (current is TemplateBuilder)
467 // pop from the id list
468 current.CloseControl ();
470 stack.Builder.AppendSubBuilder (current);
474 bool ProcessCode (TagType tagtype, string code, ILocation location)
476 ControlBuilder b = null;
477 if (tagtype == TagType.CodeRender)
478 b = new CodeRenderBuilder (code, false, location);
479 else if (tagtype == TagType.CodeRenderExpression)
480 b = new CodeRenderBuilder (code, true, location);
481 else if (tagtype == TagType.DataBinding)
482 b = new DataBindingBuilder (code, location);
484 throw new HttpException ("Should never happen");
486 stack.Builder.AppendSubBuilder (b);
490 public ILocation Location {
491 get { return location; }
494 void CheckLanguage (string lang)
496 if (lang == null || lang == "")
499 if (String.Compare (lang, tparser.Language, true) != 0) {
500 throw new ParseException (Location,
501 String.Format ("Trying to mix language '{0}' and '{1}'.",
502 tparser.Language, lang));
506 // Used to get CodeRender tags in attribute values
507 class CodeRenderParser
510 ControlBuilder builder;
512 public CodeRenderParser (string str, ControlBuilder builder)
515 this.builder = builder;
518 public void AddChildren ()
520 int index = str.IndexOf ("<%");
522 TextParsed (null, str.Substring (0, index));
523 str = str.Substring (index);
526 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
527 parser.Error += new ParseErrorHandler (ParseError);
528 parser.TagParsed += new TagParsedHandler (TagParsed);
529 parser.TextParsed += new TextParsedHandler (TextParsed);
533 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
535 if (tagtype == TagType.CodeRender)
536 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
537 else if (tagtype == TagType.CodeRenderExpression)
538 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
539 else if (tagtype == TagType.DataBinding)
540 builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
542 builder.AppendLiteralString (location.PlainText);
545 void TextParsed (ILocation location, string text)
547 builder.AppendLiteralString (text);
550 void ParseError (ILocation location, string message)
552 throw new ParseException (location, message);