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;
14 using System.Web.Util;
16 namespace System.Web.Compilation
20 public ControlBuilder Builder;
21 public ILocation Location;
23 public BuilderLocation (ControlBuilder builder, ILocation location)
25 this.Builder = builder;
26 this.Location = location;
30 class BuilderLocationStack : Stack
32 public override void Push (object o)
34 if (!(o is BuilderLocation))
35 throw new InvalidOperationException ();
40 public virtual void Push (ControlBuilder builder, ILocation location)
42 BuilderLocation bl = new BuilderLocation (builder, location);
46 public new BuilderLocation Peek ()
48 return (BuilderLocation) base.Peek ();
51 public new BuilderLocation Pop ()
53 return (BuilderLocation) base.Pop ();
56 public ControlBuilder Builder {
57 get { return Peek ().Builder; }
69 files = new Hashtable (); // may be this should be case sensitive for windows
70 parsers = new Stack ();
73 public bool Push (AspParser parser)
75 if (files.Contains (parser.Filename))
78 files [parser.Filename] = true;
79 parsers.Push (parser);
84 public AspParser Pop ()
86 if (parsers.Count == 0)
89 files.Remove (current.Filename);
90 return (AspParser) parsers.Pop ();
93 public AspParser Parser {
94 get { return current; }
97 public string Filename {
98 get { return current.Filename; }
105 BuilderLocationStack stack;
106 TemplateParser tparser;
108 RootBuilder rootBuilder;
111 static Hashtable emptyHash = new Hashtable ();
113 public AspGenerator (TemplateParser tparser)
115 this.tparser = tparser;
116 tparser.AddDependency (tparser.InputFile);
117 text = new StringBuilder ();
118 stack = new BuilderLocationStack ();
119 rootBuilder = new RootBuilder (tparser);
120 stack.Push (rootBuilder, null);
121 tparser.RootBuilder = rootBuilder;
122 pstack = new ParserStack ();
125 public AspParser Parser {
126 get { return pstack.Parser; }
129 public string Filename {
130 get { return pstack.Filename; }
133 BaseCompiler GetCompilerFromType ()
135 Type type = tparser.GetType ();
136 if (type == typeof (PageParser))
137 return new PageCompiler ((PageParser) tparser);
139 if (type == typeof (ApplicationFileParser))
140 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
142 if (type == typeof (UserControlParser))
143 return new UserControlCompiler ((UserControlParser) tparser);
145 throw new Exception ("Got type: " + type);
148 void InitParser (string filename)
150 //FIXME: use the encoding of the file or the one specified in the machine.config/web.config file.
151 StreamReader reader = new StreamReader (filename, Encoding.Default);
152 AspParser parser = new AspParser (filename, reader);
153 parser.Error += new ParseErrorHandler (ParseError);
154 parser.TagParsed += new TagParsedHandler (TagParsed);
155 parser.TextParsed += new TextParsedHandler (TextParsed);
156 if (!pstack.Push (parser))
157 throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
158 tparser.AddDependency (filename);
163 pstack.Parser.Parse ();
170 public Type GetCompiledType ()
172 InitParser (Path.GetFullPath (tparser.InputFile));
175 PrintTree (rootBuilder, 0);
179 throw new ParseException (stack.Builder.location,
180 "Expecting </" + stack.Builder.TagName + ">" + stack.Builder);
182 BaseCompiler compiler = GetCompilerFromType ();
184 return compiler.GetCompiledType ();
188 static void PrintTree (ControlBuilder builder, int indent)
193 string i = new string ('\t', indent);
195 Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
196 builder, builder.ID, builder.ControlType, builder.parentBuilder);
198 if (builder.Children != null)
199 foreach (object o in builder.Children) {
200 if (o is ControlBuilder)
201 PrintTree ((ControlBuilder) o, indent++);
206 static void PrintLocation (ILocation loc)
208 Console.WriteLine ("\tFile name: " + loc.Filename);
209 Console.WriteLine ("\tBegin line: " + loc.BeginLine);
210 Console.WriteLine ("\tEnd line: " + loc.EndLine);
211 Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
212 Console.WriteLine ("\tEnd column: " + loc.EndColumn);
213 Console.WriteLine ("\tPlainText: " + loc.PlainText);
214 Console.WriteLine ();
217 void ParseError (ILocation location, string message)
219 throw new ParseException (location, message);
222 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
224 this.location = new Location (location);
226 tparser.Location = location;
228 if (text.Length != 0)
231 if (0 == String.Compare (tagid, "script", true)) {
232 if (ProcessScript (tagtype, attributes))
237 case TagType.Directive:
239 tagid = tparser.DefaultDirectiveName;
241 tparser.AddDirective (tagid, attributes.GetDictionary (null));
244 if (!ProcessTag (tagid, attributes))
245 TextParsed (location, location.PlainText);
248 if (!CloseControl (tagid))
249 TextParsed (location, location.PlainText);
251 case TagType.SelfClosing:
252 int count = stack.Count;
253 if (!ProcessTag (tagid, attributes)) {
254 TextParsed (location, location.PlainText);
255 } else if (stack.Count != count) {
256 CloseControl (tagid);
259 case TagType.DataBinding:
260 goto case TagType.CodeRender;
261 case TagType.CodeRenderExpression:
262 goto case TagType.CodeRender;
263 case TagType.CodeRender:
264 ProcessCode (tagtype, tagid, location);
266 case TagType.Include:
267 string file = attributes ["virtual"] as string;
268 bool isvirtual = (file != null);
270 file = attributes ["file"] as string;
273 file = tparser.MapPath (file);
274 } else if (!Path.IsPathRooted (file)) {
275 file = UrlUtils.Combine (tparser.BaseVirtualDir, file);
284 //PrintLocation (location);
287 void TextParsed (ILocation location, string text)
289 if (text.IndexOf ("<%") != -1) {
290 if (this.text.Length > 0)
292 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
297 this.text.Append (text);
298 //PrintLocation (location);
303 string t = text.ToString ();
306 // TODO: store location
307 tparser.Scripts.Add (t);
311 if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
312 throw new ParseException (location, "Content not valid for application file.");
314 stack.Builder.AppendLiteralString (t);
317 bool ProcessTag (string tagid, TagAttributes atts)
319 ControlBuilder parent = stack.Builder;
320 ControlBuilder builder = null;
321 BuilderLocation bl = null;
322 Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
323 if (stack.Count > 1) {
325 builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
326 } catch (TypeLoadException e) {
327 throw new ParseException (Location, "Type not found.", e);
328 } catch (Exception e) {
329 throw new ParseException (Location, e.Message, e);
333 if (builder == null && atts != null && atts.IsRunAtServer ()) {
335 builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
336 } catch (TypeLoadException e) {
337 throw new ParseException (Location, "Type not found.", e);
338 } catch (Exception e) {
339 throw new ParseException (Location, e.Message, e);
346 builder.location = location;
347 builder.ID = htable ["id"] as string;
348 if (builder.HasBody ()) {
349 if (builder is TemplateBuilder) {
352 stack.Push (builder, location);
354 // FIXME:ObjectTags...
355 parent.AppendSubBuilder (builder);
356 builder.CloseControl ();
362 bool ProcessScript (TagType tagtype, TagAttributes attributes)
364 if (tagtype != TagType.Close && attributes != null && attributes.IsRunAtServer ()) {
365 if (tagtype == TagType.Tag) {
366 Parser.VerbatimID = "script";
368 } //else if (tagtype == TagType.SelfClosing)
369 // load script file here
374 bool result = inScript;
380 bool CloseControl (string tagid)
382 ControlBuilder current = stack.Builder;
383 string btag = current.TagName;
384 if (0 != String.Compare (tagid, btag, true))
387 // if (current is TemplateBuilder)
388 // pop from the id list
389 current.CloseControl ();
391 stack.Builder.AppendSubBuilder (current);
395 bool ProcessCode (TagType tagtype, string code, ILocation location)
397 ControlBuilder b = null;
398 if (tagtype == TagType.CodeRender)
399 b = new CodeRenderBuilder (code, false, location);
400 else if (tagtype == TagType.CodeRenderExpression)
401 b = new CodeRenderBuilder (code, true, location);
402 else if (tagtype == TagType.DataBinding)
403 b = new DataBindingBuilder (code, location);
405 throw new HttpException ("Should never happen");
407 stack.Builder.AppendSubBuilder (b);
411 public ILocation Location {
412 get { return location; }
415 // Used to get CodeRender tags in attribute values
416 class CodeRenderParser
419 ControlBuilder builder;
421 public CodeRenderParser (string str, ControlBuilder builder)
424 this.builder = builder;
427 public void AddChildren ()
429 int index = str.IndexOf ("<%");
431 TextParsed (null, str.Substring (0, index));
432 str = str.Substring (index);
435 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
436 parser.Error += new ParseErrorHandler (ParseError);
437 parser.TagParsed += new TagParsedHandler (TagParsed);
438 parser.TextParsed += new TextParsedHandler (TextParsed);
442 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
444 if (tagtype == TagType.CodeRender)
445 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
446 else if (tagtype == TagType.CodeRenderExpression)
447 builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
448 else if (tagtype == TagType.DataBinding)
449 builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
451 builder.AppendLiteralString (location.PlainText);
454 void TextParsed (ILocation location, string text)
456 builder.AppendLiteralString (text);
459 void ParseError (ILocation location, string message)
461 throw new ParseException (location, message);