/trunk/Subsonic.Metro/MichMan.Utilities/UriInferer.cs
C# | 338 lines | 241 code | 48 blank | 49 comment | 35 complexity | ce44ea8c3cca00a02e7893760ff38176 MD5 | raw file
- // (c) Copyright Microsoft Corporation.
- // This source is subject to the Microsoft Public License (Ms-PL).
- // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
- // All other rights reserved
- // Developer: Michael Antonio
- using System;
- using System.Reflection;
- using System.Linq;
- using System.Collections.Generic;
- using System.Text;
- using System.Collections;
- using MichMan.Utilities;
-
- namespace MichMan.Utilities
- {
- /// <summary>
- /// Specify the format for a URI. Something like "{host}/api/v1.0/{fname}"
- /// You can specify query params in the format, or use the QueryParam attribute, or both.
- /// Query string params specified with the QueryParam attribute go after the UriFormat.
- /// </summary>
- [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
- public class UriFormatAttribute : Attribute
- {
- public UriFormatAttribute(string format)
- {
- Format = format;
- UriKind = System.UriKind.RelativeOrAbsolute;
- }
-
- public UriFormatAttribute(string format, UriKind uriKind)
- {
- Format = format;
- UriKind = uriKind;
- }
-
- public virtual string Format { get; set; }
- public virtual UriKind UriKind { get; set; }
- }
-
- /// <summary>
- /// The idea is to have an easy way to specify headers.
- /// TODO: Implement
- /// </summary>
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
- public class HttpHeaderAttribute : FormattableAttribute
- {
- public HttpHeaderAttribute(string name)
- {
- Name = name;
- }
- public string Name { get; set; }
- public bool Required { get; set; }
- }
-
- /// <summary>
- /// A property that is used to fill in part of the UriFormat.
- /// </summary>
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
- public class UriParamAttribute : FormattableAttribute
- {
- public UriParamAttribute(string name)
- {
- Name = name;
- }
-
- public string Name { get; set; }
- }
-
- /// <summary>
- /// A list of params all with the same name. E.G. "id=foo&id=bar&id=baz"
- /// </summary>
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
- public class QueryParamListAttribute : QueryParamAttribute
- {
- public QueryParamListAttribute(string name)
- : base(name)
- {
- }
-
- public override string ToString(object value)
- {
- if (value == null)
- {
- if (Required)
- {
- // Cannot successfully construct the query parameter.
- throw new InvalidOperationException(String.Format("Required parameter \"{0}\" missing.", Name));
- }
- return null;
- }
-
- List<object> values = new List<object>();
- values.AddRange(((IEnumerable)value).Cast<object>());
-
- StringBuilder builder = new StringBuilder();
- foreach (var v in values)
- {
- if (builder.Length != 0)
- {
- builder.Append("&");
- }
- builder.Append(base.ToString(v));
- }
- return builder.ToString();
- }
- }
-
- /// <summary>
- /// Use this attribute to delcare query parameter properties on your request objects and they will be picked up automatically.
- /// </summary>
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
- public class QueryParamAttribute : FormattableAttribute
- {
- public QueryParamAttribute(string name)
- {
- Name = name;
- Required = false;
- Order = int.MaxValue;
- }
-
- /// <summary>
- /// Query string parameter name.
- /// </summary>
- public string Name { get; set; }
- /// <summary>
- /// Throws an InvalidOperationException if this is set and the property is null.
- /// </summary>
- public bool Required { get; set; }
- /// <summary>
- /// Allows you to optionally order parameters.
- /// </summary>
- public int Order { get; set; }
-
- /// <summary>
- /// Override this to do something like pairs of params or something more complex.
- /// See QueryParamList class for an example.
- /// </summary>
- /// <param name="value"></param>
- /// <returns></returns>
- public override string ToString(object value)
- {
- if (value == null)
- {
- if (Required)
- {
- // Cannot successfully construct the query parameter.
- throw new InvalidOperationException(String.Format("Required parameter \"{0}\" missing.", Name));
- }
- return null;
- }
-
- string strValue = base.ToString(value);
-
- if (String.IsNullOrEmpty(strValue) && Required)
- {
- // Cannot successfully construct the query parameters.
- throw new InvalidOperationException(String.Format("Required parameter \"{0}\" missing.", Name));
- }
-
- return String.Format("{0}={1}", Name, strValue);
- }
- }
-
- public class UriInferer
- {
- public UriInferer(object target)
- {
- Target = target;
- }
-
- public object Target { get; set; }
-
- public static Uri InferUri(object o)
- {
- return new UriInferer(o).BuildUri();
- }
-
- public static Dictionary<string, string> InferHeaders(object o)
- {
- return new UriInferer(o).BuildHeaders();
- }
-
- public Dictionary<string, string> BuildHeaders()
- {
- var headers = new Dictionary<string, string>();
-
- foreach (var prop in Target.GetType().GetProperties())
- {
- HttpHeaderAttribute httpHeaderAttribute = prop.GetCustomAttributes(typeof(HttpHeaderAttribute), true).Cast<HttpHeaderAttribute>().SingleOrDefault();
- if (httpHeaderAttribute == null)
- {
- continue;
- }
-
- string strValue = null;
- // Note that this returns null or the value for nullable types. No conversion necessary.
- object value = prop.GetValue(Target, null);
-
- if (value != null || httpHeaderAttribute.Required)
- {
- strValue = httpHeaderAttribute.ToString(value);
- }
-
- if (strValue != null)
- {
- headers.Add(httpHeaderAttribute.Name, strValue);
- }
- }
-
- return headers;
- }
-
- public Uri BuildUri()
- {
- return BuildQueryParameters(BuildPath());
- }
-
- public Uri BuildPath()
- {
- UriFormatAttribute uriFormatAttribute = Target.GetType().GetTypeInfo().GetCustomAttributes(typeof(UriFormatAttribute), true).Cast<UriFormatAttribute>().SingleOrDefault();
-
- if (uriFormatAttribute == null)
- {
- return null;
- }
-
- string format = uriFormatAttribute.Format;
- foreach (var prop in Target.GetType().GetProperties())
- {
- UriParamAttribute uriParamAttribute = prop.GetCustomAttributes(typeof(UriParamAttribute), true).Cast<UriParamAttribute>().SingleOrDefault();
- if (uriParamAttribute == null)
- {
- continue;
- }
-
- string strValue = null;
- // Note that this returns null or the value for nullable types. No conversion necessary.
- object value = prop.GetValue(Target, null);
-
- if (value == null)
- {
- strValue = string.Empty;
- }
- else
- {
- strValue = uriParamAttribute.ToString(value);
- }
-
- format = format.Replace("{" + uriParamAttribute.Name + "}", strValue);
- }
-
- if (String.IsNullOrEmpty(format))
- {
- return null;
- }
-
- return new Uri(format, uriFormatAttribute.UriKind);
- }
-
- private class QueryParam
- {
- public string Param{ get; set; }
- public int Order { get; set; }
- }
-
- /// <summary>
- /// If any properties are annotated with the QueryParam attribute, use them here.
- /// </summary>
- public Uri BuildQueryParameters(Uri uri)
- {
- if (uri == null)
- {
- return null;
- }
-
- UriFormatAttribute uriFormatAttribute = (UriFormatAttribute)Target.GetType().GetTypeInfo().GetCustomAttributes(typeof(UriFormatAttribute), true).SingleOrDefault();
-
- List<QueryParam> qParams = new List<QueryParam>();
- foreach (var prop in Target.GetType().GetProperties())
- {
- QueryParamAttribute queryParamAttribute = (QueryParamAttribute)prop.GetCustomAttributes(true).SingleOrDefault(a => typeof(QueryParamAttribute).GetTypeInfo().IsAssignableFrom(a.GetType().GetTypeInfo()));
- if (queryParamAttribute == null)
- {
- continue;
- }
-
- // Note that this returns null or the value for nullable types. No conversion necessary.
- object value = prop.GetValue(Target, null);
- string strValue = queryParamAttribute.ToString(value);
-
- if (strValue == null)
- {
- continue;
- }
-
- QueryParam qp = new QueryParam()
- {
- Param = strValue,
- Order = queryParamAttribute.Order,
- };
- qParams.Add(qp);
- }
-
- // Now we have all the annotated query parameters.
- if (qParams.Count == 0)
- {
- return uri;
- }
-
- // Sort the list by order
- qParams = qParams.OrderBy(qp => qp.Order).ToList();
-
- StringBuilder queryString = new StringBuilder();
- queryString.Append(uri.ToString().Contains("?") ? "&" : "?");
-
- foreach (var param in qParams)
- {
- if (queryString.Length > 2)
- {
- queryString.Append("&");
- }
- queryString.Append(param.Param);
- }
-
- UriKind kind;
- if (uriFormatAttribute != null)
- {
- kind = uriFormatAttribute.UriKind;
- }
- else
- {
- kind = uri.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative;
- }
-
- return new Uri( uri.ToString() + queryString, kind);
- }
- }
- }