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