27 using System.Collections.Generic;
28 using System.Globalization;
31 using System.Text.RegularExpressions;
33 namespace SiliconStudio.Core.VisualStudio
35 internal class SolutionReader : IDisposable
37 private static readonly Regex RegexConvertEscapedValues =
new Regex(
@"\\u(?<HEXACODE>[0-9a-fA-F]{4})");
38 private static readonly Regex RegexParseGlobalSection =
new Regex(
@"^(?<TYPE>GlobalSection)\((?<NAME>.*)\) = (?<STEP>.*)$");
39 private static readonly Regex RegexParseProject =
new Regex(
"^Project\\(\"(?<PROJECTTYPEGUID>.*)\"\\)\\s*=\\s*\"(?<PROJECTNAME>.*)\"\\s*,\\s*\"(?<RELATIVEPATH>.*)\"\\s*,\\s*\"(?<PROJECTGUID>.*)\"$");
40 private static readonly Regex RegexParseProjectConfigurationPlatformsName =
new Regex(
@"^(?<GUID>\{[-0-9a-zA-Z]+\})\.(?<DESCRIPTION>.*)$");
41 private static readonly Regex RegexParseProjectSection =
new Regex(
@"^(?<TYPE>ProjectSection)\((?<NAME>.*)\) = (?<STEP>.*)$");
42 private static readonly Regex RegexParsePropertyLine =
new Regex(
@"^(?<PROPERTYNAME>[^=]*)\s*=\s*(?<PROPERTYVALUE>[^=]*)$");
43 private static readonly Regex RegexParseVersionControlName =
new Regex(
@"^(?<NAME_WITHOUT_INDEX>[a-zA-Z]*)(?<INDEX>[0-9]+)$");
44 private Solution solution;
45 private int currentLineNumber;
48 public SolutionReader(
string solutionFullPath) : this(new FileStream(solutionFullPath,
FileMode.
Open, FileAccess.
Read))
52 public SolutionReader(
Stream reader)
55 currentLineNumber = 0;
67 public Solution ReadSolutionFile()
71 solution =
new Solution();
73 for (
string line = ReadLine(); line != null; line = ReadLine())
76 if (
string.IsNullOrWhiteSpace(line))
81 if (line.StartsWith(
"Project(", StringComparison.InvariantCultureIgnoreCase))
83 solution.Projects.Add(ReadProject(line));
85 else if (
String.Compare(line,
"Global", StringComparison.InvariantCultureIgnoreCase) == 0)
91 else if (RegexParsePropertyLine.Match(line).Success)
94 solution.Properties.Add(ReadPropertyLine(line));
97 throw new SolutionFileException(
string.Format(
"Invalid line read on line #{0}.\nFound: {1}\nExpected: A line beginning with 'Project(' or 'Global'.",
106 private Project FindProjectByGuid(
string guid,
int lineNumber)
108 Project p = solution.Projects.FindByGuid(
new Guid(guid));
111 throw new SolutionFileException(
string.Format(
"Invalid guid found on line #{0}.\nFound: {1}\nExpected: A guid from one of the projects in the solution.",
118 private void HandleNestedProjects(
string name,
string type,
string step, List<PropertyItem> propertyLines,
int startLineNumber)
120 int localLineNumber = startLineNumber;
121 foreach (PropertyItem propertyLine
in propertyLines)
124 Project left = FindProjectByGuid(propertyLine.Name, localLineNumber);
125 left.ParentGuid =
new Guid(propertyLine.Value);
127 solution.GlobalSections.Add(
135 private void HandleProjectConfigurationPlatforms(
string name,
string type,
string step, List<PropertyItem> propertyLines,
int startLineNumber)
137 int localLineNumber = startLineNumber;
138 foreach (PropertyItem propertyLine
in propertyLines)
141 Match match = RegexParseProjectConfigurationPlatformsName.Match(propertyLine.Name);
144 throw new SolutionFileException(
string.Format(
"Invalid format for a project configuration name on line #{0}.\nFound: {1}",
150 string projectGuid = match.Groups[
"GUID"].Value;
151 string description = match.Groups[
"DESCRIPTION"].Value;
152 Project left = FindProjectByGuid(projectGuid, localLineNumber);
153 left.PlatformProperties.Add(
156 propertyLine.Value));
158 solution.GlobalSections.Add(
166 private void HandleVersionControlLines(
string name,
string type,
string step, List<PropertyItem> propertyLines)
168 var propertyLinesByIndex =
new Dictionary<int, List<PropertyItem>>();
169 var othersVersionControlLines =
new List<PropertyItem>();
170 foreach (PropertyItem propertyLine
in propertyLines)
172 Match match = RegexParseVersionControlName.Match(propertyLine.Name);
175 string nameWithoutIndex = match.Groups[
"NAME_WITHOUT_INDEX"].Value.Trim();
176 int index = int.Parse(match.Groups[
"INDEX"].Value.Trim());
178 if (!propertyLinesByIndex.ContainsKey(index))
180 propertyLinesByIndex[index] =
new List<PropertyItem>();
182 propertyLinesByIndex[index].Add(
new PropertyItem(nameWithoutIndex, propertyLine.Value));
187 if (propertyLine.Name !=
"SccNumberOfProjects")
189 othersVersionControlLines.Add(propertyLine);
195 othersVersionControlLines.Add(
new PropertyItem(
"SccLocalPath0",
"."));
197 foreach (var item
in propertyLinesByIndex)
199 int index = item.Key;
200 List<PropertyItem> propertiesForIndex = item.Value;
202 PropertyItem uniqueNameProperty = propertiesForIndex.Find(delegate(PropertyItem property) {
return property.Name ==
"SccProjectUniqueName"; });
205 if (uniqueNameProperty != null)
207 string uniqueName = RegexConvertEscapedValues.Replace(uniqueNameProperty.Value, delegate(Match match)
209 int hexaValue = int.Parse(match.Groups[
"HEXACODE"].Value, NumberStyles.AllowHexSpecifier);
210 return char.ConvertFromUtf32(hexaValue);
212 uniqueName = uniqueName.Replace(
@"\\",
@"\");
215 foreach (
Project project
in solution.Projects)
217 if (
string.Compare(project.RelativePath, uniqueName, StringComparison.InvariantCultureIgnoreCase) == 0)
219 relatedProject = project;
222 if (relatedProject == null)
224 throw new SolutionFileException(
225 string.Format(
"Invalid value for the property 'SccProjectUniqueName{0}' of the global section '{1}'.\nFound: {2}\nExpected: A value equal to the field 'RelativePath' of one of the projects in the solution.",
231 relatedProject.VersionControlProperties.AddRange(propertiesForIndex);
235 solution.GlobalSections.Add(
240 othersVersionControlLines));
243 private void ReadGlobal()
245 for (
string line = ReadLine(); !line.StartsWith(
"EndGlobal"); line = ReadLine())
247 ReadGlobalSection(line);
251 private void ReadGlobalSection(
string firstLine)
253 Match match = RegexParseGlobalSection.Match(firstLine);
256 throw new SolutionFileException(
string.Format(
"Invalid format for a global section on line #{0}.\nFound: {1}",
262 string type = match.Groups[
"TYPE"].Value.Trim();
263 string name = match.Groups[
"NAME"].Value.Trim();
264 string step = match.Groups[
"STEP"].Value.Trim();
266 var propertyLines =
new List<PropertyItem>();
267 int startLineNumber = currentLineNumber;
268 string endOfSectionToken =
"End" + type;
269 for (
string line = ReadLine(); !line.StartsWith(endOfSectionToken, StringComparison.InvariantCultureIgnoreCase); line = ReadLine())
271 propertyLines.Add(ReadPropertyLine(line));
276 case "NestedProjects":
277 HandleNestedProjects(name, type, step, propertyLines, startLineNumber);
280 case "ProjectConfigurationPlatforms":
281 HandleProjectConfigurationPlatforms(name, type, step, propertyLines, startLineNumber);
285 if (name.EndsWith(
"Control", StringComparison.InvariantCultureIgnoreCase))
287 HandleVersionControlLines(name, type, step, propertyLines);
291 solution.GlobalSections.Add(
302 private void ReadHeader()
304 for (
int i = 1; i <= 3; i++)
306 string line = ReadLine();
307 solution.Headers.Add(line);
308 if (line.StartsWith(
"#"))
315 private string ReadLine()
317 string line = reader.ReadLine();
320 throw new SolutionFileException(
"Unexpected end of file encounted while reading the solution file.");
327 private Project ReadProject(
string firstLine)
329 Match match = RegexParseProject.Match(firstLine);
332 throw new SolutionFileException(
string.Format(
"Invalid format for a project on line #{0}.\nFound: {1}.",
338 string projectTypeGuid = match.Groups[
"PROJECTTYPEGUID"].Value.Trim();
339 string projectName = match.Groups[
"PROJECTNAME"].Value.Trim();
340 string relativePath = match.Groups[
"RELATIVEPATH"].Value.Trim();
341 string projectGuid = match.Groups[
"PROJECTGUID"].Value.Trim();
343 var projectSections =
new List<Section>();
344 for (
string line = ReadLine(); !line.StartsWith(
"EndProject"); line = ReadLine())
346 projectSections.Add(ReadProjectSection(line));
351 new Guid(projectGuid),
352 new Guid(projectTypeGuid),
361 private Section ReadProjectSection(
string firstLine)
363 Match match = RegexParseProjectSection.Match(firstLine);
366 throw new SolutionFileException(
string.Format(
"Invalid format for a project section on line #{0}.\nFound: {1}.",
372 string type = match.Groups[
"TYPE"].Value.Trim();
373 string name = match.Groups[
"NAME"].Value.Trim();
374 string step = match.Groups[
"STEP"].Value.Trim();
376 var propertyLines =
new List<PropertyItem>();
377 string endOfSectionToken =
"End" + type;
378 for (
string line = ReadLine(); !line.StartsWith(endOfSectionToken, StringComparison.InvariantCultureIgnoreCase); line = ReadLine())
380 propertyLines.Add(ReadPropertyLine(line));
382 return new Section(name, type, step, propertyLines);
385 private PropertyItem ReadPropertyLine(
string line)
387 Match match = RegexParsePropertyLine.Match(line);
390 throw new SolutionFileException(
string.Format(
"Invalid format for a property on line #{0}.\nFound: {1}.",
396 return new PropertyItem(
397 match.Groups[
"PROPERTYNAME"].Value.Trim(),
398 match.Groups[
"PROPERTYVALUE"].Value.Trim());
System.Text.Encoding Encoding
System.IO.FileMode FileMode
Opens a file. The function fails if the file does not exist.