2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sql / SqlStatementBuilder.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections;\r
29 using System.Collections.Generic;\r
30 using DbLinq.Util;\r
31 \r
32 namespace DbLinq.Data.Linq.Sql\r
33 {\r
34     /// <summary>\r
35     /// An SqlStatement is a literal SQL request, composed of different parts (SqlPart)\r
36     /// each part being either a parameter or a literal string\r
37     /// </summary>\r
38 #if !MONO_STRICT\r
39     public\r
40 #endif\r
41     class SqlStatementBuilder\r
42     {\r
43         public readonly List<SqlPart> Parts = new List<SqlPart>();\r
44 \r
45         /// <summary>\r
46         /// Returns part at given index\r
47         /// </summary>\r
48         /// <param name="index"></param>\r
49         /// <returns></returns>\r
50         public SqlPart this[int index]\r
51         {\r
52             get { return Parts[index]; }\r
53         }\r
54 \r
55         /// <summary>\r
56         /// Creates a new SqlStatement based on the current and appending new SqlParts\r
57         /// </summary>\r
58         /// <param name="newParts"></param>\r
59         /// <returns></returns>\r
60         public void Append(IList<SqlPart> newParts)\r
61         {\r
62             foreach (var part in newParts)\r
63                 AddPart(Parts, part);\r
64         }\r
65 \r
66         /// <summary>\r
67         /// Appends a single part, including (useless) optimizations\r
68         /// </summary>\r
69         /// <param name="parts"></param>\r
70         /// <param name="index"></param>\r
71         /// <param name="part"></param>\r
72         public static void InsertPart(IList<SqlPart> parts, int index, SqlPart part)\r
73         {\r
74             // optimization if top part is a literal, and the one we're adding is a literal too\r
75             // in this case, we combine both\r
76             // (this is useless, just pretty)\r
77             if (part is SqlLiteralPart && index > 0 && parts[index - 1] is SqlLiteralPart)\r
78             {\r
79                 parts[index - 1] = new SqlLiteralPart(parts[index - 1].Sql + part.Sql);\r
80             }\r
81             else\r
82                 parts.Insert(index, part);\r
83         }\r
84 \r
85         /// <summary>\r
86         /// Adds the part to the given parts list.\r
87         /// </summary>\r
88         /// <param name="parts">The parts.</param>\r
89         /// <param name="part">The part.</param>\r
90         public static void AddPart(IList<SqlPart> parts, SqlPart part)\r
91         {\r
92             InsertPart(parts, parts.Count, part);\r
93         }\r
94 \r
95         /// <summary>\r
96         /// Joins statements, separated by a given statement\r
97         /// </summary>\r
98         /// <param name="sqlStatement"></param>\r
99         /// <param name="sqlStatements"></param>\r
100         /// <returns></returns>\r
101         public void AppendJoin(SqlStatement sqlStatement, IList<SqlStatement> sqlStatements)\r
102         {\r
103             for (int index = 0; index < sqlStatements.Count; index++)\r
104             {\r
105                 if (index > 0)\r
106                     Append(sqlStatement);\r
107                 Append(sqlStatements[index]);\r
108             }\r
109         }\r
110 \r
111         /// <summary>\r
112         /// Creates an SQL statement based on a format string and SqlStatements as arguments\r
113         /// </summary>\r
114         /// <param name="format"></param>\r
115         /// <param name="sqlStatements"></param>\r
116         /// <returns></returns>\r
117         public void AppendFormat(string format, IList<SqlStatement> sqlStatements)\r
118         {\r
119             var statements = new ArrayList { format };\r
120             // the strategy divides each part containing the {0}, {1}, etc\r
121             // and inserts the required argument\r
122             for (int index = 0; index < sqlStatements.Count; index++)\r
123             {\r
124                 var newStatements = new ArrayList();\r
125                 var literalIndex = "{" + index + "}";\r
126                 // then in each statement we look for the current literalIndex\r
127                 foreach (var statement in statements)\r
128                 {\r
129                     // if we have a string, we split it around the literalIndex\r
130                     // and insert the SqlStatement between new parts\r
131                     var stringStatement = statement as string;\r
132                     if (stringStatement != null)\r
133                     {\r
134                         var parts = stringStatement.Split(new[] { literalIndex }, StringSplitOptions.None);\r
135                         for (int partIndex = 0; partIndex < parts.Length; partIndex++)\r
136                         {\r
137                             if (partIndex > 0)\r
138                                 newStatements.Add(sqlStatements[index]);\r
139                             newStatements.Add(parts[partIndex]);\r
140                         }\r
141                     }\r
142                     else // no match found? add the raw statement\r
143                         newStatements.Add(statement);\r
144                 }\r
145                 statements = newStatements;\r
146             }\r
147             // finally, convert all remaining strings to SqlStatements\r
148             foreach (var statement in statements)\r
149             {\r
150                 var stringStatement = statement as string;\r
151                 if (stringStatement != null)\r
152                     Append(new SqlStatement(stringStatement));\r
153                 else\r
154                     Append((SqlStatement)statement);\r
155             }\r
156         }\r
157 \r
158         /// <summary>\r
159         /// Formats an SqlStatement from a given string format\r
160         /// </summary>\r
161         /// <param name="format"></param>\r
162         /// <param name="sqlStatements"></param>\r
163         /// <returns></returns>\r
164         public void AppendFormat(string format, params SqlStatement[] sqlStatements)\r
165         {\r
166             AppendFormat(format, (IList<SqlStatement>)sqlStatements);\r
167         }\r
168 \r
169         /// <summary>\r
170         /// Appends a bunch of sqlStatements to the current one\r
171         /// </summary>\r
172         /// <param name="sqlStatements"></param>\r
173         /// <returns></returns>\r
174         public void Append(IList<SqlStatement> sqlStatements)\r
175         {\r
176             foreach (var sqlStatement in sqlStatements)\r
177             {\r
178                 foreach (var sqlPart in sqlStatement)\r
179                 {\r
180                     AddPart(Parts, sqlPart);\r
181                 }\r
182             }\r
183         }\r
184 \r
185 #if UNTESTED\r
186 \r
187         /// <summary>\r
188         /// Inserts statements at given position\r
189         /// </summary>\r
190         /// <param name="index"></param>\r
191         /// <param name="sqlStatements"></param>\r
192         public void Insert(int index, IList<SqlStatement> sqlStatements)\r
193         {\r
194             for (int statementIndex = sqlStatements.Count - 1; statementIndex >= 0; statementIndex--)\r
195             {\r
196                 var sqlStatement = sqlStatements[statementIndex];\r
197                 for (int partIndex = sqlStatement.Count - 1; partIndex >= 0; partIndex++)\r
198                 {\r
199                     var sqlPart = sqlStatement[partIndex];\r
200                     InsertPart(Parts, index, sqlPart);\r
201                 }\r
202             }\r
203         }\r
204 \r
205         /// <summary>\r
206         /// Inserts statements at given position\r
207         /// </summary>\r
208         /// <param name="index"></param>\r
209         /// <param name="sqlStatements"></param>\r
210         public void Insert(int index, params SqlStatement[] sqlStatements)\r
211         {\r
212             Insert(index, (IList<SqlStatement>)sqlStatements);\r
213         }\r
214 \r
215 #endif\r
216 \r
217         /// <summary>\r
218         /// Appends sqlStatements to the current one\r
219         /// </summary>\r
220         /// <param name="newStatements"></param>\r
221         /// <returns></returns>\r
222         public void Append(params SqlStatement[] newStatements)\r
223         {\r
224             Append((IList<SqlStatement>)newStatements);\r
225         }\r
226 \r
227         /// <summary>\r
228         /// Replaces the specified text, optionally ignoring the case.\r
229         /// The method does not replace cross-parts text\r
230         /// </summary>\r
231         /// <param name="oldText">The old text.</param>\r
232         /// <param name="newText">The new text.</param>\r
233         /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>\r
234         public void Replace(string oldText, string newText, bool ignoreCase)\r
235         {\r
236             for (int partIndex = 0; partIndex < Parts.Count; partIndex++)\r
237             {\r
238                 var part = Parts[partIndex];\r
239                 if (part.Sql.ContainsCase(oldText, ignoreCase))\r
240                 {\r
241                     // we know how to process only on literal strings\r
242                     if (part is SqlLiteralPart)\r
243                     {\r
244                         Parts[partIndex] = new SqlLiteralPart(part.Sql.ReplaceCase(oldText, newText, ignoreCase));\r
245                     }\r
246                 }\r
247             }\r
248         }\r
249 \r
250         /// <summary>\r
251         /// Initializes a new instance of the <see cref="SqlStatementBuilder"/> class.\r
252         /// </summary>\r
253         public SqlStatementBuilder()\r
254         {\r
255         }\r
256 \r
257         /// <summary>\r
258         /// Initializes a new instance of the <see cref="SqlStatementBuilder"/> class.\r
259         /// </summary>\r
260         /// <param name="sqlStatements">The SQL statements.</param>\r
261         public SqlStatementBuilder(params SqlStatement[] sqlStatements)\r
262         {\r
263             Append(sqlStatements);\r
264         }\r
265 \r
266         /// <summary>\r
267         /// Gets the built SqlStatement.\r
268         /// </summary>\r
269         /// <returns></returns>\r
270         public SqlStatement ToSqlStatement()\r
271         {\r
272             return new SqlStatement(Parts);\r
273         }\r
274     }\r
275 }\r