1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.Collections; 6 using System.Security; 7 using System.Security.Permissions; 8 using System.Diagnostics; 9 10 using Microsoft.Build.Framework; 11 using Microsoft.Build.BuildEngine.Shared; 12 13 namespace Microsoft.Build.BuildEngine 14 { 15 16 /// <summary> 17 /// This class wraps a project item, and provides a "view" on the item's BuildItem class that is suitable to expose to tasks. 18 /// </summary> 19 /// <owner>SumedhK</owner> 20 internal sealed class TaskItem : MarshalByRefObject, ITaskItem 21 { 22 /// <summary> 23 /// Private default constructor disallows parameterless instantiation. 24 /// </summary> 25 /// <owner>SumedhK</owner> TaskItem()26 private TaskItem() 27 { 28 // do nothing 29 } 30 31 /// <summary> 32 /// Creates an instance of this class given the item-spec. 33 /// </summary> 34 /// <owner>SumedhK</owner> 35 /// <param name="itemSpec"></param> TaskItem(string itemSpec)36 internal TaskItem(string itemSpec) 37 { 38 ErrorUtilities.VerifyThrow(itemSpec != null, "Need to specify item-spec."); 39 40 item = new BuildItem(null, itemSpec); 41 } 42 43 /// <summary> 44 /// Creates an instance of this class given the backing item. 45 /// </summary> 46 /// <owner>SumedhK</owner> 47 /// <param name="item"></param> TaskItem(BuildItem item)48 internal TaskItem(BuildItem item) 49 { 50 ErrorUtilities.VerifyThrow(item != null, "Need to specify backing item."); 51 52 this.item = item.VirtualClone(); 53 } 54 55 /// <summary> 56 /// Gets or sets the item-spec for the item. 57 /// </summary> 58 /// <owner>SumedhK</owner> 59 /// <value>Item-spec string.</value> 60 public string ItemSpec 61 { 62 get 63 { 64 return item.FinalItemSpec; 65 } 66 67 set 68 { 69 ErrorUtilities.VerifyThrowArgumentNull(value, "ItemSpec"); 70 item.SetFinalItemSpecEscaped(EscapingUtilities.Escape(value)); 71 } 72 } 73 74 /// <summary> 75 /// Gets the names of metadata on the item -- also includes the pre-defined/reserved item-spec modifiers. 76 /// </summary> 77 /// <owner>SumedhK, JomoF</owner> 78 /// <value>Collection of name strings.</value> 79 public ICollection MetadataNames 80 { 81 get 82 { 83 // Add all the custom metadata. 84 return item.MetadataNames; 85 } 86 } 87 88 /// <summary> 89 /// Gets the number of metadata set on the item. 90 /// </summary> 91 /// <owner>SumedhK</owner> 92 /// <value>Count of metadata.</value> 93 public int MetadataCount 94 { 95 get 96 { 97 return item.MetadataCount; 98 } 99 } 100 101 102 /// <summary> 103 /// Gets the names of custom metadata on the item 104 /// </summary> 105 /// <value>Collection of name strings.</value> 106 public ICollection CustomMetadataNames 107 { 108 get 109 { 110 // All the custom metadata. 111 return item.CustomMetadataNames; 112 } 113 } 114 115 /// <summary> 116 /// Gets the number of custom metadata set on the item. 117 /// </summary> 118 /// <value>Count of metadata.</value> 119 public int CustomMetadataCount 120 { 121 get 122 { 123 return item.CustomMetadataCount; 124 } 125 } 126 127 /// <summary> 128 /// Looks up the value of the given custom metadata. 129 /// </summary> 130 /// <owner>SumedhK</owner> 131 /// <param name="metadataName"></param> 132 /// <returns>value of metadata</returns> GetMetadata(string metadataName)133 public string GetMetadata(string metadataName) 134 { 135 ErrorUtilities.VerifyThrowArgumentNull(metadataName, "metadataName"); 136 137 // Return the unescaped data to the task. 138 return item.GetEvaluatedMetadata(metadataName); 139 } 140 141 /// <summary> 142 /// Sets the value of the specified custom metadata. 143 /// </summary> 144 /// <owner>SumedhK</owner> 145 /// <param name="metadataName"></param> 146 /// <param name="metadataValue"></param> SetMetadata(string metadataName, string metadataValue)147 public void SetMetadata(string metadataName, string metadataValue) 148 { 149 ErrorUtilities.VerifyThrowArgumentLength(metadataName, "metadataName"); 150 ErrorUtilities.VerifyThrowArgumentNull(metadataValue, "metadataValue"); 151 152 item.SetMetadata(metadataName, EscapingUtilities.Escape(metadataValue)); 153 } 154 155 /// <summary> 156 /// Removes the specified custom metadata. 157 /// </summary> 158 /// <owner>SumedhK</owner> 159 /// <param name="metadataName"></param> RemoveMetadata(string metadataName)160 public void RemoveMetadata(string metadataName) 161 { 162 ErrorUtilities.VerifyThrowArgumentNull(metadataName, "metadataName"); 163 164 item.RemoveMetadata(metadataName); 165 } 166 167 /// <summary> 168 /// Copy the metadata (but not the ItemSpec or other built-in metadata) to destinationItem. If a particular metadata 169 /// already exists on the destination item, then it is not overwritten -- the original value wins. 170 /// </summary> 171 /// <owner>JomoF</owner> 172 /// <param name="destinationItem"></param> CopyMetadataTo( ITaskItem destinationItem )173 public void CopyMetadataTo 174 ( 175 ITaskItem destinationItem 176 ) 177 { 178 ErrorUtilities.VerifyThrowArgumentNull(destinationItem, "destinationItem"); 179 180 // Intentionally not _computed_ properties. These are slow and don't really 181 // apply anyway. 182 foreach (DictionaryEntry entry in item.GetAllCustomEvaluatedMetadata()) 183 { 184 string key = (string)entry.Key; 185 186 string destinationValue = destinationItem.GetMetadata(key); 187 188 if ((destinationValue == null) || (destinationValue.Length == 0)) 189 { 190 destinationItem.SetMetadata(key, EscapingUtilities.UnescapeAll((string)entry.Value)); 191 } 192 } 193 194 // also copy the original item-spec under a "magic" metadata -- this is useful for tasks that forward metadata 195 // between items, and need to know the source item where the metadata came from 196 string originalItemSpec = destinationItem.GetMetadata("OriginalItemSpec"); 197 198 if ((originalItemSpec == null) || (originalItemSpec.Length == 0)) 199 { 200 destinationItem.SetMetadata("OriginalItemSpec", ItemSpec); 201 } 202 } 203 204 205 /// <summary> 206 /// Get the collection of metadata. This does not include built-in metadata. 207 /// </summary> 208 /// <remarks> 209 /// RECOMMENDED GUIDELINES FOR METHOD IMPLEMENTATIONS: 210 /// 1) this method should return a clone of the metadata 211 /// 2) writing to this dictionary should not be reflected in the underlying item. 212 /// </remarks> 213 /// <owner>JomoF</owner> CloneCustomMetadata()214 public IDictionary CloneCustomMetadata() 215 { 216 IDictionary backingItemMetadata = item.CloneCustomMetadata(); 217 218 // Go through and escape the metadata as necessary. 219 string[] keys = new string[backingItemMetadata.Count]; 220 backingItemMetadata.Keys.CopyTo(keys, 0); 221 foreach (string singleMetadataName in keys) 222 { 223 string singleMetadataValue = (string) backingItemMetadata[singleMetadataName]; 224 225 bool unescapingWasNecessary; 226 string singleMetadataValueUnescaped = EscapingUtilities.UnescapeAll(singleMetadataValue, out unescapingWasNecessary); 227 228 // It's very important for perf not to touch this IDictionary unless we really need to. Touching 229 // it in any way causes it to get cloned (in the implementation of CopyOnWriteHashtable). 230 if (unescapingWasNecessary) 231 { 232 backingItemMetadata[singleMetadataName] = singleMetadataValueUnescaped; 233 } 234 } 235 236 return backingItemMetadata; 237 } 238 239 /// <summary> 240 /// Produce a string representation. 241 /// </summary> 242 /// <owner>JomoF</owner> ToString()243 public override string ToString() 244 { 245 return ItemSpec; 246 } 247 248 /// <summary> 249 /// Overriden to give this class infinite lease time. Otherwise we end up with a limited 250 /// lease (5 minutes I think) and instances can expire if they take long time processing. 251 /// </summary> 252 [SecurityCritical] InitializeLifetimeService()253 public override object InitializeLifetimeService() 254 { 255 // null means infinite lease time 256 return null; 257 } 258 259 // the backing item 260 internal BuildItem item; 261 262 #region Operators 263 264 /// <summary> 265 /// This allows an explicit typecast from a "TaskItem" to a "string", returning the ItemSpec for this item. 266 /// </summary> 267 /// <owner>RGoel</owner> 268 /// <param name="taskItemToCast">The item to operate on.</param> 269 /// <returns>The item-spec of the item.</returns> operator string( TaskItem taskItemToCast )270 public static explicit operator string 271 ( 272 TaskItem taskItemToCast 273 ) 274 { 275 return taskItemToCast.ItemSpec; 276 } 277 278 #endregion 279 } 280 } 281