2 // Expression.cs: Stores references to items or properties.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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.
32 using System.Collections;
33 using System.Collections.Generic;
35 using System.Text.RegularExpressions;
36 using Mono.XBuild.Utilities;
38 namespace Microsoft.Build.BuildEngine {
40 // Properties and items are processed in two ways
41 // 1. Evaluate, Project calls evaluate on all the item and property groups.
42 // At this time, the items are fully expanded, all item and property
43 // references are expanded to get the item's value.
44 // Properties on the other hand, expand property refs, but _not_
47 // 2. After the 'evaluation' phase, this could be when executing a target/task,
48 // - Items : no expansion required, as they are already at final value
49 // - Properties: Item references get expanded now, in the context of the
52 // The enum ExpressionOptions is for specifying this expansion of item references.
54 // GroupingCollection.Evaluate, evaluates all properties and then items
56 internal class Expression {
58 ExpressionCollection expressionCollection;
60 static Regex item_regex;
61 static Regex property_regex;
62 static Regex metadata_regex;
66 this.expressionCollection = new ExpressionCollection ();
69 public static T ParseAs<T> (string expression, ParseOptions options, Project project)
71 Expression expr = new Expression ();
72 expr.Parse (expression, options);
73 return (T)expr.ConvertTo (project, typeof (T));
76 public static T ParseAs<T> (string expression, ParseOptions options, Project project, ExpressionOptions exprOptions)
78 Expression expr = new Expression ();
79 expr.Parse (expression, options);
80 return (T)expr.ConvertTo (project, typeof (T), exprOptions);
83 // Split: Split on ';'
84 // Eg. Property values don't need to be split
86 // AllowItems: if false, item refs should not be treated as item refs!
87 // it converts them to strings in the final expressionCollection
89 // AllowMetadata: same as AllowItems, for metadata
90 public void Parse (string expression, ParseOptions options)
92 bool split = (options & ParseOptions.Split) == ParseOptions.Split;
93 bool allowItems = (options & ParseOptions.AllowItems) == ParseOptions.AllowItems;
94 bool allowMd = (options & ParseOptions.AllowMetadata) == ParseOptions.AllowMetadata;
96 expression = expression.Replace ('\\', Path.DirectorySeparatorChar);
100 parts = expression.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
102 parts = new string [] { expression };
104 List <ArrayList> p1 = new List <ArrayList> (parts.Length);
105 List <ArrayList> p2 = new List <ArrayList> (parts.Length);
106 List <ArrayList> p3 = new List <ArrayList> (parts.Length);
108 Prepare (p1, parts.Length);
109 Prepare (p2, parts.Length);
110 Prepare (p3, parts.Length);
112 for (int i = 0; i < parts.Length; i++)
113 p1 [i] = SplitItems (parts [i], allowItems);
115 for (int i = 0; i < parts.Length; i++) {
116 p2 [i] = new ArrayList ();
117 foreach (object o in p1 [i]) {
119 p2 [i].AddRange (SplitProperties ((string) o));
125 for (int i = 0; i < parts.Length; i++) {
126 p3 [i] = new ArrayList ();
127 foreach (object o in p2 [i]) {
129 p3 [i].AddRange (SplitMetadata ((string) o));
135 CopyToExpressionCollection (p3, allowItems, allowMd);
138 void Prepare (List <ArrayList> l, int length)
140 for (int i = 0; i < length; i++)
144 void CopyToExpressionCollection (List <ArrayList> lists, bool allowItems, bool allowMd)
146 for (int i = 0; i < lists.Count; i++) {
147 foreach (object o in lists [i]) {
149 expressionCollection.Add (MSBuildUtils.Unescape ((string) o));
150 else if (!allowItems && o is ItemReference)
151 expressionCollection.Add (((ItemReference) o).OriginalString);
152 else if (!allowMd && o is MetadataReference) {
153 expressionCollection.Add (((MetadataReference) o).OriginalString);
155 else if (o is IReference)
156 expressionCollection.Add ((IReference) o);
158 if (i < lists.Count - 1)
159 expressionCollection.Add (";");
163 ArrayList SplitItems (string text, bool allowItems)
165 ArrayList phase1 = new ArrayList ();
167 m = ItemRegex.Match (text);
170 string name = null, transform = null, separator = null;
173 name = m.Groups [ItemRegex.GroupNumberFromName ("itemname")].Value;
175 if (m.Groups [ItemRegex.GroupNumberFromName ("has_transform")].Success)
176 transform = m.Groups [ItemRegex.GroupNumberFromName ("transform")].Value;
178 if (m.Groups [ItemRegex.GroupNumberFromName ("has_separator")].Success)
179 separator = m.Groups [ItemRegex.GroupNumberFromName ("separator")].Value;
181 ir = new ItemReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length),
182 name, transform, separator, m.Groups [0].Index, m.Groups [0].Length);
187 ArrayList phase2 = new ArrayList ();
189 int end = text.Length - 1;
191 foreach (ItemReference ir in phase1) {
198 phase2.Add (text.Substring (a + 1, b - a - 1));
206 phase2.Add (text.Substring (last_end + 1, end - last_end));
211 ArrayList SplitProperties (string text)
213 ArrayList phase1 = new ArrayList ();
215 m = PropertyRegex.Match (text);
219 PropertyReference pr;
221 name = m.Groups [PropertyRegex.GroupNumberFromName ("name")].Value;
223 pr = new PropertyReference (name, m.Groups [0].Index, m.Groups [0].Length);
228 ArrayList phase2 = new ArrayList ();
230 int end = text.Length - 1;
232 foreach (PropertyReference pr in phase1) {
239 phase2.Add (text.Substring (a + 1, b - a - 1));
247 phase2.Add (text.Substring (last_end + 1, end - last_end));
252 ArrayList SplitMetadata (string text)
254 ArrayList phase1 = new ArrayList ();
256 m = MetadataRegex.Match (text);
259 string name = null, meta = null;
260 MetadataReference mr;
262 if (m.Groups [MetadataRegex.GroupNumberFromName ("name")].Success)
263 name = m.Groups [MetadataRegex.GroupNumberFromName ("name")].Value;
265 meta = m.Groups [MetadataRegex.GroupNumberFromName ("meta")].Value;
267 mr = new MetadataReference (text.Substring (m.Groups [0].Index, m.Groups [0].Length),
268 name, meta, m.Groups [0].Index, m.Groups [0].Length);
273 ArrayList phase2 = new ArrayList ();
275 int end = text.Length - 1;
277 foreach (MetadataReference mr in phase1) {
284 phase2.Add (text.Substring (a + 1, b - a - 1));
292 phase2.Add (text.Substring (last_end + 1, end - last_end));
297 public object ConvertTo (Project project, Type type)
299 return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs);
302 public object ConvertTo (Project project, Type type, ExpressionOptions options)
304 return expressionCollection.ConvertTo (project, type, options);
307 public ExpressionCollection Collection {
308 get { return expressionCollection; }
311 static Regex ItemRegex {
313 if (item_regex == null)
314 item_regex = new Regex (
316 + @"(?<itemname>[_A-Za-z][_\-0-9a-zA-Z]*)"
317 + @"(?<has_transform>\s*->\s*'(?<transform>[^']*)')?"
318 + @"(?<has_separator>\s*,\s*'(?<separator>[^']*)')?"
324 static Regex PropertyRegex {
326 if (property_regex == null)
327 property_regex = new Regex (
329 + @"(?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)"
331 return property_regex;
335 static Regex MetadataRegex {
337 if (metadata_regex == null)
338 metadata_regex = new Regex (
340 + @"((?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)\.)?"
341 + @"(?<meta>[_a-zA-Z][_\-0-9a-zA-Z]*)"
343 return metadata_regex;
350 // absence of one of these flags, means
351 // false for that option
356 None = 0x8, // == no items, no metadata, and no split
358 // commonly used options
359 AllowItemsMetadataAndSplit = AllowItems | Split | AllowMetadata,
360 AllowItemsNoMetadataAndSplit = AllowItems | Split
363 enum ExpressionOptions {