2010-03-01 Jeffrey Stedfast <fejj@novell.com>
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Expression.cs
1 //
2 // Expression.cs: Stores references to items or properties.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
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 #if NET_2_0
29
30 using System;
31 using System.IO;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Text;
35 using System.Text.RegularExpressions;
36
37 namespace Microsoft.Build.BuildEngine {
38
39         // Properties and items are processed in two ways
40         // 1. Evaluate, Project calls evaluate on all the item and property groups.
41         //    At this time, the items are fully expanded, all item and property
42         //    references are expanded to get the item's value.
43         //    Properties on the other hand, expand property refs, but _not_
44         //    item references.
45         //
46         // 2. After the 'evaluation' phase, this could be when executing a target/task,
47         //    - Items : no expansion required, as they are already at final value
48         //    - Properties: Item references get expanded now, in the context of the
49         //      batching
50         //
51         // The enum ExpressionOptions is for specifying this expansion of item references.
52         //
53         // GroupingCollection.Evaluate, evaluates all properties and then items
54
55         internal class Expression {
56         
57                 ExpressionCollection expressionCollection;
58
59                 static Regex item_regex;
60                 static Regex property_regex;
61                 static Regex metadata_regex;
62         
63                 public Expression ()
64                 {
65                         this.expressionCollection = new ExpressionCollection ();
66                 }
67
68                 // Split: Split on ';'
69                 //         Eg. Property values don't need to be split
70                 //
71                 // AllowItems: if false, item refs should not be treated as item refs!
72                 //              it converts them to strings in the final expressionCollection
73                 //
74                 // AllowMetadata: same as AllowItems, for metadata
75                 public void Parse (string expression, ParseOptions options)
76                 {
77                         bool split = (options & ParseOptions.Split) == ParseOptions.Split;
78                         bool allowItems = (options & ParseOptions.AllowItems) == ParseOptions.AllowItems;
79                         bool allowMd = (options & ParseOptions.AllowMetadata) == ParseOptions.AllowMetadata;
80
81                         expression = expression.Replace ('\\', Path.DirectorySeparatorChar);
82                 
83                         string [] parts;
84                         if (split)
85                                 parts = expression.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
86                         else
87                                 parts = new string [] { expression };
88
89                         List <ArrayList> p1 = new List <ArrayList> (parts.Length);
90                         List <ArrayList> p2 = new List <ArrayList> (parts.Length);
91                         List <ArrayList> p3 = new List <ArrayList> (parts.Length);
92
93                         Prepare (p1, parts.Length);
94                         Prepare (p2, parts.Length);
95                         Prepare (p3, parts.Length);
96
97                         for (int i = 0; i < parts.Length; i++)
98                                 p1 [i] = SplitItems (parts [i], allowItems);
99
100                         for (int i = 0; i < parts.Length; i++) {
101                                 p2 [i] = new ArrayList ();
102                                 foreach (object o in p1 [i]) {
103                                         if (o is string)
104                                                 p2 [i].AddRange (SplitProperties ((string) o));
105                                         else
106                                                 p2 [i].Add (o);
107                                 }
108                         }
109
110                         for (int i = 0; i < parts.Length; i++) {
111                                 p3 [i] = new ArrayList ();
112                                 foreach (object o in p2 [i]) {
113                                         if (o is string)
114                                                 p3 [i].AddRange (SplitMetadata ((string) o));
115                                         else
116                                                 p3 [i].Add (o);
117                                 }
118                         }
119
120                         CopyToExpressionCollection (p3, allowItems, allowMd);
121                 }
122
123                 void Prepare (List <ArrayList> l, int length)
124                 {
125                         for (int i = 0; i < length; i++)
126                                 l.Add (null);
127                 }
128                 
129                 void CopyToExpressionCollection (List <ArrayList> lists, bool allowItems, bool allowMd)
130                 {
131                         for (int i = 0; i < lists.Count; i++) {
132                                 foreach (object o in lists [i]) {
133                                         if (o is string)
134                                                 expressionCollection.Add (Utilities.Unescape ((string) o));
135                                         else if (!allowItems && o is ItemReference)
136                                                 expressionCollection.Add (((ItemReference) o).OriginalString);
137                                         else if (!allowMd && o is MetadataReference) {
138                                                 expressionCollection.Add (((MetadataReference) o).OriginalString);
139                                         }
140                                         else if (o is IReference)
141                                                 expressionCollection.Add ((IReference) o);
142                                 }
143                                 if (i < lists.Count - 1)
144                                         expressionCollection.Add (";");
145                         }
146                 }
147
148                 ArrayList SplitItems (string text, bool allowItems)
149                 {
150                         ArrayList phase1 = new ArrayList ();
151                         Match m;
152                         m = ItemRegex.Match (text);
153
154                         while (m.Success) {
155                                 string name = null, transform = null, separator = null;
156                                 ItemReference ir;
157                                 
158                                 name = m.Groups [ItemRegex.GroupNumberFromName ("itemname")].Value;
159                                 
160                                 if (m.Groups [ItemRegex.GroupNumberFromName ("has_transform")].Success)
161                                         transform = m.Groups [ItemRegex.GroupNumberFromName ("transform")].Value;
162                                 
163                                 if (m.Groups [ItemRegex.GroupNumberFromName ("has_separator")].Success)
164                                         separator = m.Groups [ItemRegex.GroupNumberFromName ("separator")].Value;
165
166                                 ir = new ItemReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length),
167                                                 name, transform, separator, m.Groups [0].Index, m.Groups [0].Length);
168                                 phase1.Add (ir);
169                                 m = m.NextMatch ();
170                         }
171
172                         ArrayList phase2 = new ArrayList ();
173                         int last_end = -1;
174                         int end = text.Length - 1;
175
176                         foreach (ItemReference ir in phase1) {
177                                 int a,b;
178
179                                 a = last_end;
180                                 b = ir.Start;
181
182                                 if (b - a - 1 > 0) {
183                                         phase2.Add (text.Substring (a + 1, b - a - 1));
184                                 }
185
186                                 last_end = ir.End;
187                                 phase2.Add (ir);
188                         }
189
190                         if (last_end < end)
191                                 phase2.Add (text.Substring (last_end + 1, end - last_end));
192
193                         return phase2;
194                 }
195
196                 ArrayList SplitProperties (string text)
197                 {
198                         ArrayList phase1 = new ArrayList ();
199                         Match m;
200                         m = PropertyRegex.Match (text);
201
202                         while (m.Success) {
203                                 string name = null;
204                                 PropertyReference pr;
205                                 
206                                 name = m.Groups [PropertyRegex.GroupNumberFromName ("name")].Value;
207                                 
208                                 pr = new PropertyReference (name, m.Groups [0].Index, m.Groups [0].Length);
209                                 phase1.Add (pr);
210                                 m = m.NextMatch ();
211                         }
212
213                         ArrayList phase2 = new ArrayList ();
214                         int last_end = -1;
215                         int end = text.Length - 1;
216
217                         foreach (PropertyReference pr in phase1) {
218                                 int a,b;
219
220                                 a = last_end;
221                                 b = pr.Start;
222
223                                 if (b - a - 1 > 0) {
224                                         phase2.Add (text.Substring (a + 1, b - a - 1));
225                                 }
226
227                                 last_end = pr.End;
228                                 phase2.Add (pr);
229                         }
230
231                         if (last_end < end)
232                                 phase2.Add (text.Substring (last_end + 1, end - last_end));
233
234                         return phase2;
235                 }
236
237                 ArrayList SplitMetadata (string text)
238                 {
239                         ArrayList phase1 = new ArrayList ();
240                         Match m;
241                         m = MetadataRegex.Match (text);
242
243                         while (m.Success) {
244                                 string name = null, meta = null;
245                                 MetadataReference mr;
246                                 
247                                 if (m.Groups [MetadataRegex.GroupNumberFromName ("name")].Success)
248                                         name = m.Groups [MetadataRegex.GroupNumberFromName ("name")].Value;
249                                 
250                                 meta = m.Groups [MetadataRegex.GroupNumberFromName ("meta")].Value;
251                                 
252                                 mr = new MetadataReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length),
253                                                                 name, meta, m.Groups [0].Index, m.Groups [0].Length);
254                                 phase1.Add (mr);
255                                 m = m.NextMatch ();
256                         }
257
258                         ArrayList phase2 = new ArrayList ();
259                         int last_end = -1;
260                         int end = text.Length - 1;
261
262                         foreach (MetadataReference mr in phase1) {
263                                 int a,b;
264
265                                 a = last_end;
266                                 b = mr.Start;
267
268                                 if (b - a - 1> 0) {
269                                         phase2.Add (text.Substring (a + 1, b - a - 1));
270                                 }
271
272                                 last_end = mr.End;
273                                 phase2.Add (mr);
274                         }
275
276                         if (last_end < end)
277                                 phase2.Add (text.Substring (last_end + 1, end - last_end));
278
279                         return phase2;
280                 }
281
282                 public object ConvertTo (Project project, Type type)
283                 {
284                         return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs);
285                 }
286
287                 public object ConvertTo (Project project, Type type, ExpressionOptions options)
288                 {
289                         return expressionCollection.ConvertTo (project, type, options);
290                 }
291
292                 public ExpressionCollection Collection {
293                         get { return expressionCollection; }
294                 }
295
296                 static Regex ItemRegex {
297                         get {
298                                 if (item_regex == null)
299                                         item_regex = new Regex (
300                                                 @"@\(\s*"
301                                                 + @"(?<itemname>[_A-Za-z][_\-0-9a-zA-Z]*)"
302                                                 + @"(?<has_transform>\s*->\s*'(?<transform>[^']*)')?"
303                                                 + @"(?<has_separator>\s*,\s*'(?<separator>[^']*)')?"
304                                                 + @"\s*\)");
305                                 return item_regex;
306                         }
307                 }
308
309                 static Regex PropertyRegex {
310                         get {
311                                 if (property_regex == null)
312                                         property_regex = new Regex (
313                                                 @"\$\(\s*"
314                                                 + @"(?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)"
315                                                 + @"\s*\)");
316                                 return property_regex;
317                         }
318                 }
319
320                 static Regex MetadataRegex {
321                         get {
322                                 if (metadata_regex == null)
323                                         metadata_regex = new Regex (
324                                                 @"%\(\s*"
325                                                 + @"((?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)\.)?"
326                                                 + @"(?<meta>[_a-zA-Z][_\-0-9a-zA-Z]*)"
327                                                 + @"\s*\)");
328                                 return metadata_regex;
329                         }
330                 }
331         }
332
333         [Flags]
334         enum ParseOptions {
335                 // absence of one of these flags, means
336                 // false for that option
337                 AllowItems = 0x1,
338                 Split = 0x2,
339                 AllowMetadata = 0x4,
340
341                 None = 0x8, // == no items, no metadata, and no split
342
343                 // commonly used options
344                 AllowItemsMetadataAndSplit = AllowItems | Split | AllowMetadata,
345                 AllowItemsNoMetadataAndSplit = AllowItems | Split
346         }
347
348         enum ExpressionOptions {
349                 ExpandItemRefs,
350                 DoNotExpandItemRefs
351         }
352 }
353
354 #endif