dotnet-installer/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs

1161 lines
34 KiB
C#
Raw Normal View History

//
// SlnFile.cs
//
// Author:
// Lluis Sanchez Gual <lluis@xamarin.com>
//
// Copyright (c) 2016 Xamarin, Inc (http://www.xamarin.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.Linq;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Collections;
using System.Globalization;
using System.Reflection;
using Microsoft.DotNet.Cli.Sln.Internal.FileManipulation;
using Microsoft.DotNet.Tools.Common;
namespace Microsoft.DotNet.Cli.Sln.Internal
{
public class SlnFile
{
2016-12-13 06:45:00 -10:00
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
{
2016-12-13 06:45:00 -10:00
get { return _metadata.GetValue("VisualStudioVersion"); }
set { _metadata.SetValue("VisualStudioVersion", value); }
}
public string MinimumVisualStudioVersion
{
2016-12-13 06:45:00 -10:00
get { return _metadata.GetValue("MinimumVisualStudioVersion"); }
set { _metadata.SetValue("MinimumVisualStudioVersion", value); }
}
2016-12-13 06:45:00 -10:00
public string BaseDirectory
{
2016-12-13 06:45:00 -10:00
get { return Path.GetDirectoryName(FullPath); }
}
2016-12-13 06:45:00 -10:00
public string FullPath { get; set; }
public SlnPropertySet SolutionConfigurationsSection
{
2016-12-13 06:45:00 -10:00
get
{
return _sections
.GetOrCreateSection("SolutionConfigurationPlatforms", SlnSectionType.PreProcess)
.Properties;
}
}
public SlnPropertySetCollection ProjectConfigurationsSection
{
2016-12-13 06:45:00 -10:00
get
{
return _sections
.GetOrCreateSection("ProjectConfigurationPlatforms", SlnSectionType.PostProcess)
.NestedPropertySets;
}
}
public SlnSectionCollection Sections
{
2016-12-13 06:45:00 -10:00
get { return _sections; }
}
public SlnProjectCollection Projects
{
2016-12-13 06:45:00 -10:00
get { return _projects; }
}
2016-12-13 06:45:00 -10:00
public SlnFile()
{
_projects.ParentFile = this;
_sections.ParentFile = this;
}
2016-12-13 06:45:00 -10:00
public static SlnFile Read(string file)
{
2016-12-13 06:45:00 -10:00
SlnFile slnFile = new SlnFile();
slnFile.FullPath = Path.GetFullPath(file);
slnFile._format = FileUtil.GetTextFormatInfo(file);
using (var sr = new StreamReader(new FileStream(file, FileMode.Open)))
2016-12-13 06:45:00 -10:00
{
slnFile.Read(sr);
}
return slnFile;
}
2016-12-13 06:45:00 -10:00
private void Read(TextReader reader)
{
string line;
int curLineNum = 0;
bool globalFound = false;
bool productRead = false;
while ((line = reader.ReadLine()) != null)
{
curLineNum++;
line = line.Trim();
if (line.StartsWith("Microsoft Visual Studio Solution File", StringComparison.Ordinal))
{
int i = line.LastIndexOf(' ');
if (i == -1)
2016-12-13 06:45:00 -10:00
{
throw new InvalidSolutionFormatException(curLineNum);
2016-12-13 06:45:00 -10:00
}
FormatVersion = line.Substring(i + 1);
2016-12-13 06:45:00 -10:00
_prefixBlankLines = curLineNum - 1;
}
if (line.StartsWith("# ", StringComparison.Ordinal))
{
if (!productRead)
{
productRead = true;
ProductDescription = line.Substring(2);
}
}
else if (line.StartsWith("Project", StringComparison.Ordinal))
{
SlnProject p = new SlnProject();
p.Read(reader, line, ref curLineNum);
2016-12-13 06:45:00 -10:00
_projects.Add(p);
}
else if (line == "Global")
{
if (globalFound)
2016-12-13 06:45:00 -10:00
{
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.GlobalSectionMoreThanOnceError);
2016-12-13 06:45:00 -10:00
}
globalFound = true;
while ((line = reader.ReadLine()) != null)
{
curLineNum++;
line = line.Trim();
if (line == "EndGlobal")
{
break;
}
else if (line.StartsWith("GlobalSection", StringComparison.Ordinal))
{
var sec = new SlnSection();
sec.Read(reader, line, ref curLineNum);
2016-12-13 06:45:00 -10:00
_sections.Add(sec);
}
else // Ignore text that's out of place
2016-12-13 06:45:00 -10:00
{
continue;
2016-12-13 06:45:00 -10:00
}
}
if (line == null)
2016-12-13 06:45:00 -10:00
{
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.GlobalSectionNotClosedError);
2016-12-13 06:45:00 -10:00
}
}
else if (line.IndexOf('=') != -1)
{
2016-12-13 06:45:00 -10:00
_metadata.ReadLine(line, curLineNum);
}
}
if (FormatVersion == null)
2016-12-13 06:45:00 -10:00
{
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.FileHeaderMissingError);
2016-12-13 06:45:00 -10:00
}
}
2016-12-13 06:45:00 -10:00
public void Write(string file = null)
{
2016-12-13 06:45:00 -10:00
if (!string.IsNullOrEmpty(file))
{
FullPath = Path.GetFullPath(file);
}
var sw = new StringWriter();
Write(sw);
2016-12-13 06:45:00 -10:00
File.WriteAllText(FullPath, sw.ToString());
}
2016-12-13 06:45:00 -10:00
private void Write(TextWriter writer)
{
2016-12-13 06:45:00 -10:00
writer.NewLine = _format.NewLine;
for (int n = 0; n < _prefixBlankLines; n++)
{
writer.WriteLine();
2016-12-13 06:45:00 -10:00
}
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version " + FormatVersion);
writer.WriteLine("# " + ProductDescription);
2016-12-13 06:45:00 -10:00
_metadata.Write(writer);
2016-12-13 06:45:00 -10:00
foreach (var p in _projects)
{
p.Write(writer);
2016-12-13 06:45:00 -10:00
}
writer.WriteLine("Global");
2016-12-13 06:45:00 -10:00
foreach (SlnSection s in _sections)
{
s.Write(writer, "GlobalSection");
2016-12-13 06:45:00 -10:00
}
writer.WriteLine("EndGlobal");
}
}
public class SlnProject
{
2016-12-13 06:45:00 -10:00
private SlnSectionCollection _sections = new SlnSectionCollection();
2016-12-13 06:45:00 -10:00
private SlnFile _parentFile;
public SlnFile ParentFile
{
get
{
2016-12-13 06:45:00 -10:00
return _parentFile;
}
internal set
{
2016-12-13 06:45:00 -10:00
_parentFile = value;
_sections.ParentFile = _parentFile;
}
}
public string Id { get; set; }
public string TypeGuid { get; set; }
public string Name { get; set; }
private string _filePath;
public string FilePath
{
get
{
return _filePath;
}
set
{
_filePath = PathUtility.GetPathWithDirectorySeparator(value);
}
}
public int Line { get; private set; }
internal bool Processed { get; set; }
public SlnSectionCollection Sections
{
2016-12-13 06:45:00 -10:00
get { return _sections; }
}
internal void Read(TextReader reader, string line, ref int curLineNum)
{
Line = curLineNum;
int n = 0;
FindNext(curLineNum, line, ref n, '(');
n++;
FindNext(curLineNum, line, ref n, '"');
int n2 = n + 1;
FindNext(curLineNum, line, ref n2, '"');
TypeGuid = line.Substring(n + 1, n2 - n - 1);
n = n2 + 1;
FindNext(curLineNum, line, ref n, ')');
FindNext(curLineNum, line, ref n, '=');
FindNext(curLineNum, line, ref n, '"');
n2 = n + 1;
FindNext(curLineNum, line, ref n2, '"');
Name = line.Substring(n + 1, n2 - n - 1);
n = n2 + 1;
FindNext(curLineNum, line, ref n, ',');
FindNext(curLineNum, line, ref n, '"');
n2 = n + 1;
FindNext(curLineNum, line, ref n2, '"');
FilePath = line.Substring(n + 1, n2 - n - 1);
n = n2 + 1;
FindNext(curLineNum, line, ref n, ',');
FindNext(curLineNum, line, ref n, '"');
n2 = n + 1;
FindNext(curLineNum, line, ref n2, '"');
Id = line.Substring(n + 1, n2 - n - 1);
while ((line = reader.ReadLine()) != null)
{
curLineNum++;
line = line.Trim();
if (line == "EndProject")
{
return;
}
if (line.StartsWith("ProjectSection", StringComparison.Ordinal))
{
2016-12-13 06:45:00 -10:00
if (_sections == null)
{
_sections = new SlnSectionCollection();
}
var sec = new SlnSection();
2016-12-13 06:45:00 -10:00
_sections.Add(sec);
sec.Read(reader, line, ref curLineNum);
}
}
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.ProjectSectionNotClosedError);
}
2016-12-13 06:45:00 -10:00
private void FindNext(int ln, string line, ref int i, char c)
{
i = line.IndexOf(c, i);
if (i == -1)
2016-12-13 06:45:00 -10:00
{
throw new InvalidSolutionFormatException(ln);
2016-12-13 06:45:00 -10:00
}
}
2016-12-13 06:45:00 -10:00
internal void Write(TextWriter writer)
{
writer.Write("Project(\"");
writer.Write(TypeGuid);
writer.Write("\") = \"");
writer.Write(Name);
writer.Write("\", \"");
writer.Write(PathUtility.GetPathWithBackSlashes(FilePath));
writer.Write("\", \"");
writer.Write(Id);
writer.WriteLine("\"");
2016-12-13 06:45:00 -10:00
if (_sections != null)
{
2016-12-13 06:45:00 -10:00
foreach (SlnSection s in _sections)
{
s.Write(writer, "ProjectSection");
2016-12-13 06:45:00 -10:00
}
}
writer.WriteLine("EndProject");
}
}
public class SlnSection
{
2016-12-13 06:45:00 -10:00
private SlnPropertySetCollection _nestedPropertySets;
private SlnPropertySet _properties;
private List<string> _sectionLines;
private int _baseIndex;
public string Id { get; set; }
public int Line { get; private set; }
internal bool Processed { get; set; }
public SlnFile ParentFile { get; internal set; }
public bool IsEmpty
{
get
{
2016-12-13 06:45:00 -10:00
return (_properties == null || _properties.Count == 0) &&
(_nestedPropertySets == null || _nestedPropertySets.All(t => t.IsEmpty)) &&
(_sectionLines == null || _sectionLines.Count == 0);
}
}
/// <summary>
/// If true, this section won't be written to the file if it is empty
/// </summary>
/// <value><c>true</c> if skip if empty; otherwise, <c>false</c>.</value>
public bool SkipIfEmpty { get; set; }
public void Clear()
{
2016-12-13 06:45:00 -10:00
_properties = null;
_nestedPropertySets = null;
_sectionLines = null;
}
public SlnPropertySet Properties
{
get
{
2016-12-13 06:45:00 -10:00
if (_properties == null)
{
2016-12-13 06:45:00 -10:00
_properties = new SlnPropertySet();
_properties.ParentSection = this;
if (_sectionLines != null)
{
2016-12-13 06:45:00 -10:00
foreach (var line in _sectionLines)
{
_properties.ReadLine(line, Line);
}
_sectionLines = null;
}
}
2016-12-13 06:45:00 -10:00
return _properties;
}
}
public SlnPropertySetCollection NestedPropertySets
{
get
{
2016-12-13 06:45:00 -10:00
if (_nestedPropertySets == null)
{
2016-12-13 06:45:00 -10:00
_nestedPropertySets = new SlnPropertySetCollection(this);
if (_sectionLines != null)
{
LoadPropertySets();
2016-12-13 06:45:00 -10:00
}
}
2016-12-13 06:45:00 -10:00
return _nestedPropertySets;
}
}
public void SetContent(IEnumerable<KeyValuePair<string, string>> lines)
{
2016-12-13 06:45:00 -10:00
_sectionLines = new List<string>(lines.Select(p => p.Key + " = " + p.Value));
_properties = null;
_nestedPropertySets = null;
}
public IEnumerable<KeyValuePair<string, string>> GetContent()
{
2016-12-13 06:45:00 -10:00
if (_sectionLines != null)
{
return _sectionLines.Select(li =>
{
int i = li.IndexOf('=');
if (i != -1)
2016-12-13 06:45:00 -10:00
{
return new KeyValuePair<string, string>(li.Substring(0, i).Trim(), li.Substring(i + 1).Trim());
2016-12-13 06:45:00 -10:00
}
else
2016-12-13 06:45:00 -10:00
{
return new KeyValuePair<string, string>(li.Trim(), "");
2016-12-13 06:45:00 -10:00
}
});
2016-12-13 06:45:00 -10:00
}
else
2016-12-13 06:45:00 -10:00
{
return new KeyValuePair<string, string>[0];
2016-12-13 06:45:00 -10:00
}
}
public SlnSectionType SectionType { get; set; }
2016-12-13 06:45:00 -10:00
private SlnSectionType ToSectionType(int curLineNum, string s)
{
if (s == "preSolution" || s == "preProject")
2016-12-13 06:45:00 -10:00
{
return SlnSectionType.PreProcess;
2016-12-13 06:45:00 -10:00
}
if (s == "postSolution" || s == "postProject")
2016-12-13 06:45:00 -10:00
{
return SlnSectionType.PostProcess;
2016-12-13 06:45:00 -10:00
}
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, String.Format(LocalizableStrings.InvalidSectionTypeError, s));
}
2016-12-13 06:45:00 -10:00
private string FromSectionType(bool isProjectSection, SlnSectionType type)
{
if (type == SlnSectionType.PreProcess)
2016-12-13 06:45:00 -10:00
{
return isProjectSection ? "preProject" : "preSolution";
2016-12-13 06:45:00 -10:00
}
else
2016-12-13 06:45:00 -10:00
{
return isProjectSection ? "postProject" : "postSolution";
2016-12-13 06:45:00 -10:00
}
}
internal void Read(TextReader reader, string line, ref int curLineNum)
{
Line = curLineNum;
int k = line.IndexOf('(');
if (k == -1)
2016-12-13 06:45:00 -10:00
{
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.SectionIdMissingError);
2016-12-13 06:45:00 -10:00
}
var tag = line.Substring(0, k).Trim();
var k2 = line.IndexOf(')', k);
if (k2 == -1)
2016-12-13 06:45:00 -10:00
{
throw new InvalidSolutionFormatException(curLineNum);
2016-12-13 06:45:00 -10:00
}
Id = line.Substring(k + 1, k2 - k - 1);
k = line.IndexOf('=', k2);
SectionType = ToSectionType(curLineNum, line.Substring(k + 1).Trim());
var endTag = "End" + tag;
2016-12-13 06:45:00 -10:00
_sectionLines = new List<string>();
_baseIndex = ++curLineNum;
while ((line = reader.ReadLine()) != null)
{
curLineNum++;
line = line.Trim();
if (line == endTag)
2016-12-13 06:45:00 -10:00
{
break;
2016-12-13 06:45:00 -10:00
}
_sectionLines.Add(line);
}
if (line == null)
2016-12-13 06:45:00 -10:00
{
2016-12-16 21:45:55 -08:00
throw new InvalidSolutionFormatException(curLineNum, LocalizableStrings.ClosingSectionTagNotFoundError);
2016-12-13 06:45:00 -10:00
}
}
2016-12-13 06:45:00 -10:00
private void LoadPropertySets()
{
2016-12-13 06:45:00 -10:00
if (_sectionLines != null)
{
SlnPropertySet curSet = null;
2016-12-13 06:45:00 -10:00
for (int n = 0; n < _sectionLines.Count; n++)
{
2016-12-13 06:45:00 -10:00
var line = _sectionLines[n];
if (string.IsNullOrEmpty(line.Trim()))
2016-12-13 06:45:00 -10:00
{
continue;
2016-12-13 06:45:00 -10:00
}
var i = line.IndexOf('.');
if (i == -1)
2016-12-13 06:45:00 -10:00
{
throw new InvalidSolutionFormatException(_baseIndex + n);
}
var id = line.Substring(0, i);
if (curSet == null || id != curSet.Id)
{
curSet = new SlnPropertySet(id);
2016-12-13 06:45:00 -10:00
_nestedPropertySets.Add(curSet);
}
2016-12-13 06:45:00 -10:00
curSet.ReadLine(line.Substring(i + 1), _baseIndex + n);
}
2016-12-13 06:45:00 -10:00
_sectionLines = null;
}
}
internal void Write(TextWriter writer, string sectionTag)
{
if (SkipIfEmpty && IsEmpty)
2016-12-13 06:45:00 -10:00
{
return;
2016-12-13 06:45:00 -10:00
}
writer.Write("\t");
writer.Write(sectionTag);
writer.Write('(');
writer.Write(Id);
writer.Write(") = ");
writer.WriteLine(FromSectionType(sectionTag == "ProjectSection", SectionType));
2016-12-13 06:45:00 -10:00
if (_sectionLines != null)
{
2016-12-13 06:45:00 -10:00
foreach (var l in _sectionLines)
{
writer.WriteLine("\t\t" + l);
2016-12-13 06:45:00 -10:00
}
}
else if (_properties != null)
{
_properties.Write(writer);
}
2016-12-13 06:45:00 -10:00
else if (_nestedPropertySets != null)
{
2016-12-13 06:45:00 -10:00
foreach (var ps in _nestedPropertySets)
{
ps.Write(writer);
2016-12-13 06:45:00 -10:00
}
}
writer.WriteLine("\tEnd" + sectionTag);
}
}
/// <summary>
/// A collection of properties
/// </summary>
public class SlnPropertySet : IDictionary<string, string>
{
2016-12-13 06:45:00 -10:00
private OrderedDictionary _values = new OrderedDictionary();
private bool _isMetadata;
internal bool Processed { get; set; }
public SlnFile ParentFile
{
get { return ParentSection != null ? ParentSection.ParentFile : null; }
}
public SlnSection ParentSection { get; set; }
/// <summary>
/// Text file line of this section in the original file
/// </summary>
/// <value>The line.</value>
public int Line { get; private set; }
internal SlnPropertySet()
{
}
/// <summary>
/// Creates a new property set with the specified ID
/// </summary>
/// <param name="id">Identifier.</param>
public SlnPropertySet(string id)
{
Id = id;
}
internal SlnPropertySet(bool isMetadata)
{
2016-12-13 06:45:00 -10:00
_isMetadata = isMetadata;
}
public bool IsEmpty
{
get
{
2016-12-13 06:45:00 -10:00
return _values.Count == 0;
}
}
internal void ReadLine(string line, int currentLine)
{
if (Line == 0)
2016-12-13 06:45:00 -10:00
{
Line = currentLine;
2016-12-13 06:45:00 -10:00
}
int k = line.IndexOf('=');
if (k != -1)
{
var name = line.Substring(0, k).Trim();
var val = line.Substring(k + 1).Trim();
2016-12-13 06:45:00 -10:00
_values[name] = val;
}
else
{
line = line.Trim();
if (!string.IsNullOrWhiteSpace(line))
2016-12-13 06:45:00 -10:00
{
_values.Add(line, null);
}
}
}
internal void Write(TextWriter writer)
{
2016-12-13 06:45:00 -10:00
foreach (DictionaryEntry e in _values)
{
2016-12-13 06:45:00 -10:00
if (!_isMetadata)
{
writer.Write("\t\t");
2016-12-13 06:45:00 -10:00
}
if (Id != null)
2016-12-13 06:45:00 -10:00
{
writer.Write(Id + ".");
2016-12-13 06:45:00 -10:00
}
writer.WriteLine(e.Key + " = " + e.Value);
}
}
public string Id { get; private set; }
public string GetValue(string name, string defaultValue = null)
{
string res;
if (TryGetValue(name, out res))
2016-12-13 06:45:00 -10:00
{
return res;
2016-12-13 06:45:00 -10:00
}
else
2016-12-13 06:45:00 -10:00
{
return defaultValue;
2016-12-13 06:45:00 -10:00
}
}
public T GetValue<T>(string name)
{
return (T)GetValue(name, typeof(T), default(T));
}
public T GetValue<T>(string name, T defaultValue)
{
return (T)GetValue(name, typeof(T), defaultValue);
}
public object GetValue(string name, Type t, object defaultValue)
{
string val;
if (TryGetValue(name, out val))
{
if (t == typeof(bool))
2016-12-13 06:45:00 -10:00
{
return (object)val.Equals("true", StringComparison.OrdinalIgnoreCase);
2016-12-13 06:45:00 -10:00
}
if (t.GetTypeInfo().IsEnum)
2016-12-13 06:45:00 -10:00
{
return Enum.Parse(t, val, true);
2016-12-13 06:45:00 -10:00
}
if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var at = t.GetTypeInfo().GetGenericArguments()[0];
if (string.IsNullOrEmpty(val))
2016-12-13 06:45:00 -10:00
{
return null;
2016-12-13 06:45:00 -10:00
}
return Convert.ChangeType(val, at, CultureInfo.InvariantCulture);
}
return Convert.ChangeType(val, t, CultureInfo.InvariantCulture);
}
else
2016-12-13 06:45:00 -10:00
{
return defaultValue;
2016-12-13 06:45:00 -10:00
}
}
public void SetValue(string name, string value, string defaultValue = null, bool preserveExistingCase = false)
{
if (value == null && defaultValue == "")
2016-12-13 06:45:00 -10:00
{
value = "";
2016-12-13 06:45:00 -10:00
}
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;
2016-12-13 06:45:00 -10:00
if (TryGetValue(name, out res) &&
!string.Equals(defaultValue ?? "",
res, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
{
Remove(name);
2016-12-13 06:45:00 -10:00
}
return;
}
string currentValue;
2016-12-13 06:45:00 -10:00
if (preserveExistingCase && TryGetValue(name, out currentValue) &&
string.Equals(value, currentValue, StringComparison.OrdinalIgnoreCase))
{
return;
2016-12-13 06:45:00 -10:00
}
_values[name] = value;
}
public void SetValue(string name, object value, object defaultValue = null)
{
var isDefault = object.Equals(value, defaultValue);
if (isDefault)
{
// if the value is default, only remove the property if it was not already the default
// to avoid unnecessary project file churn
2016-12-13 06:45:00 -10:00
if (ContainsKey(name) && (defaultValue == null ||
!object.Equals(defaultValue, GetValue(name, defaultValue.GetType(), null))))
{
Remove(name);
2016-12-13 06:45:00 -10:00
}
return;
}
if (value is bool)
2016-12-13 06:45:00 -10:00
{
_values[name] = (bool)value ? "TRUE" : "FALSE";
}
else
2016-12-13 06:45:00 -10:00
{
_values[name] = Convert.ToString(value, CultureInfo.InvariantCulture);
}
}
void IDictionary<string, string>.Add(string key, string value)
{
SetValue(key, value);
}
public bool ContainsKey(string key)
{
2016-12-13 06:45:00 -10:00
return _values.Contains(key);
}
public bool Remove(string key)
{
2016-12-13 06:45:00 -10:00
var wasThere = _values.Contains(key);
_values.Remove(key);
return wasThere;
}
public bool TryGetValue(string key, out string value)
{
2016-12-13 06:45:00 -10:00
value = (string)_values[key];
return value != null;
}
public string this[string index]
{
get
{
2016-12-13 06:45:00 -10:00
return (string)_values[index];
}
set
{
2016-12-13 06:45:00 -10:00
_values[index] = value;
}
}
public ICollection<string> Values
{
get
{
2016-12-13 06:45:00 -10:00
return _values.Values.Cast<string>().ToList();
}
}
public ICollection<string> Keys
{
2016-12-13 06:45:00 -10:00
get { return _values.Keys.Cast<string>().ToList(); }
}
void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item)
{
SetValue(item.Key, item.Value);
}
public void Clear()
{
2016-12-13 06:45:00 -10:00
_values.Clear();
}
internal void ClearExcept(HashSet<string> keys)
{
2016-12-13 06:45:00 -10:00
foreach (var k in _values.Keys.Cast<string>().Except(keys).ToArray())
{
_values.Remove(k);
}
}
bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item)
{
var val = GetValue(item.Key);
return val == item.Value;
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
2016-12-13 06:45:00 -10:00
foreach (DictionaryEntry de in _values)
{
array[arrayIndex++] = new KeyValuePair<string, string>((string)de.Key, (string)de.Value);
2016-12-13 06:45:00 -10:00
}
}
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
{
if (((ICollection<KeyValuePair<string, string>>)this).Contains(item))
{
Remove(item.Key);
return true;
}
else
2016-12-13 06:45:00 -10:00
{
return false;
2016-12-13 06:45:00 -10:00
}
}
public int Count
{
get
{
2016-12-13 06:45:00 -10:00
return _values.Count;
}
}
internal void SetLines(IEnumerable<KeyValuePair<string, string>> lines)
{
2016-12-13 06:45:00 -10:00
_values.Clear();
foreach (var line in lines)
2016-12-13 06:45:00 -10:00
{
_values[line.Key] = line.Value;
}
}
bool ICollection<KeyValuePair<string, string>>.IsReadOnly
{
get
{
return false;
}
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
2016-12-13 06:45:00 -10:00
foreach (DictionaryEntry de in _values)
{
yield return new KeyValuePair<string, string>((string)de.Key, (string)de.Value);
2016-12-13 06:45:00 -10:00
}
}
IEnumerator IEnumerable.GetEnumerator()
{
2016-12-13 06:45:00 -10:00
foreach (DictionaryEntry de in _values)
{
yield return new KeyValuePair<string, string>((string)de.Key, (string)de.Value);
2016-12-13 06:45:00 -10:00
}
}
}
public class SlnProjectCollection : Collection<SlnProject>
{
2016-12-13 06:45:00 -10:00
private SlnFile _parentFile;
internal SlnFile ParentFile
{
get
{
2016-12-13 06:45:00 -10:00
return _parentFile;
}
set
{
2016-12-13 06:45:00 -10:00
_parentFile = value;
foreach (var it in this)
2016-12-13 06:45:00 -10:00
{
it.ParentFile = _parentFile;
}
}
}
public SlnProject GetProject(string id)
{
return this.FirstOrDefault(s => s.Id == id);
}
public SlnProject GetOrCreateProject(string id)
{
var p = this.FirstOrDefault(s => s.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
if (p == null)
{
p = new SlnProject { Id = id };
Add(p);
}
return p;
}
protected override void InsertItem(int index, SlnProject item)
{
base.InsertItem(index, item);
item.ParentFile = ParentFile;
}
protected override void SetItem(int index, SlnProject item)
{
base.SetItem(index, item);
item.ParentFile = ParentFile;
}
protected override void RemoveItem(int index)
{
var it = this[index];
it.ParentFile = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var it in this)
2016-12-13 06:45:00 -10:00
{
it.ParentFile = null;
2016-12-13 06:45:00 -10:00
}
base.ClearItems();
}
}
public class SlnSectionCollection : Collection<SlnSection>
{
2016-12-13 06:45:00 -10:00
private SlnFile _parentFile;
internal SlnFile ParentFile
{
get
{
2016-12-13 06:45:00 -10:00
return _parentFile;
}
set
{
2016-12-13 06:45:00 -10:00
_parentFile = value;
foreach (var it in this)
2016-12-13 06:45:00 -10:00
{
it.ParentFile = _parentFile;
}
}
}
public SlnSection GetSection(string id)
{
return this.FirstOrDefault(s => s.Id == id);
}
public SlnSection GetSection(string id, SlnSectionType sectionType)
{
return this.FirstOrDefault(s => s.Id == id && s.SectionType == sectionType);
}
public SlnSection GetOrCreateSection(string id, SlnSectionType sectionType)
{
if (id == null)
2016-12-13 06:45:00 -10:00
{
throw new ArgumentNullException("id");
2016-12-13 06:45:00 -10:00
}
var sec = this.FirstOrDefault(s => s.Id == id);
if (sec == null)
{
sec = new SlnSection { Id = id };
sec.SectionType = sectionType;
Add(sec);
}
return sec;
}
public void RemoveSection(string id)
{
if (id == null)
2016-12-13 06:45:00 -10:00
{
throw new ArgumentNullException("id");
2016-12-13 06:45:00 -10:00
}
var s = GetSection(id);
if (s != null)
2016-12-13 06:45:00 -10:00
{
Remove(s);
2016-12-13 06:45:00 -10:00
}
}
protected override void InsertItem(int index, SlnSection item)
{
base.InsertItem(index, item);
item.ParentFile = ParentFile;
}
protected override void SetItem(int index, SlnSection item)
{
base.SetItem(index, item);
item.ParentFile = ParentFile;
}
protected override void RemoveItem(int index)
{
var it = this[index];
it.ParentFile = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var it in this)
2016-12-13 06:45:00 -10:00
{
it.ParentFile = null;
2016-12-13 06:45:00 -10:00
}
base.ClearItems();
}
}
public class SlnPropertySetCollection : Collection<SlnPropertySet>
{
2016-12-13 06:45:00 -10:00
private SlnSection _parentSection;
internal SlnPropertySetCollection(SlnSection parentSection)
{
2016-12-13 06:45:00 -10:00
_parentSection = parentSection;
}
public SlnPropertySet GetPropertySet(string id, bool ignoreCase = false)
{
var sc = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return this.FirstOrDefault(s => s.Id.Equals(id, sc));
}
public SlnPropertySet GetOrCreatePropertySet(string id, bool ignoreCase = false)
{
var ps = GetPropertySet(id, ignoreCase);
if (ps == null)
{
ps = new SlnPropertySet(id);
Add(ps);
}
return ps;
}
protected override void InsertItem(int index, SlnPropertySet item)
{
base.InsertItem(index, item);
2016-12-13 06:45:00 -10:00
item.ParentSection = _parentSection;
}
protected override void SetItem(int index, SlnPropertySet item)
{
base.SetItem(index, item);
2016-12-13 06:45:00 -10:00
item.ParentSection = _parentSection;
}
protected override void RemoveItem(int index)
{
var it = this[index];
it.ParentSection = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var it in this)
2016-12-13 06:45:00 -10:00
{
it.ParentSection = null;
2016-12-13 06:45:00 -10:00
}
base.ClearItems();
}
}
class InvalidSolutionFormatException : Exception
{
public InvalidSolutionFormatException(int line) : base("Invalid format in line " + line)
{
}
2016-12-13 06:45:00 -10:00
public InvalidSolutionFormatException(int line, string msg)
: base("Invalid format in line " + line + ": " + msg)
{
}
}
public enum SlnSectionType
{
PreProcess,
PostProcess
}
}