mirror of
https://github.com/zoriya/octokit.net.git
synced 2026-06-05 03:30:34 +00:00
Reflection abstractions to reduce code duplication
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Octokit.Helpers;
|
||||
using Octokit.Internal;
|
||||
using Octokit.Reflection;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
internal class PropertyOrField
|
||||
{
|
||||
readonly PropertyInfo _propertyInfo;
|
||||
readonly FieldInfo _fieldInfo;
|
||||
|
||||
public PropertyOrField(PropertyInfo propertyInfo) : this((MemberInfo)propertyInfo)
|
||||
{
|
||||
_propertyInfo = propertyInfo;
|
||||
|
||||
CanRead = propertyInfo.CanRead;
|
||||
CanWrite = propertyInfo.CanWrite;
|
||||
IsStatic = ReflectionUtils.GetGetterMethodInfo(propertyInfo).IsStatic;
|
||||
IsPublic = ReflectionUtils.GetGetterMethodInfo(propertyInfo).IsPublic;
|
||||
}
|
||||
|
||||
public PropertyOrField(FieldInfo fieldInfo) : this((MemberInfo)fieldInfo)
|
||||
{
|
||||
_fieldInfo = fieldInfo;
|
||||
|
||||
CanRead = true;
|
||||
CanWrite = true;
|
||||
IsStatic = fieldInfo.IsStatic;
|
||||
IsPublic = fieldInfo.IsPublic;
|
||||
}
|
||||
|
||||
protected PropertyOrField(MemberInfo memberInfo)
|
||||
{
|
||||
MemberInfo = memberInfo;
|
||||
Base64Encoded = memberInfo.GetCustomAttribute<SerializeAsBase64Attribute>() != null;
|
||||
SerializeNull = memberInfo.GetCustomAttribute<SerializeNullAttribute>() != null;
|
||||
}
|
||||
|
||||
public bool CanRead { get; private set; }
|
||||
|
||||
public bool CanWrite { get; private set; }
|
||||
|
||||
public bool Base64Encoded { get; private set; }
|
||||
|
||||
public bool SerializeNull { get; private set; }
|
||||
|
||||
public bool IsStatic { get; private set; }
|
||||
|
||||
public bool IsPublic { get; private set; }
|
||||
|
||||
public MemberInfo MemberInfo { get; private set; }
|
||||
|
||||
public object GetValue(object instance)
|
||||
{
|
||||
if (_propertyInfo != null)
|
||||
return _propertyInfo.GetValue(instance);
|
||||
if (_fieldInfo != null)
|
||||
return _fieldInfo.GetValue(instance);
|
||||
throw new InvalidOperationException("Property and Field cannot both be null");
|
||||
}
|
||||
|
||||
public void SetValue(object instance, object value)
|
||||
{
|
||||
if (_propertyInfo != null)
|
||||
{
|
||||
_propertyInfo.SetValue(instance, value);
|
||||
return;
|
||||
}
|
||||
if (_fieldInfo != null)
|
||||
{
|
||||
_fieldInfo.SetValue(instance, value);
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException("Property and Field cannot both be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Octokit.Reflection;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
internal static class ReflectionExtensions
|
||||
{
|
||||
public static IEnumerable<PropertyOrField> GetPropertiesAndFields(this Type type)
|
||||
{
|
||||
return ReflectionUtils.GetProperties(type).Select(property => new PropertyOrField(property))
|
||||
.Union(ReflectionUtils.GetFields(type).Select(field => new PropertyOrField(field)))
|
||||
.Where(p => p.IsPublic && !p.IsStatic);
|
||||
}
|
||||
|
||||
public static bool IsDateTimeOffset(this Type type)
|
||||
{
|
||||
return type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Octokit
|
||||
@@ -31,6 +32,17 @@ namespace Octokit
|
||||
return System.Net.WebUtility.UrlEncode(input);
|
||||
}
|
||||
|
||||
public static string ToBase64String(this string input)
|
||||
{
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(input));
|
||||
}
|
||||
|
||||
public static string FromBase64String(this string encoded)
|
||||
{
|
||||
var decodedBytes = Convert.FromBase64String(encoded);
|
||||
return Encoding.UTF8.GetString(decodedBytes, 0, decodedBytes.Length);
|
||||
}
|
||||
|
||||
static readonly Regex _optionalQueryStringRegex = new Regex("\\{\\?([^}]+)\\}");
|
||||
public static Uri ExpandUriTemplate(this string template, object values)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Octokit.Helpers;
|
||||
using Octokit.Reflection;
|
||||
|
||||
namespace Octokit.Internal
|
||||
@@ -24,8 +22,8 @@ namespace Octokit.Internal
|
||||
|
||||
class GitHubSerializerStrategy : PocoJsonSerializerStrategy
|
||||
{
|
||||
readonly List<string> membersWhichShouldPublishNull = new List<string>();
|
||||
readonly List<string> membersWhichShouldBeBase64Encoded = new List<string>();
|
||||
readonly List<string> _membersWhichShouldPublishNull = new List<string>();
|
||||
readonly List<string> _membersWhichShouldBeBase64Encoded = new List<string>();
|
||||
|
||||
protected override string MapClrMemberToJsonFieldName(MemberInfo member)
|
||||
{
|
||||
@@ -50,37 +48,17 @@ namespace Octokit.Internal
|
||||
// to identify the right fields and properties to serialize
|
||||
// but it then filters on the presence of SerializeNullAttribute.
|
||||
|
||||
foreach (var propertyInfo in ReflectionUtils.GetProperties(type))
|
||||
foreach (var propertyOrField in type.GetPropertiesAndFields())
|
||||
{
|
||||
if (!propertyInfo.CanRead)
|
||||
if (!propertyOrField.CanRead)
|
||||
continue;
|
||||
var getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
|
||||
if (getMethod.IsStatic || !getMethod.IsPublic)
|
||||
continue;
|
||||
var base64Attribute = propertyInfo.GetCustomAttribute<SerializeAsBase64Attribute>();
|
||||
if (base64Attribute != null)
|
||||
if (propertyOrField.Base64Encoded)
|
||||
{
|
||||
membersWhichShouldBeBase64Encoded.Add(fullName + MapClrMemberToJsonFieldName(propertyInfo));
|
||||
_membersWhichShouldBeBase64Encoded.Add(fullName + MapClrMemberToJsonFieldName(propertyOrField.MemberInfo));
|
||||
}
|
||||
var serializeNullAttribute = propertyInfo.GetCustomAttribute<SerializeNullAttribute>();
|
||||
if (serializeNullAttribute == null)
|
||||
if (!propertyOrField.SerializeNull)
|
||||
continue;
|
||||
membersWhichShouldPublishNull.Add(fullName + MapClrMemberToJsonFieldName(propertyInfo));
|
||||
}
|
||||
|
||||
foreach (var fieldInfo in ReflectionUtils.GetFields(type))
|
||||
{
|
||||
if (fieldInfo.IsStatic || !fieldInfo.IsPublic)
|
||||
continue;
|
||||
var base64Attribute = fieldInfo.GetCustomAttribute<SerializeAsBase64Attribute>();
|
||||
if (base64Attribute != null)
|
||||
{
|
||||
membersWhichShouldBeBase64Encoded.Add(fullName + MapClrMemberToJsonFieldName(fieldInfo));
|
||||
}
|
||||
var attribute = fieldInfo.GetCustomAttribute<SerializeNullAttribute>();
|
||||
if (attribute == null)
|
||||
continue;
|
||||
membersWhichShouldPublishNull.Add(fullName + MapClrMemberToJsonFieldName(fieldInfo));
|
||||
_membersWhichShouldPublishNull.Add(fullName + MapClrMemberToJsonFieldName(propertyOrField.MemberInfo));
|
||||
}
|
||||
|
||||
return base.GetterValueFactory(type);
|
||||
@@ -103,16 +81,16 @@ namespace Octokit.Internal
|
||||
if (value == null)
|
||||
{
|
||||
var key = type.FullName + "-" + getter.Key;
|
||||
if (!membersWhichShouldPublishNull.Contains(key))
|
||||
if (!_membersWhichShouldPublishNull.Contains(key))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = type.FullName + "-" + getter.Key;
|
||||
if (membersWhichShouldBeBase64Encoded.Contains(key))
|
||||
if (_membersWhichShouldBeBase64Encoded.Contains(key))
|
||||
{
|
||||
var stringValue = value as string ?? "";
|
||||
value = Convert.ToBase64String(Encoding.UTF8.GetBytes(stringValue));
|
||||
value = stringValue.ToBase64String();
|
||||
}
|
||||
}
|
||||
jsonObject.Add(getter.Key, value);
|
||||
@@ -166,34 +144,21 @@ namespace Octokit.Internal
|
||||
var deserialized = base.DeserializeObject(value, type);
|
||||
|
||||
// Handle base64 encoding
|
||||
foreach (var propertyInfo in ReflectionUtils.GetProperties(type))
|
||||
foreach (var propertyInfo in type.GetPropertiesAndFields())
|
||||
{
|
||||
if (!propertyInfo.CanRead) continue;
|
||||
if (!propertyInfo.CanWrite) continue;
|
||||
if (propertyInfo.GetCustomAttribute<SerializeAsBase64Attribute>() != null)
|
||||
if (propertyInfo.Base64Encoded)
|
||||
{
|
||||
var propertyValue = propertyInfo.GetValue(deserialized) as string;
|
||||
if (propertyValue != null)
|
||||
{
|
||||
var unencoded = Encoding.UTF8.GetString(Convert.FromBase64String(propertyValue));
|
||||
var unencoded = propertyValue.FromBase64String();
|
||||
propertyInfo.SetValue(deserialized, unencoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var fieldInfo in ReflectionUtils.GetFields(type))
|
||||
{
|
||||
if (fieldInfo.GetCustomAttribute<SerializeAsBase64Attribute>() != null)
|
||||
{
|
||||
var propertyValue = fieldInfo.GetValue(deserialized) as string;
|
||||
if (propertyValue != null)
|
||||
{
|
||||
var unencoded = Encoding.UTF8.GetString(Convert.FromBase64String(propertyValue));
|
||||
fieldInfo.SetValue(deserialized, unencoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deserialized;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@
|
||||
<Compile Include="Models\Response\RepositoryContentChangeSet.cs" />
|
||||
<Compile Include="Models\Response\CommitContent.cs" />
|
||||
<Compile Include="Models\Response\SignatureResponse.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -368,6 +368,7 @@
|
||||
<Compile Include="Models\Response\RepositoryContentChangeSet.cs" />
|
||||
<Compile Include="Models\Response\CommitContent.cs" />
|
||||
<Compile Include="Models\Response\SignatureResponse.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -363,6 +363,7 @@
|
||||
<Compile Include="Models\Response\RepositoryContentChangeSet.cs" />
|
||||
<Compile Include="Models\Response\CommitContent.cs" />
|
||||
<Compile Include="Models\Response\SignatureResponse.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.MonoTouch.CSharp.targets" />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
@@ -354,6 +354,7 @@
|
||||
<Compile Include="Models\Response\RepositoryContentChangeSet.cs" />
|
||||
<Compile Include="Models\Response\CommitContent.cs" />
|
||||
<Compile Include="Models\Response\SignatureResponse.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CodeAnalysisDictionary Include="..\CustomDictionary.xml">
|
||||
|
||||
@@ -358,6 +358,7 @@
|
||||
<Compile Include="Models\Response\RepositoryContentChangeSet.cs" />
|
||||
<Compile Include="Models\Response\CommitContent.cs" />
|
||||
<Compile Include="Models\Response\SignatureResponse.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CodeAnalysisDictionary Include="..\CustomDictionary.xml">
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
<Compile Include="Helpers\ApiUrls.Keys.cs" />
|
||||
<Compile Include="Helpers\ConcurrentCache.cs" />
|
||||
<Compile Include="Helpers\HttpClientExtensions.cs" />
|
||||
<Compile Include="Helpers\PropertyOrField.cs" />
|
||||
<Compile Include="Helpers\SerializeNullAttribute.cs" />
|
||||
<Compile Include="Http\ProductHeaderValue.cs" />
|
||||
<Compile Include="Models\Request\Signature.cs" />
|
||||
|
||||
Reference in New Issue
Block a user