355 lines
12 KiB
C#
355 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
|
|
namespace MontoyaTech.Rest.Net
|
|
{
|
|
/// <summary>
|
|
/// A class that can take a set of routes and generate code
|
|
/// for a client that can be used to interact with them.
|
|
/// </summary>
|
|
public class RestClientGenerator
|
|
{
|
|
/// <summary>
|
|
/// The name of the client to generate.
|
|
/// </summary>
|
|
public string ClientName = "Client";
|
|
|
|
/// <summary>
|
|
/// Returns whether or not a given type belongs to DotNet.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
protected virtual bool IsTypeDotNet(Type type)
|
|
{
|
|
if (type.Assembly.GetName().Name == "System.Private.CoreLib")
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the dependencies for a given type and returns them.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
protected virtual List<Type> FindTypeDependencies(Type type)
|
|
{
|
|
var dependencies = new HashSet<Type>();
|
|
|
|
if (this.IsTypeDotNet(type))
|
|
return dependencies.ToList();
|
|
|
|
dependencies.Add(type);
|
|
|
|
if (type.IsEnum)
|
|
return dependencies.ToList();
|
|
|
|
{
|
|
var types = this.FindTypeDependencies(type.BaseType);
|
|
|
|
for (int i = 0; i < types.Count; i++)
|
|
if (!dependencies.Contains(types[i]))
|
|
dependencies.Add(types[i]);
|
|
}
|
|
|
|
var arguments = type.GetGenericArguments();
|
|
|
|
if (arguments != null)
|
|
{
|
|
foreach (var argument in arguments)
|
|
{
|
|
var types = this.FindTypeDependencies(argument);
|
|
|
|
for (int i = 0; i < types.Count; i++)
|
|
if (!dependencies.Contains(types[i]))
|
|
dependencies.Add(types[i]);
|
|
}
|
|
}
|
|
|
|
var fields = type.GetFields();
|
|
|
|
if (fields != null)
|
|
{
|
|
foreach (var field in fields)
|
|
{
|
|
if (field.IsPublic && !field.IsSpecialName)
|
|
{
|
|
var types = this.FindTypeDependencies(field.FieldType);
|
|
|
|
for (int i = 0; i < types.Count; i++)
|
|
if (!dependencies.Contains(types[i]))
|
|
dependencies.Add(types[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
var properties = type.GetProperties();
|
|
|
|
if (properties != null)
|
|
{
|
|
foreach (var property in properties)
|
|
{
|
|
if (!property.IsSpecialName && property.GetSetMethod() != null && property.GetGetMethod() != null)
|
|
{
|
|
var types = this.FindTypeDependencies(property.PropertyType);
|
|
|
|
for (int i = 0; i < types.Count; i++)
|
|
if (!dependencies.Contains(types[i]))
|
|
dependencies.Add(types[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dependencies.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the types that a given route depends on to function.
|
|
/// </summary>
|
|
/// <param name="route"></param>
|
|
/// <returns></returns>
|
|
protected virtual List<Type> FindRouteDependencies(Route route)
|
|
{
|
|
var dependencies = new HashSet<Type>();
|
|
|
|
var methodInfo = route.GetTarget().GetMethodInfo();
|
|
|
|
//If this route is hidden, return nothing.
|
|
if (methodInfo.GetCustomAttribute<RouteHidden>() != null)
|
|
return dependencies.ToList();
|
|
|
|
if (methodInfo != null)
|
|
{
|
|
var parameters = methodInfo.GetParameters();
|
|
|
|
if (parameters != null)
|
|
{
|
|
for (int i = 1; i < parameters.Length; i++)
|
|
{
|
|
var types = this.FindTypeDependencies(parameters[i].ParameterType);
|
|
|
|
foreach (var type in types)
|
|
if (!dependencies.Contains(type))
|
|
dependencies.Add(type);
|
|
}
|
|
}
|
|
|
|
var routeRequest = methodInfo.GetCustomAttribute<RouteRequest>();
|
|
|
|
if (routeRequest != null)
|
|
{
|
|
var types = this.FindTypeDependencies(routeRequest.RequestType);
|
|
|
|
foreach (var type in types)
|
|
if (!dependencies.Contains(type))
|
|
dependencies.Add(type);
|
|
}
|
|
|
|
var routeResponse = methodInfo.GetCustomAttribute<RouteResponse>();
|
|
|
|
if (routeResponse != null)
|
|
{
|
|
var types = this.FindTypeDependencies(routeResponse.ResponseType);
|
|
|
|
foreach (var type in types)
|
|
if (!dependencies.Contains(type))
|
|
dependencies.Add(type);
|
|
}
|
|
}
|
|
|
|
return dependencies.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the types that a given set of routes depend on to function.
|
|
/// </summary>
|
|
/// <param name="routes"></param>
|
|
/// <returns></returns>
|
|
protected virtual List<Type> FindRoutesDependencies(List<Route> routes)
|
|
{
|
|
var dependencies = new HashSet<Type>();
|
|
|
|
foreach (var route in routes)
|
|
{
|
|
var types = this.FindRouteDependencies(route);
|
|
|
|
if (types != null)
|
|
for (int i = 0; i < types.Count; i++)
|
|
if (!dependencies.Contains(types[i]))
|
|
dependencies.Add(types[i]);
|
|
}
|
|
|
|
return dependencies.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the fully resolve name for a given type.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
protected virtual string GetTypeFullyResolvedName(Type type)
|
|
{
|
|
if (this.IsTypeDotNet(type))
|
|
{
|
|
var typeCode = Type.GetTypeCode(type);
|
|
|
|
switch (typeCode)
|
|
{
|
|
case TypeCode.Boolean:
|
|
return "bool";
|
|
case TypeCode.Byte:
|
|
return "byte";
|
|
case TypeCode.Char:
|
|
return "char";
|
|
case TypeCode.DateTime:
|
|
return "DateTime";
|
|
case TypeCode.Decimal:
|
|
return "decimal";
|
|
case TypeCode.Double:
|
|
return "double";
|
|
case TypeCode.Int16:
|
|
return "short";
|
|
case TypeCode.Int32:
|
|
return "int";
|
|
case TypeCode.Int64:
|
|
return "long";
|
|
case TypeCode.SByte:
|
|
return "sbyte";
|
|
case TypeCode.Single:
|
|
return "float";
|
|
case TypeCode.String:
|
|
return "string";
|
|
case TypeCode.UInt16:
|
|
return "ushort";
|
|
case TypeCode.UInt32:
|
|
return "uint";
|
|
case TypeCode.UInt64:
|
|
return "ulong";
|
|
default:
|
|
var builder = new StringBuilder();
|
|
|
|
builder.Append(type.Namespace);
|
|
|
|
int genericSymbol = type.Name.IndexOf('`');
|
|
|
|
if (genericSymbol == -1)
|
|
builder.Append(".").Append(type.Name);
|
|
else
|
|
builder.Append(".").Append(type.Name.Substring(0, genericSymbol));
|
|
|
|
var genericArguments = type.GetGenericArguments();
|
|
|
|
if (genericArguments != null && genericArguments.Length > 0)
|
|
{
|
|
builder.Append("<");
|
|
|
|
for (int i = 0; i < genericArguments.Length; i++)
|
|
{
|
|
if (i > 0)
|
|
builder.Append(", ");
|
|
|
|
builder.Append(this.GetTypeFullyResolvedName(genericArguments[i]));
|
|
}
|
|
|
|
builder.Append(">");
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var builder = new StringBuilder();
|
|
|
|
int genericSymbol = type.Name.IndexOf('`');
|
|
|
|
if (genericSymbol == -1)
|
|
builder.Append(type.Name);
|
|
else
|
|
builder.Append(type.Name.Substring(0, genericSymbol));
|
|
|
|
var genericArguments = type.GetGenericArguments();
|
|
|
|
if (genericArguments != null && genericArguments.Length > 0)
|
|
{
|
|
builder.Append("<");
|
|
|
|
for (int i = 0; i < genericArguments.Length; i++)
|
|
{
|
|
if (i > 0)
|
|
builder.Append(", ");
|
|
|
|
builder.Append(this.GetTypeFullyResolvedName(genericArguments[i]));
|
|
}
|
|
|
|
builder.Append(">");
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the route groupings from a set of routes and returns those groups.
|
|
/// </summary>
|
|
/// <param name="routes"></param>
|
|
/// <returns></returns>
|
|
protected virtual Dictionary<string, List<Route>> FindRouteGroups(List<Route> routes)
|
|
{
|
|
var groups = new Dictionary<string, List<Route>>();
|
|
|
|
foreach (var route in routes)
|
|
{
|
|
var methodInfo = route.GetTarget().GetMethodInfo();
|
|
|
|
//Skip any routes that are hidden.
|
|
if (methodInfo.GetCustomAttribute<RouteHidden>() != null)
|
|
continue;
|
|
|
|
var routeGroup = methodInfo.GetCustomAttribute<RouteGroup>();
|
|
|
|
//If there wasn't a route group on the method, see if there is one on the declaring type.
|
|
if (routeGroup == null)
|
|
routeGroup = methodInfo.DeclaringType.GetCustomAttribute<RouteGroup>();
|
|
|
|
//Group this route.
|
|
string group = (routeGroup != null ? routeGroup.Name : methodInfo.DeclaringType.Name);
|
|
|
|
if (groups.ContainsKey(group))
|
|
groups[group].Add(route);
|
|
else
|
|
groups.Add(group, new List<Route>() { route });
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a Rest Client from a RouteListener and returns the code.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string Generate(RouteListener listener)
|
|
{
|
|
return this.Generate(listener.Routes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a Rest Client from a set of routes and returns the code.
|
|
/// </summary>
|
|
/// <param name="routes"></param>
|
|
/// <returns></returns>
|
|
public virtual string Generate(List<Route> routes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|