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
;
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_
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
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
;
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
);
85 parts
= expression
.Split (new char [] {';'}
, StringSplitOptions
.RemoveEmptyEntries
);
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
]) {
104 p2
[i
].AddRange (SplitProperties ((string) o
));
110 for (int i
= 0; i
< parts
.Length
; i
++) {
111 p3
[i
] = new ArrayList ();
112 foreach (object o
in p2
[i
]) {
114 p3
[i
].AddRange (SplitMetadata ((string) o
));
120 CopyToExpressionCollection (p3
, allowItems
, allowMd
);
123 void Prepare (List
<ArrayList
> l
, int length
)
125 for (int i
= 0; i
< length
; i
++)
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
]) {
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 ();
152 m
= ItemRegex
.Match (text
);
155 string name
= null, transform
= null, separator
= null;
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
);
172 ArrayList phase2
= new ArrayList ();
174 int end
= text
.Length
- 1;
176 foreach (ItemReference ir
in phase1
) {
183 phase2
.Add (text
.Substring (a
+ 1, b
- a
- 1));
191 phase2
.Add (text
.Substring (last_end
+ 1, end
- last_end
));
196 ArrayList
SplitProperties (string text
)
198 ArrayList phase1
= new ArrayList ();
200 m
= PropertyRegex
.Match (text
);
204 PropertyReference pr
;
206 name
= m
.Groups
[PropertyRegex
.GroupNumberFromName ("name")].Value
;
208 pr
= new PropertyReference (name
, m
.Groups
[0].Index
, m
.Groups
[0].Length
);
213 ArrayList phase2
= new ArrayList ();
215 int end
= text
.Length
- 1;
217 foreach (PropertyReference pr
in phase1
) {
224 phase2
.Add (text
.Substring (a
+ 1, b
- a
- 1));
232 phase2
.Add (text
.Substring (last_end
+ 1, end
- last_end
));
237 ArrayList
SplitMetadata (string text
)
239 ArrayList phase1
= new ArrayList ();
241 m
= MetadataRegex
.Match (text
);
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
);
258 ArrayList phase2
= new ArrayList ();
260 int end
= text
.Length
- 1;
262 foreach (MetadataReference mr
in phase1
) {
269 phase2
.Add (text
.Substring (a
+ 1, b
- a
- 1));
277 phase2
.Add (text
.Substring (last_end
+ 1, end
- last_end
));
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
{
298 if (item_regex
== null)
299 item_regex
= new Regex (
301 + @"(?<itemname>[_A-Za-z][_\-0-9a-zA-Z]*)"
302 + @"(?<has_transform>\s*->\s*'(?<transform>[^']*)')?"
303 + @"(?<has_separator>\s*,\s*'(?<separator>[^']*)')?"
309 static Regex PropertyRegex
{
311 if (property_regex
== null)
312 property_regex
= new Regex (
314 + @"(?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)"
316 return property_regex
;
320 static Regex MetadataRegex
{
322 if (metadata_regex
== null)
323 metadata_regex
= new Regex (
325 + @"((?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)\.)?"
326 + @"(?<meta>[_a-zA-Z][_\-0-9a-zA-Z]*)"
328 return metadata_regex
;
335 // absence of one of these flags, means
336 // false for that option
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
{