diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FilePath.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FilePath.cs deleted file mode 100644 index e05063c74..000000000 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FilePath.cs +++ /dev/null @@ -1,400 +0,0 @@ -// -// FilePath.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (c) 2011 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Linq; - -namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation -{ - public struct FilePath : IComparable, IComparable, IEquatable - { - public static readonly StringComparer PathComparer = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; - public static readonly StringComparison PathComparison = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - - readonly string fileName; - - public static readonly FilePath Null = new FilePath(null); - public static readonly FilePath Empty = new FilePath(string.Empty); - - public FilePath(string name) - { - if (name != null && name.Length > 6 && name[0] == 'f' && name.StartsWith("file://", StringComparison.Ordinal)) - name = new Uri(name).LocalPath; - - fileName = name; - } - - public bool IsNull - { - get { return fileName == null; } - } - - public bool IsNullOrEmpty - { - get { return string.IsNullOrEmpty(fileName); } - } - - public bool IsNotNull - { - get { return fileName != null; } - } - - public bool IsEmpty - { - get { return fileName != null && fileName.Length == 0; } - } - - const int PATHMAX = 4096 + 1; - - static readonly char[] invalidPathChars = Path.GetInvalidPathChars(); - public static char[] GetInvalidPathChars() - { - return (char[])invalidPathChars.Clone(); - } - - static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); - public static char[] GetInvalidFileNameChars() - { - return (char[])invalidFileNameChars.Clone(); - } - - public FilePath FullPath - { - get - { - return new FilePath(!string.IsNullOrEmpty(fileName) ? Path.GetFullPath(fileName) : ""); - } - } - - public bool IsDirectory - { - get - { - return Directory.Exists(FullPath); - } - } - - /// - /// Returns a path in standard form, which can be used to be compared - /// for equality with other canonical paths. It is similar to FullPath, - /// but unlike FullPath, the directory "/a/b" is considered equal to "/a/b/" - /// - public FilePath CanonicalPath - { - get - { - if (fileName == null) - return FilePath.Null; - if (fileName.Length == 0) - return FilePath.Empty; - string fp = Path.GetFullPath(fileName); - if (fp.Length > 0) - { - if (fp[fp.Length - 1] == Path.DirectorySeparatorChar) - return fp.TrimEnd(Path.DirectorySeparatorChar); - if (fp[fp.Length - 1] == Path.AltDirectorySeparatorChar) - return fp.TrimEnd(Path.AltDirectorySeparatorChar); - } - return fp; - } - } - - public string FileName - { - get - { - return Path.GetFileName(fileName); - } - } - - public string Extension - { - get - { - return Path.GetExtension(fileName); - } - } - - public bool HasExtension(string extension) - { - return fileName.Length > extension.Length - && fileName.EndsWith(extension, StringComparison.OrdinalIgnoreCase) - && fileName[fileName.Length - extension.Length - 1] != Path.PathSeparator; - } - - public string FileNameWithoutExtension - { - get - { - return Path.GetFileNameWithoutExtension(fileName); - } - } - - public FilePath ParentDirectory - { - get - { - return new FilePath(Path.GetDirectoryName(fileName)); - } - } - - public bool IsAbsolute - { - get { return Path.IsPathRooted(fileName); } - } - - public bool IsChildPathOf(FilePath basePath) - { - if (basePath.fileName[basePath.fileName.Length - 1] != Path.DirectorySeparatorChar) - return fileName.StartsWith(basePath.fileName + Path.DirectorySeparatorChar, PathComparison); - else - return fileName.StartsWith(basePath.fileName, PathComparison); - } - - public FilePath ChangeExtension(string ext) - { - return Path.ChangeExtension(fileName, ext); - } - - /// - /// Returns a file path with the name changed to the provided name, but keeping the extension - /// - /// The new file path - /// New file name - public FilePath ChangeName(string newName) - { - return ParentDirectory.Combine(newName) + Extension; - } - - public FilePath Combine(params FilePath[] paths) - { - string path = fileName; - foreach (FilePath p in paths) - path = Path.Combine(path, p.fileName); - return new FilePath(path); - } - - public FilePath Combine(params string[] paths) - { - return new FilePath(Path.Combine(fileName, Path.Combine(paths))); - } - - public Task DeleteAsync() - { - return Task.Run((System.Action)Delete); - } - - public void Delete() - { - // Ensure that this file/directory and all children are writable - MakeWritable(true); - - // Also ensure the directory containing this file/directory is writable, - // otherwise we will not be able to delete it - ParentDirectory.MakeWritable(false); - - if (Directory.Exists(this)) - { - Directory.Delete(this, true); - } - else if (File.Exists(this)) - { - File.Delete(this); - } - } - - public void MakeWritable() - { - MakeWritable(false); - } - - public void MakeWritable(bool recurse) - { - if (Directory.Exists(this)) - { - try - { - var info = new DirectoryInfo(this); - info.Attributes &= ~FileAttributes.ReadOnly; - } - catch - { - - } - - if (recurse) - { - foreach (var sub in Directory.GetFileSystemEntries(this)) - { - ((FilePath)sub).MakeWritable(recurse); - } - } - } - else if (File.Exists(this)) - { - try - { - // Try/catch is to work around a mono bug where dangling symlinks - // blow up when you call SetFileAttributes. Just ignore this case - // until mono 2.10.7/8 is released which fixes it. - var info = new FileInfo(this); - info.Attributes &= ~FileAttributes.ReadOnly; - } - catch - { - - } - } - } - - /// - /// Builds a path by combining all provided path sections - /// - public static FilePath Build(params string[] paths) - { - return Empty.Combine(paths); - } - - public static FilePath GetCommonRootPath(IEnumerable paths) - { - FilePath root = FilePath.Null; - foreach (FilePath p in paths) - { - if (root.IsNull) - root = p; - else if (root == p) - continue; - else if (root.IsChildPathOf(p)) - root = p; - else - { - while (!root.IsNullOrEmpty && !p.IsChildPathOf(root)) - root = root.ParentDirectory; - } - } - return root; - } - - public FilePath ToAbsolute(FilePath basePath) - { - if (IsAbsolute) - return FullPath; - else - return Combine(basePath, this).FullPath; - } - - public static implicit operator FilePath(string name) - { - return new FilePath(name); - } - - public static implicit operator string(FilePath filePath) - { - return filePath.fileName; - } - - public static bool operator ==(FilePath name1, FilePath name2) - { - return PathComparer.Equals(name1.fileName, name2.fileName); - } - - public static bool operator !=(FilePath name1, FilePath name2) - { - return !(name1 == name2); - } - - public override bool Equals(object obj) - { - if (!(obj is FilePath)) - return false; - - FilePath fn = (FilePath)obj; - return this == fn; - } - - public override int GetHashCode() - { - if (fileName == null) - return 0; - return PathComparer.GetHashCode(fileName); - } - - public override string ToString() - { - return fileName; - } - - public int CompareTo(FilePath filePath) - { - return PathComparer.Compare(fileName, filePath.fileName); - } - - int IComparable.CompareTo(object obj) - { - if (!(obj is FilePath)) - return -1; - return CompareTo((FilePath)obj); - } - - #region IEquatable Members - - bool IEquatable.Equals(FilePath other) - { - return this == other; - } - - #endregion - } - - public static class FilePathUtil - { - public static string[] ToStringArray(this FilePath[] paths) - { - string[] array = new string[paths.Length]; - for (int n = 0; n < paths.Length; n++) - array[n] = paths[n].ToString(); - return array; - } - - public static FilePath[] ToFilePathArray(this string[] paths) - { - var array = new FilePath[paths.Length]; - for (int n = 0; n < paths.Length; n++) - array[n] = paths[n]; - return array; - } - - public static IEnumerable ToPathStrings(this IEnumerable paths) - { - foreach (FilePath p in paths) - yield return p.ToString(); - } - } -} diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs index 34f5405af..ce4329199 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs @@ -30,9 +30,9 @@ using System.Text; namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation { - static class FileUtil + static internal class FileUtil { - public static TextFormatInfo GetTextFormatInfo(string file) + internal static TextFormatInfo GetTextFormatInfo(string file) { var info = new TextFormatInfo(); @@ -45,16 +45,22 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation int nread, i; if ((nread = fs.Read(buf, 0, buf.Length)) <= 0) + { return info; + } if (TryParse(buf, nread, out encoding)) + { i = encoding.GetPreamble().Length; + } else + { + encoding = null; i = 0; + } do { - // Read to the first newline to figure out which line endings this file is using while (i < nread) { if (buf[i] == '\r') @@ -83,7 +89,6 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation } } while (newLine == null); - // Check for a blank line at the end info.EndsWithEmptyLine = fs.Seek(-1, SeekOrigin.End) > 0 && fs.ReadByte() == (int)'\n'; info.NewLine = newLine; info.Encoding = encoding; @@ -100,7 +105,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation bool matched = true; if (available < table[i].GetPreamble().Length) + { continue; + } for (int j = 0; j < table[i].GetPreamble().Length; j++) { @@ -132,11 +139,13 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation }; } - class TextFormatInfo + internal class TextFormatInfo { public TextFormatInfo() { NewLine = Environment.NewLine; + Encoding = null; + EndsWithEmptyLine = true; } public string NewLine { get; set; } @@ -144,4 +153,3 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation public bool EndsWithEmptyLine { get; set; } } } - diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs index 96fe779df..3786c2355 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs @@ -40,113 +40,85 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { public class SlnFile { - SlnProjectCollection projects = new SlnProjectCollection(); - SlnSectionCollection sections = new SlnSectionCollection(); - SlnPropertySet metadata = new SlnPropertySet(true); - int prefixBlankLines = 1; - TextFormatInfo format = new TextFormatInfo { NewLine = "\r\n" }; + private SlnProjectCollection _projects = new SlnProjectCollection(); + private SlnSectionCollection _sections = new SlnSectionCollection(); + private SlnPropertySet _metadata = new SlnPropertySet(true); + private int _prefixBlankLines = 1; + private TextFormatInfo _format = new TextFormatInfo(); public string FormatVersion { get; set; } public string ProductDescription { get; set; } public string VisualStudioVersion { - get { return metadata.GetValue("VisualStudioVersion"); } - set { metadata.SetValue("VisualStudioVersion", value); } + get { return _metadata.GetValue("VisualStudioVersion"); } + set { _metadata.SetValue("VisualStudioVersion", value); } } public string MinimumVisualStudioVersion { - get { return metadata.GetValue("MinimumVisualStudioVersion"); } - set { metadata.SetValue("MinimumVisualStudioVersion", value); } + get { return _metadata.GetValue("MinimumVisualStudioVersion"); } + set { _metadata.SetValue("MinimumVisualStudioVersion", value); } } - public SlnFile() + public string BaseDirectory { - projects.ParentFile = this; - sections.ParentFile = this; + get { return Path.GetDirectoryName(FullPath); } } - /// - /// Gets the sln format version of the provided solution file - /// - /// The file version. - /// File. - public static string GetFileVersion(string file) + public string FullPath { get; set; } + + public SlnPropertySet SolutionConfigurationsSection { - string strVersion; - using (var reader = new StreamReader(new FileStream(file, FileMode.Open))) + get { - var strInput = reader.ReadLine(); - if (strInput == null) - return null; - - var match = slnVersionRegex.Match(strInput); - if (!match.Success) - { - strInput = reader.ReadLine(); - if (strInput == null) - return null; - match = slnVersionRegex.Match(strInput); - if (!match.Success) - return null; - } - - strVersion = match.Groups[1].Value; - return strVersion; + return _sections + .GetOrCreateSection("SolutionConfigurationPlatforms", SlnSectionType.PreProcess) + .Properties; } } - static Regex slnVersionRegex = new Regex(@"Microsoft Visual Studio Solution File, Format Version (\d?\d.\d\d)"); - - /// - /// The directory to be used as base for converting absolute paths to relative - /// - public FilePath BaseDirectory - { - get { return FileName.ParentDirectory; } - } - - /// - /// Gets the solution configurations section. - /// - /// The solution configurations section. - public SlnPropertySet SolutionConfigurationsSection - { - get { return sections.GetOrCreateSection("SolutionConfigurationPlatforms", SlnSectionType.PreProcess).Properties; } - } - - /// - /// Gets the project configurations section. - /// - /// The project configurations section. public SlnPropertySetCollection ProjectConfigurationsSection { - get { return sections.GetOrCreateSection("ProjectConfigurationPlatforms", SlnSectionType.PostProcess).NestedPropertySets; } + get + { + return _sections + .GetOrCreateSection("ProjectConfigurationPlatforms", SlnSectionType.PostProcess) + .NestedPropertySets; + } } public SlnSectionCollection Sections { - get { return sections; } + get { return _sections; } } public SlnProjectCollection Projects { - get { return projects; } + get { return _projects; } } - public FilePath FileName { get; set; } - - public void Read(string file) + public SlnFile() { - FileName = file; - format = FileUtil.GetTextFormatInfo(file); + _projects.ParentFile = this; + _sections.ParentFile = this; + } + + public static SlnFile Read(string file) + { + SlnFile slnFile = new SlnFile(); + slnFile.FullPath = Path.GetFullPath(file); + slnFile._format = FileUtil.GetTextFormatInfo(file); using (var sr = new StreamReader(new FileStream(file, FileMode.Open))) - Read(sr); + { + slnFile.Read(sr); + } + + return slnFile; } - public void Read(TextReader reader) + private void Read(TextReader reader) { string line; int curLineNum = 0; @@ -161,9 +133,11 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { int i = line.LastIndexOf(' '); if (i == -1) + { throw new InvalidSolutionFormatException(curLineNum); + } FormatVersion = line.Substring(i + 1); - prefixBlankLines = curLineNum - 1; + _prefixBlankLines = curLineNum - 1; } if (line.StartsWith("# ", StringComparison.Ordinal)) { @@ -177,12 +151,14 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { SlnProject p = new SlnProject(); p.Read(reader, line, ref curLineNum); - projects.Add(p); + _projects.Add(p); } else if (line == "Global") { if (globalFound) + { throw new InvalidSolutionFormatException(curLineNum, "Global section specified more than once"); + } globalFound = true; while ((line = reader.ReadLine()) != null) { @@ -196,67 +172,82 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { var sec = new SlnSection(); sec.Read(reader, line, ref curLineNum); - sections.Add(sec); + _sections.Add(sec); } else // Ignore text that's out of place + { continue; + } } if (line == null) + { throw new InvalidSolutionFormatException(curLineNum, "Global section not closed"); + } } else if (line.IndexOf('=') != -1) { - metadata.ReadLine(line, curLineNum); + _metadata.ReadLine(line, curLineNum); } } if (FormatVersion == null) + { throw new InvalidSolutionFormatException(curLineNum, "File header is missing"); + } } - public void Write(string file) + public void Write(string file = null) { - FileName = file; + if (!string.IsNullOrEmpty(file)) + { + FullPath = Path.GetFullPath(file); + } var sw = new StringWriter(); Write(sw); - File.WriteAllText(file, sw.ToString()); + File.WriteAllText(FullPath, sw.ToString()); } - public void Write(TextWriter writer) + private void Write(TextWriter writer) { - writer.NewLine = format.NewLine; - for (int n = 0; n < prefixBlankLines; n++) + writer.NewLine = _format.NewLine; + for (int n = 0; n < _prefixBlankLines; n++) + { writer.WriteLine(); + } writer.WriteLine("Microsoft Visual Studio Solution File, Format Version " + FormatVersion); writer.WriteLine("# " + ProductDescription); - metadata.Write(writer); + _metadata.Write(writer); - foreach (var p in projects) + foreach (var p in _projects) + { p.Write(writer); + } writer.WriteLine("Global"); - foreach (SlnSection s in sections) + foreach (SlnSection s in _sections) + { s.Write(writer, "GlobalSection"); + } writer.WriteLine("EndGlobal"); } } public class SlnProject { - SlnSectionCollection sections = new SlnSectionCollection(); + private SlnSectionCollection _sections = new SlnSectionCollection(); - SlnFile parentFile; + private SlnFile _parentFile; public SlnFile ParentFile { get { - return parentFile; + return _parentFile; } internal set { - parentFile = value; - sections.ParentFile = parentFile; + _parentFile = value; + _sections.ParentFile = _parentFile; } } @@ -269,7 +260,7 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public SlnSectionCollection Sections { - get { return sections; } + get { return _sections; } } internal void Read(TextReader reader, string line, ref int curLineNum) @@ -317,10 +308,12 @@ namespace Microsoft.DotNet.Cli.Sln.Internal } if (line.StartsWith("ProjectSection", StringComparison.Ordinal)) { - if (sections == null) - sections = new SlnSectionCollection(); + if (_sections == null) + { + _sections = new SlnSectionCollection(); + } var sec = new SlnSection(); - sections.Add(sec); + _sections.Add(sec); sec.Read(reader, line, ref curLineNum); } } @@ -328,14 +321,16 @@ namespace Microsoft.DotNet.Cli.Sln.Internal throw new InvalidSolutionFormatException(curLineNum, "Project section not closed"); } - void FindNext(int ln, string line, ref int i, char c) + private void FindNext(int ln, string line, ref int i, char c) { i = line.IndexOf(c, i); if (i == -1) + { throw new InvalidSolutionFormatException(ln); + } } - public void Write(TextWriter writer) + internal void Write(TextWriter writer) { writer.Write("Project(\""); writer.Write(TypeGuid); @@ -346,10 +341,12 @@ namespace Microsoft.DotNet.Cli.Sln.Internal writer.Write("\", \""); writer.Write(Id); writer.WriteLine("\""); - if (sections != null) + if (_sections != null) { - foreach (SlnSection s in sections) + foreach (SlnSection s in _sections) + { s.Write(writer, "ProjectSection"); + } } writer.WriteLine("EndProject"); } @@ -357,10 +354,10 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public class SlnSection { - SlnPropertySetCollection nestedPropertySets; - SlnPropertySet properties; - List sectionLines; - int baseIndex; + private SlnPropertySetCollection _nestedPropertySets; + private SlnPropertySet _properties; + private List _sectionLines; + private int _baseIndex; public string Id { get; set; } public int Line { get; private set; } @@ -373,7 +370,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { get { - return (properties == null || properties.Count == 0) && (nestedPropertySets == null || nestedPropertySets.All(t => t.IsEmpty)) && (sectionLines == null || sectionLines.Count == 0); + return (_properties == null || _properties.Count == 0) && + (_nestedPropertySets == null || _nestedPropertySets.All(t => t.IsEmpty)) && + (_sectionLines == null || _sectionLines.Count == 0); } } @@ -385,27 +384,29 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public void Clear() { - properties = null; - nestedPropertySets = null; - sectionLines = null; + _properties = null; + _nestedPropertySets = null; + _sectionLines = null; } public SlnPropertySet Properties { get { - if (properties == null) + if (_properties == null) { - properties = new SlnPropertySet(); - properties.ParentSection = this; - if (sectionLines != null) + _properties = new SlnPropertySet(); + _properties.ParentSection = this; + if (_sectionLines != null) { - foreach (var line in sectionLines) - properties.ReadLine(line, Line); - sectionLines = null; + foreach (var line in _sectionLines) + { + _properties.ReadLine(line, Line); + } + _sectionLines = null; } } - return properties; + return _properties; } } @@ -413,55 +414,73 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { get { - if (nestedPropertySets == null) + if (_nestedPropertySets == null) { - nestedPropertySets = new SlnPropertySetCollection(this); - if (sectionLines != null) + _nestedPropertySets = new SlnPropertySetCollection(this); + if (_sectionLines != null) + { LoadPropertySets(); + } } - return nestedPropertySets; + return _nestedPropertySets; } } public void SetContent(IEnumerable> lines) { - sectionLines = new List(lines.Select(p => p.Key + " = " + p.Value)); - properties = null; - nestedPropertySets = null; + _sectionLines = new List(lines.Select(p => p.Key + " = " + p.Value)); + _properties = null; + _nestedPropertySets = null; } public IEnumerable> GetContent() { - if (sectionLines != null) - return sectionLines.Select(li => + if (_sectionLines != null) + { + return _sectionLines.Select(li => { int i = li.IndexOf('='); if (i != -1) + { return new KeyValuePair(li.Substring(0, i).Trim(), li.Substring(i + 1).Trim()); + } else + { return new KeyValuePair(li.Trim(), ""); + } }); + } else + { return new KeyValuePair[0]; + } } public SlnSectionType SectionType { get; set; } - SlnSectionType ToSectionType(int curLineNum, string s) + private SlnSectionType ToSectionType(int curLineNum, string s) { if (s == "preSolution" || s == "preProject") + { return SlnSectionType.PreProcess; + } if (s == "postSolution" || s == "postProject") + { return SlnSectionType.PostProcess; + } throw new InvalidSolutionFormatException(curLineNum, "Invalid section type: " + s); } - string FromSectionType(bool isProjectSection, SlnSectionType type) + private string FromSectionType(bool isProjectSection, SlnSectionType type) { if (type == SlnSectionType.PreProcess) + { return isProjectSection ? "preProject" : "preSolution"; + } else + { return isProjectSection ? "postProject" : "postSolution"; + } } internal void Read(TextReader reader, string line, ref int curLineNum) @@ -469,11 +488,15 @@ namespace Microsoft.DotNet.Cli.Sln.Internal Line = curLineNum; int k = line.IndexOf('('); if (k == -1) + { throw new InvalidSolutionFormatException(curLineNum, "Section id missing"); + } var tag = line.Substring(0, k).Trim(); var k2 = line.IndexOf(')', k); if (k2 == -1) + { throw new InvalidSolutionFormatException(curLineNum); + } Id = line.Substring(k + 1, k2 - k - 1); k = line.IndexOf('=', k2); @@ -481,49 +504,59 @@ namespace Microsoft.DotNet.Cli.Sln.Internal var endTag = "End" + tag; - sectionLines = new List(); - baseIndex = ++curLineNum; + _sectionLines = new List(); + _baseIndex = ++curLineNum; while ((line = reader.ReadLine()) != null) { curLineNum++; line = line.Trim(); if (line == endTag) + { break; - sectionLines.Add(line); + } + _sectionLines.Add(line); } if (line == null) + { throw new InvalidSolutionFormatException(curLineNum, "Closing section tag not found"); + } } - void LoadPropertySets() + private void LoadPropertySets() { - if (sectionLines != null) + if (_sectionLines != null) { SlnPropertySet curSet = null; - for (int n = 0; n < sectionLines.Count; n++) + for (int n = 0; n < _sectionLines.Count; n++) { - var line = sectionLines[n]; + var line = _sectionLines[n]; if (string.IsNullOrEmpty(line.Trim())) + { continue; + } var i = line.IndexOf('.'); if (i == -1) - throw new InvalidSolutionFormatException(baseIndex + n); + { + throw new InvalidSolutionFormatException(_baseIndex + n); + } var id = line.Substring(0, i); if (curSet == null || id != curSet.Id) { curSet = new SlnPropertySet(id); - nestedPropertySets.Add(curSet); + _nestedPropertySets.Add(curSet); } - curSet.ReadLine(line.Substring(i + 1), baseIndex + n); + curSet.ReadLine(line.Substring(i + 1), _baseIndex + n); } - sectionLines = null; + _sectionLines = null; } } internal void Write(TextWriter writer, string sectionTag) { if (SkipIfEmpty && IsEmpty) + { return; + } writer.Write("\t"); writer.Write(sectionTag); @@ -531,17 +564,23 @@ namespace Microsoft.DotNet.Cli.Sln.Internal writer.Write(Id); writer.Write(") = "); writer.WriteLine(FromSectionType(sectionTag == "ProjectSection", SectionType)); - if (sectionLines != null) + if (_sectionLines != null) { - foreach (var l in sectionLines) + foreach (var l in _sectionLines) + { writer.WriteLine("\t\t" + l); + } } - else if (properties != null) - properties.Write(writer); - else if (nestedPropertySets != null) + else if (_properties != null) { - foreach (var ps in nestedPropertySets) + _properties.Write(writer); + } + else if (_nestedPropertySets != null) + { + foreach (var ps in _nestedPropertySets) + { ps.Write(writer); + } } writer.WriteLine("\tEnd" + sectionTag); } @@ -552,8 +591,8 @@ namespace Microsoft.DotNet.Cli.Sln.Internal /// public class SlnPropertySet : IDictionary { - OrderedDictionary values = new OrderedDictionary(); - bool isMetadata; + private OrderedDictionary _values = new OrderedDictionary(); + private bool _isMetadata; internal bool Processed { get; set; } @@ -585,65 +624,69 @@ namespace Microsoft.DotNet.Cli.Sln.Internal internal SlnPropertySet(bool isMetadata) { - this.isMetadata = isMetadata; + _isMetadata = isMetadata; } - /// - /// Gets a value indicating whether this property set is empty. - /// - /// true if this instance is empty; otherwise, false. public bool IsEmpty { get { - return values.Count == 0; + return _values.Count == 0; } } internal void ReadLine(string line, int currentLine) { if (Line == 0) + { Line = currentLine; + } int k = line.IndexOf('='); if (k != -1) { var name = line.Substring(0, k).Trim(); var val = line.Substring(k + 1).Trim(); - values[name] = val; + _values[name] = val; } else { line = line.Trim(); if (!string.IsNullOrWhiteSpace(line)) - values.Add(line, null); + { + _values.Add(line, null); + } } } internal void Write(TextWriter writer) { - foreach (DictionaryEntry e in values) + foreach (DictionaryEntry e in _values) { - if (!isMetadata) + if (!_isMetadata) + { writer.Write("\t\t"); + } if (Id != null) + { writer.Write(Id + "."); + } writer.WriteLine(e.Key + " = " + e.Value); } } - /// - /// Gets the identifier of the property set - /// - /// The identifier. public string Id { get; private set; } public string GetValue(string name, string defaultValue = null) { string res; if (TryGetValue(name, out res)) + { return res; + } else + { return defaultValue; + } } public T GetValue(string name) @@ -662,40 +705,57 @@ namespace Microsoft.DotNet.Cli.Sln.Internal if (TryGetValue(name, out val)) { if (t == typeof(bool)) + { return (object)val.Equals("true", StringComparison.OrdinalIgnoreCase); + } if (t.GetTypeInfo().IsEnum) + { return Enum.Parse(t, val, true); + } if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) { var at = t.GetTypeInfo().GetGenericArguments()[0]; if (string.IsNullOrEmpty(val)) + { return null; + } return Convert.ChangeType(val, at, CultureInfo.InvariantCulture); } return Convert.ChangeType(val, t, CultureInfo.InvariantCulture); } else + { return defaultValue; + } } public void SetValue(string name, string value, string defaultValue = null, bool preserveExistingCase = false) { if (value == null && defaultValue == "") + { value = ""; + } if (value == defaultValue) { // if the value is default, only remove the property if it was not already the default // to avoid unnecessary project file churn string res; - if (TryGetValue(name, out res) && !string.Equals(defaultValue ?? "", res, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + if (TryGetValue(name, out res) && + !string.Equals(defaultValue ?? "", + res, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + { Remove(name); + } return; } string currentValue; - if (preserveExistingCase && TryGetValue(name, out currentValue) && string.Equals(value, currentValue, StringComparison.OrdinalIgnoreCase)) + if (preserveExistingCase && TryGetValue(name, out currentValue) && + string.Equals(value, currentValue, StringComparison.OrdinalIgnoreCase)) + { return; - values[name] = value; + } + _values[name] = value; } public void SetValue(string name, object value, object defaultValue = null) @@ -705,15 +765,22 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { // if the value is default, only remove the property if it was not already the default // to avoid unnecessary project file churn - if (ContainsKey(name) && (defaultValue == null || !object.Equals(defaultValue, GetValue(name, defaultValue.GetType(), null)))) + if (ContainsKey(name) && (defaultValue == null || + !object.Equals(defaultValue, GetValue(name, defaultValue.GetType(), null)))) + { Remove(name); + } return; } if (value is bool) - values[name] = (bool)value ? "TRUE" : "FALSE"; + { + _values[name] = (bool)value ? "TRUE" : "FALSE"; + } else - values[name] = Convert.ToString(value, CultureInfo.InvariantCulture); + { + _values[name] = Convert.ToString(value, CultureInfo.InvariantCulture); + } } void IDictionary.Add(string key, string value) @@ -721,52 +788,33 @@ namespace Microsoft.DotNet.Cli.Sln.Internal SetValue(key, value); } - /// - /// Determines whether the current instance contains an entry with the specified key - /// - /// true, if key was containsed, false otherwise. - /// Key. public bool ContainsKey(string key) { - return values.Contains(key); + return _values.Contains(key); } - /// - /// Removes a property - /// - /// Property name public bool Remove(string key) { - var wasThere = values.Contains(key); - values.Remove(key); + var wasThere = _values.Contains(key); + _values.Remove(key); return wasThere; } - /// - /// Tries to get the value of a property - /// - /// true, if the property exists, false otherwise. - /// Property name - /// Value. public bool TryGetValue(string key, out string value) { - value = (string)values[key]; + value = (string)_values[key]; return value != null; } - /// - /// Gets or sets the value of a property - /// - /// Index. public string this[string index] { get { - return (string)values[index]; + return (string)_values[index]; } set { - values[index] = value; + _values[index] = value; } } @@ -774,13 +822,13 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { get { - return values.Values.Cast().ToList(); + return _values.Values.Cast().ToList(); } } public ICollection Keys { - get { return values.Keys.Cast().ToList(); } + get { return _values.Keys.Cast().ToList(); } } void ICollection>.Add(KeyValuePair item) @@ -790,13 +838,15 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public void Clear() { - values.Clear(); + _values.Clear(); } internal void ClearExcept(HashSet keys) { - foreach (var k in values.Keys.Cast().Except(keys).ToArray()) - values.Remove(k); + foreach (var k in _values.Keys.Cast().Except(keys).ToArray()) + { + _values.Remove(k); + } } bool ICollection>.Contains(KeyValuePair item) @@ -807,8 +857,10 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public void CopyTo(KeyValuePair[] array, int arrayIndex) { - foreach (DictionaryEntry de in values) + foreach (DictionaryEntry de in _values) + { array[arrayIndex++] = new KeyValuePair((string)de.Key, (string)de.Value); + } } bool ICollection>.Remove(KeyValuePair item) @@ -819,22 +871,26 @@ namespace Microsoft.DotNet.Cli.Sln.Internal return true; } else + { return false; + } } public int Count { get { - return values.Count; + return _values.Count; } } internal void SetLines(IEnumerable> lines) { - values.Clear(); + _values.Clear(); foreach (var line in lines) - values[line.Key] = line.Value; + { + _values[line.Key] = line.Value; + } } bool ICollection>.IsReadOnly @@ -847,32 +903,38 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public IEnumerator> GetEnumerator() { - foreach (DictionaryEntry de in values) + foreach (DictionaryEntry de in _values) + { yield return new KeyValuePair((string)de.Key, (string)de.Value); + } } IEnumerator IEnumerable.GetEnumerator() { - foreach (DictionaryEntry de in values) + foreach (DictionaryEntry de in _values) + { yield return new KeyValuePair((string)de.Key, (string)de.Value); + } } } public class SlnProjectCollection : Collection { - SlnFile parentFile; + private SlnFile _parentFile; internal SlnFile ParentFile { get { - return parentFile; + return _parentFile; } set { - parentFile = value; + _parentFile = value; foreach (var it in this) - it.ParentFile = parentFile; + { + it.ParentFile = _parentFile; + } } } @@ -914,26 +976,30 @@ namespace Microsoft.DotNet.Cli.Sln.Internal protected override void ClearItems() { foreach (var it in this) + { it.ParentFile = null; + } base.ClearItems(); } } public class SlnSectionCollection : Collection { - SlnFile parentFile; + private SlnFile _parentFile; internal SlnFile ParentFile { get { - return parentFile; + return _parentFile; } set { - parentFile = value; + _parentFile = value; foreach (var it in this) - it.ParentFile = parentFile; + { + it.ParentFile = _parentFile; + } } } @@ -950,7 +1016,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public SlnSection GetOrCreateSection(string id, SlnSectionType sectionType) { if (id == null) + { throw new ArgumentNullException("id"); + } var sec = this.FirstOrDefault(s => s.Id == id); if (sec == null) { @@ -964,10 +1032,14 @@ namespace Microsoft.DotNet.Cli.Sln.Internal public void RemoveSection(string id) { if (id == null) + { throw new ArgumentNullException("id"); + } var s = GetSection(id); if (s != null) + { Remove(s); + } } protected override void InsertItem(int index, SlnSection item) @@ -992,18 +1064,20 @@ namespace Microsoft.DotNet.Cli.Sln.Internal protected override void ClearItems() { foreach (var it in this) + { it.ParentFile = null; + } base.ClearItems(); } } public class SlnPropertySetCollection : Collection { - SlnSection parentSection; + private SlnSection _parentSection; internal SlnPropertySetCollection(SlnSection parentSection) { - this.parentSection = parentSection; + _parentSection = parentSection; } public SlnPropertySet GetPropertySet(string id, bool ignoreCase = false) @@ -1026,13 +1100,13 @@ namespace Microsoft.DotNet.Cli.Sln.Internal protected override void InsertItem(int index, SlnPropertySet item) { base.InsertItem(index, item); - item.ParentSection = parentSection; + item.ParentSection = _parentSection; } protected override void SetItem(int index, SlnPropertySet item) { base.SetItem(index, item); - item.ParentSection = parentSection; + item.ParentSection = _parentSection; } protected override void RemoveItem(int index) @@ -1045,7 +1119,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal protected override void ClearItems() { foreach (var it in this) + { it.ParentSection = null; + } base.ClearItems(); } } @@ -1056,9 +1132,9 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { } - public InvalidSolutionFormatException(int line, string msg) : base("Invalid format in line " + line + ": " + msg) + public InvalidSolutionFormatException(int line, string msg) + : base("Invalid format in line " + line + ": " + msg) { - } } diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs index eef8acf4b..5a583fe62 100644 --- a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs +++ b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs @@ -372,7 +372,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration return (solutionFile == null) ? new List() : new List(solutionFile.Projects.Select(p => - Path.Combine(solutionFile.BaseDirectory.FullPath, Path.GetDirectoryName(p.FilePath)))); + Path.Combine(solutionFile.BaseDirectory, Path.GetDirectoryName(p.FilePath)))); } private static string ResolveRootDirectory(string projectPath) diff --git a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs index 6d75026cd..947d2729b 100644 --- a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs +++ b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs @@ -115,7 +115,7 @@ namespace Microsoft.DotNet.Tools.Migrate foreach (var project in _slnFile.Projects) { var projectDirectory = Path.Combine( - _slnFile.BaseDirectory.FullPath, + _slnFile.BaseDirectory, Path.GetDirectoryName(project.FilePath)); var csprojFiles = new DirectoryInfo(projectDirectory) @@ -129,8 +129,7 @@ namespace Microsoft.DotNet.Tools.Migrate } } - _slnFile.Write(Path.Combine(_slnFile.BaseDirectory.FullPath, - Path.GetFileName(_slnFile.FileName))); + _slnFile.Write(); } private void MoveProjectJsonArtifactsToBackup(MigrationReport migrationReport) @@ -404,13 +403,14 @@ namespace Microsoft.DotNet.Tools.Migrate throw new Exception($"Unable to find the solution file at {slnPath}"); } - _slnFile = new SlnFile(); - _slnFile.Read(slnPath); + _slnFile = SlnFile.Read(slnPath); foreach (var project in _slnFile.Projects) { - var projectFilePath = Path.Combine(_slnFile.BaseDirectory.FullPath, - Path.Combine(Path.GetDirectoryName(project.FilePath), Project.FileName)); + var projectFilePath = Path.Combine( + _slnFile.BaseDirectory, + Path.GetDirectoryName(project.FilePath), + Project.FileName); if (File.Exists(projectFilePath)) { diff --git a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs index 3e3a43a48..fe18cc5cd 100644 --- a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs +++ b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs @@ -21,17 +21,14 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.Tests var solutionFullPath = Path.Combine(solutionDirectory, "TestAppWithSln.sln"); - var slnFile = new SlnFile(); - slnFile.Read(solutionFullPath); + var slnFile = SlnFile.Read(solutionFullPath); slnFile.FormatVersion.Should().Be("12.00"); slnFile.ProductDescription.Should().Be("Visual Studio 14"); slnFile.VisualStudioVersion.Should().Be("14.0.25420.1"); slnFile.MinimumVisualStudioVersion.Should().Be("10.0.40219.1"); slnFile.BaseDirectory.Should().Be(solutionDirectory); - slnFile.FileName.FileName.Should().Be("TestAppWithSln.sln"); - - SlnFile.GetFileVersion(solutionFullPath).Should().Be("12.00"); + slnFile.FullPath.Should().Be(solutionFullPath); slnFile.Projects.Count.Should().Be(1); var project = slnFile.Projects[0]; @@ -49,8 +46,8 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.Tests var solutionFullPath = Path.Combine(solutionDirectory, "TestAppWithSln.sln"); - var slnFile = new SlnFile(); - slnFile.Read(solutionFullPath); + var slnFile = SlnFile.Read(solutionFullPath); + slnFile.FullPath.Should().Be(solutionFullPath); slnFile.Projects.Count.Should().Be(1); var project = slnFile.Projects[0]; @@ -62,23 +59,19 @@ namespace Microsoft.DotNet.Cli.Sln.Internal.Tests var newSolutionFullPath = Path.Combine(solutionDirectory, "TestAppWithSln_modified.sln"); slnFile.Write(newSolutionFullPath); - slnFile = new SlnFile(); - slnFile.Read(newSolutionFullPath); + slnFile = SlnFile.Read(newSolutionFullPath); slnFile.FormatVersion.Should().Be("12.00"); slnFile.ProductDescription.Should().Be("Visual Studio 14"); slnFile.VisualStudioVersion.Should().Be("14.0.25420.1"); slnFile.MinimumVisualStudioVersion.Should().Be("10.0.40219.1"); slnFile.BaseDirectory.Should().Be(solutionDirectory); - slnFile.FileName.FileName.Should().Be("TestAppWithSln_modified.sln"); - SlnFile.GetFileVersion(solutionFullPath).Should().Be("12.00"); + slnFile.FullPath.Should().Be(newSolutionFullPath); slnFile.Projects.Count.Should().Be(1); project = slnFile.Projects[0]; project.Id.Should().Be("{0138CB8F-4AA9-4029-A21E-C07C30F425BA}"); project.TypeGuid.Should().Be("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}"); project.Name.Should().Be("New Project Name"); project.FilePath.Should().Be("New File Path"); - slnFile.Projects.Count.Should().Be(1); - project = slnFile.Projects[0]; } } }