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 // </copyright> 5 // <summary>Represents a set of item definitions all applying to the same item-type.</summary> 6 //----------------------------------------------------------------------- 7 8 using System.Diagnostics; 9 using System.Diagnostics.CodeAnalysis; 10 using Microsoft.Build.Collections; 11 using Microsoft.Build.Construction; 12 using Microsoft.Build.Evaluation; 13 using Microsoft.Build.Execution; 14 using Microsoft.Build.Shared; 15 using System.Collections.Generic; 16 using System; 17 using Microsoft.Build.BackEnd; 18 19 namespace Microsoft.Build.Execution 20 { 21 /// <summary> 22 /// An evaluated item definition for a particular item-type, divested of all references to XML. 23 /// Immutable. 24 /// </summary> 25 [DebuggerDisplay("{_itemType} #Metadata={MetadataCount}")] 26 public class ProjectItemDefinitionInstance : IKeyed, IMetadataTable, IItemDefinition<ProjectMetadataInstance>, INodePacketTranslatable 27 { 28 /// <summary> 29 /// Item type, for example "Compile", that this item definition applies to 30 /// </summary> 31 private string _itemType; 32 33 /// <summary> 34 /// Collection of metadata that link the XML metadata and instance metadata 35 /// Since evaluation has occurred, this is an unordered collection. 36 /// Is never null or empty. 37 /// </summary> 38 private CopyOnWritePropertyDictionary<ProjectMetadataInstance> _metadata; 39 40 /// <summary> 41 /// Constructs an empty project item definition instance. 42 /// </summary> 43 /// <param name="projectInstance">The project instance to which this item definition belongs.</param> 44 /// <param name="itemType">The type of item this definition object represents.</param> ProjectItemDefinitionInstance(ProjectInstance projectInstance, string itemType)45 internal ProjectItemDefinitionInstance(ProjectInstance projectInstance, string itemType) 46 { 47 ErrorUtilities.VerifyThrowArgumentNull(itemType, "itemType"); 48 49 _itemType = itemType; 50 } 51 52 /// <summary> 53 /// Called when a ProjectInstance is created. 54 /// </summary> 55 /// <remarks> 56 /// Assumes that the itemType string originated in a ProjectItemDefinitionElement and therefore 57 /// was already validated. 58 /// </remarks> ProjectItemDefinitionInstance(ProjectInstance projectInstance, ProjectItemDefinition itemDefinition)59 internal ProjectItemDefinitionInstance(ProjectInstance projectInstance, ProjectItemDefinition itemDefinition) 60 : this(projectInstance, itemDefinition.ItemType) 61 { 62 if (itemDefinition.MetadataCount > 0) 63 { 64 _metadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>(itemDefinition.MetadataCount); 65 } 66 67 foreach (ProjectMetadata originalMetadata in itemDefinition.Metadata) 68 { 69 _metadata.Set(new ProjectMetadataInstance(originalMetadata)); 70 } 71 } 72 ProjectItemDefinitionInstance()73 private ProjectItemDefinitionInstance() 74 { 75 } 76 77 /// <summary> 78 /// Type of this item definition. 79 /// </summary> 80 [DebuggerBrowsable(DebuggerBrowsableState.Never)] 81 public string ItemType 82 { 83 [DebuggerStepThrough] 84 get 85 { return _itemType; } 86 } 87 88 /// <summary> 89 /// Metadata on the item definition. 90 /// If there is no metadata, returns empty collection. 91 /// This is a read-only collection. 92 /// </summary> 93 [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This is a reasonable choice. API review approved")] 94 public ICollection<ProjectMetadataInstance> Metadata 95 { 96 get 97 { 98 if (_metadata == null) 99 { 100 return ReadOnlyEmptyCollection<ProjectMetadataInstance>.Instance; 101 } 102 103 return new ReadOnlyCollection<ProjectMetadataInstance>(_metadata); 104 } 105 } 106 107 /// <summary> 108 /// Number of pieces of metadata on this item definition. 109 /// </summary> 110 public int MetadataCount 111 { 112 get { return (_metadata == null) ? 0 : _metadata.Count; } 113 } 114 115 /// <summary> 116 /// Names of all metadata on this item definition 117 /// </summary> 118 public IEnumerable<string> MetadataNames 119 { 120 get 121 { 122 if (_metadata == null) 123 { 124 yield break; 125 } 126 127 foreach (ProjectMetadataInstance metadatum in _metadata) 128 { 129 yield return metadatum.Name; 130 } 131 } 132 } 133 134 /// <summary> 135 /// Implementation of IKeyed exposing the item type, so these 136 /// can be put in a dictionary conveniently. 137 /// </summary> 138 [DebuggerBrowsable(DebuggerBrowsableState.Never)] 139 string IKeyed.Key 140 { 141 get { return ItemType; } 142 } 143 144 /// <summary> 145 /// Get any metadata in the item that has the specified name, 146 /// otherwise returns null 147 /// </summary> 148 [DebuggerStepThrough] GetMetadata(string name)149 public ProjectMetadataInstance GetMetadata(string name) 150 { 151 return (_metadata == null) ? null : _metadata[name]; 152 } 153 154 #region IMetadataTable Members 155 156 /// <summary> 157 /// Returns the specified metadata. 158 /// </summary> 159 /// <param name="name">The metadata name.</param> 160 /// <returns>The metadata value, or an empty string if none exists.</returns> IMetadataTable.GetEscapedValue(string name)161 string IMetadataTable.GetEscapedValue(string name) 162 { 163 return ((IMetadataTable)this).GetEscapedValue(null, name); 164 } 165 166 /// <summary> 167 /// Returns the metadata for the specified item type. 168 /// </summary> 169 /// <param name="specifiedItemType">The item type.</param> 170 /// <param name="name">The metadata name.</param> 171 /// <returns>The metadata value, or an empty string if none exists.</returns> IMetadataTable.GetEscapedValue(string specifiedItemType, string name)172 string IMetadataTable.GetEscapedValue(string specifiedItemType, string name) 173 { 174 return ((IMetadataTable)this).GetEscapedValueIfPresent(specifiedItemType, name) ?? String.Empty; 175 } 176 177 /// <summary> 178 /// Returns the metadata for the specified item type. 179 /// </summary> 180 /// <param name="specifiedItemType">The item type.</param> 181 /// <param name="name">The metadata name.</param> 182 /// <returns>The metadata value, or an null if none exists.</returns> IMetadataTable.GetEscapedValueIfPresent(string specifiedItemType, string name)183 string IMetadataTable.GetEscapedValueIfPresent(string specifiedItemType, string name) 184 { 185 if (specifiedItemType == null || String.Equals(_itemType, specifiedItemType, StringComparison.OrdinalIgnoreCase)) 186 { 187 ProjectMetadataInstance metadatum = GetMetadata(name); 188 if (metadatum != null) 189 { 190 return metadatum.EvaluatedValueEscaped; 191 } 192 } 193 194 return null; 195 } 196 197 #endregion 198 199 /// <summary> 200 /// Sets a new metadata value. Called by the evaluator only. 201 /// Discards predecessor as this information is only useful at design time. 202 /// </summary> SetMetadata(ProjectMetadataElement xml, string evaluatedValue, ProjectMetadataInstance predecessor)203 ProjectMetadataInstance IItemDefinition<ProjectMetadataInstance>.SetMetadata(ProjectMetadataElement xml, string evaluatedValue, ProjectMetadataInstance predecessor) 204 { 205 // No mutability check as this is used during creation (evaluation) 206 _metadata = _metadata ?? new CopyOnWritePropertyDictionary<ProjectMetadataInstance>(); 207 208 ProjectMetadataInstance metadatum = new ProjectMetadataInstance(xml.Name, evaluatedValue); 209 _metadata[xml.Name] = metadatum; 210 211 return metadatum; 212 } 213 214 /// <summary> 215 /// Creates a ProjectItemDefinitionElement representing this instance. 216 /// </summary> ToProjectItemDefinitionElement(ProjectElementContainer parent)217 internal ProjectItemDefinitionElement ToProjectItemDefinitionElement(ProjectElementContainer parent) 218 { 219 ProjectItemDefinitionElement element = parent.ContainingProject.CreateItemDefinitionElement(ItemType); 220 parent.AppendChild(element); 221 foreach (ProjectMetadataInstance metadataInstance in _metadata) 222 { 223 element.AddMetadata(metadataInstance.Name, metadataInstance.EvaluatedValue); 224 } 225 226 return element; 227 } 228 INodePacketTranslatable.Translate(INodePacketTranslator translator)229 void INodePacketTranslatable.Translate(INodePacketTranslator translator) 230 { 231 translator.Translate(ref _itemType); 232 translator.TranslateDictionary(ref _metadata, ProjectMetadataInstance.FactoryForDeserialization); 233 } 234 FactoryForDeserialization(INodePacketTranslator translator)235 internal static ProjectItemDefinitionInstance FactoryForDeserialization(INodePacketTranslator translator) 236 { 237 var instance = new ProjectItemDefinitionInstance(); 238 ((INodePacketTranslatable) instance).Translate(translator); 239 240 return instance; 241 } 242 } 243 } 244