Add [Category ("NotWorking")] to failing test.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BuildItem.cs
1 //
2 // BuildItem.cs:
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.Collections;
31 using System.Collections.Generic;
32 using System.Collections.Specialized;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36 using Microsoft.Build.Framework;
37 using Microsoft.Build.Utilities;
38 using Mono.XBuild.Utilities;
39
40 namespace Microsoft.Build.BuildEngine {
41         public class BuildItem {
42
43                 List<BuildItem> child_items;
44                 XmlElement      itemElement;
45                 string          finalItemSpec;
46                 bool            isImported;
47                 string          itemInclude;
48                 string          name;
49                 BuildItemGroup  parent_item_group;
50                 BuildItem       parent_item;
51                 //string                recursiveDir;
52                 IDictionary     evaluatedMetadata;
53                 IDictionary     unevaluatedMetadata;
54                 bool            isDynamic;
55                 bool            keepDuplicates = true;
56                 string          removeMetadata, keepMetadata;
57
58                 BuildItem ()
59                 {
60                 }
61                 
62                 public BuildItem (string itemName, ITaskItem taskItem)
63                 {
64                         if (taskItem == null)
65                                 throw new ArgumentNullException ("taskItem");
66
67                         this.name = itemName;
68                         this.finalItemSpec = taskItem.ItemSpec;
69                         this.itemInclude = MSBuildUtils.Escape (taskItem.ItemSpec);
70                         this.evaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
71                         this.unevaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
72                 }
73
74                 public BuildItem (string itemName, string itemInclude)
75                 {
76                         if (itemInclude == null)
77                                 throw new ArgumentNullException ("itemInclude");
78                         if (itemInclude == String.Empty)
79                                 throw new ArgumentException ("Parameter \"itemInclude\" cannot have zero length.");
80
81                         name = itemName;
82                         finalItemSpec = itemInclude;
83                         this.itemInclude = itemInclude;
84                         unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
85                         evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
86                 }
87
88                 internal BuildItem (XmlElement itemElement, BuildItemGroup parentItemGroup)
89                 {
90                         child_items = new List<BuildItem> ();
91                         isImported = parentItemGroup.IsImported;
92                         unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
93                         evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
94                         this.parent_item_group = parentItemGroup;
95                         
96                         this.itemElement = itemElement;
97                         isDynamic = parentItemGroup.IsDynamic;
98
99                         if (IsDynamic) {
100                                 if (!string.IsNullOrEmpty (Remove)) {
101                                         if (!string.IsNullOrEmpty (Include) || !string.IsNullOrEmpty (Exclude))
102                                                 throw new InvalidProjectFileException (string.Format ("The attribute \"Remove\" in element <{0}> is unrecognized.", Name));
103                                         if (itemElement.HasChildNodes)
104                                                 throw new InvalidProjectFileException ("Children are not allowed below an item remove element.");
105                                 }
106                                 if (string.IsNullOrEmpty (Include) && !string.IsNullOrEmpty (Exclude))
107                                         throw new InvalidProjectFileException (string.Format ("The attribute \"Exclude\" in element <{0}> is unrecognized.", Name));
108                         } else {
109                                 if (string.IsNullOrEmpty (Include))
110                                         throw new InvalidProjectFileException (string.Format ("The required attribute \"Include\" is missing from element <{0}>.", Name));
111                                 if (!string.IsNullOrEmpty (Remove))
112                                         throw new InvalidProjectFileException (string.Format ("The attribute \"Remove\" in element <{0}> is unrecognized.", Name));
113                         }
114
115                         foreach (XmlAttribute attr in itemElement.Attributes) {
116                                 if (attr.Name == "Include" || attr.Name == "Exclude" || attr.Name == "Condition")
117                                         continue;
118                                 if (!IsDynamic)
119                                         throw new InvalidProjectFileException (string.Format ("The attribute \"{0}\" in element <{1}> is unrecognized.", attr.Name, Name));
120
121                                 switch (attr.Name) {
122                                 case "Remove":
123                                         Remove = attr.Value;
124                                         break;
125                                 case "KeepDuplicates":
126                                         KeepDuplicates = bool.Parse (attr.Value);
127                                         break;
128                                 case "RemoveMetadata":
129                                         removeMetadata = attr.Value;
130                                         break;
131                                 case "KeepMetadata":
132                                         keepMetadata = attr.Value;
133                                         break;
134                                 default:
135                                         throw new InvalidProjectFileException (string.Format ("The attribute \"{0}\" in element <{1}> is unrecognized.", attr.Name, Name));
136                                 }
137                         }
138                 }
139
140                 BuildItem (BuildItem parent)
141                 {
142                         isImported = parent.isImported;
143                         name = parent.Name;
144                         parent_item = parent;
145                         parent_item.child_items.Add (this);
146                         parent_item_group = parent.parent_item_group;
147                         unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.unevaluatedMetadata);
148                         evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.evaluatedMetadata);
149                 }
150                 
151                 public void CopyCustomMetadataTo (BuildItem destinationItem)
152                 {
153                         if (destinationItem == null)
154                                 throw new ArgumentNullException ("destinationItem");
155
156                         foreach (DictionaryEntry de in unevaluatedMetadata)
157                                 destinationItem.AddMetadata ((string) de.Key, (string) de.Value);
158                 }
159                 
160                 [MonoTODO]
161                 public BuildItem Clone ()
162                 {
163                         return (BuildItem) this.MemberwiseClone ();
164                 }
165
166                 public string GetEvaluatedMetadata (string metadataName)
167                 {
168                         if (ReservedNameUtils.IsReservedMetadataName (metadataName)) {
169                                 string metadata = ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName, evaluatedMetadata);
170                                 return string.Equals (metadataName, "fullpath", StringComparison.OrdinalIgnoreCase)
171                                                 ? MSBuildUtils.Escape (metadata)
172                                                 : metadata;
173                         }
174
175                         if (evaluatedMetadata.Contains (metadataName))
176                                 return (string) evaluatedMetadata [metadataName];
177                         else
178                                 return String.Empty;
179                 }
180
181                 public string GetMetadata (string metadataName)
182                 {
183                         if (ReservedNameUtils.IsReservedMetadataName (metadataName)) {
184                                 string metadata = ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName, unevaluatedMetadata);
185                                 return string.Equals (metadataName, "fullpath", StringComparison.OrdinalIgnoreCase)
186                                         ? MSBuildUtils.Escape (metadata)
187                                         : metadata;
188                         } else if (unevaluatedMetadata.Contains (metadataName))
189                                 return (string) unevaluatedMetadata [metadataName];
190                         else
191                                 return String.Empty;
192                 }
193                 
194                 public bool HasMetadata (string metadataName)
195                 {
196                         if (ReservedNameUtils.IsReservedMetadataName (metadataName))
197                                 return true;
198                         else
199                                 return evaluatedMetadata.Contains (metadataName);
200                 }
201
202                 public void RemoveMetadata (string metadataName)
203                 {
204                         if (metadataName == null)
205                                 throw new ArgumentNullException ("metadataName");
206                         
207                         if (ReservedNameUtils.IsReservedMetadataName (metadataName))
208                                 throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
209                                         metadataName));
210
211                         if (FromXml) {
212                                 if (unevaluatedMetadata.Contains (metadataName)) {
213                                         XmlNode node = itemElement [metadataName];
214                                         itemElement.RemoveChild (node);
215                                 }
216                         } else if (HasParentItem) {
217                                 if (parent_item.child_items.Count > 1)
218                                         SplitParentItem ();
219                                 parent_item.RemoveMetadata (metadataName);
220                         } 
221                         
222                         DeleteMetadata (metadataName);
223                 }
224
225                 public void SetMetadata (string metadataName,
226                                          string metadataValue)
227                 {
228                         SetMetadata (metadataName, metadataValue, false);
229                 }
230                 
231                 public void SetMetadata (string metadataName,
232                                          string metadataValue,
233                                          bool treatMetadataValueAsLiteral)
234                 {
235                         if (metadataName == null)
236                                 throw new ArgumentNullException ("metadataName");
237                         
238                         if (metadataValue == null)
239                                 throw new ArgumentNullException ("metadataValue");
240                         
241                         if (ReservedNameUtils.IsReservedMetadataName (metadataName))
242                                 throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
243                                         metadataName));
244
245                         if (treatMetadataValueAsLiteral && !HasParentItem)
246                                 metadataValue = MSBuildUtils.Escape (metadataValue);
247
248                         if (FromXml) {
249                                 XmlElement element = itemElement [metadataName];
250                                 if (element == null) {
251                                         element = itemElement.OwnerDocument.CreateElement (metadataName, Project.XmlNamespace);
252                                         element.InnerText = metadataValue;
253                                         itemElement.AppendChild (element);
254                                 } else
255                                         element.InnerText = metadataValue;
256                         } else if (HasParentItem) {
257                                 if (parent_item.child_items.Count > 1)
258                                         SplitParentItem ();
259                                 parent_item.SetMetadata (metadataName, metadataValue, treatMetadataValueAsLiteral);
260                         }
261                         if (FromXml || HasParentItem) {
262                                 parent_item_group.ParentProject.MarkProjectAsDirty ();
263                                 parent_item_group.ParentProject.NeedToReevaluate ();
264                         }
265                         
266                         DeleteMetadata (metadataName);
267                         AddMetadata (metadataName, metadataValue);
268                 }
269
270                 void AddMetadata (string name, string value)
271                 {
272                         var options = IsDynamic ?
273                                       ParseOptions.AllowItemsMetadataAndSplit : ParseOptions.AllowItemsNoMetadataAndSplit;
274
275                         if (parent_item_group != null) {
276                                 Expression e = new Expression ();
277                                 e.Parse (value, options);
278                                 evaluatedMetadata [name] = (string) e.ConvertTo (parent_item_group.ParentProject,
279                                                 typeof (string), ExpressionOptions.ExpandItemRefs);
280                         } else
281                                 evaluatedMetadata [name] = MSBuildUtils.Unescape (value);
282                                 
283                         unevaluatedMetadata [name] = value;
284                 }
285
286                 void DeleteMetadata (string name)
287                 {
288                         if (evaluatedMetadata.Contains (name))
289                                 evaluatedMetadata.Remove (name);
290                         
291                         if (unevaluatedMetadata.Contains (name))
292                                 unevaluatedMetadata.Remove (name);
293                 }
294
295                 internal void Evaluate (Project project, bool evaluatedTo)
296                 {
297                         // FIXME: maybe make Expression.ConvertTo (null, ...) work as MSBuildUtils.Unescape ()?
298                         if (project == null) {
299                                 this.finalItemSpec = MSBuildUtils.Unescape (Include);
300                                 return;
301                         }
302
303                         foreach (XmlNode xn in itemElement.ChildNodes) {
304                                 XmlElement xe = xn as XmlElement;
305                                 if (xe != null && ConditionParser.ParseAndEvaluate (xe.GetAttribute ("Condition"), project))
306                                         AddMetadata (xe.Name, xe.InnerText);
307                         }
308
309                         if (IsDynamic) {
310                                 if (!evaluatedTo)
311                                         return;
312
313                                 if (!string.IsNullOrEmpty (Remove)) {
314                                         RemoveItems (project);
315                                         return;
316                                 }
317
318                                 if (string.IsNullOrEmpty (Include)) {
319                                         UpdateMetadata (project);
320                                         return;
321                                 }
322                         }
323                         
324                         DirectoryScanner directoryScanner;
325                         Expression includeExpr, excludeExpr;
326                         ITaskItem[] includes, excludes;
327
328                         var options = IsDynamic ?
329                                 ParseOptions.AllowItemsMetadataAndSplit : ParseOptions.AllowItemsNoMetadataAndSplit;
330
331                         includeExpr = new Expression ();
332                         includeExpr.Parse (Include, options);
333                         excludeExpr = new Expression ();
334                         excludeExpr.Parse (Exclude, options);
335                         
336                         includes = (ITaskItem[]) includeExpr.ConvertTo (project, typeof (ITaskItem[]),
337                                                                 ExpressionOptions.ExpandItemRefs);
338                         excludes = (ITaskItem[]) excludeExpr.ConvertTo (project, typeof (ITaskItem[]),
339                                                                 ExpressionOptions.ExpandItemRefs);
340
341                         this.finalItemSpec = (string) includeExpr.ConvertTo (project, typeof (string),
342                                                         ExpressionOptions.ExpandItemRefs);
343
344                         directoryScanner = new DirectoryScanner ();
345                         
346                         directoryScanner.Includes = includes;
347                         directoryScanner.Excludes = excludes;
348
349                         if (project.FullFileName != String.Empty)
350                                 directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (project.FullFileName));
351                         else
352                                 directoryScanner.BaseDirectory = new DirectoryInfo (Directory.GetCurrentDirectory ());
353                         
354                         directoryScanner.Scan ();
355                         
356                         foreach (ITaskItem matchedItem in directoryScanner.MatchedItems)
357                                 AddEvaluatedItem (project, evaluatedTo, matchedItem);
358                 }
359
360                 bool CheckCondition (Project project)
361                 {
362                         if (parent_item_group != null && !ConditionParser.ParseAndEvaluate (parent_item_group.Condition, project))
363                                 return false;
364                         if (parent_item != null && !parent_item.CheckCondition (project))
365                                 return false;
366                         return ConditionParser.ParseAndEvaluate (Condition, project);
367                 }
368
369                 void UpdateMetadata (Project project)
370                 {
371                         BuildItemGroup group;
372                         if (!project.TryGetEvaluatedItemByNameBatched (Name, out group))
373                                 return;
374
375                         foreach (BuildItem item in group) {
376                                 if (!item.CheckCondition (project))
377                                         continue;
378                                 
379                                 foreach (string name in evaluatedMetadata.Keys) {
380                                         item.SetMetadata (name, (string)evaluatedMetadata [name]);
381                                 }
382
383                                 AddAndRemoveMetadata (project, item);
384                         }
385                 }
386
387                 void AddAndRemoveMetadata (Project project, BuildItem item)
388                 {
389                         if (!string.IsNullOrEmpty (removeMetadata)) {
390                                 var removeExpr = new Expression ();
391                                 removeExpr.Parse (removeMetadata, ParseOptions.AllowItemsNoMetadataAndSplit);
392
393                                 var removeSpec = (string[]) removeExpr.ConvertTo (
394                                         project, typeof (string[]), ExpressionOptions.ExpandItemRefs);
395
396                                 foreach (var remove in removeSpec) {
397                                         item.DeleteMetadata (remove);
398                                 }
399                         }
400
401                         if (!string.IsNullOrEmpty (keepMetadata)) {
402                                 var keepExpr = new Expression ();
403                                 keepExpr.Parse (keepMetadata, ParseOptions.AllowItemsNoMetadataAndSplit);
404
405                                 var keepSpec = (string[]) keepExpr.ConvertTo (
406                                         project, typeof (string[]), ExpressionOptions.ExpandItemRefs);
407
408                                 var metadataNames = new string [item.evaluatedMetadata.Count];
409                                 item.evaluatedMetadata.Keys.CopyTo (metadataNames, 0);
410
411                                 foreach (string name in metadataNames) {
412                                         if (!keepSpec.Contains (name))
413                                                 item.DeleteMetadata (name);
414                                 }
415                         }
416                 }
417
418                 void RemoveItems (Project project)
419                 {
420                         BuildItemGroup group;
421                         if (!project.TryGetEvaluatedItemByNameBatched (Name, out group))
422                                 return;
423
424                         var removeExpr = new Expression ();
425                         removeExpr.Parse (Remove, ParseOptions.AllowItemsNoMetadataAndSplit);
426
427                         var removes = (ITaskItem[]) removeExpr.ConvertTo (
428                                 project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
429
430                         var directoryScanner = new DirectoryScanner ();
431                         
432                         directoryScanner.Includes = removes;
433
434                         if (project.FullFileName != String.Empty)
435                                 directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (project.FullFileName));
436                         else
437                                 directoryScanner.BaseDirectory = new DirectoryInfo (Directory.GetCurrentDirectory ());
438                         
439                         directoryScanner.Scan ();
440
441                         foreach (ITaskItem matchedItem in directoryScanner.MatchedItems) {
442                                 group.RemoveItem (matchedItem);
443                         }
444                 }
445
446                 bool ContainsItem (Project project, ITaskItem taskItem)
447                 {
448                         BuildItemGroup group;
449                         if (!project.TryGetEvaluatedItemByNameBatched (Name, out group))
450                                 return false;
451
452                         var item = group.FindItem (taskItem);
453                         if (item == null)
454                                 return false;
455
456                         foreach (string metadataName in evaluatedMetadata.Keys) {
457                                 string metadataValue = (string)evaluatedMetadata [metadataName];
458                                 if (!metadataValue.Equals (item.evaluatedMetadata [metadataName]))
459                                         return false;
460                         }
461                         
462                         foreach (string metadataName in item.evaluatedMetadata.Keys) {
463                                 string metadataValue = (string)item.evaluatedMetadata [metadataName];
464                                 if (!metadataValue.Equals (evaluatedMetadata [metadataName]))
465                                         return false;
466                         }
467
468                         return true;
469                 }
470
471                 void AddEvaluatedItem (Project project, bool evaluatedTo, ITaskItem taskitem)
472                 {
473                         if (IsDynamic && evaluatedTo && !KeepDuplicates && ContainsItem (project, taskitem))
474                                 return;
475
476                         BuildItemGroup big;                     
477                         BuildItem bi = new BuildItem (this);
478                         bi.finalItemSpec = taskitem.ItemSpec;
479
480                         foreach (DictionaryEntry de in taskitem.CloneCustomMetadata ()) {
481                                 bi.unevaluatedMetadata.Add ((string) de.Key, (string) de.Value);
482                                 bi.evaluatedMetadata.Add ((string) de.Key, (string) de.Value);
483                         }
484
485                         project.EvaluatedItemsIgnoringCondition.AddItem (bi);
486
487                         if (evaluatedTo) {
488                                 project.EvaluatedItems.AddItem (bi);
489         
490                                 if (!project.EvaluatedItemsByName.ContainsKey (bi.Name)) {
491                                         big = new BuildItemGroup (null, project, null, true);
492                                         project.EvaluatedItemsByName.Add (bi.Name, big);
493                                 } else {
494                                         big = project.EvaluatedItemsByName [bi.Name];
495                                 }
496
497                                 big.AddItem (bi);
498                         }
499
500                         if (!project.EvaluatedItemsByNameIgnoringCondition.ContainsKey (bi.Name)) {
501                                 big = new BuildItemGroup (null, project, null, true);
502                                 project.EvaluatedItemsByNameIgnoringCondition.Add (bi.Name, big);
503                         } else {
504                                 big = project.EvaluatedItemsByNameIgnoringCondition [bi.Name];
505                         }
506
507                         big.AddItem (bi);
508
509                         if (IsDynamic)
510                                 AddAndRemoveMetadata (project, bi);
511                 }
512                 
513                 // during item's eval phase, any item refs in this item, have either
514                 // already been expanded or are non-existant, so expand can be _false_
515                 //
516                 // during prop's eval phase, this isn't reached, as it parses expressions
517                 // with allowItems=false, so no ItemReferences are created at all
518                 //
519                 // at other times, item refs have already been expanded, so expand: false
520                 internal string ConvertToString (Expression transform, ExpressionOptions options)
521                 {
522                         return GetItemSpecFromTransform (transform, options);
523                 }
524                 
525                 internal ITaskItem ConvertToITaskItem (Expression transform, ExpressionOptions options)
526                 {
527                         TaskItem taskItem;
528                         taskItem = new TaskItem (GetItemSpecFromTransform (transform, options), evaluatedMetadata);
529                         return taskItem;
530                 }
531
532                 internal void Detach ()
533                 {
534                         if (FromXml)
535                                 itemElement.ParentNode.RemoveChild (itemElement);
536                         else if (HasParentItem) {
537                                 if (parent_item.child_items.Count > 1)
538                                         SplitParentItem ();
539                                 parent_item.Detach ();
540                         }
541                 }
542
543                 string GetItemSpecFromTransform (Expression transform, ExpressionOptions options)
544                 {
545                         StringBuilder sb;
546                 
547                         if (transform == null) {
548                                 if (options == ExpressionOptions.ExpandItemRefs) {
549                                         // With usual code paths, this will never execute,
550                                         // but letting this be here, incase BI.ConvertTo*
551                                         // is called directly
552                                         Expression expr = new Expression ();
553                                         expr.Parse (finalItemSpec, ParseOptions.AllowItemsNoMetadataAndSplit);
554
555                                         return (string) expr.ConvertTo (parent_item_group.ParentProject,
556                                                         typeof (string), ExpressionOptions.ExpandItemRefs);
557                                 } else {
558                                         return finalItemSpec;
559                                 }
560                         } else {
561                                 // Transform, _DONT_ expand itemrefs
562                                 sb = new StringBuilder ();
563                                 foreach (object o in transform.Collection) {
564                                         if (o is string) {
565                                                 sb.Append ((string)o);
566                                         } else if (o is PropertyReference) {
567                                                 sb.Append (((PropertyReference)o).ConvertToString (
568                                                                         parent_item_group.ParentProject,
569                                                                         ExpressionOptions.DoNotExpandItemRefs));
570                                         } else if (o is ItemReference) {
571                                                 sb.Append (((ItemReference)o).ConvertToString (
572                                                                         parent_item_group.ParentProject,
573                                                                         ExpressionOptions.DoNotExpandItemRefs));
574                                         } else if (o is MetadataReference) {
575                                                 sb.Append (GetMetadata (((MetadataReference)o).MetadataName));
576                                         }
577                                 }
578                                 return sb.ToString ();
579                         }
580                 }
581
582                 void SplitParentItem ()
583                 {
584                         BuildItem parent = parent_item;
585                         List <BuildItem> list = new List <BuildItem> ();
586                         XmlElement insertAfter = parent.itemElement;
587                         foreach (BuildItem bi in parent.child_items) {
588                                 BuildItem added = InsertElementAfter (parent, bi, insertAfter);
589                                 insertAfter = added.itemElement;
590                                 list.Add (added);
591                         }
592                         parent.parent_item_group.ReplaceWith (parent, list);
593                         parent.itemElement.ParentNode.RemoveChild (parent.itemElement);                 
594                 }
595
596                 static BuildItem InsertElementAfter (BuildItem parent, BuildItem child, XmlElement insertAfter)
597                 {
598                         BuildItem newParent;
599
600                         XmlDocument doc = parent.itemElement.OwnerDocument;
601                         XmlElement newElement = doc.CreateElement (child.Name, Project.XmlNamespace);
602                         newElement.SetAttribute ("Include", child.FinalItemSpec);
603                         if (parent.itemElement.HasAttribute ("Condition"))
604                                 newElement.SetAttribute ("Condition", parent.itemElement.GetAttribute ("Condition"));
605                         foreach (XmlNode xn in parent.itemElement)
606                                 newElement.AppendChild (xn.Clone ());
607                         parent.itemElement.ParentNode.InsertAfter (newElement, insertAfter);
608
609                         newParent = new BuildItem (newElement, parent.parent_item_group);
610                         newParent.child_items.Add (child);
611                         child.parent_item = newParent;
612
613                         return newParent;
614                 }
615
616                 public string Condition {
617                         get {
618                                 if (FromXml)
619                                         return itemElement.GetAttribute ("Condition");
620                                 else
621                                         return String.Empty;
622                         }
623                         set {
624                                 if (FromXml)
625                                         itemElement.SetAttribute ("Condition", value);
626                                 else if (!HasParentItem)
627                                         throw new InvalidOperationException ("Cannot set a condition on an object not represented by an XML element in the project file.");
628                         }
629                 }
630
631                 public string Exclude {
632                         get {
633                                 if (FromXml)
634                                         return itemElement.GetAttribute ("Exclude");
635                                 else
636                                         return String.Empty;
637                         }
638                         set {
639                                 if (FromXml)
640                                         itemElement.SetAttribute ("Exclude", value);
641                                 else
642                                         throw new InvalidOperationException ("Assigning the \"Exclude\" attribute of a virtual item is not allowed.");
643                         }
644                 }
645
646                 public string FinalItemSpec {
647                         get { return finalItemSpec; }
648                 }
649
650                 public string Include {
651                         get {
652                                 if (FromXml)
653                                         return itemElement.GetAttribute ("Include");
654                                 else if (HasParentItem)
655                                         return parent_item.Include;
656                                 else
657                                         return itemInclude;
658                         }
659                         set {
660                                 if (FromXml)
661                                         itemElement.SetAttribute ("Include", value);
662                                 else if (HasParentItem) {
663                                         if (parent_item.child_items.Count > 1)
664                                                 SplitParentItem ();
665                                         parent_item.Include = value;
666                                 } else
667                                         itemInclude = value;
668                         }
669                 }
670
671                 internal bool IsDynamic {
672                         get { return isDynamic; }
673                 }
674
675                 internal string Remove {
676                         get;
677                         private set;
678                 }
679
680                 internal bool KeepDuplicates {
681                         get { return keepDuplicates; }
682                         private set { keepDuplicates = value; }
683                 }
684
685                 public bool IsImported {
686                         get { return isImported; }
687                 }
688
689                 public string Name {
690                         get {
691                                 if (FromXml)
692                                         return itemElement.Name;
693                                 else if (HasParentItem)
694                                         return parent_item.Name;
695                                 else
696                                         return name;
697                         }
698                         set {
699                                 if (FromXml) {
700                                         XmlElement newElement = itemElement.OwnerDocument.CreateElement (value, Project.XmlNamespace);
701                                         newElement.SetAttribute ("Include", itemElement.GetAttribute ("Include"));
702                                         newElement.SetAttribute ("Condition", itemElement.GetAttribute ("Condition"));
703                                         foreach (XmlNode xn in itemElement)
704                                                 newElement.AppendChild (xn.Clone ());
705                                         itemElement.ParentNode.ReplaceChild (newElement, itemElement);
706                                         itemElement = newElement;
707                                 } else if (HasParentItem) {
708                                         if (parent_item.child_items.Count > 1)
709                                                 SplitParentItem ();
710                                         parent_item.Name = value;
711                                 } else
712                                         name = value;
713                         }
714                 }
715                 
716                 internal bool FromXml {
717                         get { return itemElement != null; }
718                 }
719
720                 internal XmlElement XmlElement {
721                         get { return itemElement; }
722                 }
723                 
724                 internal bool HasParentItem {
725                         get { return parent_item != null; }
726                 }
727
728                 internal BuildItem ParentItem {
729                         get { return parent_item; }
730                 }
731
732                 internal BuildItemGroup ParentItemGroup {
733                         get { return parent_item_group; }
734                         set { parent_item_group = value; }
735                 }
736         }
737 }