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