Initial Commit
commit
25ac0b0e1f
|
@ -0,0 +1,5 @@
|
|||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
|
@ -0,0 +1,13 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/projectSettingsUpdater.xml
|
||||
/.idea.ln.configurator.iml
|
||||
/modules.xml
|
||||
/contentModel.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.configurator", "ln.configurator\ln.configurator.csproj", "{CB9BB772-07BE-4116-B44C-A96D9491819B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CB9BB772-07BE-4116-B44C-A96D9491819B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB9BB772-07BE-4116-B44C-A96D9491819B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB9BB772-07BE-4116-B44C-A96D9491819B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB9BB772-07BE-4116-B44C-A96D9491819B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ln.configurator
|
||||
{
|
||||
public class ClassConfigurationValue : ConfigurationValue
|
||||
{
|
||||
public object Instance { get; set; }
|
||||
public Type Type { get; }
|
||||
|
||||
protected Func<object> ReadValueDelegate;
|
||||
protected Action<object> ApplyValueDelegate;
|
||||
|
||||
public ClassConfigurationValue(FieldInfo fieldInfo, object instance)
|
||||
:base(String.Format("{0}.{1}",fieldInfo.DeclaringType.FullName, fieldInfo.Name))
|
||||
{
|
||||
FileName = String.Format("{0}.config", fieldInfo.DeclaringType.FullName);
|
||||
Type = fieldInfo.FieldType;
|
||||
|
||||
ReadValueDelegate = () => fieldInfo.GetValue(Instance);
|
||||
ApplyValueDelegate = (v) => fieldInfo.SetValue(Instance, v);
|
||||
}
|
||||
public ClassConfigurationValue(PropertyInfo propertyInfo, object instance)
|
||||
:base(String.Format("{0}.{1}",propertyInfo.DeclaringType.FullName, propertyInfo.Name))
|
||||
{
|
||||
FileName = String.Format("{0}.config", propertyInfo.DeclaringType.FullName);
|
||||
Type = propertyInfo.PropertyType;
|
||||
|
||||
ReadValueDelegate = () => propertyInfo.GetValue(Instance);
|
||||
ApplyValueDelegate = (v) => propertyInfo.SetValue(Instance, v);
|
||||
}
|
||||
|
||||
public override string Value
|
||||
{
|
||||
get => Convert.ChangeType(ReadValueDelegate(), typeof(string)).ToString();
|
||||
set => ApplyValueDelegate(Convert.ChangeType(value, Type));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
namespace ln.configurator
|
||||
{
|
||||
public class ConfigurationOption
|
||||
{
|
||||
public string OptionName { get; set; }
|
||||
public char OptionCharacter { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
public ConfigurationOption(string key, OptionAttribute optionAttribute)
|
||||
:this(key, optionAttribute.OptionName, optionAttribute.OptionCharacter)
|
||||
{ }
|
||||
|
||||
public ConfigurationOption(string key, string optionName, char optionCharacter)
|
||||
{
|
||||
Key = key;
|
||||
OptionName = optionName;
|
||||
OptionCharacter = optionCharacter;
|
||||
}
|
||||
|
||||
public ConfigurationOption(string key, string optionName)
|
||||
:this(key, optionName, (char)0){ }
|
||||
public ConfigurationOption(string key, char optionCharacter)
|
||||
:this(key, null, optionCharacter){ }
|
||||
|
||||
public ConfigurationOption(string optionName)
|
||||
:this(null, optionName, (char)0){ }
|
||||
public ConfigurationOption(char optionCharacter)
|
||||
:this(null, null, optionCharacter){ }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace ln.configurator
|
||||
{
|
||||
public class ConfigurationValue
|
||||
{
|
||||
public string Key { get; }
|
||||
public string FileName { get; set; }
|
||||
|
||||
public virtual string Value { get; set; }
|
||||
|
||||
|
||||
public ConfigurationValue(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => (obj is ConfigurationValue cv) && Object.Equals(Key, cv.Key);
|
||||
public override int GetHashCode() => Key.GetHashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ln.configurator
|
||||
{
|
||||
public class Configurator
|
||||
{
|
||||
public string DefaultFileName { get; set; }
|
||||
|
||||
private Dictionary<string, ConfigurationValue> ConfigurationValues =
|
||||
new Dictionary<string, ConfigurationValue>();
|
||||
private HashSet<ConfigurationOption> ConfigurationOptions = new HashSet<ConfigurationOption>();
|
||||
private Dictionary<string, object> ConfigurableInstances = new Dictionary<string, object>();
|
||||
private Dictionary<string, string> TextValues = new Dictionary<string, string>();
|
||||
|
||||
public Configurator()
|
||||
{
|
||||
}
|
||||
|
||||
public Configurator(string defaultFilename)
|
||||
{
|
||||
DefaultFileName = defaultFilename;
|
||||
}
|
||||
|
||||
public bool ParseArguments(ref string[] arguments) => ParseArguments(ref arguments, false);
|
||||
public bool ParseArguments(ref string[] arguments, bool allowDynamicKeyPaths)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
foreach (string filename in GetConfigurationFileList())
|
||||
Load(filename);
|
||||
|
||||
if (DefaultFileName != null)
|
||||
Load(DefaultFileName);
|
||||
}
|
||||
public void Load(string filename)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
using (StreamReader sr = new StreamReader(filename))
|
||||
{
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string[] ltoken = sr.ReadLine().Split('=', 2, StringSplitOptions.TrimEntries);
|
||||
string key, value;
|
||||
|
||||
key = ltoken[0];
|
||||
value = (ltoken.Length == 2) ? ltoken[1] : string.Empty;
|
||||
if (TryGetConfigurationValue(key, true, out ConfigurationValue ccv))
|
||||
ccv.Value = value;
|
||||
else
|
||||
TextValues[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save() => Save(DefaultFileName);
|
||||
public void Save(string filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Configurator Add(ConfigurationValue configurationValue)
|
||||
{
|
||||
ConfigurationValues.Add(configurationValue.Key, configurationValue);
|
||||
if (TextValues.TryGetValue(configurationValue.Key, out string textValue))
|
||||
{
|
||||
configurationValue.Value = textValue;
|
||||
TextValues.Remove(configurationValue.Key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Configurator Add(ConfigurationOption configurationOption)
|
||||
{
|
||||
ConfigurationOptions.Add(configurationOption);
|
||||
return this;
|
||||
}
|
||||
public Configurator AddInstance(object configurableInstance)
|
||||
{
|
||||
Type type = configurableInstance.GetType();
|
||||
ConfigurableInstances.Add(configurableInstance.GetType().FullName, configurableInstance);
|
||||
|
||||
foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (fieldInfo.IsPublic || (fieldInfo.GetCustomAttribute<OptionAttribute>() != null))
|
||||
AddField(fieldInfo, fieldInfo.IsStatic ? null : configurableInstance);
|
||||
}
|
||||
|
||||
foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if ((propertyInfo.GetMethod?.IsPublic ?? false) || (propertyInfo.GetCustomAttribute<OptionAttribute>() != null))
|
||||
AddProperty(propertyInfo, configurableInstance);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Configurator AddStatic(Type type)
|
||||
{
|
||||
foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (fieldInfo.IsPublic || (fieldInfo.GetCustomAttribute<OptionAttribute>() != null))
|
||||
AddField(fieldInfo, null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Configurator AddField(FieldInfo fieldInfo, object instance)
|
||||
{
|
||||
OptionAttribute optionAttribute = fieldInfo.GetCustomAttribute<OptionAttribute>();
|
||||
|
||||
if (Object.ReferenceEquals(null, instance) && !fieldInfo.IsStatic)
|
||||
throw new NotSupportedException(String.Format("Field {0}.{1} is not static and instance == null", fieldInfo.DeclaringType?.FullName,fieldInfo.Name));
|
||||
|
||||
ClassConfigurationValue ccv = new ClassConfigurationValue(fieldInfo, instance);
|
||||
Add(ccv);
|
||||
|
||||
if (optionAttribute != null)
|
||||
Add(new ConfigurationOption(ccv.Key, optionAttribute));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Configurator AddProperty(PropertyInfo propertyInfo, object instance)
|
||||
{
|
||||
OptionAttribute optionAttribute = propertyInfo.GetCustomAttribute<OptionAttribute>();
|
||||
|
||||
if (Object.ReferenceEquals(null, instance))
|
||||
throw new NotSupportedException(String.Format("Property {0}.{1} cannot be static and instance == null", propertyInfo.DeclaringType?.FullName,propertyInfo.Name));
|
||||
|
||||
ClassConfigurationValue ccv = new ClassConfigurationValue(propertyInfo, instance);
|
||||
Add(ccv);
|
||||
|
||||
if (optionAttribute != null)
|
||||
Add(new ConfigurationOption(ccv.Key, optionAttribute));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool TryGetConfigurationValue(string key, out ConfigurationValue ccv) =>
|
||||
TryGetConfigurationValue(key, false, out ccv);
|
||||
public bool TryGetConfigurationValue(string key, bool createDynamicValue, out ConfigurationValue ccv)
|
||||
{
|
||||
if (ConfigurationValues.TryGetValue(key, out ccv))
|
||||
return true;
|
||||
|
||||
if (createDynamicValue)
|
||||
{
|
||||
string[] keytokens = key.Split('.', StringSplitOptions.RemoveEmptyEntries);
|
||||
string classname = string.Join('.', keytokens[0..^-1]);
|
||||
string attributename = keytokens[^-1];
|
||||
|
||||
ConfigurableInstances.TryGetValue(classname, out object classInstance);
|
||||
|
||||
Type classType = classInstance != null ? classInstance.GetType() : Type.GetType(classname);
|
||||
if (classType == null)
|
||||
return false;
|
||||
|
||||
FieldInfo fieldInfo = classType.GetField(attributename,
|
||||
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (fieldInfo != null)
|
||||
ccv = new ClassConfigurationValue(fieldInfo, fieldInfo.IsStatic ? null : classInstance);
|
||||
else
|
||||
{
|
||||
PropertyInfo propertyInfo = classType.GetProperty(attributename,
|
||||
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (propertyInfo != null)
|
||||
ccv = new ClassConfigurationValue(propertyInfo, classInstance);
|
||||
}
|
||||
|
||||
if (ccv != null)
|
||||
Add(ccv);
|
||||
}
|
||||
|
||||
return ccv != null;
|
||||
}
|
||||
|
||||
public void SetTextValue(string key, string textValue)
|
||||
{
|
||||
if (TryGetConfigurationValue(key, out ConfigurationValue ccv))
|
||||
ccv.Value = textValue;
|
||||
else
|
||||
TextValues[key] = textValue;
|
||||
}
|
||||
public string GetTextValue(string key)
|
||||
{
|
||||
if (TryGetConfigurationValue(key, out ConfigurationValue ccv))
|
||||
return ccv.Value;
|
||||
if (TextValues.TryGetValue(key, out string textValue))
|
||||
return textValue;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected string[] GetConfigurationFileList()
|
||||
{
|
||||
HashSet<string> filenames = new HashSet<string>();
|
||||
|
||||
// if (DefaultFileName != null)
|
||||
// filenames.Add(DefaultFileName);
|
||||
|
||||
foreach (ConfigurationValue configurationValue in ConfigurationValues.Values)
|
||||
{
|
||||
if (configurationValue.FileName != null)
|
||||
filenames.Add(configurationValue.FileName);
|
||||
}
|
||||
|
||||
return filenames.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ln.configurator
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class OptionAttribute : Attribute
|
||||
{
|
||||
public string OptionName { get; set; }
|
||||
public char OptionCharacter { get; set; }
|
||||
|
||||
public OptionAttribute()
|
||||
{}
|
||||
|
||||
public OptionAttribute(string optionName, char optionCharacter)
|
||||
{
|
||||
OptionName = optionName;
|
||||
OptionCharacter = optionCharacter;
|
||||
}
|
||||
|
||||
public OptionAttribute(string optionName)
|
||||
{
|
||||
OptionName = optionName;
|
||||
}
|
||||
public OptionAttribute(char optionCharacter)
|
||||
{
|
||||
OptionCharacter = optionCharacter;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue