public FileProviderGlobbingDirectory(
            IFileProvider fileProvider,
            IFileInfo fileInfo,
            FileProviderGlobbingDirectory parent)
        {
            if (fileProvider == null)
            {
                throw new ArgumentNullException(nameof(fileProvider));
            }

            _fileProvider = fileProvider;
            _fileInfo     = fileInfo;
            _parent       = parent;

            if (_fileInfo == null)
            {
                // We're the root of the directory tree
                RelativePath = string.Empty;
                _isRoot      = true;
            }
            else if (!string.IsNullOrEmpty(parent?.RelativePath))
            {
                // We have a parent and they have a relative path so concat that with my name
                RelativePath = _parent.RelativePath + DirectorySeparatorChar + _fileInfo.Name;
            }
            else
            {
                // We have a parent which is the root, so just use my name
                RelativePath = _fileInfo.Name;
            }
        }
        /// <summary>
        /// Apply build actions from the nuspec to items from the contentFiles folder.
        /// </summary>
        internal static List <LockFileContentFile> GetContentFileGroup(
            NuspecReader nuspec,
            List <ContentItemGroup> contentFileGroups)
        {
            var results = new List <LockFileContentFile>(contentFileGroups.Count);
            var rootFolderPathLength = ContentFilesFolderName.Length;

            // Read the contentFiles section of the nuspec
            // Read the entries so that the bottom entry has priority
            var nuspecContentFiles = nuspec.GetContentFiles().ToList();

            // Initialize mappings
            var entryMappings    = new Dictionary <string, List <ContentFilesEntry> >(StringComparer.OrdinalIgnoreCase);
            var languageMappings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            foreach (var group in contentFileGroups)
            {
                var codeLanguage = group.Properties[ManagedCodeConventions.PropertyNames.CodeLanguage] as string;

                foreach (var item in group.Items)
                {
                    if (!entryMappings.ContainsKey(item.Path))
                    {
                        entryMappings.Add(item.Path, new List <ContentFilesEntry>());
                        languageMappings.Add(item.Path, codeLanguage);
                    }
                }
            }

            // Virtual root for file globbing
            var rootDirectory = new VirtualFileInfo(VirtualFileProvider.RootDir, isDirectory: true);

            // Apply all nuspec property mappings to the files returned by content model
            foreach (var filesEntry in nuspecContentFiles)
            {
                // this is validated in the nuspec reader
                Debug.Assert(filesEntry.Include != null, "invalid contentFiles entry");

                // Create a filesystem matcher for globbing patterns
                var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
                matcher.AddInclude(filesEntry.Include);

                if (filesEntry.Exclude != null)
                {
                    matcher.AddExclude(filesEntry.Exclude);
                }

                // Check each file against the patterns
                foreach (var file in entryMappings.Keys)
                {
                    // Remove contentFiles/ from the string
                    Debug.Assert(file.StartsWith(ContentFilesFolderName, StringComparison.OrdinalIgnoreCase),
                                 "invalid file path: " + file);

                    // All files should begin with the same root folder
                    if (file.Length > rootFolderPathLength)
                    {
                        var relativePath = file.Substring(rootFolderPathLength, file.Length - rootFolderPathLength);

                        // Check if the nuspec group include/exclude patterns apply to the file
                        var virtualDirectory = new VirtualFileProvider(new List <string>()
                        {
                            relativePath
                        });
                        var globbingDirectory = new FileProviderGlobbingDirectory(
                            virtualDirectory,
                            fileInfo: rootDirectory,
                            parent: null);

                        // Currently Matcher only returns the file name not the full path, each file must be
                        // check individually.
                        var matchResults = matcher.Execute(globbingDirectory);

                        if (matchResults.Files.Any())
                        {
                            entryMappings[file].Add(filesEntry);
                        }
                    }
                }
            }

            // Create lock file entries for each item in the contentFiles folder
            foreach (var file in entryMappings.Keys)
            {
                // defaults
                var action       = BuildAction.Parse(PackagingConstants.ContentFilesDefaultBuildAction);
                var copyToOutput = false;
                var flatten      = false;

                // _._ is needed for empty codeLanguage groups
                if (file.EndsWith(PackagingCoreConstants.ForwardSlashEmptyFolder, StringComparison.Ordinal))
                {
                    action = BuildAction.None;
                }
                else
                {
                    // apply each entry
                    // entries may not have all the attributes, if a value is null
                    // ignore it and continue using the previous value.
                    foreach (var filesEntry in entryMappings[file])
                    {
                        if (!string.IsNullOrEmpty(filesEntry.BuildAction))
                        {
                            action = BuildAction.Parse(filesEntry.BuildAction);
                        }

                        if (filesEntry.CopyToOutput.HasValue)
                        {
                            copyToOutput = filesEntry.CopyToOutput.Value;
                        }

                        if (filesEntry.Flatten.HasValue)
                        {
                            flatten = filesEntry.Flatten.Value;
                        }
                    }
                }

                // Add attributes to the lock file item
                var lockFileItem = new LockFileContentFile(file);

                // Add the language from the directory path
                lockFileItem.CodeLanguage = languageMappings[file].ToLowerInvariant();

                if (!action.IsKnown)
                {
                    // Throw an error containing the package identity, invalid action, and file where it occurred.
                    var message = string.Format(CultureInfo.CurrentCulture, Strings.Error_UnknownBuildAction, nuspec.GetIdentity(), action, file);
                    throw new PackagingException(message);
                }

                lockFileItem.BuildAction  = action;
                lockFileItem.CopyToOutput = copyToOutput;

                // Check if this is a .pp transform. If the filename is ".pp" ignore it since it will
                // have no file name after the transform.
                var isPP = lockFileItem.Path.EndsWith(".pp", StringComparison.OrdinalIgnoreCase) &&
                           !string.IsNullOrEmpty(Path.GetFileNameWithoutExtension(lockFileItem.Path));

                if (copyToOutput)
                {
                    string destination = null;

                    if (flatten)
                    {
                        destination = Path.GetFileName(lockFileItem.Path);
                    }
                    else
                    {
                        // Find path relative to the TxM
                        // Ex: contentFiles/cs/net45/config/config.xml -> config/config.xml
                        destination = GetContentFileFolderRelativeToFramework(file);
                    }

                    if (isPP)
                    {
                        // Remove .pp from the output file path
                        destination = destination.Substring(0, destination.Length - 3);
                    }

                    lockFileItem.OutputPath = destination;
                }

                // Add the pp transform file if one exists
                if (isPP)
                {
                    var destination = lockFileItem.Path.Substring(0, lockFileItem.Path.Length - 3);
                    destination = GetContentFileFolderRelativeToFramework(destination);

                    lockFileItem.PPOutputPath = destination;
                }

                results.Add(lockFileItem);
            }

            return(results);
        }