Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Rewrite / ConditionTextExtractor.cs
1 //
2 // ConditionTextExtractor.cs
3 //
4 // Authors:
5 //      Chris Bacon (chrisbacon76@gmail.com)
6 //
7 // Copyright (C) 2010 Chris Bacon
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Linq;
32 using System.Text;
33 using Mono.CodeContracts.Rewrite.AstVisitors;
34 using System.IO;
35
36 namespace Mono.CodeContracts.Rewrite {
37         class ConditionTextExtractor {
38
39                 public ConditionTextExtractor (string filename, SourcePositionVisitor.CodePosition start, SourcePositionVisitor.CodePosition end)
40                 {
41                         this.filename = filename;
42                         this.start = start;
43                         this.end = end;
44                 }
45
46                 private string filename;
47                 private SourcePositionVisitor.CodePosition start, end;
48
49                 enum State {
50                         FunctionName,
51                         Parameters,
52                 };
53
54                 private State state;
55
56                 public string GetConditionText ()
57                 {
58                         if (this.filename == null || this.start.IsEmpty || this.end.IsEmpty){
59                                 return "<unknown source code position>";
60                         }
61                         string[] lines;
62                         try {
63                                 lines = File.ReadAllLines (this.filename);
64                         } catch {
65                                 return "<cannot access source code>";
66                         }
67                         try {
68                                 StringBuilder sb = new StringBuilder ();
69                                 for (int i = this.start.Line; i <= this.end.Line; i++) {
70                                         string line = lines [i - 1];
71                                         if (i == this.end.Line && this.end.Column != 0) {
72                                                 line = line.Substring (0, this.end.Column - 1);
73                                         }
74                                         if (i == this.start.Line && this.start.Column != 0) {
75                                                 line = line.Substring (this.start.Column - 1);
76                                         }
77                                         sb.Append (line.Trim());
78                                 }
79                                 string cndStr = sb.ToString ();
80         
81                                 this.state = State.FunctionName;
82         
83                                 var cnd = this.RunStateMachine (cndStr);
84         
85                                 return cnd.ToString ().Trim ();
86                         } catch {
87                                 return "<source-code parse error>";
88                         }
89                 }
90
91                 private StringBuilder RunStateMachine (string line)
92                 {
93                         StringBuilder cnd = new StringBuilder ();
94                         int inBrackets = 0;
95                         bool inDoubleQuotes = false;
96                         bool inSingleQuotes = false;
97                         bool inEscape = false;
98                         foreach (char c in line) {
99                                 switch (this.state) {
100                                 case State.FunctionName:
101                                         if (c == '(') {
102                                                 this.state = State.Parameters;
103                                         }
104                                         break;
105                                 case State.Parameters:
106                                         switch (c) {
107                                         case ',':
108                                                 if (inBrackets == 0 && !inDoubleQuotes && !inSingleQuotes) {
109                                                         return cnd;
110                                                 }
111                                                 break;
112                                         case '(':
113                                                 if (!inDoubleQuotes && !inSingleQuotes) {
114                                                         inBrackets++;
115                                                 }
116                                                 break;
117                                         case ')':
118                                                 if (!inDoubleQuotes && !inSingleQuotes) {
119                                                         if (inBrackets == 0) {
120                                                                 return cnd;
121                                                         }
122                                                         inBrackets--;
123                                                 }
124                                                 break;
125                                         case '"':
126                                                 if (!inEscape) {
127                                                         inDoubleQuotes = !inDoubleQuotes;
128                                                 }
129                                                 break;
130                                         case '\'':
131                                                 if (!inEscape) {
132                                                         inSingleQuotes = !inSingleQuotes;
133                                                 }
134                                                 break;
135                                         case '\\':
136                                                 inEscape = true;
137                                                 goto forceEscape;
138                                         }
139                                         inEscape = false;
140                                 forceEscape:
141                                         cnd.Append (c);
142                                         break;
143                                 default:
144                                         throw new NotSupportedException ("Cannot handle state: " + this.state);
145                                 }
146                         }
147
148                         return new StringBuilder ("<bad source code>");
149                 }
150
151         }
152 }