From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- .../InstallPath.cs | 1073 ++++++++++++++++++++ 1 file changed, 1073 insertions(+) create mode 100644 src/dtf/WixToolset.Dtf.WindowsInstaller.Package/InstallPath.cs (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller.Package/InstallPath.cs') diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/InstallPath.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/InstallPath.cs new file mode 100644 index 00000000..e3ba81b5 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/InstallPath.cs @@ -0,0 +1,1073 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Dtf.WindowsInstaller.Package +{ + using System; + using System.IO; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + + /// + /// Represents the installation path of a file or directory from an installer product database. + /// + public class InstallPath + { + /// + /// Creates a new InstallPath, specifying a filename. + /// + /// The name of the file or directory. Not a full path. + public InstallPath(string name) : this(name, false) { } + + /// + /// Creates a new InstallPath, parsing out either the short or long filename. + /// + /// The name of the file or directory, in short|long syntax for a filename + /// or targetshort|targetlong:sourceshort|sourcelong syntax for a directory. + /// true to parse the short part of the combined filename; false to parse the long part + public InstallPath(string name, bool useShortNames) + { + if(name == null) + { + throw new ArgumentNullException(); + } + this.parentPath = null; + ParseName(name, useShortNames); + } + + private void ParseName(string name, bool useShortNames) + { + string[] parse = name.Split(new char[] { ':' }, 3); + if(parse.Length == 3) + { + // Syntax was targetshort:sourceshort|targetlong:sourcelong. + // Chnage it to targetshort|targetlong:sourceshort|sourcelong. + parse = name.Split(new char[] { ':', '|' }, 4); + if(parse.Length == 4) + parse = new string[] { parse[0] + '|' + parse[2], parse[1] + '|' + parse[3] }; + else + parse = new string[] { parse[0] + '|' + parse[1], parse[1] + '|' + parse[2] }; + } + string targetName = parse[0]; + string sourceName = (parse.Length == 2 ? parse[1] : parse[0]); + parse = targetName.Split(new char[] { '|' }, 2); + if(parse.Length == 2) targetName = (useShortNames ? parse[0] : parse[1]); + parse = sourceName.Split(new char[] { '|' }, 2); + if(parse.Length == 2) sourceName = (useShortNames ? parse[0] : parse[1]); + + this.SourceName = sourceName; + this.TargetName = targetName; + } + + /// + /// Gets the path of the parent directory. + /// + public InstallPath ParentPath + { + get + { + return parentPath; + } + } + internal void SetParentPath(InstallPath value) + { + parentPath = value; + ResetSourcePath(); + ResetTargetPath(); + } + private InstallPath parentPath; + + /// + /// Gets the set of child paths if this InstallPath object represents a a directory. + /// + public InstallPathCollection ChildPaths + { + get + { + if(childPaths == null) + { + childPaths = new InstallPathCollection(this); + } + return childPaths; + } + } + private InstallPathCollection childPaths; + + /// + /// Gets or sets the source name of the InstallPath. + /// + public string SourceName + { + get + { + return sourceName; + } + set + { + if(value == null) + { + throw new ArgumentNullException(); + } + sourceName = value; + ResetSourcePath(); + } + } + private string sourceName; + + /// + /// Gets or sets the target name of the install path. + /// + public string TargetName + { + get + { + return targetName; + } + set + { + if(value == null) + { + throw new ArgumentNullException(); + } + targetName = value; + ResetTargetPath(); + } + } + private string targetName; + + /// + /// Gets the full source path. + /// + public string SourcePath + { + get + { + if(sourcePath == null) + { + if(parentPath != null) + { + sourcePath = (sourceName.Equals(".") ? parentPath.SourcePath + : Path.Combine(parentPath.SourcePath, sourceName)); + } + else + { + sourcePath = sourceName; + } + } + return sourcePath; + } + set + { + ResetSourcePath(); + sourcePath = value; + } + } + private string sourcePath; + + /// + /// Gets the full target path. + /// + public string TargetPath + { + get + { + if(targetPath == null) + { + if(parentPath != null) + { + targetPath = (targetName.Equals(".") ? parentPath.TargetPath + : Path.Combine(parentPath.TargetPath, targetName)); + } + else + { + targetPath = targetName; + } + } + return targetPath; + } + set + { + ResetTargetPath(); + targetPath = value; + } + } + private string targetPath; + + private void ResetSourcePath() + { + if(sourcePath != null) + { + sourcePath = null; + if(childPaths != null) + { + foreach(InstallPath ip in childPaths) + { + ip.ResetSourcePath(); + } + } + } + } + + private void ResetTargetPath() + { + if(targetPath != null) + { + targetPath = null; + if(childPaths != null) + { + foreach(InstallPath ip in childPaths) + { + ip.ResetTargetPath(); + } + } + } + } + + /// + /// Gets the full source path. + /// + /// + public override String ToString() + { + return SourcePath; + } + } + + /// + /// Represents a collection of InstallPaths that are the child paths of the same parent directory. + /// + public class InstallPathCollection : IList + { + private InstallPath parentPath; + private List items; + + internal InstallPathCollection(InstallPath parentPath) + { + this.parentPath = parentPath; + this.items = new List(); + } + + /// + /// Gets or sets the element at the specified index. + /// + public InstallPath this[int index] + { + get + { + return this.items[index]; + } + set + { + this.OnSet(this.items[index], value); + this.items[index] = value; + } + } + + /// + /// Adds a new child path to the collection. + /// + /// The InstallPath to add. + public void Add(InstallPath item) + { + this.OnInsert(item); + this.items.Add(item); + } + + /// + /// Removes a child path to the collection. + /// + /// The InstallPath to remove. + public bool Remove(InstallPath item) + { + int index = this.items.IndexOf(item); + if (index >= 0) + { + this.OnRemove(item); + this.items.RemoveAt(index); + return true; + } + else + { + return false; + } + } + + /// + /// Gets the index of a child path in the collection. + /// + /// The InstallPath to search for. + /// The index of the item, or -1 if not found. + public int IndexOf(InstallPath item) + { + return this.items.IndexOf(item); + } + + /// + /// Inserts a child path into the collection. + /// + /// The insertion index. + /// The InstallPath to insert. + public void Insert(int index, InstallPath item) + { + this.OnInsert(item); + this.items.Insert(index, item); + } + + /// + /// Tests if the collection contains a child path. + /// + /// The InstallPath to search for. + /// true if the item is found; false otherwise + public bool Contains(InstallPath item) + { + return this.items.Contains(item); + } + + /// + /// Copies the collection into an array. + /// + /// The array to copy into. + /// The starting index in the destination array. + public void CopyTo(InstallPath[] array, int index) + { + this.items.CopyTo(array, index); + } + + private void OnInsert(InstallPath item) + { + if (item.ParentPath != null) + { + item.ParentPath.ChildPaths.Remove(item); + } + + item.SetParentPath(this.parentPath); + } + + private void OnRemove(InstallPath item) + { + item.SetParentPath(null); + } + + private void OnSet(InstallPath oldItem, InstallPath newItem) + { + this.OnRemove(oldItem); + this.OnInsert(newItem); + } + + /// + /// Removes an item from the collection. + /// + /// The index of the item to remove. + public void RemoveAt(int index) + { + this.OnRemove(this[index]); + this.items.RemoveAt(index); + } + + /// + /// Removes all items from the collection. + /// + public void Clear() + { + foreach (InstallPath item in this) + { + this.OnRemove(item); + } + + this.items.Clear(); + } + + /// + /// Gets the number of items in the collection. + /// + public int Count + { + get + { + return this.items.Count; + } + } + + bool ICollection.IsReadOnly + { + get + { + return false; + } + } + + /// + /// Gets an enumerator over all items in the collection. + /// + /// An enumerator for the collection. + public IEnumerator GetEnumerator() + { + return this.items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) this).GetEnumerator(); + } + } + + /// + /// Represents a mapping of install paths for all directories, components, or files in + /// an installation database. + /// + public class InstallPathMap : IDictionary + { + /// + /// Builds a mapping from File keys to installation paths. + /// + /// Installation database. + /// Component mapping returned by . + /// true to use short file names; false to use long names + /// An InstallPathMap with the described mapping. + public static InstallPathMap BuildFilePathMap(Database db, InstallPathMap componentPathMap, + bool useShortNames) + { + if(db == null) + { + throw new ArgumentNullException("db"); + } + + if(componentPathMap == null) + { + componentPathMap = BuildComponentPathMap(db, BuildDirectoryPathMap(db, useShortNames)); + } + + InstallPathMap filePathMap = new InstallPathMap(); + + using (View fileView = db.OpenView("SELECT `File`, `Component_`, `FileName` FROM `File`")) + { + fileView.Execute(); + + foreach (Record fileRec in fileView) + { + using (fileRec) + { + string file = (string) fileRec[1]; + string comp = (string) fileRec[2]; + string fileName = (string) fileRec[3]; + + InstallPath compPath = (InstallPath) componentPathMap[comp]; + if(compPath != null) + { + InstallPath filePath = new InstallPath(fileName, useShortNames); + compPath.ChildPaths.Add(filePath); + filePathMap[file] = filePath; + } + } + } + } + + return filePathMap; + } + + /// + /// Builds a mapping from Component keys to installation paths. + /// + /// Installation database. + /// Directory mapping returned by + /// . + /// An InstallPathMap with the described mapping. + public static InstallPathMap BuildComponentPathMap(Database db, InstallPathMap directoryPathMap) + { + if(db == null) + { + throw new ArgumentNullException("db"); + } + + InstallPathMap compPathMap = new InstallPathMap(); + + using (View compView = db.OpenView("SELECT `Component`, `Directory_` FROM `Component`")) + { + compView.Execute(); + + foreach (Record compRec in compView) + { + using (compRec) + { + string comp = (string) compRec[1]; + InstallPath dirPath = (InstallPath) directoryPathMap[(string) compRec[2]]; + + if (dirPath != null) + { + compPathMap[comp] = dirPath; + } + } + } + } + + return compPathMap; + } + + /// + /// Builds a mapping from Directory keys to installation paths. + /// + /// Installation database. + /// true to use short directory names; false to use long names + /// An InstallPathMap with the described mapping. + public static InstallPathMap BuildDirectoryPathMap(Database db, bool useShortNames) + { + return BuildDirectoryPathMap(db, useShortNames, null, null); + } + + /// + /// Builds a mapping of Directory keys to directory paths, specifying root directories + /// for the source and target paths. + /// + /// Database containing the Directory table. + /// true to use short directory names; false to use long names + /// The root directory path of all source paths, or null to leave them relative. + /// The root directory path of all source paths, or null to leave them relative. + /// An InstallPathMap with the described mapping. + public static InstallPathMap BuildDirectoryPathMap(Database db, bool useShortNames, + string sourceRootDir, string targetRootDir) + { + if(db == null) + { + throw new ArgumentNullException("db"); + } + + if(sourceRootDir == null) sourceRootDir = ""; + if(targetRootDir == null) targetRootDir = ""; + + InstallPathMap dirMap = new InstallPathMap(); + IDictionary dirTreeMap = new Hashtable(); + + using (View dirView = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) + { + dirView.Execute(); + + foreach (Record dirRec in dirView) using (dirRec) + { + string key = (string) dirRec[1]; + string parentKey = (string) dirRec[2]; + InstallPath dir = new InstallPath((string) dirRec[3], useShortNames); + + dirMap[key] = dir; + + InstallPathMap siblingDirs = (InstallPathMap) dirTreeMap[parentKey]; + if (siblingDirs == null) + { + siblingDirs = new InstallPathMap(); + dirTreeMap[parentKey] = siblingDirs; + } + siblingDirs.Add(key, dir); + } + } + + foreach (KeyValuePair entry in (InstallPathMap) dirTreeMap[""]) + { + string key = (string) entry.Key; + InstallPath dir = (InstallPath) entry.Value; + LinkSubdirectories(key, dir, dirTreeMap); + } + + InstallPath targetDirPath = (InstallPath) dirMap["TARGETDIR"]; + if(targetDirPath != null) + { + targetDirPath.SourcePath = sourceRootDir; + targetDirPath.TargetPath = targetRootDir; + } + + return dirMap; + } + + private static void LinkSubdirectories(string key, InstallPath dir, IDictionary dirTreeMap) + { + InstallPathMap subDirs = (InstallPathMap) dirTreeMap[key]; + if(subDirs != null) + { + foreach (KeyValuePair entry in subDirs) + { + string subKey = (string) entry.Key; + InstallPath subDir = (InstallPath) entry.Value; + dir.ChildPaths.Add(subDir); + LinkSubdirectories(subKey, subDir, dirTreeMap); + } + } + } + + private Dictionary items; + + /// + /// Creates a new empty InstallPathMap. + /// + public InstallPathMap() + { + this.items = new Dictionary(StringComparer.Ordinal); + } + + /// + /// Gets a mapping from keys to source paths. + /// + public IDictionary SourcePaths + { + get + { + return new SourcePathMap(this); + } + } + + /// + /// Gets a mapping from keys to target paths. + /// + public IDictionary TargetPaths + { + get + { + return new TargetPathMap(this); + } + } + + /// + /// Gets or sets an install path for a direcotry, component, or file key. + /// + /// Depending on the type of InstallPathMap, this is the primary key from the + /// either the Directory, Component, or File table. + /// + /// Changing an install path does not modify the Database used to generate this InstallPathMap. + /// + public InstallPath this[string key] + { + get + { + InstallPath value = null; + this.items.TryGetValue(key, out value); + return value; + } + set + { + this.items[key] = value; + } + } + + /// + /// Gets the collection of keys in the InstallPathMap. Depending on the type of InstallPathMap, + /// they are all directory, component, or file key strings. + /// + public ICollection Keys + { + get + { + return this.items.Keys; + } + } + + /// + /// Gets the collection of InstallPath values in the InstallPathMap. + /// + public ICollection Values + { + get + { + return this.items.Values; + } + } + + /// + /// Sets an install path for a direcotry, component, or file key. + /// + /// Depending on the type of InstallPathMap, this is the primary key from the + /// either the Directory, Component, or File table. + /// The install path of the key item. + /// + /// Changing an install path does not modify the Database used to generate this InstallPathMap. + /// + public void Add(string key, InstallPath installPath) + { + this.items.Add(key, installPath); + } + + /// + /// Removes an install path from the map. + /// + /// Depending on the type of InstallPathMap, this is the primary key from the + /// either the Directory, Component, or File table. + /// true if the item was removed, false if it did not exist + /// + /// Changing an install path does not modify the Database used to generate this InstallPathMap. + /// + public bool Remove(string key) + { + return this.items.Remove(key); + } + + /// + /// Tests whether a direcotry, component, or file key exists in the map. + /// + /// Depending on the type of InstallPathMap, this is the primary key from the + /// either the Directory, Component, or File table. + /// true if the key is found; false otherwise + public bool ContainsKey(string key) + { + return this.items.ContainsKey(key); + } + + /* + public override string ToString() + { + System.Text.StringBuilder buf = new System.Text.StringBuilder(); + foreach(KeyValuePair entry in this) + { + buf.AppendFormat("{0}={1}", entry.Key, entry.Value); + buf.Append("\n"); + } + return buf.ToString(); + } + */ + + /// + /// Attempts to get a value from the dictionary. + /// + /// The key to lookup. + /// Receives the value, or null if they key was not found. + /// True if the value was found, else false. + public bool TryGetValue(string key, out InstallPath value) + { + return this.items.TryGetValue(key, out value); + } + + void ICollection>.Add(KeyValuePair item) + { + ((ICollection>) this.items).Add(item); + } + + /// + /// Removes all entries from the dictionary. + /// + public void Clear() + { + this.items.Clear(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return ((ICollection>) this.items).Contains(item); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>) this.items).CopyTo(array, arrayIndex); + } + + /// + /// Gets the number of entries in the dictionary. + /// + public int Count + { + get + { + return this.items.Count; + } + } + + bool ICollection>.IsReadOnly + { + get + { + return false; + } + } + + bool ICollection>.Remove(KeyValuePair item) + { + return ((ICollection>) this.items).Remove(item); + } + + /// + /// Gets an enumerator over all entries in the dictionary. + /// + /// An enumerator for the dictionary. + public IEnumerator> GetEnumerator() + { + return this.items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.items.GetEnumerator(); + } + } + + internal class SourcePathMap : IDictionary + { + private const string RO_MSG = + "The SourcePathMap collection is read-only. " + + "Modify the InstallPathMap instead."; + + private InstallPathMap map; + + internal SourcePathMap(InstallPathMap map) + { + this.map = map; + } + + public void Add(string key, string value) + { + throw new InvalidOperationException(RO_MSG); + } + + public bool ContainsKey(string key) + { + return this.map.ContainsKey(key); + } + + public ICollection Keys + { + get + { + return this.map.Keys; + } + } + + public bool Remove(string key) + { + throw new InvalidOperationException(RO_MSG); + } + + public bool TryGetValue(string key, out string value) + { + InstallPath installPath; + if (this.map.TryGetValue(key, out installPath)) + { + value = installPath.SourcePath; + return true; + } + else + { + value = null; + return false; + } + } + + public ICollection Values + { + get + { + List values = new List(this.Count); + foreach (KeyValuePair entry in this.map) + { + values.Add(entry.Value.SourcePath); + } + return values; + } + } + + public string this[string key] + { + get + { + string value = null; + this.TryGetValue(key, out value); + return value; + } + set + { + throw new InvalidOperationException(RO_MSG); + } + } + + public void Add(KeyValuePair item) + { + throw new InvalidOperationException(RO_MSG); + } + + public void Clear() + { + throw new InvalidOperationException(RO_MSG); + } + + public bool Contains(KeyValuePair item) + { + string value = this[item.Key]; + return value == item.Value; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (KeyValuePair entry in this) + { + array[arrayIndex] = entry; + arrayIndex++; + } + } + + public int Count + { + get + { + return this.map.Count; + } + } + + public bool IsReadOnly + { + get + { + return true; + } + } + + public bool Remove(KeyValuePair item) + { + throw new InvalidOperationException(RO_MSG); + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair entry in this.map) + { + yield return new KeyValuePair( + entry.Key, entry.Value.SourcePath); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } + + internal class TargetPathMap : IDictionary + { + private const string RO_MSG = + "The TargetPathMap collection is read-only. " + + "Modify the InstallPathMap instead."; + + private InstallPathMap map; + + internal TargetPathMap(InstallPathMap map) + { + this.map = map; + } + + public void Add(string key, string value) + { + throw new InvalidOperationException(RO_MSG); + } + + public bool ContainsKey(string key) + { + return this.map.ContainsKey(key); + } + + public ICollection Keys + { + get + { + return this.map.Keys; + } + } + + public bool Remove(string key) + { + throw new InvalidOperationException(RO_MSG); + } + + public bool TryGetValue(string key, out string value) + { + InstallPath installPath; + if (this.map.TryGetValue(key, out installPath)) + { + value = installPath.TargetPath; + return true; + } + else + { + value = null; + return false; + } + } + + public ICollection Values + { + get + { + List values = new List(this.Count); + foreach (KeyValuePair entry in this.map) + { + values.Add(entry.Value.TargetPath); + } + return values; + } + } + + public string this[string key] + { + get + { + string value = null; + this.TryGetValue(key, out value); + return value; + } + set + { + throw new InvalidOperationException(RO_MSG); + } + } + + public void Add(KeyValuePair item) + { + throw new InvalidOperationException(RO_MSG); + } + + public void Clear() + { + throw new InvalidOperationException(RO_MSG); + } + + public bool Contains(KeyValuePair item) + { + string value = this[item.Key]; + return value == item.Value; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (KeyValuePair entry in this) + { + array[arrayIndex] = entry; + arrayIndex++; + } + } + + public int Count + { + get + { + return this.map.Count; + } + } + + public bool IsReadOnly + { + get + { + return true; + } + } + + public bool Remove(KeyValuePair item) + { + throw new InvalidOperationException(RO_MSG); + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair entry in this.map) + { + yield return new KeyValuePair( + entry.Key, entry.Value.TargetPath); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} -- cgit v1.2.3-55-g6feb