Skip to content
This repository has been archived by the owner on Dec 18, 2017. It is now read-only.

Commit

Permalink
Added ProjectFormatException and more detailed errors to DTH
Browse files Browse the repository at this point in the history
- ProjectFormatException has the project path, line and Column
(1 based), where the exception ocurred in the project.json file.
- Give more specific errors when they aren't just JsonReaderExceptions,
but semantic exceptions within the project format itself (beyond just JSON).
- Added Path, Line and Column to ErrorMessage in DTH

#1052
  • Loading branch information
davidfowl committed Jan 13, 2015
1 parent a7a9e92 commit 52f2bd6
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 16 deletions.
9 changes: 8 additions & 1 deletion src/Microsoft.Framework.DesignTimeHost/ApplicationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using System.Runtime.Versioning;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Framework.DesignTimeHost.Models;
using Microsoft.Framework.DesignTimeHost.Models.IncomingMessages;
using Microsoft.Framework.DesignTimeHost.Models.OutgoingMessages;
Expand Down Expand Up @@ -103,6 +102,14 @@ public void ProcessLoop(object state)
Message = ex.Message
};

var projectFormatException = ex as ProjectFormatException;
if (projectFormatException != null)
{
error.Path = projectFormatException.Path;
error.Line = projectFormatException.Line;
error.Column = projectFormatException.Column;
}

var message = new Message
{
ContextId = Id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ namespace Microsoft.Framework.DesignTimeHost.Models.OutgoingMessages
public class ErrorMessage
{
public string Message { get; set; }
public string Path { get; set; }
public int Line { get; set; }
public int Column { get; set; }
}
}
97 changes: 82 additions & 15 deletions src/Microsoft.Framework.Runtime/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet;

Expand Down Expand Up @@ -253,33 +255,67 @@ public static bool TryGetProject(string path, out Project project)
projectPath = Path.Combine(path, ProjectFileName);
}

var json = File.ReadAllText(projectPath);

// Assume the directory name is the project name if none was specified
var projectName = GetDirectoryName(path);
projectPath = Path.GetFullPath(projectPath);

project = GetProject(json, projectName, projectPath);
try
{
using (var stream = File.OpenRead(projectPath))
{
project = GetProject(stream, projectName, projectPath);
}
}
catch (JsonReaderException ex)
{
throw ProjectFormatException.Create(ex, projectPath);
}

return true;
}

public static Project GetProject(string json, string projectName, string projectPath)
{
var ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
return GetProject(ms, projectName, projectPath);
}

public static Project GetProject(Stream stream, string projectName, string projectPath)
{
var project = new Project();

var rawProject = JObject.Parse(json);
var reader = new JsonTextReader(new StreamReader(stream));
var rawProject = JObject.Load(reader);

// Metadata properties
var version = rawProject["version"];
var authors = rawProject["authors"];
var tags = rawProject["tags"];

project.Name = projectName;
project.Version = version == null ? new SemanticVersion("1.0.0") : new SemanticVersion(version.Value<string>());
project.ProjectFilePath = Path.GetFullPath(projectPath);

if (version == null)
{
project.Version = new SemanticVersion("1.0.0");
}
else
{
try
{
project.Version = new SemanticVersion(version.Value<string>());
}
catch (Exception ex)
{
var lineInfo = (IJsonLineInfo)version;

throw ProjectFormatException.Create(ex, version, project.ProjectFilePath);
}
}

project.Description = GetValue<string>(rawProject, "description");
project.Authors = authors == null ? new string[] { } : authors.ToObject<string[]>();
project.Dependencies = new List<LibraryDependency>();
project.ProjectFilePath = Path.GetFullPath(projectPath);
project.WebRoot = GetValue<string>(rawProject, "webroot");
project.EntryPoint = GetValue<string>(rawProject, "entryPoint");
project.ProjectUrl = GetValue<string>(rawProject, "projectUrl");
Expand Down Expand Up @@ -342,8 +378,10 @@ public static Project GetProject(string json, string projectName, string project
}
else
{
throw new InvalidDataException(string.Format(
"The value of a script in {0} can only be a string or an array of strings", ProjectFileName));
throw ProjectFormatException.Create(
string.Format("The value of a script in {0} can only be a string or an array of strings", ProjectFileName),
value,
project.ProjectFilePath);
}
}
}
Expand All @@ -357,6 +395,7 @@ public static Project GetProject(string json, string projectName, string project
project.BuildTargetFrameworksAndConfigurations(rawProject);

PopulateDependencies(
project.ProjectFilePath,
project.Dependencies,
rawProject,
"dependencies",
Expand Down Expand Up @@ -426,6 +465,7 @@ private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
}

private static void PopulateDependencies(
string projectPath,
IList<LibraryDependency> results,
JObject settings,
string propertyName,
Expand All @@ -436,9 +476,13 @@ private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
{
foreach (var dependency in dependencies)
{
if (String.IsNullOrEmpty(dependency.Key))
if (string.IsNullOrEmpty(dependency.Key))
{
throw new InvalidDataException("Unable to resolve dependency ''.");

throw ProjectFormatException.Create(
"Unable to resolve dependency ''.",
dependency.Value,
projectPath);
}

// Support
Expand All @@ -447,8 +491,11 @@ private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
// }

var dependencyValue = dependency.Value;
string dependencyVersionValue = null;
var dependencyTypeValue = LibraryDependencyType.Default;

string dependencyVersionValue = null;
JToken dependencyVersionToken = dependencyValue;

if (dependencyValue.Type == JTokenType.String)
{
dependencyVersionValue = dependencyValue.Value<string>();
Expand All @@ -457,7 +504,7 @@ private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
{
if (dependencyValue.Type == JTokenType.Object)
{
var dependencyVersionToken = dependencyValue["version"];
dependencyVersionToken = dependencyValue["version"];
if (dependencyVersionToken != null && dependencyVersionToken.Type == JTokenType.String)
{
dependencyVersionValue = dependencyVersionToken.Value<string>();
Expand All @@ -472,9 +519,20 @@ private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
}

SemanticVersion dependencyVersion = null;
if (!String.IsNullOrEmpty(dependencyVersionValue))

if (!string.IsNullOrEmpty(dependencyVersionValue))
{
dependencyVersion = SemanticVersion.Parse(dependencyVersionValue);
try
{
dependencyVersion = SemanticVersion.Parse(dependencyVersionValue);
}
catch (Exception ex)
{
throw ProjectFormatException.Create(
ex,
dependencyVersionToken,
projectPath);
}
}

results.Add(new LibraryDependency(
Expand Down Expand Up @@ -601,7 +659,14 @@ private void BuildTargetFrameworksAndConfigurations(JObject rawProject)
{
foreach (var framework in frameworks)
{
BuildTargetFrameworkNode(framework);
try
{
BuildTargetFrameworkNode(framework);
}
catch (Exception ex)
{
throw ProjectFormatException.Create(ex, framework.Value, ProjectFilePath);
}
}
}
}
Expand Down Expand Up @@ -642,13 +707,15 @@ private bool BuildTargetFrameworkNode(KeyValuePair<string, JToken> targetFramewo
var properties = targetFramework.Value.Value<JObject>();

PopulateDependencies(
ProjectFilePath,
targetFrameworkInformation.Dependencies,
properties,
"dependencies",
isGacOrFrameworkReference: false);

var frameworkAssemblies = new List<LibraryDependency>();
PopulateDependencies(
ProjectFilePath,
frameworkAssemblies,
properties,
"frameworkAssemblies",
Expand Down
64 changes: 64 additions & 0 deletions src/Microsoft.Framework.Runtime/ProjectFormatException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Microsoft.Framework.Runtime
{
public sealed class ProjectFormatException : Exception
{
public ProjectFormatException(string message) :
base(message)
{
}

public ProjectFormatException(string message, Exception innerException) :
base(message, innerException)
{

}

public string Path { get; private set; }
public int Line { get; private set; }
public int Column { get; private set; }

private ProjectFormatException WithLineInfo(IJsonLineInfo lineInfo)
{
Line = lineInfo.LineNumber;
Column = lineInfo.LinePosition;

return this;
}

public static ProjectFormatException Create(Exception exception, JToken value, string path)
{
var lineInfo = (IJsonLineInfo)value;

return new ProjectFormatException(exception.Message, exception)
{
Path = path
}
.WithLineInfo(lineInfo);
}

public static ProjectFormatException Create(string message, JToken value, string path)
{
var lineInfo = (IJsonLineInfo)value;

return new ProjectFormatException(message)
{
Path = path
}
.WithLineInfo(lineInfo);
}

internal static ProjectFormatException Create(JsonReaderException exception, string path)
{
return new ProjectFormatException(exception.Message, exception)
{
Path = path,
Column = exception.LineNumber,
Line = exception.LinePosition
};
}
}
}

0 comments on commit 52f2bd6

Please sign in to comment.