/// <summary> /// Changes the item type of this item. /// Until reevaluation puts it in the correct place, it will be placed at /// the end of the list of items of its new type. /// </summary> /// <remarks> /// This is a little involved, as it requires replacing /// the XmlElement, and updating the project's datastructures. /// </remarks> internal void ChangeItemType(string newItemType) { ErrorUtilities.VerifyThrowArgumentLength(newItemType, "ItemType"); Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent != null && _xml.Parent.Parent != null, "OM_ObjectIsNoLongerActive"); if (String.Equals(ItemType, newItemType, StringComparison.Ordinal)) { return; } _project.SplitItemElementIfNecessary(_xml); _project.RemoveItemBeforeItemTypeChange(this); // xml.ChangeItemType will throw if new item type is invalid. Make sure we re-add the item anyway try { _xml.ChangeItemType(newItemType); } finally { _project.ReAddExistingItemAfterItemTypeChange(this); } }
/// <summary> /// Renames the item. /// Equivalent to setting the <see cref="UnevaluatedInclude"/> value. /// Generally, no expansion occurs. This is because it would potentially result in several items, /// which is not meaningful semantics when renaming a single item. /// However if the item does not need to be split (which would invalidate its ProjectItemElement), /// and the new value expands to exactly one item, then its evaluated include is updated /// with the expanded value, rather than the unexpanded value. /// </summary> /// <remarks> /// Even if the new value expands to zero items, we do not expand it. /// The common case we are interested in for expansion here is setting something /// like "$(sourcesroot)\foo.cs" and expanding that to a single item. /// If say "@(foo)" is set as the new name, and it expands to blank, that might /// be surprising to the host and maybe even unhandled, if on full reevaluation /// it wouldn’t expand to blank. That’s why we're being cautious and supporting /// the most common scenario only. /// Many hosts will do a ReevaluateIfNecessary before reading anyway. /// </remarks> public void Rename(string name) { Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent != null && _xml.Parent.Parent != null, "OM_ObjectIsNoLongerActive"); if (String.Equals(UnevaluatedInclude, name, StringComparison.Ordinal)) { return; } _fullPath = null; // Clear cached value if (_xml.Count == 0 /* no metadata */ && _project.IsSuitableExistingItemXml(_xml, name, null /* no metadata */) && !FileMatcher.HasWildcardsSemicolonItemOrPropertyReferences(name)) { _evaluatedIncludeEscaped = name; // Fast item lookup tables are invalid now. // Make sure that when the caller invokes ReevaluateIfNecessary() that they'll be refreshed. Project.MarkDirty(); return; } bool splitOccurred = _project.SplitItemElementIfNecessary(_xml); _xml.Include = name; if (splitOccurred) { _evaluatedIncludeEscaped = name; } else { _evaluatedIncludeEscaped = _project.ExpandItemIncludeBestEffortLeaveEscaped(_xml); } }
/// <summary> /// Removes any metadata with the specified name. /// Returns true if the evaluated metadata existed, otherwise false. /// If the metadata name is one of the built-in metadata, like "FullPath", throws InvalidArgumentException. /// If the metadata originates in an item definition, and was not overridden, throws InvalidOperationException. /// </summary> public bool RemoveMetadata(string name) { ErrorUtilities.VerifyThrowArgumentLength(name, "name"); ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent != null && _xml.Parent.Parent != null, "OM_ObjectIsNoLongerActive"); ProjectMetadata metadatum = (_directMetadata == null) ? null : _directMetadata[name]; if (metadatum == null) { ProjectMetadata itemDefinitionMetadata = GetItemDefinitionMetadata(name); ErrorUtilities.VerifyThrowInvalidOperation(itemDefinitionMetadata == null, "OM_CannotRemoveMetadataOriginatingFromItemDefinition", name); return(false); } _project.SplitItemElementIfNecessary(_xml); // New metadata objects may have been created metadatum = _directMetadata[name]; _xml.RemoveChild(metadatum.Xml); _directMetadata.Remove(name); return(true); }
private ProjectMetadata SetMetadataOperation(string name, string unevaluatedValue, bool propagateMetadataToSiblingItems) { Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); XmlUtilities.VerifyThrowArgumentValidElementName(name); ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent != null && _xml.Parent.Parent != null, "OM_ObjectIsNoLongerActive"); if (!propagateMetadataToSiblingItems) { _project.SplitItemElementIfNecessary(_xml); } ProjectMetadata metadatum; if (_directMetadata != null && _directMetadata.Contains(name)) { metadatum = _directMetadata[name]; metadatum.UnevaluatedValue = unevaluatedValue; } else { ProjectMetadataElement metadatumXml = _xml.AddMetadata(name, unevaluatedValue); string evaluatedValueEscaped = _project.ExpandMetadataValueBestEffortLeaveEscaped(this, unevaluatedValue, metadatumXml.Location); metadatum = new ProjectMetadata(this, metadatumXml, evaluatedValueEscaped, null /* predecessor unknown */); } if (!propagateMetadataToSiblingItems) { _directMetadata = _directMetadata ?? new PropertyDictionary <ProjectMetadata>(); _directMetadata.Set(metadatum); } else { var siblingItems = _project.Items.Where(i => i._xml == _xml); foreach (var siblingItem in siblingItems) { siblingItem._directMetadata = siblingItem._directMetadata ?? new PropertyDictionary <ProjectMetadata>(); siblingItem._directMetadata.Set(metadatum.DeepClone()); } } return(metadatum); }
/// <summary> /// Sets a new metadata value on the ItemDefinition. /// </summary> /// <remarks>Unevaluated value is assumed to be escaped as necessary</remarks> public ProjectMetadata SetMetadataValue(string name, string unevaluatedValue) { if (Link != null) { return(Link.SetMetadataValue(name, unevaluatedValue)); } XmlUtilities.VerifyThrowArgumentValidElementName(name); ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name); ProjectMetadata metadatum; if (_metadata != null) { metadatum = _metadata[name]; if (metadatum != null) { Project.VerifyThrowInvalidOperationNotImported(metadatum.Xml.ContainingProject); metadatum.UnevaluatedValue = unevaluatedValue; return(metadatum); } } // We can't use the item definition that this object came from as a root, as it doesn't map directly // to a single XML element. Instead, add a new one to the project. Best we can do. ProjectItemDefinitionElement itemDefinition = _project.Xml.AddItemDefinition(_itemType); ProjectMetadataElement metadatumXml = itemDefinition.AddMetadata(name, unevaluatedValue); _metadata = _metadata ?? new PropertyDictionary <ProjectMetadata>(); string evaluatedValueEscaped = _project.ExpandMetadataValueBestEffortLeaveEscaped(this, unevaluatedValue, metadatumXml.Location); metadatum = new ProjectMetadata(this, metadatumXml, evaluatedValueEscaped, null /* predecessor unknown */); _metadata.Set(metadatum); return(metadatum); }
/// <summary> /// Adds metadata with the specified name and value to the item. /// Updates an existing metadata if one already exists with the same name on the item directly, as opposed to inherited from an item definition. /// Updates the evaluated project, but does not affect anything else in the project until reevaluation. For example, /// if a piece of metadata named "m" is added on item of type "i", it does not affect "j" which is evaluated from "@(j->'%(m)')" until reevaluation. /// Also if the unevaluated value of "m" is set to something that is modified by evaluation, such as "$(p)", the evaluated value will be set to literally "$(p)" until reevaluation. /// This is a convenience that it is understood does not necessarily leave the project in a perfectly self consistent state. /// Returns the new or existing metadatum. /// </summary> /// <remarks>Unevaluated value is assumed to be escaped as necessary</remarks> public ProjectMetadata SetMetadataValue(string name, string unevaluatedValue) { Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); XmlUtilities.VerifyThrowArgumentValidElementName(name); ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(XMakeElements.IllegalItemPropertyNames[name] == null, "CannotModifyReservedItemMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent != null && _xml.Parent.Parent != null, "OM_ObjectIsNoLongerActive"); _project.SplitItemElementIfNecessary(_xml); ProjectMetadata metadatum; if (_directMetadata != null) { metadatum = _directMetadata[name]; if (metadatum != null) { metadatum.UnevaluatedValue = unevaluatedValue; return(metadatum); } } ProjectMetadataElement metadatumXml = _xml.AddMetadata(name, unevaluatedValue); _directMetadata = _directMetadata ?? new PropertyDictionary <ProjectMetadata>(); string evaluatedValueEscaped = _project.ExpandMetadataValueBestEffortLeaveEscaped(this, unevaluatedValue, metadatumXml.Location); metadatum = new ProjectMetadata(this, metadatumXml, evaluatedValueEscaped, null /* predecessor unknown */); _directMetadata.Set(metadatum); return(metadatum); }