internal static ProjectItemGroupTaskItemInstance FactoryForDeserialization(ITranslator translator)
        {
            var instance = new ProjectItemGroupTaskItemInstance();

            ((ITranslatable)instance).Translate(translator);

            return(instance);
        }
示例#2
0
 /// <summary>
 /// Cloning constructor
 /// </summary>
 private ProjectItemGroupTaskItemInstance(ProjectItemGroupTaskItemInstance that)
 {
     // All fields are immutable
     _itemType       = that._itemType;
     _include        = that._include;
     _exclude        = that._exclude;
     _remove         = that._remove;
     _keepMetadata   = that._keepMetadata;
     _removeMetadata = that._removeMetadata;
     _keepDuplicates = that._keepDuplicates;
     _condition      = that._condition;
     _metadata       = that._metadata;
 }
        /// <summary>
        /// Takes an item specification, evaluates it and expands it into a list of items
        /// </summary>
        /// <param name="originalItem">The original item data</param>
        /// <param name="expander">The expander to use.</param>
        /// <remarks>
        /// This code is very close to that which exists in the Evaluator.EvaluateItemXml method.  However, because
        /// it invokes type constructors, and those constructors take arguments of fundamentally different types, it has not
        /// been refactored.
        /// </remarks>
        /// <returns>A list of items.</returns>
        private IList<ProjectItemInstance> ExpandItemIntoItems
        (
            ProjectItemGroupTaskItemInstance originalItem,
            Expander<ProjectPropertyInstance, ProjectItemInstance> expander,
            ISet<string> keepMetadata,
            ISet<string> removeMetadata
        )
        {
            ProjectErrorUtilities.VerifyThrowInvalidProject(!(keepMetadata != null && removeMetadata != null), originalItem.KeepMetadataLocation, "KeepAndRemoveMetadataMutuallyExclusive");
            IList<ProjectItemInstance> items = new List<ProjectItemInstance>();

            // UNDONE: (Refactor)  This code also exists in largely the same form in Evaluator.CreateItemsFromInclude.
            // STEP 1: Expand properties and metadata in Include
            string evaluatedInclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Include, ExpanderOptions.ExpandPropertiesAndMetadata, originalItem.IncludeLocation);

            // STEP 2: Split Include on any semicolons, and take each split in turn
            if (evaluatedInclude.Length > 0)
            {
                IList<string> includeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedInclude);
                ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(this.Project, originalItem.ItemType);

                foreach (string includeSplit in includeSplits)
                {
                    // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                    bool throwaway;

                    IList<ProjectItemInstance> itemsFromSplit = expander.ExpandSingleItemVectorExpressionIntoItems(includeSplit, itemFactory, ExpanderOptions.ExpandItems, false /* do not include null expansion results */, out throwaway, originalItem.IncludeLocation);

                    if (itemsFromSplit != null)
                    {
                        // Expression is in form "@(X)", so add these items directly.
                        foreach (ProjectItemInstance item in itemsFromSplit)
                        {
                            items.Add(item);
                        }
                    }
                    else
                    {
                        // The expression is not of the form "@(X)". Treat as string
                        string[] includeSplitFiles = EngineFileUtilities.GetFileListEscaped(Project.Directory, includeSplit);

                        foreach (string includeSplitFile in includeSplitFiles)
                        {
                            items.Add(new ProjectItemInstance(Project, originalItem.ItemType, includeSplitFile, includeSplit /* before wildcard expansion */, null, null, originalItem.Location.File));
                        }
                    }
                }

                // STEP 4: Evaluate, split, expand and subtract any Exclude
                if (originalItem.Exclude.Length > 0)
                {
                    string evaluatedExclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Exclude, ExpanderOptions.ExpandAll, originalItem.ExcludeLocation);

                    if (evaluatedExclude.Length > 0)
                    {
                        IList<string> excludeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedExclude);
                        HashSet<string> excludesUnescapedForComparison = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

                        foreach (string excludeSplit in excludeSplits)
                        {
                            string[] excludeSplitFiles = EngineFileUtilities.GetFileListUnescaped(Project.Directory, excludeSplit);

                            foreach (string excludeSplitFile in excludeSplitFiles)
                            {
                                excludesUnescapedForComparison.Add(excludeSplitFile);
                            }
                        }

                        List<ProjectItemInstance> remainingItems = new List<ProjectItemInstance>();

                        for (int i = 0; i < items.Count; i++)
                        {
                            if (!excludesUnescapedForComparison.Contains(((IItem)items[i]).EvaluatedInclude))
                            {
                                remainingItems.Add(items[i]);
                            }
                        }

                        items = remainingItems;
                    }
                }
            }

            // Filter the metadata as appropriate
            if (keepMetadata != null)
            {
                foreach (var item in items)
                {
                    var metadataToRemove = item.MetadataNames.Where(name => !keepMetadata.Contains(name));
                    foreach (var metadataName in metadataToRemove)
                    {
                        item.RemoveMetadata(metadataName);
                    }
                }
            }
            else if (removeMetadata != null)
            {
                foreach (var item in items)
                {
                    var metadataToRemove = item.MetadataNames.Where(name => removeMetadata.Contains(name));
                    foreach (var metadataName in metadataToRemove)
                    {
                        item.RemoveMetadata(metadataName);
                    }
                }
            }

            return items;
        }
        /// <summary>
        /// Adds batchable parameters from an item element into the list. If the item element was a task, these
        /// would be its raw parameter values.
        /// </summary>
        /// <param name="parameterValues">The list of batchable values</param>
        /// <param name="child">The item from which to find batchable values</param>
        private void GetBatchableValuesFromBuildItemGroupChild(List<string> parameterValues, ProjectItemGroupTaskItemInstance child)
        {
            AddIfNotEmptyString(parameterValues, child.Include);
            AddIfNotEmptyString(parameterValues, child.Exclude);
            AddIfNotEmptyString(parameterValues, child.Remove);
            AddIfNotEmptyString(parameterValues, child.Condition);

            foreach (ProjectItemGroupTaskMetadataInstance metadataElement in child.Metadata)
            {
                AddIfNotEmptyString(parameterValues, metadataElement.Value);
                AddIfNotEmptyString(parameterValues, metadataElement.Condition);
            }
        }
        /// <summary>
        /// Modifies items in the world - specifically, changes their metadata. Changes to items that are part of the project manifest are backed up, so 
        /// they can be reverted when the project is reset after the end of the build.
        /// </summary>
        /// <param name="child">The item specification to evaluate and modify.</param>
        /// <param name="bucket">The batching bucket.</param>
        private void ExecuteModify(ProjectItemGroupTaskItemInstance child, ItemBucket bucket, ISet<string> keepMetadata, ISet<string> removeMetadata)
        {
            ICollection<ProjectItemInstance> group = bucket.Lookup.GetItems(child.ItemType);
            if (group == null || group.Count == 0)
            {
                // No items of this type to modify
                return;
            }

            // Figure out what metadata names and values we need to set
            var metadataToSet = new Lookup.MetadataModifications(keepMetadata != null);

            // Filter the metadata as appropriate
            if (keepMetadata != null)
            {
                foreach (var metadataName in keepMetadata)
                {
                    metadataToSet[metadataName] = Lookup.MetadataModification.CreateFromNoChange();
                }
            }
            else if (removeMetadata != null)
            {
                foreach (var metadataName in removeMetadata)
                {
                    metadataToSet[metadataName] = Lookup.MetadataModification.CreateFromRemove();
                }
            }

            foreach (ProjectItemGroupTaskMetadataInstance metadataInstance in child.Metadata)
            {
                bool condition = ConditionEvaluator.EvaluateCondition
                    (
                    metadataInstance.Condition,
                    ParserOptions.AllowAll,
                    bucket.Expander,
                    ExpanderOptions.ExpandAll,
                    Project.Directory,
                    metadataInstance.ConditionLocation,
                    LoggingContext.LoggingService,
                    LoggingContext.BuildEventContext
                    );

                if (condition)
                {
                    string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, ExpanderOptions.ExpandAll, metadataInstance.Location);
                    metadataToSet[metadataInstance.Name] = Lookup.MetadataModification.CreateFromNewValue(evaluatedValue);
                }
            }

            // Now apply the changes.  This must be done after filtering, since explicitly set metadata overrides filters.
            bucket.Lookup.ModifyItems(child.ItemType, group, metadataToSet);
        }
        /// <summary>
        /// Remove items from the world. Removes to items that are part of the project manifest are backed up, so 
        /// they can be reverted when the project is reset after the end of the build.
        /// </summary>
        /// <param name="child">The item specification to evaluate and remove.</param>
        /// <param name="bucket">The batching bucket.</param>
        private void ExecuteRemove(ProjectItemGroupTaskItemInstance child, ItemBucket bucket)
        {
            ICollection<ProjectItemInstance> group = bucket.Lookup.GetItems(child.ItemType);
            if (group == null)
            {
                // No items of this type to remove
                return;
            }

            List<ProjectItemInstance> itemsToRemove = FindItemsMatchingSpecification(group, child.Remove, child.RemoveLocation, bucket.Expander);

            if (itemsToRemove != null)
            {
                if (LogTaskInputs && !LoggingContext.LoggingService.OnlyLogCriticalEvents && itemsToRemove.Count > 0)
                {
                    var itemGroupText = ItemGroupLoggingHelper.GetParameterText(ResourceUtilities.GetResourceString("ItemGroupRemoveLogMessage"), child.ItemType, itemsToRemove.ToArray());
                    LoggingContext.LogCommentFromText(MessageImportance.Low, itemGroupText);
                }

                bucket.Lookup.RemoveItems(itemsToRemove);
            }
        }
        /// <summary>
        /// Add items to the world. This is the in-target equivalent of an item include expression outside of a target.
        /// </summary>
        /// <param name="child">The item specification to evaluate and add.</param>
        /// <param name="bucket">The batching bucket.</param>
        private void ExecuteAdd(ProjectItemGroupTaskItemInstance child, ItemBucket bucket, ISet<string> keepMetadata, ISet<string> removeMetadata)
        {
            // First, collect up the appropriate metadata collections.  We need the one from the item definition, if any, and
            // the one we are using for this batching bucket.
            ProjectItemDefinitionInstance itemDefinition = null;
            Project.ItemDefinitions.TryGetValue(child.ItemType, out itemDefinition);

            // The NestedMetadataTable will handle the aggregation of the different metadata collections
            NestedMetadataTable metadataTable = new NestedMetadataTable(child.ItemType, bucket.Expander.Metadata, itemDefinition);
            IMetadataTable originalMetadataTable = bucket.Expander.Metadata;

            bucket.Expander.Metadata = metadataTable;

            // Second, expand the item include and exclude, and filter existing metadata as appropriate.
            IList<ProjectItemInstance> itemsToAdd = ExpandItemIntoItems(child, bucket.Expander, keepMetadata, removeMetadata);

            // Third, expand the metadata.           
            foreach (ProjectItemGroupTaskMetadataInstance metadataInstance in child.Metadata)
            {
                bool condition = ConditionEvaluator.EvaluateCondition
                    (
                    metadataInstance.Condition,
                    ParserOptions.AllowAll,
                    bucket.Expander,
                    ExpanderOptions.ExpandAll,
                    Project.Directory,
                    metadataInstance.Location,
                    LoggingContext.LoggingService,
                    LoggingContext.BuildEventContext
                    );

                if (condition)
                {
                    string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, ExpanderOptions.ExpandAll, metadataInstance.Location);

                    // This both stores the metadata so we can add it to all the items we just created later, and 
                    // exposes this metadata to further metadata evaluations in subsequent loop iterations.
                    metadataTable.SetValue(metadataInstance.Name, evaluatedValue);
                }
            }

            // Finally, copy the added metadata onto the new items.  The set call is additive.
            ProjectItemInstance.SetMetadata(metadataTable.AddedMetadata, itemsToAdd); // Add in one operation for potential copy-on-write

            // Restore the original metadata table.
            bucket.Expander.Metadata = originalMetadataTable;

            // Determine if we should NOT add duplicate entries
            bool keepDuplicates = ConditionEvaluator.EvaluateCondition
                (
                child.KeepDuplicates,
                ParserOptions.AllowAll,
                bucket.Expander,
                ExpanderOptions.ExpandAll,
                Project.Directory,
                child.KeepDuplicatesLocation,
                LoggingContext.LoggingService,
                LoggingContext.BuildEventContext
                );

            if (LogTaskInputs && !LoggingContext.LoggingService.OnlyLogCriticalEvents && itemsToAdd != null && itemsToAdd.Count > 0)
            {
                var itemGroupText = ItemGroupLoggingHelper.GetParameterText(ResourceUtilities.GetResourceString("ItemGroupIncludeLogMessagePrefix"), child.ItemType, itemsToAdd.ToArray());
                LoggingContext.LogCommentFromText(MessageImportance.Low, itemGroupText);
            }

            // Now add the items we created to the lookup.
            bucket.Lookup.AddNewItemsOfItemType(child.ItemType, itemsToAdd, !keepDuplicates); // Add in one operation for potential copy-on-write
        }
 /// <summary>
 /// Cloning constructor
 /// </summary>
 private ProjectItemGroupTaskItemInstance(ProjectItemGroupTaskItemInstance that)
 {
     // All fields are immutable
     _itemType = that._itemType;
     _include = that._include;
     _exclude = that._exclude;
     _remove = that._remove;
     _keepMetadata = that._keepMetadata;
     _removeMetadata = that._removeMetadata;
     _keepDuplicates = that._keepDuplicates;
     _condition = that._condition;
     _metadata = that._metadata;
 }