2003-05-04 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
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 //
9 using System;
10 using System.Collections;
11 using System.IO;
12 using System.Text;
13 using System.Web.UI;
14
15 namespace System.Web.Compilation
16 {
17         class BuilderLocation
18         {
19                 public ControlBuilder Builder;
20                 public ILocation Location;
21
22                 public BuilderLocation (ControlBuilder builder, ILocation location)
23                 {
24                         this.Builder = builder;
25                         this.Location = location;
26                 }
27         }
28         
29         class BuilderLocationStack : Stack
30         {
31                 public override void Push (object o)
32                 {
33                         if (!(o is BuilderLocation))
34                                 throw new InvalidOperationException ();
35
36                         base.Push (o);
37                 }
38                 
39                 public virtual void Push (ControlBuilder builder, ILocation location)
40                 {
41                         BuilderLocation bl = new BuilderLocation (builder, location);
42                         Push (bl);
43                 }
44
45                 public new BuilderLocation Peek ()
46                 {
47                         return (BuilderLocation) base.Peek ();
48                 }
49
50                 public new BuilderLocation Pop ()
51                 {
52                         return (BuilderLocation) base.Pop ();
53                 }
54
55                 public ControlBuilder Builder {
56                         get { return Peek ().Builder; }
57                 }
58         }
59
60         class AspGenerator
61         {
62                 string filename;
63                 AspParser parser;
64                 BuilderLocationStack stack;
65                 TemplateParser tparser;
66                 StringBuilder text;
67                 RootBuilder rootBuilder;
68                 bool inScript;
69                 ILocation location;
70                 static Hashtable emptyHash = new Hashtable ();
71
72                 public AspGenerator (TemplateParser tparser)
73                 {
74                         this.tparser = tparser;
75                         this.filename = Path.GetFullPath (tparser.InputFile);
76                         tparser.AddDependency (tparser.InputFile);
77                         text = new StringBuilder ();
78                         stack = new BuilderLocationStack ();
79                         rootBuilder = new RootBuilder (tparser);
80                         stack.Push (rootBuilder, null);
81                         tparser.RootBuilder = rootBuilder;
82                 }
83
84                 BaseCompiler GetCompilerFromType ()
85                 {
86                         Type type = tparser.GetType ();
87                         if (type == typeof (PageParser))
88                                 return new PageCompiler ((PageParser) tparser);
89
90                         if (type == typeof (ApplicationFileParser))
91                                 return new GlobalAsaxCompiler ((ApplicationFileParser) tparser);
92
93                         if (type == typeof (UserControlParser))
94                                 return new UserControlCompiler ((UserControlParser) tparser);
95
96                         throw new Exception ("Got type: " + type);
97                 }
98                 
99                 public Type GetCompiledType ()
100                 {
101                         //FIXME: use the encoding of the file or the one specified in the machine.config/web.config file.
102                         StreamReader reader = new StreamReader (filename, Encoding.Default);
103                         parser = new AspParser (filename, reader);
104                         parser.Error += new ParseErrorHandler (ParseError);
105                         parser.TagParsed += new TagParsedHandler (TagParsed);
106                         parser.TextParsed += new TextParsedHandler (TextParsed);
107
108                         parser.Parse ();
109                         if (text.Length > 0)
110                                 FlushText ();
111
112 #if DEBUG
113                         PrintTree (rootBuilder, 0);
114 #endif
115
116                         if (stack.Count > 1)
117                                 throw new ParseException (stack.Builder.location,
118                                                 "Expecting </" + stack.Builder.TagName + ">" + stack.Builder);
119
120                         BaseCompiler compiler = GetCompilerFromType ();
121
122                         return compiler.GetCompiledType ();
123                 }
124
125 #if DEBUG
126                 static void PrintTree (ControlBuilder builder, int indent)
127                 {
128                         if (builder == null)
129                                 return;
130
131                         string i = new string ('\t', indent);
132                         Console.Write (i);
133                         Console.WriteLine ("b: {0} id: {1} type: {2} parent: {3}",
134                                            builder, builder.ID, builder.ControlType, builder.parentBuilder);
135
136                         if (builder.Children != null)
137                         foreach (object o in builder.Children) {
138                                 if (o is ControlBuilder)
139                                         PrintTree ((ControlBuilder) o, indent++);
140                         }
141                 }
142 #endif
143                 
144                 static void PrintLocation (ILocation loc)
145                 {
146                         Console.WriteLine ("\tFile name: " + loc.Filename);
147                         Console.WriteLine ("\tBegin line: " + loc.BeginLine);
148                         Console.WriteLine ("\tEnd line: " + loc.EndLine);
149                         Console.WriteLine ("\tBegin column: " + loc.BeginColumn);
150                         Console.WriteLine ("\tEnd column: " + loc.EndColumn);
151                         Console.WriteLine ("\tPlainText: " + loc.PlainText);
152                         Console.WriteLine ();
153                 }
154
155                 void ParseError (ILocation location, string message)
156                 {
157                         throw new ParseException (location, message);
158                 }
159
160                 void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
161                 {
162                         this.location = new Location (location);
163                         if (tparser != null)
164                                 tparser.Location = location;
165
166                         if (text.Length != 0)
167                                 FlushText ();
168
169                         if (0 == String.Compare (tagid, "script", true)) {
170                                 if (ProcessScript (tagtype, attributes))
171                                         return;
172                         }
173
174                         switch (tagtype) {
175                         case TagType.Directive:
176                                 if (tagid == "")
177                                         tagid = tparser.DefaultDirectiveName;
178
179                                 tparser.AddDirective (tagid, attributes.GetDictionary (null));
180                                 break;
181                         case TagType.Tag:
182                                 if (!ProcessTag (tagid, attributes))
183                                         TextParsed (location, location.PlainText);
184                                 break;
185                         case TagType.Close:
186                                 if (!CloseControl (tagid))
187                                         TextParsed (location, location.PlainText);
188                                 break;
189                         case TagType.SelfClosing:
190                                 int count = stack.Count;
191                                 if (!ProcessTag (tagid, attributes)) {
192                                         TextParsed (location, location.PlainText);
193                                 } else if (stack.Count != count) {
194                                         CloseControl (tagid);
195                                 }
196                                 break;
197                         case TagType.DataBinding:
198                                 goto case TagType.CodeRender;
199                         case TagType.CodeRenderExpression:
200                                 goto case TagType.CodeRender;
201                         case TagType.CodeRender:
202                                 ProcessCode (tagtype, tagid, location);
203                                 break;
204                         case TagType.Include:
205                                 string file = attributes ["virtual"] as string;
206                                 bool isvirtual = (file != null);
207                                 if (!isvirtual)
208                                         file = attributes ["file"] as string;
209
210                                 TextParsed (location, tparser.ProcessInclude (isvirtual, file));
211                                 break;
212                         default:
213                                 break;
214                         }
215                         //PrintLocation (location);
216                 }
217
218                 void TextParsed (ILocation location, string text)
219                 {
220                         if (text.IndexOf ("<%") != -1) {
221                                 if (this.text.Length > 0)
222                                         FlushText ();
223                                 CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
224                                 r.AddChildren ();
225                                 return;
226                         }
227
228                         this.text.Append (text);
229                         //PrintLocation (location);
230                 }
231
232                 void FlushText ()
233                 {
234                         string t = text.ToString ();
235                         text.Length = 0;
236                         if (inScript) {
237                                 // TODO: store location
238                                 tparser.Scripts.Add (t);
239                                 return;
240                         }
241
242                         if (tparser.DefaultDirectiveName == "application" && t.Trim () != "")
243                                 throw new ParseException (location, "Content not valid for application file.");
244
245                         stack.Builder.AppendLiteralString (t);
246                 }
247
248                 bool ProcessTag (string tagid, TagAttributes atts)
249                 {
250                         ControlBuilder parent = stack.Builder;
251                         ControlBuilder builder = null;
252                         BuilderLocation bl = null;
253                         Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
254                         if (stack.Count > 1)
255                                 builder = parent.CreateSubBuilder (tagid, htable, null, tparser, location);
256
257                         if (builder == null && atts != null && atts.IsRunAtServer ())
258                                 builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
259                         
260                         if (builder == null)
261                                 return false;
262
263                         builder.location = location;
264                         builder.ID = htable ["id"] as string;
265                         if (builder.HasBody ()) {
266                                 if (builder is TemplateBuilder) {
267                                 //      push the id list
268                                 }
269                                 stack.Push (builder, location);
270                         } else {
271                                 // FIXME:ObjectTags...
272                                 parent.AppendSubBuilder (builder);
273                                 builder.CloseControl ();
274                         }
275
276                         return true;
277                 }
278
279                 bool ProcessScript (TagType tagtype, TagAttributes attributes)
280                 {
281                         if (tagtype != TagType.Close && attributes != null && attributes.IsRunAtServer ()) {
282                                 if (tagtype == TagType.Tag) {
283                                         parser.VerbatimID = "script";
284                                         inScript = true;
285                                 } //else if (tagtype == TagType.SelfClosing)
286                                         // load script file here
287
288                                 return true;
289                         }
290
291                         bool result = inScript;
292                         inScript = false;
293
294                         return result;
295                 }
296
297                 bool CloseControl (string tagid)
298                 {
299                         ControlBuilder current = stack.Builder;
300                         string btag = current.TagName;
301                         if (0 != String.Compare (tagid, btag, true))
302                                 return false;
303
304                         // if (current is TemplateBuilder)
305                         //      pop from the id list
306                         current.CloseControl ();
307                         stack.Pop ();
308                         stack.Builder.AppendSubBuilder (current);
309                         return true;
310                 }
311
312                 bool ProcessCode (TagType tagtype, string code, ILocation location)
313                 {
314                         ControlBuilder b = null;
315                         if (tagtype == TagType.CodeRender)
316                                 b = new CodeRenderBuilder (code, false, location);
317                         else if (tagtype == TagType.CodeRenderExpression)
318                                 b = new CodeRenderBuilder (code, true, location);
319                         else if (tagtype == TagType.DataBinding)
320                                 b = new DataBindingBuilder (code, location);
321                         else
322                                 throw new HttpException ("Should never happen");
323
324                         stack.Builder.AppendSubBuilder (b);
325                         return true;
326                 }
327
328                 public ILocation Location {
329                         get { return location; }
330                 }
331
332                 // Used to get CodeRender tags in attribute values
333                 class CodeRenderParser
334                 {
335                         string str;
336                         ControlBuilder builder;
337
338                         public CodeRenderParser (string str, ControlBuilder builder)
339                         {
340                                 this.str = str;
341                                 this.builder = builder;
342                         }
343
344                         public void AddChildren ()
345                         {
346                                 int index = str.IndexOf ("<%");
347                                 if (index > 0) {
348                                         TextParsed (null, str.Substring (0, index));
349                                         str = str.Substring (index);
350                                 }
351
352                                 AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
353                                 parser.Error += new ParseErrorHandler (ParseError);
354                                 parser.TagParsed += new TagParsedHandler (TagParsed);
355                                 parser.TextParsed += new TextParsedHandler (TextParsed);
356                                 parser.Parse ();
357                         }
358
359                         void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
360                         {
361                                 if (tagtype == TagType.CodeRender)
362                                         builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
363                                 else if (tagtype == TagType.CodeRenderExpression)
364                                         builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
365                                 else if (tagtype == TagType.DataBinding)
366                                         builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
367                                 else
368                                         builder.AppendLiteralString (location.PlainText);
369                         }
370
371                         void TextParsed (ILocation location, string text)
372                         {
373                                 builder.AppendLiteralString (text);
374                         }
375
376                         void ParseError (ILocation location, string message)
377                         {
378                                 throw new ParseException (location, message);
379                         }
380                 }
381         }
382 }
383