/// <summary>
        /// Parse current and obsolete file references from a package's
        /// manifest files.
        /// </summary>
        /// <param name="metadataByVersion">Metadata for files ordered by
        /// version number.  If the metadata does not have the isManifest
        /// attribute it is ignored.</param>
        /// <param name="metadataSet">Set of all metadata files in the
        /// project.  This is used to handle file renaming in the parsed
        /// manifest.  If the manifest contains files that have been
        /// renamed it's updated with the new filenames.</param>
        /// <returns>true if data was parsed from the specified file metadata,
        /// false otherwise.</returns>
        public bool ParseManifests(FileMetadataByVersion metadataByVersion,
                                   FileMetadataSet metadataSet) {
            currentFiles = new HashSet<string>();
            obsoleteFiles = new HashSet<string>();

            int versionIndex = 0;
            int numberOfVersions = metadataByVersion.Values.Count;
            foreach (FileMetadata metadata in metadataByVersion.Values) {
                versionIndex++;
                if (!metadata.isManifest) return false;
                bool manifestNeedsUpdate = false;
                HashSet<string> filesInManifest =
                    versionIndex < numberOfVersions ?
                        obsoleteFiles : currentFiles;
                StreamReader manifestFile =
                    new StreamReader(metadata.filename);
                string line;
                while ((line = manifestFile.ReadLine()) != null) {
                    var manifestFileMetadata = new FileMetadata(line.Trim());
                    string filename = manifestFileMetadata.filename;
                    // Check for a renamed file.
                    var existingFileMetadata =
                        metadataSet.FindMetadata(
                            manifestFileMetadata.filenameCanonical,
                            manifestFileMetadata.CalculateVersion());
                    if (existingFileMetadata != null &&
                        !manifestFileMetadata.filename.Equals(
                            existingFileMetadata.filename)) {
                        filename = existingFileMetadata.filename;
                        manifestNeedsUpdate = true;
                    }
                    filesInManifest.Add(filename);
                }
                manifestFile.Close();

                // If this is the most recent manifest version, remove all
                // current files from the set to delete.
                if (versionIndex == numberOfVersions) {
                    currentMetadata = metadata;
                    foreach (var currentFile in filesInManifest) {
                        obsoleteFiles.Remove(currentFile);
                    }
                }

                // Rewrite the manifest to track renamed files.
                if (manifestNeedsUpdate) {
                    File.Delete(metadata.filename);
                    var writer = new StreamWriter(metadata.filename);
                    foreach (var filename in filesInManifest) {
                        writer.WriteLine(filename);
                    }
                    writer.Close();
                }
            }
            this.filenameCanonical = metadataByVersion.filenameCanonical;
            return true;
        }
 /// <summary>
 /// Add file metadata to the set.
 /// </summary>
 public void Add(FileMetadata metadata) {
     FileMetadataByVersion metadataByVersion;
     string filenameCanonical = metadata.filenameCanonical;
     if (!metadataByCanonicalFilename.TryGetValue(
             filenameCanonical, out metadataByVersion)) {
         metadataByVersion =
             new FileMetadataByVersion(filenameCanonical);
     }
     metadataByVersion.Add(metadata);
     metadataByCanonicalFilename[filenameCanonical] = metadataByVersion;
 }