2
// System.Web.Compilation.AspParser
5
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
7
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
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:
19
// The above copyright notice and this permission notice shall be
20
// included in all copies or substantial portions of the Software.
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.
31
using System.Collections;
32
using System.Globalization;
35
using System.Web.Util;
37
namespace AspNetEdit.Editor.Persistence
39
delegate void ParseErrorHandler (ILocation location, string message);
40
delegate void TextParsedHandler (ILocation location, string text);
41
delegate void TagParsedHandler (ILocation location, TagType tagtype, string id, TagAttributes attributes);
43
class AspParser : ILocation
45
AspTokenizer tokenizer;
46
int beginLine, endLine;
47
int beginColumn, endColumn;
48
int beginPosition, endPosition;
53
public AspParser (string filename, TextReader input)
55
this.filename = filename;
56
fileText = input.ReadToEnd ();
57
StringReader reader = new StringReader (fileText);
58
tokenizer = new AspTokenizer (reader);
61
public int BeginLine {
62
get { return beginLine; }
65
public int BeginColumn {
66
get { return beginColumn; }
70
get { return endLine; }
73
public int EndColumn {
74
get { return endColumn; }
77
public string PlainText {
79
if (beginPosition >= endPosition)
82
return fileText.Substring (beginPosition, endPosition - beginPosition);
86
public string Filename {
87
get { return filename; }
90
public string VerbatimID {
92
tokenizer.Verbatim = true;
93
verbatimID = value.ToUpper (CultureInfo.InvariantCulture);
97
bool Eat (int expected_token)
99
if (tokenizer.get_token () != expected_token) {
100
tokenizer.put_back ();
104
endLine = tokenizer.EndLine;
105
endColumn = tokenizer.EndColumn;
111
beginLine = tokenizer.BeginLine;
112
beginColumn = tokenizer.BeginColumn;
113
beginPosition = tokenizer.Position - 1;
118
endLine = tokenizer.EndLine;
119
endColumn = tokenizer.EndColumn;
120
endPosition = tokenizer.Position;
127
TagAttributes attributes;
128
TagType tagtype = TagType.Text;
129
StringBuilder text = new StringBuilder ();
131
while ((token = tokenizer.get_token ()) != Token.EOF) {
134
if (tokenizer.Verbatim){
135
string end_verbatim = "</" + verbatimID + ">";
136
string verbatim_text = GetVerbatim (token, end_verbatim);
138
if (verbatim_text == null)
139
OnError ("Unexpected EOF processing " + verbatimID);
141
tokenizer.Verbatim = false;
144
endPosition -= end_verbatim.Length;
145
OnTextParsed (verbatim_text);
146
beginPosition = endPosition;
147
endPosition += end_verbatim.Length;
148
OnTagParsed (TagType.Close, verbatimID, null);
153
GetTag (out tagtype, out id, out attributes);
155
if (tagtype == TagType.ServerComment)
158
if (tagtype == TagType.Text)
161
OnTagParsed (tagtype, id, attributes);
166
if (tokenizer.Value.Trim () == "" && tagtype == TagType.Directive) {
172
text.Append (tokenizer.Value);
173
token = tokenizer.get_token ();
174
} while (token != '<' && token != Token.EOF);
176
tokenizer.put_back ();
178
OnTextParsed (text.ToString ());
182
bool GetInclude (string str, out string pathType, out string filename)
186
str = str.Substring (2).Trim ();
187
int len = str.Length;
188
int lastQuote = str.LastIndexOf ('"');
189
if (len < 10 || lastQuote != len - 1)
192
if (!StrUtils.StartsWith (str, "#include ", true))
195
str = str.Substring (9).Trim ();
196
bool isfile = (StrUtils.StartsWith (str ,"file", true));
197
if (!isfile && !StrUtils.StartsWith (str, "virtual", true))
200
pathType = (isfile) ? "file" : "virtual";
201
if (str.Length < pathType.Length + 3)
204
str = str.Substring (pathType.Length).Trim ();
205
if (str.Length < 3 || str [0] != '=')
209
for (; index < str.Length; index++) {
210
if (Char.IsWhiteSpace (str [index]))
212
else if (str [index] == '"')
216
if (index == str.Length || index == lastQuote)
219
str = str.Substring (index);
220
if (str.Length == 2) { // only quotes
221
OnError ("Empty file name.");
225
filename = str.Trim ().Substring (index, str.Length - 2);
226
if (filename.LastIndexOf ('"') != -1)
227
return false; // file=""" -> no error
232
void GetTag (out TagType tagtype, out string id, out TagAttributes attributes)
234
int token = tokenizer.get_token ();
236
tagtype = TagType.ServerComment;
241
GetServerTag (out tagtype, out id, out attributes);
244
if (!Eat (Token.IDENTIFIER))
245
OnError ("expecting TAGNAME");
247
id = tokenizer.Value;
249
OnError ("expecting '>'. Got '" + id + "'");
251
tagtype = TagType.Close;
254
bool double_dash = Eat (Token.DOUBLEDASH);
256
tokenizer.put_back ();
258
tokenizer.Verbatim = true;
259
string end = double_dash ? "-->" : ">";
260
string comment = GetVerbatim (tokenizer.get_token (), end);
261
tokenizer.Verbatim = false;
263
OnError ("Unfinished HTML comment/DTD");
265
string pathType, filename;
266
if (double_dash && GetInclude (comment, out pathType, out filename)) {
267
tagtype = TagType.Include;
268
attributes = new TagAttributes ();
269
attributes.Add (pathType, filename);
271
tagtype = TagType.Text;
272
id = "<!" + comment + end;
275
case Token.IDENTIFIER:
276
if (this.filename == "@@inner_string@@") {
277
// Actually not tag but "xxx < yyy" stuff in inner_string!
278
tagtype = TagType.Text;
279
tokenizer.InTag = false;
280
id = "<" + tokenizer.Odds + tokenizer.Value;
282
id = tokenizer.Value;
284
attributes = GetAttributes ();
285
} catch (Exception e) {
290
tagtype = TagType.Tag;
291
if (Eat ('/') && Eat ('>')) {
292
tagtype = TagType.SelfClosing;
293
} else if (!Eat ('>')) {
294
if (attributes.IsRunAtServer ()) {
295
OnError ("The server tag is not well formed.");
298
tokenizer.Verbatim = true;
299
attributes.Add ("", GetVerbatim (tokenizer.get_token (), ">") + ">");
300
tokenizer.Verbatim = false;
306
tagtype = TagType.Text;
307
tokenizer.InTag = false;
308
id = "<" + tokenizer.Value;
313
TagAttributes GetAttributes ()
316
TagAttributes attributes;
319
attributes = new TagAttributes ();
320
while ((token = tokenizer.get_token ()) != Token.EOF){
321
if (token == '<' && Eat ('%')) {
322
tokenizer.Verbatim = true;
323
attributes.Add ("", "<%" +
324
GetVerbatim (tokenizer.get_token (), "%>") + "%>");
325
tokenizer.Verbatim = false;
326
tokenizer.InTag = true;
330
if (token != Token.IDENTIFIER)
333
id = tokenizer.Value;
335
if (Eat (Token.ATTVALUE)){
336
attributes.Add (id, tokenizer.Value);
337
} else if (Eat ('<') && Eat ('%')) {
338
tokenizer.Verbatim = true;
339
attributes.Add (id, "<%" +
340
GetVerbatim (tokenizer.get_token (), "%>") + "%>");
341
tokenizer.Verbatim = false;
342
tokenizer.InTag = true;
344
OnError ("expected ATTVALUE");
348
attributes.Add (id, null);
352
tokenizer.put_back ();
356
string GetVerbatim (int token, string end)
358
StringBuilder vb_text = new StringBuilder ();
361
if (tokenizer.Value.Length > 1){
362
// May be we have a put_back token that is not a single character
363
vb_text.Append (tokenizer.Value);
364
token = tokenizer.get_token ();
367
while (token != Token.EOF){
368
if (Char.ToUpper ((char) token, CultureInfo.InvariantCulture) == end [i]){
369
if (++i >= end.Length)
371
token = tokenizer.get_token ();
374
for (int j = 0; j < i; j++)
375
vb_text.Append (end [j]);
379
vb_text.Append ((char) token);
380
token = tokenizer.get_token ();
383
return RemoveComments (vb_text.ToString ());
386
string RemoveComments (string text)
389
int start = text.IndexOf ("<%--");
391
while (start != -1) {
392
end = text.IndexOf ("--%>");
393
if (end == -1 || end <= start + 1)
396
text = text.Remove (start, end - start + 4);
397
start = text.IndexOf ("<%--");
403
void GetServerTag (out TagType tagtype, out string id, out TagAttributes attributes)
406
bool old = tokenizer.ExpectAttrValue;
408
tokenizer.ExpectAttrValue = false;
410
tokenizer.ExpectAttrValue = old;
411
tagtype = TagType.Directive;
413
if (Eat (Token.DIRECTIVE))
414
id = tokenizer.Value;
416
attributes = GetAttributes ();
417
if (!Eat ('%') || !Eat ('>'))
418
OnError ("expecting '%>'");
423
if (Eat (Token.DOUBLEDASH)) {
424
tokenizer.ExpectAttrValue = old;
425
tokenizer.Verbatim = true;
426
inside_tags = GetVerbatim (tokenizer.get_token (), "--%>");
427
tokenizer.Verbatim = false;
430
tagtype = TagType.ServerComment;
434
tokenizer.ExpectAttrValue = old;
438
databinding = !varname && Eat ('#');
440
tokenizer.Verbatim = true;
441
inside_tags = GetVerbatim (tokenizer.get_token (), "%>");
442
tokenizer.Verbatim = false;
445
tagtype = (databinding ? TagType.DataBinding :
446
(varname ? TagType.CodeRenderExpression : TagType.CodeRender));
449
public event ParseErrorHandler Error;
450
public event TagParsedHandler TagParsed;
451
public event TextParsedHandler TextParsed;
453
void OnError (string msg)
459
void OnTagParsed (TagType tagtype, string id, TagAttributes attributes)
461
if (TagParsed != null)
462
TagParsed (this, tagtype, id, attributes);
465
void OnTextParsed (string text)
467
if (TextParsed != null)
468
TextParsed (this, text);