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;
15 using System.Web.Util;
17 namespace System.Web.Compilation
21 public ControlBuilder Builder;
22 public ILocation Location;
24 public BuilderLocation (ControlBuilder builder, ILocation location)
26 this.Builder = builder;
27 this.Location = location;
31 class BuilderLocationStack : Stack
33 public override void Push (object o)
35 if (!(o is BuilderLocation))
36 throw new InvalidOperationException ();
41 public virtual void Push (ControlBuilder builder, ILocation location)
43 BuilderLocation bl = new BuilderLocation (builder, location);
47 public new BuilderLocation Peek ()
49 return (BuilderLocation) base.Peek ();
52 public new BuilderLocation Pop ()
54 return (BuilderLocation) base.Pop ();
57 public ControlBuilder Builder {
58 get { return Peek ().Builder; }
70 files = new Hashtable (); // may be this should be case sensitive for windows
71 parsers = new Stack ();
74 public bool Push (AspParser parser)
76 if (files.Contains (parser.Filename))
79 files [parser.Filename] = true;
80 parsers.Push (parser);
85 public AspParser Pop ()
87 if (parsers.Count == 0)
90 files.Remove (current.Filename);
91 AspParser result = (AspParser) parsers.Pop ();
92 if (parsers.Count > 0)
93 current = (AspParser) parsers.Peek ();
100 public AspParser Parser {
101 get { return current; }
104 public string Filename {
105 get { return current.Filename; }
112 BuilderLocationStack stack;
113 TemplateParser tparser;
115 RootBuilder rootBuilder;
116 bool inScript, javascript;
118 static Hashtable emptyHash = new Hashtable ();
120 public AspGenerator (TemplateParser tparser)
122 this.tparser = tparser;
123 tparser.AddDependency (tparser.InputFile);
124 text = new StringBuilder ();
125 stack = new BuilderLocationStack ();
126 rootBuilder = new RootBuilder (tparser);
127 stack.Push (rootBuilder, null);
128 tparser.RootBuilder = rootBuilder;
129 pstack = new ParserStack ();
132 public AspParser Parser {
133 get { return pstack.Parser; }
136 public string Filename {
137 get { return pstack.Filename; }
140 BaseCompiler GetCompilerFromType ()
142 Type type = tparser.GetType ();
143 if (type == typeof (PageParser))
144 return new PageCompiler ((PageParser) tparser);
146 if (type == typeof (ApplicationFileParser))
147 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
149 if (type == typeof (UserControlParser))
150 return new UserControlCompiler ((UserControlParser) tparser);
152 throw new Exception ("Got type: " + type);
155 void InitParser (string filename)
157 StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
158 AspParser parser = new AspParser (filename, reader);
159 parser.Error += new ParseErrorHandler (ParseError);
160 parser.TagParsed += new TagParsedHandler (TagParsed);
161 parser.TextParsed += new TextParsedHandler (TextParsed);
162 if (!pstack.Push (parser))
163 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
164 tparser.AddDependency (filename);
169 pstack.Parser.Parse ();
176 public Type GetCompiledType ()
178 InitParser (Path.GetFullPath (tparser.InputFile));
181 PrintTree (rootBuilder, 0);
185 throw new ParseException (stack.Builder.location,
186 "Expecting </" + stack.Builder.TagName + ">" + stack.Builder);
188 BaseCompiler compiler = GetCompilerFromType ();
190 return compiler.GetCompiledType ();
194 static void PrintTree (ControlBuilder builder, int indent)
199 string i = new string ('\t', indent);
201 Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
202 builder, builder.ID, builder.ControlType, builder.parentBuilder);
204 if (builder.Children != null)
205 foreach (object o in builder.Children) {
206 if (o is ControlBuilder)
207 PrintTree ((ControlBuilder) o, indent++);
212 static void PrintLocation (ILocation loc)
214 Console.WriteLine ("\tFile name: " + loc.Filename);
215 Console.WriteLine ("\tBegin line: " + loc.BeginLine);
216 Console.WriteLine ("\tEnd line: " + loc.EndLine);
217 Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
218 Console.WriteLine ("\tEnd column: " + loc.EndColumn);
219 Console.WriteLine ("\tPlainText: " + loc.PlainText);
220 Console.WriteLine ();
223 void ParseError (ILocation location, string message)
225 throw new ParseException (location, message);
228 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
230 this.location = new Location (location);
232 tparser.Location = location;
234 if (text.Length != 0)
237 if (0 == String.Compare (tagid, "script", true)) {
238 if (ProcessScript (tagtype, attributes))
243 case TagType.Directive:
245 tagid = tparser.DefaultDirectiveName;
247 tparser.AddDirective (tagid, attributes.GetDictionary (null));
250 if (!ProcessTag (tagid, attributes))
251 TextParsed (location, location.PlainText);
254 if (!CloseControl (tagid))
255 TextParsed (location, location.PlainText);
257 case TagType.SelfClosing:
258 int count = stack.Count;
259 if (!ProcessTag (tagid, attributes)) {
260 TextParsed (location, location.PlainText);
261 } else if (stack.Count != count) {
262 CloseControl (tagid);
265 case TagType.DataBinding:
266 goto case TagType.CodeRender;
267 case TagType.CodeRenderExpression:
268 goto case TagType.CodeRender;
269 case TagType.CodeRender:
270 ProcessCode (tagtype, tagid, location);
272 case TagType.Include:
273 string file = attributes ["virtual"] as string;
274 bool isvirtual = (file != null);
276 file = attributes ["file"] as string;
279 file = tparser.MapPath (file);
280 } else if (!Path.IsPathRooted (file)) {
281 file = UrlUtils.Combine (tparser.BaseVirtualDir, file);
290 //PrintLocation (location);
293 void TextParsed (ILocation location, string text)
295 if (text.IndexOf ("<%") != -1 && !inScript && !javascript) {
296 if (this.text.Length > 0)
298 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
303 this.text.Append (text);
304 //PrintLocation (location);
309 string t = text.ToString ();
312 // TODO: store location
313 tparser.Scripts.Add (t);
317 if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
318 throw new ParseException (location, "Content not valid for application file.");
320 stack.Builder.AppendLiteralString (t);
323 bool ProcessTag (string tagid, TagAttributes atts)
325 ControlBuilder parent = stack.Builder;
326 ControlBuilder builder = null;
327 BuilderLocation bl = null;
328 Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
329 if (stack.Count > 1) {
331 builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
332 } catch (TypeLoadException e) {
333 throw new ParseException (Location, "Type not found.", e);
334 } catch (Exception e) {
335 throw new ParseException (Location, e.Message, e);
339 if (builder == null && atts != null && atts.IsRunAtServer ()) {
340 string id = htable ["id"] as string;
341 if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
342 throw new ParseException (Location, "'" + id + "' is not a valid identifier");
345 builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
346 } catch (TypeLoadException e) {
347 throw new ParseException (Location, "Type not found.", e);
348 } catch (Exception e) {
349 throw new ParseException (Location, e.Message, e);
356 builder.location = location;
357 builder.ID = htable ["id"] as string;
358 if (builder.HasBody ()) {
359 if (builder is TemplateBuilder) {
362 stack.Push (builder, location);
364 // FIXME:ObjectTags...
365 parent.AppendSubBuilder (builder);
366 builder.CloseControl ();
372 bool ProcessScript (TagType tagtype, TagAttributes attributes)
374 if (tagtype != TagType.Close) {
375 if (attributes != null && attributes.IsRunAtServer ()) {
376 CheckLanguage ((string) attributes ["language"]);
377 if (tagtype == TagType.Tag) {
378 Parser.VerbatimID = "script";
380 } //else if (tagtype == TagType.SelfClosing)
381 // load script file here
385 Parser.VerbatimID = "script";
387 TextParsed (location, location.PlainText);
399 TextParsed (location, location.PlainText);
405 bool CloseControl (string tagid)
407 ControlBuilder current = stack.Builder;
408 string btag = current.TagName;
409 if (0 != String.Compare (tagid, btag, true))
412 // if (current is TemplateBuilder)
413 // pop from the id list
414 current.CloseControl ();
416 stack.Builder.AppendSubBuilder (current);
420 bool ProcessCode (TagType tagtype, string code, ILocation location)
422 ControlBuilder b = null;
423 if (tagtype == TagType.CodeRender)
424 b = new CodeRenderBuilder (code, false, location);
425 else if (tagtype == TagType.CodeRenderExpression)
426 b = new CodeRenderBuilder (code, true, location);
427 else if (tagtype == TagType.DataBinding)
428 b = new DataBindingBuilder (code, location);
430 throw new HttpException ("Should never happen");
432 stack.Builder.AppendSubBuilder (b);
436 public ILocation Location {
437 get { return location; }
440 void CheckLanguage (string lang)
442 if (lang == null || lang == "")
445 if (String.Compare (lang, tparser.Language, true) != 0) {
446 throw new ParseException (Location,
447 String.Format ("Trying to mix language '{0}' and '{1}'.",
448 tparser.Language, lang));
451 // Used to get CodeRender tags in attribute values
452 class CodeRenderParser
455 ControlBuilder builder;
457 public CodeRenderParser (string str, ControlBuilder builder)
460 this.builder = builder;
463 public void AddChildren ()
465 int index = str.IndexOf ("<%");
467 TextParsed (null, str.Substring (0, index));
468 str = str.Substring (index);
471 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
472 parser.Error += new ParseErrorHandler (ParseError);
473 parser.TagParsed += new TagParsedHandler (TagParsed);
474 parser.TextParsed += new TextParsedHandler (TextParsed);
478 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
480 if (tagtype == TagType.CodeRender)
481 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
482 else if (tagtype == TagType.CodeRenderExpression)
483 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
484 else if (tagtype == TagType.DataBinding)
485 builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
487 builder.AppendLiteralString (location.PlainText);
490 void TextParsed (ILocation location, string text)
492 builder.AppendLiteralString (text);
495 void ParseError (ILocation location, string message)
497 throw new ParseException (location, message);