Initial Commit

master
Harald Wolff 2021-08-10 11:49:33 +02:00
commit 25ac0b0e1f
12 changed files with 403 additions and 0 deletions

5
.gitignore vendored 100644
View File

@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

View File

@ -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/

View File

@ -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>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -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>

View File

@ -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

View File

@ -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));
}
}
}

View File

@ -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){ }
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>