Merge pull request #600 from tr8dr/master
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BuildItemGroup.cs
1 //
2 // BuildItemGroup.cs: Represents a group of build items.
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 using System;
29 using System.Linq;
30 using System.Reflection;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Xml;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Utilities;
36
37 namespace Microsoft.Build.BuildEngine {
38         public class BuildItemGroup : IEnumerable {
39         
40                 List <BuildItem>        buildItems;
41                 ImportedProject         importedProject;
42                 XmlElement              itemGroupElement;
43                 GroupingCollection      parentCollection;
44                 Project                 parentProject;
45                 bool                    read_only;
46                 bool                    evaluated;
47                 bool                    isDynamic;
48
49                 public BuildItemGroup ()
50                         : this (null, null, null, false)
51                 {
52                 }
53
54                 internal BuildItemGroup (Project project)
55                         : this (null, project, null, false)
56                 {
57                 }
58
59                 internal BuildItemGroup (XmlElement xmlElement, Project project, ImportedProject importedProject, bool readOnly)
60                         : this (xmlElement, project, importedProject, readOnly, false)
61                 {
62                 }
63
64                 internal BuildItemGroup (XmlElement xmlElement, Project project, ImportedProject importedProject, bool readOnly, bool dynamic)
65                 {
66                         this.buildItems = new List <BuildItem> ();
67                         this.importedProject = importedProject;
68                         this.itemGroupElement = xmlElement;
69                         this.parentProject = project;
70                         this.read_only = readOnly;
71                         this.isDynamic = dynamic;
72                         
73                         if (!FromXml)
74                                 return;
75
76                         foreach (XmlNode xn in xmlElement.ChildNodes) {
77                                 if (!(xn is XmlElement))
78                                         continue;
79                                         
80                                 XmlElement xe = (XmlElement) xn;
81                                 BuildItem bi = CreateItem (project, xe);
82                                 buildItems.Add (bi);
83                                 project.LastItemGroupContaining [bi.Name] = this;
84                         }
85
86                         DefinedInFileName = importedProject != null ? importedProject.FullFileName :
87                                                 project != null ? project.FullFileName : null;
88                 }
89
90                 internal virtual BuildItem CreateItem (Project project, XmlElement xe)
91                 {
92                         return new BuildItem (xe, this);
93                 }
94
95                 public BuildItem AddNewItem (string itemName,
96                                              string itemInclude)
97                 {
98                         return AddNewItem (itemName, itemInclude, false);
99                 }
100                 
101                 [MonoTODO]
102                 public BuildItem AddNewItem (string itemName,
103                                              string itemInclude,
104                                              bool treatItemIncludeAsLiteral)
105                 {
106                         BuildItem item;
107
108                         if (treatItemIncludeAsLiteral)
109                                 itemInclude = Utilities.Escape (itemInclude);
110
111                         if (FromXml) {
112                                 XmlElement element = itemGroupElement.OwnerDocument.CreateElement (itemName, Project.XmlNamespace);
113                                 itemGroupElement.AppendChild (element);
114                                 element.SetAttribute ("Include", itemInclude);
115                                 item = new BuildItem (element, this);
116                         } else {
117                                 item = new BuildItem (itemName, itemInclude);
118                                 item.ParentItemGroup = this;
119                         }
120
121                         item.Evaluate (null, true);
122
123                         if (!read_only)
124                                 buildItems.Add (item);
125
126                         if (parentProject != null) {
127                                 parentProject.MarkProjectAsDirty ();
128                                 parentProject.NeedToReevaluate ();
129                         }
130
131                         return item;
132                 }
133                 
134                 public void Clear ()
135                 {
136                         if (FromXml)
137                                 itemGroupElement.RemoveAll ();
138                         
139                         buildItems = new List <BuildItem> ();
140
141                         if (parentProject != null) {
142                                 parentProject.MarkProjectAsDirty ();
143                                 parentProject.NeedToReevaluate ();
144                         }
145                 }
146
147                 [MonoTODO]
148                 public BuildItemGroup Clone (bool deepClone)
149                 {
150                         if (deepClone) {
151                                 if (FromXml)
152                                         throw new NotImplementedException ();
153                                 else
154                                         throw new NotImplementedException ();
155                         } else {
156                                 if (FromXml)
157                                         throw new InvalidOperationException ("A shallow clone of this object cannot be created.");
158                                 else
159                                         throw new NotImplementedException ();
160                         }
161                 }
162
163                 public IEnumerator GetEnumerator ()
164                 {
165                         return buildItems.GetEnumerator ();
166                 }
167
168                 public void RemoveItem (BuildItem itemToRemove)
169                 {
170                         if (itemToRemove == null)
171                                 return;
172
173                         itemToRemove.Detach ();
174
175                         buildItems.Remove (itemToRemove);
176                 }
177
178                 public void RemoveItemAt (int index)
179                 {
180                         BuildItem item = buildItems [index];
181
182                         RemoveItem (item);
183                 }
184
185                 internal BuildItem FindItem (ITaskItem taskItem)
186                 {
187                         return buildItems.FirstOrDefault (i => i.FinalItemSpec == taskItem.ItemSpec);
188                 }
189
190                 internal void RemoveItem (ITaskItem itemToRemove)
191                 {
192                         if (itemToRemove == null)
193                                 return;
194
195                         var item = FindItem (itemToRemove);
196                         if (item == null)
197                                 return;
198
199                         item.Detach ();
200                         buildItems.Remove (item);
201                 }
202
203                 public BuildItem[] ToArray ()
204                 {
205                         return buildItems.ToArray ();
206                 }
207
208                 internal void AddItem (BuildItem buildItem)
209                 {
210                         buildItems.Add (buildItem);
211                 }
212
213                 internal void AddItem (string name, ITaskItem taskItem)
214                 {
215                         BuildItem buildItem;
216                         buildItem = new BuildItem (name, taskItem);
217                         buildItem.ParentItemGroup = this;
218                         buildItems.Add (buildItem);
219                 }
220
221                 // In eval phase, any ref'ed item would've already been expanded
222                 // or it doesnt exist, so dont expand again
223                 // In non-eval, items have _already_ been expanded, so dont expand again
224                 // So, ignore @options
225                 internal string ConvertToString (Expression transform,
226                                                  Expression separator, ExpressionOptions options)
227                 {
228                         string separatorString;
229                         
230                         // Item refs are not expanded for separator or transform
231                         if (separator == null)
232                                 separatorString = ";";
233                         else
234                                 separatorString = (string) separator.ConvertTo (parentProject, typeof (string),
235                                                                 ExpressionOptions.DoNotExpandItemRefs);
236                 
237                         string[] items = new string [buildItems.Count];
238                         int i = 0;
239                         foreach (BuildItem bi in  buildItems)
240                                 items [i++] = bi.ConvertToString (transform, ExpressionOptions.DoNotExpandItemRefs);
241                         return String.Join (separatorString, items);
242                 }
243
244                 // In eval phase, any ref'ed item would've already been expanded
245                 // or it doesnt exist, so dont expand again
246                 // In non-eval, items have _already_ been expanded, so dont expand again
247                 // So, ignore @options
248                 internal ITaskItem[] ConvertToITaskItemArray (Expression transform, Expression separator, ExpressionOptions options)
249                 {
250                         if (separator != null)
251                                 // separator present, so return as a single "join'ed" string
252                                 return new ITaskItem [] {
253                                         new TaskItem (ConvertToString (transform, separator, options))
254                                 };
255
256                         ITaskItem[] array = new ITaskItem [buildItems.Count];
257                         int i = 0;
258                         foreach (BuildItem item in buildItems)
259                                 array [i++] = item.ConvertToITaskItem (transform, ExpressionOptions.DoNotExpandItemRefs);
260                         return array;
261                 }
262
263                 internal void Detach ()
264                 {
265                         if (!FromXml)
266                                 throw new InvalidOperationException ();
267
268                         itemGroupElement.ParentNode.RemoveChild (itemGroupElement);
269                 }
270
271                 internal void Evaluate ()
272                 {
273                         if (!isDynamic && evaluated)
274                                 return;
275                         foreach (BuildItem bi in buildItems) {
276                                 if (bi.Condition == String.Empty)
277                                         bi.Evaluate (parentProject, true);
278                                 else {
279                                         ConditionExpression ce = ConditionParser.ParseCondition (bi.Condition);
280                                         bi.Evaluate (parentProject, ce.BoolEvaluate (parentProject));
281                                 }
282                         }
283                         evaluated = true;
284                 }               
285
286                 internal void ReplaceWith (BuildItem item, List <BuildItem> list)
287                 {
288                         int index = buildItems.IndexOf (item);
289                         buildItems.RemoveAt (index);
290                         buildItems.InsertRange (index, list);
291                 }
292                 
293                 public string Condition {
294                         get {
295                                 if (FromXml)
296                                         return itemGroupElement.GetAttribute ("Condition");
297                                 else
298                                         return String.Empty;
299                         }
300                         set {
301                                 if (FromXml)
302                                         itemGroupElement.SetAttribute ("Condition", value);
303                                 else
304                                         throw new InvalidOperationException ("Cannot set a condition on an object not represented by an XML element in the project file.");
305                         }
306                 }
307
308                 public int Count {
309                         get { return buildItems.Count; }
310                 }
311
312                 public bool IsImported {
313                         get { return importedProject != null; }
314                 }
315
316                 
317                 [MonoTODO]
318                 public BuildItem this [int index] {
319                         get {
320                                 return buildItems [index];
321                         }
322                 }
323                 
324                 internal GroupingCollection GroupingCollection {
325                         get { return parentCollection; }
326                         set { parentCollection = value; }
327                 }
328                 
329                 internal Project ParentProject {
330                         get { return parentProject; }
331                         set {
332                                 if (parentProject != null)
333                                         throw new InvalidOperationException ("parentProject is already set");
334                                 parentProject = value;
335                         }
336                 }
337
338                 internal string DefinedInFileName { get; private set; }
339
340                 internal bool FromXml {
341                         get {
342                                 return itemGroupElement != null;
343                         }
344                 }
345
346                 internal XmlElement XmlElement {
347                         get {
348                                 return itemGroupElement;
349                         }       
350                 }
351
352                 internal bool IsDynamic {
353                         get {
354                                 return isDynamic;
355                         }
356                 }
357         }
358 }