2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Expression.cs
blob7d4efa51ab29e12395c5656a9e76f4a14ab051cb
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
28 #if NET_2_0
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;
37 namespace Microsoft.Build.BuildEngine {
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.
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
51 // The enum ExpressionOptions is for specifying this expansion of item references.
53 // GroupingCollection.Evaluate, evaluates all properties and then items
55 internal class Expression {
57 ExpressionCollection expressionCollection;
59 static Regex item_regex;
60 static Regex property_regex;
61 static Regex metadata_regex;
63 public Expression ()
65 this.expressionCollection = new ExpressionCollection ();
68 // Split: Split on ';'
69 // Eg. Property values don't need to be split
71 // AllowItems: if false, item refs should not be treated as item refs!
72 // it converts them to strings in the final expressionCollection
74 // AllowMetadata: same as AllowItems, for metadata
75 public void Parse (string expression, ParseOptions options)
77 bool split = (options & ParseOptions.Split) == ParseOptions.Split;
78 bool allowItems = (options & ParseOptions.AllowItems) == ParseOptions.AllowItems;
79 bool allowMd = (options & ParseOptions.AllowMetadata) == ParseOptions.AllowMetadata;
81 expression = expression.Replace ('\\', Path.DirectorySeparatorChar);
83 string [] parts;
84 if (split)
85 parts = expression.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
86 else
87 parts = new string [] { expression };
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);
93 Prepare (p1, parts.Length);
94 Prepare (p2, parts.Length);
95 Prepare (p3, parts.Length);
97 for (int i = 0; i < parts.Length; i++)
98 p1 [i] = SplitItems (parts [i], allowItems);
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);
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);
120 CopyToExpressionCollection (p3, allowItems, allowMd);
123 void Prepare (List <ArrayList> l, int length)
125 for (int i = 0; i < length; i++)
126 l.Add (null);
129 void CopyToExpressionCollection (List <ArrayList> lists, bool allowItems, bool allowMd)
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);
140 else if (o is IReference)
141 expressionCollection.Add ((IReference) o);
143 if (i < lists.Count - 1)
144 expressionCollection.Add (";");
148 ArrayList SplitItems (string text, bool allowItems)
150 ArrayList phase1 = new ArrayList ();
151 Match m;
152 m = ItemRegex.Match (text);
154 while (m.Success) {
155 string name = null, transform = null, separator = null;
156 ItemReference ir;
158 name = m.Groups [ItemRegex.GroupNumberFromName ("itemname")].Value;
160 if (m.Groups [ItemRegex.GroupNumberFromName ("has_transform")].Success)
161 transform = m.Groups [ItemRegex.GroupNumberFromName ("transform")].Value;
163 if (m.Groups [ItemRegex.GroupNumberFromName ("has_separator")].Success)
164 separator = m.Groups [ItemRegex.GroupNumberFromName ("separator")].Value;
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 ();
172 ArrayList phase2 = new ArrayList ();
173 int last_end = -1;
174 int end = text.Length - 1;
176 foreach (ItemReference ir in phase1) {
177 int a,b;
179 a = last_end;
180 b = ir.Start;
182 if (b - a - 1 > 0) {
183 phase2.Add (text.Substring (a + 1, b - a - 1));
186 last_end = ir.End;
187 phase2.Add (ir);
190 if (last_end < end)
191 phase2.Add (text.Substring (last_end + 1, end - last_end));
193 return phase2;
196 ArrayList SplitProperties (string text)
198 ArrayList phase1 = new ArrayList ();
199 Match m;
200 m = PropertyRegex.Match (text);
202 while (m.Success) {
203 string name = null;
204 PropertyReference pr;
206 name = m.Groups [PropertyRegex.GroupNumberFromName ("name")].Value;
208 pr = new PropertyReference (name, m.Groups [0].Index, m.Groups [0].Length);
209 phase1.Add (pr);
210 m = m.NextMatch ();
213 ArrayList phase2 = new ArrayList ();
214 int last_end = -1;
215 int end = text.Length - 1;
217 foreach (PropertyReference pr in phase1) {
218 int a,b;
220 a = last_end;
221 b = pr.Start;
223 if (b - a - 1 > 0) {
224 phase2.Add (text.Substring (a + 1, b - a - 1));
227 last_end = pr.End;
228 phase2.Add (pr);
231 if (last_end < end)
232 phase2.Add (text.Substring (last_end + 1, end - last_end));
234 return phase2;
237 ArrayList SplitMetadata (string text)
239 ArrayList phase1 = new ArrayList ();
240 Match m;
241 m = MetadataRegex.Match (text);
243 while (m.Success) {
244 string name = null, meta = null;
245 MetadataReference mr;
247 if (m.Groups [MetadataRegex.GroupNumberFromName ("name")].Success)
248 name = m.Groups [MetadataRegex.GroupNumberFromName ("name")].Value;
250 meta = m.Groups [MetadataRegex.GroupNumberFromName ("meta")].Value;
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 ();
258 ArrayList phase2 = new ArrayList ();
259 int last_end = -1;
260 int end = text.Length - 1;
262 foreach (MetadataReference mr in phase1) {
263 int a,b;
265 a = last_end;
266 b = mr.Start;
268 if (b - a - 1> 0) {
269 phase2.Add (text.Substring (a + 1, b - a - 1));
272 last_end = mr.End;
273 phase2.Add (mr);
276 if (last_end < end)
277 phase2.Add (text.Substring (last_end + 1, end - last_end));
279 return phase2;
282 public object ConvertTo (Project project, Type type)
284 return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs);
287 public object ConvertTo (Project project, Type type, ExpressionOptions options)
289 return expressionCollection.ConvertTo (project, type, options);
292 public ExpressionCollection Collection {
293 get { return expressionCollection; }
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;
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;
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;
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,
341 None = 0x8, // == no items, no metadata, and no split
343 // commonly used options
344 AllowItemsMetadataAndSplit = AllowItems | Split | AllowMetadata,
345 AllowItemsNoMetadataAndSplit = AllowItems | Split
348 enum ExpressionOptions {
349 ExpandItemRefs,
350 DoNotExpandItemRefs
354 #endif