In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BuildItem.cs
index 2d8ed69dec63ee3a8f64f1f7183392d03bfe12ed..45792185cb879213d6d91e197977b0e4ac36be8b 100644 (file)
@@ -29,6 +29,7 @@
 
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.IO;
 using System.Text;
@@ -40,57 +41,80 @@ using Mono.XBuild.Utilities;
 namespace Microsoft.Build.BuildEngine {
        public class BuildItem {
 
+               List<BuildItem> child_items;
                XmlElement      itemElement;
                string          finalItemSpec;
                bool            isImported;
+               string          itemInclude;
                string          name;
-               BuildItemGroup  parentItemGroup;
-               string          recursiveDir;
+               BuildItemGroup  parent_item_group;
+               BuildItem       parent_item;
+               //string                recursiveDir;
                IDictionary     evaluatedMetadata;
                IDictionary     unevaluatedMetadata;
 
-               private BuildItem ()
+               BuildItem ()
                {
                }
                
                public BuildItem (string itemName, ITaskItem taskItem)
                {
+                       if (taskItem == null)
+                               throw new ArgumentNullException ("taskItem");
+
                        this.name = itemName;
                        this.finalItemSpec = taskItem.ItemSpec;
+                       this.itemInclude = Utilities.Escape (taskItem.ItemSpec);
                        this.evaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
                        this.unevaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
                }
 
                public BuildItem (string itemName, string itemInclude)
                {
-                       this.name = itemName;
-                       this.finalItemSpec = itemInclude;
-                       this.unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
-                       this.evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
+                       if (itemInclude == null)
+                               throw new ArgumentNullException ("itemInclude");
+                       if (itemInclude == String.Empty)
+                               throw new ArgumentException ("Parameter \"itemInclude\" cannot have zero length.");
+
+                       name = itemName;
+                       finalItemSpec = itemInclude;
+                       this.itemInclude = itemInclude;
+                       unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
+                       evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
                }
-               
+
                internal BuildItem (XmlElement itemElement, BuildItemGroup parentItemGroup)
                {
-                       this.isImported = false;
-                       this.unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
-                       this.evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
-                       this.parentItemGroup = parentItemGroup;
-                       BindToXml (itemElement);
+                       child_items = new List<BuildItem> ();
+                       isImported = parentItemGroup.IsImported;
+                       unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
+                       evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
+                       this.parent_item_group = parentItemGroup;
+                       
+                       this.itemElement = itemElement;
+                       
+                       if (Include == String.Empty)
+                               throw new InvalidProjectFileException (String.Format ("The required attribute \"Include\" is missing from element <{0}>.", Name));
                }
                
-               private BuildItem (BuildItem parent)
+               BuildItem (BuildItem parent)
                {
-                       this.isImported = parent.isImported;
-                       this.name = parent.name;
-                       this.parentItemGroup = parent.parentItemGroup;
-                       this.unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.unevaluatedMetadata);
-                       this.evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.evaluatedMetadata);
+                       isImported = parent.isImported;
+                       name = parent.Name;
+                       parent_item = parent;
+                       parent_item.child_items.Add (this);
+                       parent_item_group = parent.parent_item_group;
+                       unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.unevaluatedMetadata);
+                       evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.evaluatedMetadata);
                }
                
                public void CopyCustomMetadataTo (BuildItem destinationItem)
                {
+                       if (destinationItem == null)
+                               throw new ArgumentNullException ("destinationItem");
+
                        foreach (DictionaryEntry de in unevaluatedMetadata)
-                               destinationItem.SetMetadata ((string) de.Key, (string) de.Value);
+                               destinationItem.AddMetadata ((string) de.Key, (string) de.Value);
                }
                
                [MonoTODO]
@@ -101,6 +125,11 @@ namespace Microsoft.Build.BuildEngine {
 
                public string GetEvaluatedMetadata (string metadataName)
                {
+                       if (ReservedNameUtils.IsReservedMetadataName (metadataName)) {
+                               string metadata = ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName);
+                               return (metadataName.ToLower () == "fullpath") ? Utilities.Escape (metadata) : metadata;
+                       }
+
                        if (evaluatedMetadata.Contains (metadataName))
                                return (string) evaluatedMetadata [metadataName];
                        else
@@ -109,17 +138,21 @@ namespace Microsoft.Build.BuildEngine {
 
                public string GetMetadata (string metadataName)
                {
-                       if (ReservedNameUtils.IsReservedMetadataName (metadataName))
-                               return ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName);
-                       else if (evaluatedMetadata.Contains (metadataName) == true)
-                               return (string) evaluatedMetadata [metadataName];
+                       if (ReservedNameUtils.IsReservedMetadataName (metadataName)) {
+                               string metadata = ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName);
+                               return (metadataName.ToLower () == "fullpath") ? Utilities.Escape (metadata) : metadata;
+                       } else if (unevaluatedMetadata.Contains (metadataName))
+                               return (string) unevaluatedMetadata [metadataName];
                        else
                                return String.Empty;
                }
                
                public bool HasMetadata (string metadataName)
                {
-                       return evaluatedMetadata.Contains (metadataName);
+                       if (ReservedNameUtils.IsReservedMetadataName (metadataName))
+                               return true;
+                       else
+                               return evaluatedMetadata.Contains (metadataName);
                }
 
                public void RemoveMetadata (string metadataName)
@@ -128,13 +161,21 @@ namespace Microsoft.Build.BuildEngine {
                                throw new ArgumentNullException ("metadataName");
                        
                        if (ReservedNameUtils.IsReservedMetadataName (metadataName))
-                               throw new ArgumentException ("Can't remove reserved metadata.");
-                       
-                       if (evaluatedMetadata.Contains (metadataName))
-                               evaluatedMetadata.Remove (metadataName);
+                               throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
+                                       metadataName));
+
+                       if (FromXml) {
+                               if (unevaluatedMetadata.Contains (metadataName)) {
+                                       XmlNode node = itemElement [metadataName];
+                                       itemElement.RemoveChild (node);
+                               }
+                       } else if (HasParentItem) {
+                               if (parent_item.child_items.Count > 1)
+                                       SplitParentItem ();
+                               parent_item.RemoveMetadata (metadataName);
+                       } 
                        
-                       if (unevaluatedMetadata.Contains (metadataName))
-                               unevaluatedMetadata.Remove (metadataName);
+                       DeleteMetadata (metadataName);
                }
 
                public void SetMetadata (string metadataName,
@@ -143,8 +184,6 @@ namespace Microsoft.Build.BuildEngine {
                        SetMetadata (metadataName, metadataValue, false);
                }
                
-               // FIXME: don't use expression when we treat it as literal
-               [MonoTODO]
                public void SetMetadata (string metadataName,
                                         string metadataValue,
                                         bool treatMetadataValueAsLiteral)
@@ -156,45 +195,77 @@ namespace Microsoft.Build.BuildEngine {
                                throw new ArgumentNullException ("metadataValue");
                        
                        if (ReservedNameUtils.IsReservedMetadataName (metadataName))
-                               throw new ArgumentException ("Can't modify reserved metadata.");
-                       
-                       RemoveMetadata (metadataName);
+                               throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
+                                       metadataName));
+
+                       if (treatMetadataValueAsLiteral && !HasParentItem)
+                               metadataValue = Utilities.Escape (metadataValue);
+
+                       if (FromXml) {
+                               XmlElement element = itemElement [metadataName];
+                               if (element == null) {
+                                       element = itemElement.OwnerDocument.CreateElement (metadataName, Project.XmlNamespace);
+                                       element.InnerText = metadataValue;
+                                       itemElement.AppendChild (element);
+                               } else
+                                       element.InnerText = metadataValue;
+                       } else if (HasParentItem) {
+                               if (parent_item.child_items.Count > 1)
+                                       SplitParentItem ();
+                               parent_item.SetMetadata (metadataName, metadataValue, treatMetadataValueAsLiteral);
+                       }
+                       if (FromXml || HasParentItem) {
+                               parent_item_group.ParentProject.MarkProjectAsDirty ();
+                               parent_item_group.ParentProject.NeedToReevaluate ();
+                       }
                        
-                       unevaluatedMetadata.Add (metadataName, metadataValue);
-                       OldExpression finalValue = new OldExpression (parentItemGroup.Project);
-                       finalValue.ParseSource (metadataValue);
-                       evaluatedMetadata.Add (metadataName, (string) finalValue.ConvertTo (typeof (string)));
+                       DeleteMetadata (metadataName);
+                       AddMetadata (metadataName, metadataValue);
                }
-               
-               private void BindToXml (XmlElement xmlElement)
+
+               void AddMetadata (string name, string value)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
-                       
-                       this.itemElement = xmlElement;
-                       this.name = xmlElement.Name;
-                       
-                       if (Include == String.Empty)
-                               throw new InvalidProjectFileException ("Item must have Include attribute.");
+                       if (parent_item_group != null) {
+                               Expression e = new Expression ();
+                               e.Parse (value, true);
+                               evaluatedMetadata.Add (name, (string) e.ConvertTo (parent_item_group.ParentProject, typeof (string)));
+                       } else
+                               evaluatedMetadata.Add (name, Utilities.Unescape (value));
+                               
+                               unevaluatedMetadata.Add (name, value);
+               }
+
+               void DeleteMetadata (string name)
+               {
+                       if (evaluatedMetadata.Contains (name))
+                               evaluatedMetadata.Remove (name);
                        
-                       foreach (XmlElement xe in xmlElement.ChildNodes) {
-                               this.SetMetadata (xe.Name, xe.InnerText);
-                       }
+                       if (unevaluatedMetadata.Contains (name))
+                               unevaluatedMetadata.Remove (name);
                }
 
-               internal void Evaluate ()
+               internal void Evaluate (Project project, bool evaluatedTo)
                {
+                       // FIXME: maybe make Expression.ConvertTo (null, ...) work as Utilities.Unescape ()?
+                       if (project == null) {
+                               this.finalItemSpec = Utilities.Unescape (Include);
+                               return;
+                       }
+                       
+                       foreach (XmlElement xe in itemElement.ChildNodes)
+                               AddMetadata (xe.Name, xe.InnerText);
+
                        DirectoryScanner directoryScanner;
-                       OldExpression includeExpr, excludeExpr;
+                       Expression includeExpr, excludeExpr;
                        string includes, excludes;
 
-                       includeExpr = new OldExpression (parentItemGroup.Project);
-                       includeExpr.ParseSource (Include);
-                       excludeExpr = new OldExpression (parentItemGroup.Project);
-                       excludeExpr.ParseSource (Exclude);
+                       includeExpr = new Expression ();
+                       includeExpr.Parse (Include, true);
+                       excludeExpr = new Expression ();
+                       excludeExpr.Parse (Exclude, true);
                        
-                       includes = (string) includeExpr.ConvertTo (typeof (string));
-                       excludes = (string) excludeExpr.ConvertTo (typeof (string));
+                       includes = (string) includeExpr.ConvertTo (project, typeof (string));
+                       excludes = (string) excludeExpr.ConvertTo (project, typeof (string));
 
                        this.finalItemSpec = includes;
                        
@@ -202,48 +273,71 @@ namespace Microsoft.Build.BuildEngine {
                        
                        directoryScanner.Includes = includes;
                        directoryScanner.Excludes = excludes;
-                       if (parentItemGroup.Project.FullFileName != String.Empty) {
-                               directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (parentItemGroup.Project.FullFileName));
-                       } else {
+
+                       if (project.FullFileName != String.Empty)
+                               directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (project.FullFileName));
+                       else
                                directoryScanner.BaseDirectory = new DirectoryInfo (Directory.GetCurrentDirectory ());
-                       }
                        
                        directoryScanner.Scan ();
                        
-                       foreach (string matchedFile in directoryScanner.MatchedFilenames) {
-                               AddEvaluatedItem (matchedFile);
-                       }
+                       foreach (string matchedFile in directoryScanner.MatchedFilenames)
+                               AddEvaluatedItem (project, evaluatedTo, matchedFile);
                }
                
-               private void AddEvaluatedItem (string itemSpec)
+               void AddEvaluatedItem (Project project, bool evaluatedTo, string itemSpec)
                {
-                       Project project = this.parentItemGroup.Project;
-                       
+                       BuildItemGroup big;                     
                        BuildItem bi = new BuildItem (this);
                        bi.finalItemSpec = itemSpec;
-                       project.EvaluatedItems.AddItem (bi);
-                       if (project.EvaluatedItemsByName.Contains (bi.name) == false) {
-                               BuildItemGroup big = new BuildItemGroup (null, project);
-                               project.EvaluatedItemsByName.Add (bi.name, big);
+
+                       if (evaluatedTo) {
+                               project.EvaluatedItems.AddItem (bi);
+       
+                               if (!project.EvaluatedItemsByName.ContainsKey (bi.Name)) {
+                                       big = new BuildItemGroup (null, project, null, true);
+                                       project.EvaluatedItemsByName.Add (bi.Name, big);
+                               } else {
+                                       big = project.EvaluatedItemsByName [bi.Name];
+                               }
+
                                big.AddItem (bi);
+                       }
+
+                       if (!project.EvaluatedItemsByNameIgnoringCondition.ContainsKey (bi.Name)) {
+                               big = new BuildItemGroup (null, project, null, true);
+                               project.EvaluatedItemsByNameIgnoringCondition.Add (bi.Name, big);
                        } else {
-                               ((BuildItemGroup) project.EvaluatedItemsByName [bi.name]).AddItem (bi);
+                               big = project.EvaluatedItemsByNameIgnoringCondition [bi.Name];
                        }
+
+                       big.AddItem (bi);
                }
                
-               internal string ConvertToString (OldExpression transform)
+               internal string ConvertToString (Expression transform)
                {
                        return GetItemSpecFromTransform (transform);
                }
                
-               internal ITaskItem ConvertToITaskItem (OldExpression transform)
+               internal ITaskItem ConvertToITaskItem (Expression transform)
                {
                        TaskItem taskItem;
                        taskItem = new TaskItem (GetItemSpecFromTransform (transform), evaluatedMetadata);
                        return taskItem;
                }
 
-               private string GetItemSpecFromTransform (OldExpression transform)
+               internal void Detach ()
+               {
+                       if (FromXml)
+                               itemElement.ParentNode.RemoveChild (itemElement);
+                       else if (HasParentItem) {
+                               if (parent_item.child_items.Count > 1)
+                                       SplitParentItem ();
+                               parent_item.Detach ();
+                       }
+               }
+
+               string GetItemSpecFromTransform (Expression transform)
                {
                        StringBuilder sb;
                
@@ -255,9 +349,9 @@ namespace Microsoft.Build.BuildEngine {
                                        if (o is string) {
                                                sb.Append ((string)o);
                                        } else if (o is PropertyReference) {
-                                               sb.Append (((PropertyReference)o).ConvertToString ());
+                                               sb.Append (((PropertyReference)o).ConvertToString (parent_item_group.ParentProject));
                                        } else if (o is ItemReference) {
-                                               sb.Append (((ItemReference)o).ConvertToString ());
+                                               sb.Append (((ItemReference)o).ConvertToString (parent_item_group.ParentProject));
                                        } else if (o is MetadataReference) {
                                                sb.Append (GetMetadata (((MetadataReference)o).MetadataName));
                                        }
@@ -266,14 +360,68 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
+               void SplitParentItem ()
+               {
+                       BuildItem parent = parent_item;
+                       List <BuildItem> list = new List <BuildItem> ();
+                       XmlElement insertAfter = parent.itemElement;
+                       foreach (BuildItem bi in parent.child_items) {
+                               BuildItem added = InsertElementAfter (parent, bi, insertAfter);
+                               insertAfter = added.itemElement;
+                               list.Add (added);
+                       }
+                       parent.parent_item_group.ReplaceWith (parent, list);
+                       parent.itemElement.ParentNode.RemoveChild (parent.itemElement);                 
+               }
+
+               static BuildItem InsertElementAfter (BuildItem parent, BuildItem child, XmlElement insertAfter)
+               {
+                       BuildItem newParent;
+
+                       XmlDocument doc = parent.itemElement.OwnerDocument;
+                       XmlElement newElement = doc.CreateElement (child.Name, Project.XmlNamespace);
+                       newElement.SetAttribute ("Include", child.FinalItemSpec);
+                       if (parent.itemElement.HasAttribute ("Condition"))
+                               newElement.SetAttribute ("Condition", parent.itemElement.GetAttribute ("Condition"));
+                       foreach (XmlElement xe in parent.itemElement)
+                               newElement.AppendChild (xe.Clone ());
+                       parent.itemElement.ParentNode.InsertAfter (newElement, insertAfter);
+
+                       newParent = new BuildItem (newElement, parent.parent_item_group);
+                       newParent.child_items.Add (child);
+                       child.parent_item = newParent;
+
+                       return newParent;
+               }
+
                public string Condition {
-                       get { return itemElement.GetAttribute ("Condition"); }
-                       set { itemElement.SetAttribute ("Condition", value); }
+                       get {
+                               if (FromXml)
+                                       return itemElement.GetAttribute ("Condition");
+                               else
+                                       return String.Empty;
+                       }
+                       set {
+                               if (FromXml)
+                                       itemElement.SetAttribute ("Condition", value);
+                               else if (!HasParentItem)
+                                       throw new InvalidOperationException ("Cannot set a condition on an object not represented by an XML element in the project file.");
+                       }
                }
 
                public string Exclude {
-                       get { return itemElement.GetAttribute ("Exclude"); }
-                       set { itemElement.SetAttribute ("Exclude", value); }
+                       get {
+                               if (FromXml)
+                                       return itemElement.GetAttribute ("Exclude");
+                               else
+                                       return String.Empty;
+                       }
+                       set {
+                               if (FromXml)
+                                       itemElement.SetAttribute ("Exclude", value);
+                               else
+                                       throw new InvalidOperationException ("Assigning the \"Exclude\" attribute of a virtual item is not allowed.");
+                       }
                }
 
                public string FinalItemSpec {
@@ -281,8 +429,24 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                public string Include {
-                       get { return itemElement.GetAttribute ("Include"); }
-                       set { itemElement.SetAttribute ("Include", value); }
+                       get {
+                               if (FromXml)
+                                       return itemElement.GetAttribute ("Include");
+                               else if (HasParentItem)
+                                       return parent_item.Include;
+                               else
+                                       return itemInclude;
+                       }
+                       set {
+                               if (FromXml)
+                                       itemElement.SetAttribute ("Include", value);
+                               else if (HasParentItem) {
+                                       if (parent_item.child_items.Count > 1)
+                                               SplitParentItem ();
+                                       parent_item.Include = value;
+                               } else
+                                       itemInclude = value;
+                       }
                }
 
                public bool IsImported {
@@ -290,14 +454,47 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                public string Name {
-                       get { return name; }
-                       set { name = value; }
+                       get {
+                               if (FromXml)
+                                       return itemElement.Name;
+                               else if (HasParentItem)
+                                       return parent_item.Name;
+                               else
+                                       return name;
+                       }
+                       set {
+                               if (FromXml) {
+                                       XmlElement newElement = itemElement.OwnerDocument.CreateElement (value, Project.XmlNamespace);
+                                       newElement.SetAttribute ("Include", itemElement.GetAttribute ("Include"));
+                                       newElement.SetAttribute ("Condition", itemElement.GetAttribute ("Condition"));
+                                       foreach (XmlElement xe in itemElement)
+                                               newElement.AppendChild (xe.Clone ());
+                                       itemElement.ParentNode.ReplaceChild (newElement, itemElement);
+                                       itemElement = newElement;
+                               } else if (HasParentItem) {
+                                       if (parent_item.child_items.Count > 1)
+                                               SplitParentItem ();
+                                       parent_item.Name = value;
+                               } else
+                                       name = value;
+                       }
                }
                
                internal bool FromXml {
-                       get {
-                               return itemElement != null;
-                       }
+                       get { return itemElement != null; }
+               }
+               
+               internal bool HasParentItem {
+                       get { return parent_item != null; }
+               }
+
+               internal BuildItem ParentItem {
+                       get { return parent_item; }
+               }
+
+               internal BuildItemGroup ParentItemGroup {
+                       get { return parent_item_group; }
+                       set { parent_item_group = value; }
                }
        }
 }