5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Collections.Specialized;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
39 using Mono.XBuild.Utilities;
41 namespace Microsoft.Build.BuildEngine {
42 public class BuildItem {
44 BuildItemGroup child_items;
45 XmlElement itemElement;
50 BuildItemGroup parent_item_group;
51 BuildItem parent_item;
52 //string recursiveDir;
53 IDictionary evaluatedMetadata;
54 IDictionary unevaluatedMetadata;
60 public BuildItem (string itemName, ITaskItem taskItem)
63 throw new ArgumentNullException ("taskItem");
66 this.finalItemSpec = taskItem.ItemSpec;
67 this.itemInclude = Utilities.Escape (taskItem.ItemSpec);
68 this.evaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
69 this.unevaluatedMetadata = (Hashtable) taskItem.CloneCustomMetadata ();
72 public BuildItem (string itemName, string itemInclude)
74 if (itemInclude == null)
75 throw new ArgumentNullException ("itemInclude");
76 if (itemInclude == String.Empty)
77 throw new ArgumentException ("Parameter \"itemInclude\" cannot have zero length.");
80 finalItemSpec = itemInclude;
81 this.itemInclude = itemInclude;
82 unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
83 evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
86 internal BuildItem (XmlElement itemElement, BuildItemGroup parentItemGroup)
88 child_items = new BuildItemGroup ();
89 isImported = parentItemGroup.IsImported;
90 unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
91 evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable ();
92 this.parent_item_group = parentItemGroup;
94 this.itemElement = itemElement;
96 if (Include == String.Empty)
97 throw new InvalidProjectFileException (String.Format ("The required attribute \"Include\" is missing from element <{0}>.", Name));
100 BuildItem (BuildItem parent)
102 isImported = parent.isImported;
104 parent_item = parent;
105 parent_item.child_items.AddItem (this);
106 parent_item_group = parent.parent_item_group;
107 unevaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.unevaluatedMetadata);
108 evaluatedMetadata = CollectionsUtil.CreateCaseInsensitiveHashtable (parent.evaluatedMetadata);
111 public void CopyCustomMetadataTo (BuildItem destinationItem)
113 if (destinationItem == null)
114 throw new ArgumentNullException ("destinationItem");
116 foreach (DictionaryEntry de in unevaluatedMetadata)
117 destinationItem.AddMetadata ((string) de.Key, (string) de.Value);
121 public BuildItem Clone ()
123 return (BuildItem) this.MemberwiseClone ();
126 public string GetEvaluatedMetadata (string metadataName)
128 if (evaluatedMetadata.Contains (metadataName))
129 return (string) evaluatedMetadata [metadataName];
134 public string GetMetadata (string metadataName)
136 if (ReservedNameUtils.IsReservedMetadataName (metadataName)) {
137 string metadata = ReservedNameUtils.GetReservedMetadata (FinalItemSpec, metadataName);
138 return (metadataName.ToLower () == "fullpath") ? Utilities.Escape (metadata) : metadata;
139 } else if (unevaluatedMetadata.Contains (metadataName))
140 return (string) unevaluatedMetadata [metadataName];
145 public bool HasMetadata (string metadataName)
147 if (ReservedNameUtils.IsReservedMetadataName (metadataName))
150 return evaluatedMetadata.Contains (metadataName);
153 public void RemoveMetadata (string metadataName)
155 if (metadataName == null)
156 throw new ArgumentNullException ("metadataName");
158 if (ReservedNameUtils.IsReservedMetadataName (metadataName))
159 throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
163 if (unevaluatedMetadata.Contains (metadataName)) {
164 XmlNode node = itemElement [metadataName];
165 itemElement.RemoveChild (node);
167 } else if (HasParent) {
168 if (parent_item.child_items.Count > 1)
170 parent_item.RemoveMetadata (metadataName);
173 DeleteMetadata (metadataName);
176 public void SetMetadata (string metadataName,
177 string metadataValue)
179 SetMetadata (metadataName, metadataValue, false);
182 public void SetMetadata (string metadataName,
183 string metadataValue,
184 bool treatMetadataValueAsLiteral)
186 if (metadataName == null)
187 throw new ArgumentNullException ("metadataName");
189 if (metadataValue == null)
190 throw new ArgumentNullException ("metadataValue");
192 if (ReservedNameUtils.IsReservedMetadataName (metadataName))
193 throw new ArgumentException (String.Format ("\"{0}\" is a reserved item meta-data, and cannot be modified or deleted.",
196 if (treatMetadataValueAsLiteral && !HasParent)
197 metadataValue = Utilities.Escape (metadataValue);
200 XmlElement element = itemElement [metadataName];
201 if (element == null) {
202 element = itemElement.OwnerDocument.CreateElement (metadataName, Project.XmlNamespace);
203 element.InnerText = metadataValue;
204 itemElement.AppendChild (element);
206 element.InnerText = metadataValue;
207 } else if (HasParent) {
208 if (parent_item.child_items.Count > 1)
210 parent_item.SetMetadata (metadataName, metadataValue, treatMetadataValueAsLiteral);
212 if (FromXml || HasParent) {
213 parent_item_group.ParentProject.MarkProjectAsDirty ();
214 parent_item_group.ParentProject.NeedToReevaluate ();
217 DeleteMetadata (metadataName);
218 AddMetadata (metadataName, metadataValue);
221 void AddMetadata (string name, string value)
223 if (parent_item_group != null) {
224 Expression e = new Expression ();
225 e.Parse (value, true);
226 evaluatedMetadata.Add (name, (string) e.ConvertTo (parent_item_group.ParentProject, typeof (string)));
228 evaluatedMetadata.Add (name, Utilities.Unescape (value));
230 unevaluatedMetadata.Add (name, value);
233 void DeleteMetadata (string name)
235 if (evaluatedMetadata.Contains (name))
236 evaluatedMetadata.Remove (name);
238 if (unevaluatedMetadata.Contains (name))
239 unevaluatedMetadata.Remove (name);
242 internal void Evaluate (Project project, bool evaluatedTo)
244 // FIXME: maybe make Expression.ConvertTo (null, ...) work as Utilities.Unescape ()?
245 if (project == null) {
246 this.finalItemSpec = Utilities.Unescape (Include);
250 foreach (XmlElement xe in itemElement.ChildNodes)
251 AddMetadata (xe.Name, xe.InnerText);
253 DirectoryScanner directoryScanner;
254 Expression includeExpr, excludeExpr;
255 string includes, excludes;
257 includeExpr = new Expression ();
258 includeExpr.Parse (Include, true);
259 excludeExpr = new Expression ();
260 excludeExpr.Parse (Exclude, true);
262 includes = (string) includeExpr.ConvertTo (project, typeof (string));
263 excludes = (string) excludeExpr.ConvertTo (project, typeof (string));
265 this.finalItemSpec = includes;
267 directoryScanner = new DirectoryScanner ();
269 directoryScanner.Includes = includes;
270 directoryScanner.Excludes = excludes;
272 if (project.FullFileName != String.Empty)
273 directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (project.FullFileName));
275 directoryScanner.BaseDirectory = new DirectoryInfo (Directory.GetCurrentDirectory ());
277 directoryScanner.Scan ();
279 foreach (string matchedFile in directoryScanner.MatchedFilenames)
280 AddEvaluatedItem (project, evaluatedTo, matchedFile);
283 void AddEvaluatedItem (Project project, bool evaluatedTo, string itemSpec)
286 BuildItem bi = new BuildItem (this);
287 bi.finalItemSpec = itemSpec;
290 project.EvaluatedItems.AddItem (bi);
292 if (!project.EvaluatedItemsByName.ContainsKey (bi.Name)) {
293 big = new BuildItemGroup (null, project, null, true);
294 project.EvaluatedItemsByName.Add (bi.Name, big);
296 big = project.EvaluatedItemsByName [bi.Name];
302 if (!project.EvaluatedItemsByNameIgnoringCondition.ContainsKey (bi.Name)) {
303 big = new BuildItemGroup (null, project, null, true);
304 project.EvaluatedItemsByNameIgnoringCondition.Add (bi.Name, big);
306 big = project.EvaluatedItemsByNameIgnoringCondition [bi.Name];
312 internal string ConvertToString (Expression transform)
314 return GetItemSpecFromTransform (transform);
317 internal ITaskItem ConvertToITaskItem (Expression transform)
320 taskItem = new TaskItem (GetItemSpecFromTransform (transform), evaluatedMetadata);
324 internal void Detach ()
327 itemElement.ParentNode.RemoveChild (itemElement);
328 else if (HasParent) {
329 if (parent_item.child_items.Count > 1)
331 parent_item.Detach ();
335 string GetItemSpecFromTransform (Expression transform)
339 if (transform == null)
340 return finalItemSpec;
342 sb = new StringBuilder ();
343 foreach (object o in transform.Collection) {
345 sb.Append ((string)o);
346 } else if (o is PropertyReference) {
347 sb.Append (((PropertyReference)o).ConvertToString (parent_item_group.ParentProject));
348 } else if (o is ItemReference) {
349 sb.Append (((ItemReference)o).ConvertToString (parent_item_group.ParentProject));
350 } else if (o is MetadataReference) {
351 sb.Append (GetMetadata (((MetadataReference)o).MetadataName));
354 return sb.ToString ();
358 void SplitParentItem ()
360 BuildItem parent = parent_item;
361 List <BuildItem> list = new List <BuildItem> ();
362 XmlElement insertAfter = parent.itemElement;
363 foreach (BuildItem bi in parent.child_items) {
364 BuildItem added = InsertElementAfter (parent, bi, insertAfter);
365 insertAfter = added.itemElement;
368 parent.parent_item_group.ReplaceWith (parent, list);
369 parent.itemElement.ParentNode.RemoveChild (parent.itemElement);
372 static BuildItem InsertElementAfter (BuildItem parent, BuildItem child, XmlElement insertAfter)
376 XmlDocument doc = parent.itemElement.OwnerDocument;
377 XmlElement newElement = doc.CreateElement (child.Name, Project.XmlNamespace);
378 newElement.SetAttribute ("Include", child.FinalItemSpec);
379 if (parent.itemElement.HasAttribute ("Condition"))
380 newElement.SetAttribute ("Condition", parent.itemElement.GetAttribute ("Condition"));
381 foreach (XmlElement xe in parent.itemElement)
382 newElement.AppendChild (xe.Clone ());
383 parent.itemElement.ParentNode.InsertAfter (newElement, insertAfter);
385 newParent = new BuildItem (newElement, parent.parent_item_group);
386 newParent.child_items.AddItem (child);
387 child.parent_item = newParent;
392 public string Condition {
395 return itemElement.GetAttribute ("Condition");
401 itemElement.SetAttribute ("Condition", value);
403 throw new InvalidOperationException ("Cannot set a condition on an object not represented by an XML element in the project file.");
407 public string Exclude {
410 return itemElement.GetAttribute ("Exclude");
416 itemElement.SetAttribute ("Exclude", value);
418 throw new InvalidOperationException ("Assigning the \"Exclude\" attribute of a virtual item is not allowed.");
422 public string FinalItemSpec {
423 get { return finalItemSpec; }
426 public string Include {
429 return itemElement.GetAttribute ("Include");
431 return parent_item.Include;
437 itemElement.SetAttribute ("Include", value);
438 else if (HasParent) {
439 if (parent_item.child_items.Count > 1)
441 parent_item.Include = value;
447 public bool IsImported {
448 get { return isImported; }
454 return itemElement.Name;
456 return parent_item.Name;
462 XmlElement newElement = itemElement.OwnerDocument.CreateElement (value, Project.XmlNamespace);
463 newElement.SetAttribute ("Include", itemElement.GetAttribute ("Include"));
464 newElement.SetAttribute ("Condition", itemElement.GetAttribute ("Condition"));
465 foreach (XmlElement xe in itemElement)
466 newElement.AppendChild (xe.Clone ());
467 itemElement.ParentNode.ReplaceChild (newElement, itemElement);
468 itemElement = newElement;
469 } else if (HasParent) {
470 if (parent_item.child_items.Count > 1)
472 parent_item.Name = value;
478 internal bool FromXml {
479 get { return itemElement != null; }
482 internal bool HasParent {
483 get { return parent_item != null; }
486 internal BuildItem ParentItem {
487 get { return parent_item; }
490 internal BuildItemGroup ParentItemGroup {
491 get { return parent_item_group; }