|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// THE ASSEMBLY BUILT FROM THIS SOURCE FILE HAS BEEN DEPRECATED FOR YEARS. IT IS BUILT ONLY TO PROVIDE
// BACKWARD COMPATIBILITY FOR API USERS WHO HAVE NOT YET MOVED TO UPDATED APIS. PLEASE DO NOT SEND PULL
// REQUESTS THAT CHANGE THIS FILE WITHOUT FIRST CHECKING WITH THE MAINTAINERS THAT THE FIX IS REQUIRED.
using System;
using System.Collections.Generic;
using System.Xml;
using Microsoft.Build.BuildEngine.Shared;
using MetadataDictionary = System.Collections.Generic.Dictionary<string, string>;
using ItemDefinitionsDictionary = System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, string>>;
namespace Microsoft.Build.BuildEngine
{
/// <summary>
/// A library of default metadata values by item type.
/// Projects each have exactly one of these.
/// BuildItems consult the appropriate library to check
/// for default metadata values when they do not have an
/// explicit value set.
/// </summary>
internal class ItemDefinitionLibrary
{
#region Fields
private Project parentProject;
private List<ItemDefinitionLibrary.BuildItemDefinitionGroupXml> itemDefinitions;
private ItemDefinitionsDictionary itemDefinitionsDictionary;
private bool evaluated;
#endregion
#region Constructors
/// <summary>
/// Creates a new item definition library.
/// The project is required only to give error context.
/// </summary>
internal ItemDefinitionLibrary(Project parentProject)
{
this.parentProject = parentProject;
this.itemDefinitions = new List<BuildItemDefinitionGroupXml>();
this.itemDefinitionsDictionary = new ItemDefinitionsDictionary(StringComparer.OrdinalIgnoreCase);
}
#endregion
#region Properties
internal bool IsEvaluated
{
get { return evaluated; }
}
#endregion
#region Methods
/// <summary>
/// Create a BuildItemDefinitionGroupXml element and add it to the end of our ordered list.
/// </summary>
/// <exception cref="InvalidProjectFileException">If element does not represent a valid ItemDefinitionGroup element</exception>
internal void Add(XmlElement element)
{
BuildItemDefinitionGroupXml itemDefinitionGroupXml = new BuildItemDefinitionGroupXml(element, parentProject);
itemDefinitions.Add(itemDefinitionGroupXml);
evaluated = false;
}
/// <summary>
/// Go through each <BuildItemDefinition> element in order, evaluating it using the
/// supplied properties and any previously evaluated definitions, to build up a complete
/// library of item types and their default metadata values.
/// </summary>
internal void Evaluate(BuildPropertyGroup evaluatedProperties)
{
// Clear out previous data first
itemDefinitionsDictionary.Clear();
foreach (BuildItemDefinitionGroupXml itemDefinitionGroupXml in itemDefinitions)
{
itemDefinitionGroupXml.Evaluate(evaluatedProperties, itemDefinitionsDictionary);
}
evaluated = true;
}
/// <summary>
/// Returns any default metadata value for the specified item type and metadata name.
/// If no default exists, returns null.
/// </summary>
internal string GetDefaultMetadataValue(string itemType, string metadataName)
{
MustBeEvaluated();
string value = null;
MetadataDictionary metadataDictionary;
if (itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary))
{
metadataDictionary.TryGetValue(metadataName, out value);
}
return value;
}
/// <summary>
/// Count of default metadata for the specified item type
/// </summary>
internal int GetDefaultedMetadataCount(string itemType)
{
MustBeEvaluated();
MetadataDictionary metadataDictionary;
if (itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary))
{
return metadataDictionary.Count;
}
return 0;
}
/// <summary>
/// Names of metadata that have defaults for the specified item type.
/// Null if there are none.
/// </summary>
internal ICollection<string> GetDefaultedMetadataNames(string itemType)
{
MustBeEvaluated();
MetadataDictionary metadataDictionary = GetDefaultedMetadata(itemType);
if (metadataDictionary != null)
{
return metadataDictionary.Keys;
}
return null;
}
/// <summary>
/// All default metadata names and values for the specified item type.
/// Null if there are none.
/// </summary>
internal MetadataDictionary GetDefaultedMetadata(string itemType)
{
MustBeEvaluated();
MetadataDictionary metadataDictionary;
if (itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary))
{
return metadataDictionary;
}
return null;
}
/// <summary>
/// Verify this library has already been evaluated
/// </summary>
private void MustBeEvaluated()
{
ErrorUtilities.VerifyThrowNoAssert(evaluated, "Must be evaluated to query");
}
#endregion
/// <summary>
/// Encapsulates an <ItemDefinitionGroup> tag.
/// </summary>
/// <remarks>
/// Only used by ItemDefinitionLibrary -- private and nested inside it as no other class should know about this.
/// Since at present this has no OM or editing support, and is not passed around,
/// there are currently no separate classes for the child tags, and no separate BuildItemDefinitionGroup class.
/// They can be broken out in future if necessary.
/// </remarks>
private class BuildItemDefinitionGroupXml
{
#region Fields
private XmlElement element;
private Project parentProject;
private XmlAttribute conditionAttribute;
private string condition;
#endregion
#region Constructors
/// <summary>
/// Read in and validate an <ItemDefinitionGroup> element and all its children.
/// This is currently only called from ItemDefinitionLibrary. Projects don't know about it.
/// </summary>
internal BuildItemDefinitionGroupXml(XmlElement element, Project parentProject)
{
ProjectXmlUtilities.VerifyThrowElementName(element, XMakeElements.itemDefinitionGroup);
ProjectXmlUtilities.VerifyThrowProjectValidNamespace(element);
this.element = element;
this.parentProject = parentProject;
this.conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(element, /* sole attribute */ true);
this.condition = ProjectXmlUtilities.GetAttributeValue(conditionAttribute);
// Currently, don't bother validating the children until evaluation time
}
#endregion
#region Public Methods
/// <summary>
/// Given the properties and dictionary of previously encountered item definitions, evaluates
/// this group of item definitions and adds to the dictionary as necessary.
/// </summary>
/// <exception cref="InvalidProjectFileException">If the item definitions are incorrectly defined</exception>
internal void Evaluate(BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary)
{
Expander expander = new Expander(properties);
if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowProperties, parentProject))
{
return;
}
List<XmlElement> childElements = ProjectXmlUtilities.GetValidChildElements(element);
foreach (XmlElement child in childElements)
{
EvaluateItemDefinitionElement(child, properties, itemDefinitionsDictionary);
}
}
/// <summary>
/// Given the properties and dictionary of previously encountered item definitions, evaluates
/// this specific item definition element and adds to the dictionary as necessary.
/// </summary>
/// <exception cref="InvalidProjectFileException">If the item definition is incorrectly defined</exception>
private void EvaluateItemDefinitionElement(XmlElement itemDefinitionElement, BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary)
{
ProjectXmlUtilities.VerifyThrowProjectValidNameAndNamespace(itemDefinitionElement);
XmlAttribute conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(itemDefinitionElement, /* sole attribute */ true);
string condition = ProjectXmlUtilities.GetAttributeValue(conditionAttribute);
string itemType = itemDefinitionElement.Name;
MetadataDictionary metadataDictionary;
itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary);
Expander expander = new Expander(properties, itemType, metadataDictionary);
if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowPropertiesAndItemMetadata, parentProject))
{
return;
}
List<XmlElement> childElements = ProjectXmlUtilities.GetValidChildElements(itemDefinitionElement);
foreach (XmlElement child in childElements)
{
EvaluateItemDefinitionChildElement(child, properties, itemDefinitionsDictionary);
}
}
/// <summary>
/// Given the properties and dictionary of previously encountered item definitions, evaluates
/// this specific item definition child element and adds to the dictionary as necessary.
/// </summary>
/// <exception cref="InvalidProjectFileException">If the item definition is incorrectly defined</exception>
private void EvaluateItemDefinitionChildElement(XmlElement itemDefinitionChildElement, BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary)
{
ProjectXmlUtilities.VerifyThrowProjectValidNameAndNamespace(itemDefinitionChildElement);
ProjectErrorUtilities.VerifyThrowInvalidProject(!FileUtilities.IsItemSpecModifier(itemDefinitionChildElement.Name), itemDefinitionChildElement, "ItemSpecModifierCannotBeCustomMetadata", itemDefinitionChildElement.Name);
ProjectErrorUtilities.VerifyThrowInvalidProject(XMakeElements.IllegalItemPropertyNames[itemDefinitionChildElement.Name] == null, itemDefinitionChildElement, "CannotModifyReservedItemMetadata", itemDefinitionChildElement.Name);
XmlAttribute conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(itemDefinitionChildElement, /* sole attribute */ true);
string condition = ProjectXmlUtilities.GetAttributeValue(conditionAttribute);
string itemType = itemDefinitionChildElement.ParentNode.Name;
MetadataDictionary metadataDictionary;
itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary);
Expander expander = new Expander(properties, itemType, metadataDictionary);
if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowPropertiesAndItemMetadata, parentProject))
{
return;
}
string unevaluatedMetadataValue = Utilities.GetXmlNodeInnerContents(itemDefinitionChildElement);
bool containsItemVector = ItemExpander.ExpressionContainsItemVector(unevaluatedMetadataValue);
// We don't allow expressions like @(foo) in the value, as no items exist at this point.
ProjectErrorUtilities.VerifyThrowInvalidProject(!containsItemVector, itemDefinitionChildElement, "MetadataDefinitionCannotContainItemVectorExpression", unevaluatedMetadataValue, itemDefinitionChildElement.Name);
string evaluatedMetadataValue = expander.ExpandAllIntoStringLeaveEscaped(unevaluatedMetadataValue, itemDefinitionChildElement);
if (metadataDictionary == null)
{
metadataDictionary = new MetadataDictionary(StringComparer.OrdinalIgnoreCase);
itemDefinitionsDictionary.Add(itemType, metadataDictionary);
}
// We only store the evaluated value; build items store the unevaluated value as well, but apparently only to
// gather recursive portions (its re-evaluation always goes back to the XML).
// Overwrite any existing default value for this particular metadata
metadataDictionary[itemDefinitionChildElement.Name] = evaluatedMetadataValue;
}
#endregion
}
}
#region Related Types
/// <summary>
/// A limited read-only wrapper around an item definition library,
/// specific to a particular item type.
/// </summary>
internal class SpecificItemDefinitionLibrary
{
private string itemType;
private ItemDefinitionLibrary itemDefinitionLibrary;
/// <summary>
/// Constructor
/// </summary>
internal SpecificItemDefinitionLibrary(string itemType, ItemDefinitionLibrary itemDefinitionLibrary)
{
this.itemType = itemType;
this.itemDefinitionLibrary = itemDefinitionLibrary;
}
/// <summary>
/// Returns the item type for which this library is specific.
/// </summary>
internal string ItemType
{
get { return itemType; }
}
/// <summary>
/// Get the default if any for the specified metadata name.
/// Returns null if there is none.
/// </summary>
internal string GetDefaultMetadataValue(string metadataName)
{
return itemDefinitionLibrary.GetDefaultMetadataValue(itemType, metadataName);
}
}
#endregion
}
|