/// <summary> /// Returns a list of all items in the provided item group whose itemspecs match the specification, after it is split and any wildcards are expanded. /// If not items match, returns null. /// </summary> internal static List <BuildItem> FindItemsMatchingSpecification(BuildItemGroup items, string specification, XmlAttribute attribute, Expander expander, string baseDirectory) { if (items.Count == 0 || specification.Length == 0) { return(null); } // This is a hashtable whose key is the filename for the individual items // in the Exclude list, after wildcard expansion. The value in the hash table // is just an empty string. Hashtable specificationsToFind = new Hashtable(StringComparer.OrdinalIgnoreCase); // Split by semicolons List <string> specificationPieces = expander.ExpandAllIntoStringListLeaveEscaped(specification, attribute); foreach (string piece in specificationPieces) { // Take each individual path or file expression, and expand any // wildcards. Then loop through each file returned, and add it // to our hashtable. // Don't unescape wildcards just yet - if there were any escaped, the caller wants to treat them // as literals. Everything else is safe to unescape at this point, since we're only matching // against the file system. string[] fileList = EngineFileUtilities.GetFileListEscaped(baseDirectory, piece); foreach (string file in fileList) { // Now unescape everything, because this is the end of the road for this filename. // We're just going to compare it to the unescaped include path to filter out the // file excludes. specificationsToFind[EscapingUtilities.UnescapeAll(file)] = String.Empty; } } if (specificationsToFind.Count == 0) { return(null); } // Now loop through our list and filter out any that match a // filename in the remove list. List <BuildItem> itemsRemoved = new List <BuildItem>(); foreach (BuildItem item in items) { // Even if the case for the excluded files is different, they // will still get excluded, as expected. However, if the excluded path // references the same file in a different way, such as by relative // path instead of absolute path, we will not realize that they refer // to the same file, and thus we will not exclude it. if (specificationsToFind.ContainsKey(item.FinalItemSpec)) { itemsRemoved.Add(item); } } return(itemsRemoved); }