/// <summary> /// Tests whether a pattern is compatible with another pattern (that is, that the number and type of wildcards match) /// </summary> /// <param name="Other">Pattern to compare against</param> /// <returns>Whether the patterns are compatible.</returns> public bool IsCompatibleWith(FilePattern Other) { // Check there are the same number of tokens in each pattern if (Tokens.Count != Other.Tokens.Count) { return(false); } // Check all the wildcard tokens match for (int Idx = 1; Idx < Tokens.Count; Idx += 2) { if (Tokens[Idx] != Other.Tokens[Idx]) { return(false); } } return(true); }
/// <summary> /// Creates a file mapping between a set of source patterns and a target pattern. All patterns should have a matching order and number of wildcards. /// </summary> /// <param name="Files">Files to use for the mapping</param> /// <param name="SourcePatterns">List of source patterns</param> /// <param name="TargetPattern">Matching output pattern</param> /// <param name="Filter">Filter to apply to source files</param> /// <param name="TargetFileToSourceFile">Dictionary to receive a mapping from target file to source file. An exception is thrown if multiple source files map to one target file, or a source file is also used as a target file.</param> public static bool TryCreateMapping(HashSet <FileReference> Files, FilePattern SourcePattern, FilePattern TargetPattern, out Dictionary <FileReference, FileReference> OutTargetFileToSourceFile) { bool bResult = true; // If the source pattern ends in a directory separator, or a set of input files are specified and it doesn't contain wildcards, treat it as a full directory match if (SourcePattern.EndsWithDirectorySeparator()) { SourcePattern = new FilePattern(SourcePattern.BaseDirectory, String.Join("", SourcePattern.Tokens) + "..."); } else if (Files != null) { SourcePattern = SourcePattern.AsDirectoryPattern(); } // If we have multiple potential source files, but no wildcards in the output pattern, assume it's a directory and append the pattern from the source. if (SourcePattern.ContainsWildcards() && !TargetPattern.ContainsWildcards()) { StringBuilder NewPattern = new StringBuilder(); foreach (string Token in TargetPattern.Tokens) { NewPattern.Append(Token); } if (NewPattern.Length > 0 && NewPattern[NewPattern.Length - 1] != Path.DirectorySeparatorChar) { NewPattern.Append(Path.DirectorySeparatorChar); } foreach (string Token in SourcePattern.Tokens) { NewPattern.Append(Token); } TargetPattern = new FilePattern(TargetPattern.BaseDirectory, NewPattern.ToString()); } // If the target pattern ends with a directory separator, treat it as a full directory match if it has wildcards, or a copy of the source pattern if not if (TargetPattern.EndsWithDirectorySeparator()) { TargetPattern = new FilePattern(TargetPattern.BaseDirectory, String.Join("", TargetPattern.Tokens) + "..."); } // Handle the case where source and target pattern are both individual files Dictionary <FileReference, FileReference> TargetFileToSourceFile = new Dictionary <FileReference, FileReference>(); if (SourcePattern.ContainsWildcards() || TargetPattern.ContainsWildcards()) { // Check the two patterns are compatible if (!SourcePattern.IsCompatibleWith(TargetPattern)) { CommandUtils.LogError("File patterns '{0}' and '{1}' do not have matching wildcards", SourcePattern, TargetPattern); OutTargetFileToSourceFile = null; return(false); } // Create a filter to match the source files FileFilter Filter = new FileFilter(FileFilterType.Exclude); Filter.Include(String.Join("", SourcePattern.Tokens)); // Apply it to the source directory List <FileReference> SourceFiles; if (Files == null) { SourceFiles = Filter.ApplyToDirectory(SourcePattern.BaseDirectory, true); } else { SourceFiles = CheckInputFiles(Files, SourcePattern.BaseDirectory, ref bResult); } // Map them onto output files FileReference[] TargetFiles = new FileReference[SourceFiles.Count]; // Get the source and target regexes string SourceRegex = SourcePattern.GetRegexPattern(); string TargetRegex = TargetPattern.GetRegexReplacementPattern(); for (int Idx = 0; Idx < SourceFiles.Count; Idx++) { string SourceRelativePath = SourceFiles[Idx].MakeRelativeTo(SourcePattern.BaseDirectory); string TargetRelativePath = Regex.Replace(SourceRelativePath, SourceRegex, TargetRegex); TargetFiles[Idx] = FileReference.Combine(TargetPattern.BaseDirectory, TargetRelativePath); } // Add them to the output map for (int Idx = 0; Idx < TargetFiles.Length; Idx++) { FileReference ExistingSourceFile; if (TargetFileToSourceFile.TryGetValue(TargetFiles[Idx], out ExistingSourceFile) && ExistingSourceFile != SourceFiles[Idx]) { CommandUtils.LogError("Output file '{0}' is mapped from '{1}' and '{2}'", TargetFiles[Idx], ExistingSourceFile, SourceFiles[Idx]); bResult = false; } TargetFileToSourceFile[TargetFiles[Idx]] = SourceFiles[Idx]; } } else { // Just copy a single file FileReference SourceFile = SourcePattern.GetSingleFile(); if (SourceFile.Exists()) { FileReference TargetFile = TargetPattern.GetSingleFile(); TargetFileToSourceFile[TargetFile] = SourceFile; } else { CommandUtils.LogError("Source file '{0}' does not exist", SourceFile); bResult = false; } } // Check that no source file is also destination file foreach (FileReference SourceFile in TargetFileToSourceFile.Values) { if (TargetFileToSourceFile.ContainsKey(SourceFile)) { CommandUtils.LogError("'{0}' is listed as a source and target file", SourceFile); bResult = false; } } // Set the output map if (bResult) { OutTargetFileToSourceFile = TargetFileToSourceFile; return(true); } else { OutTargetFileToSourceFile = null; return(false); } }