FixAntenna/NetCore/Configuration/TemplatePropertiesWrapper.cs (191 lines of code) (raw):
// Copyright (c) 2021 EPAM Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Epam.FixAntenna.NetCore.Common;
using Epam.FixAntenna.NetCore.Helpers;
namespace Epam.FixAntenna.NetCore.Configuration
{
/// <summary>
/// Wraps <see cref="Properties"/> for handle templates like key=${value}.
/// </summary>
internal sealed class TemplatePropertiesWrapper : ICloneable
{
private const string TemplStart = "${";
private const string TemplEnd = "}";
private const string TemplNegation = "!";
private static readonly int TemplNegationLen = TemplNegation.Length;
private readonly Dictionary<string, (string, string)> _properties = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase);
public TemplatePropertiesWrapper()
{
}
public TemplatePropertiesWrapper(Properties properties) : this(properties?.ToDictionary())
{
}
public TemplatePropertiesWrapper(IDictionary<string, string> properties)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties));
}
foreach (var prop in properties)
{
_properties[prop.Key] = (prop.Key, prop.Value);
}
}
private TemplatePropertiesWrapper(IDictionary<string, (string, string)> properties)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties));
}
foreach (var prop in properties)
{
_properties[prop.Key] = prop.Value;
}
}
public object Clone()
{
var cloned = new TemplatePropertiesWrapper(_properties);
return cloned;
}
public void Put(string key, string value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
_properties[key.ToLowerInvariant()] = (key, value);
}
/// <summary>
/// Gets value by key.
/// If value is template methods replace it by template value.
/// If template value doesn't exist it will be replace by defaultValue .
/// </summary>
/// <returns> String </returns>
public string GetProperty(string key, string defaultValue = null)
{
var evValue = ReadEnvironmentVariable(key);
if (!string.IsNullOrWhiteSpace(evValue))
{
ParamSources.Instance.Set(key, ParamSource.Environment);
return evValue;
}
else if (_properties.TryGetValue(key.ToLowerInvariant(), out var value))
{
ParamSources.Instance.Set(key, ParamSource.Config);
return GetTemplateProperty(value.Item2);
}
else
{
ParamSources.Instance.Set(key, ParamSource.Default);
return defaultValue;
}
}
private string ReadEnvironmentVariable(string key)
{
// try to read session level environment variable first
// if no success, then try to read default session environment variable
// if no success, then try to read global environment variable
var sessionId = GetSessionId();
var readFuncs = new Func<string>[]
{
() => Properties.ReadEnvironmentVariable(key, sessionId),
() => Properties.ReadEnvironmentVariable(key, Properties.DefaultSessionId),
() => Properties.ReadEnvironmentVariable(key),
};
var canReadFuncs = new Func<bool>[]
{
() => !string.IsNullOrWhiteSpace(sessionId),
// if no session id, then this is not a config for a session
// so, don't read the session default values
() => !string.IsNullOrWhiteSpace(sessionId),
() => true
};
string value = null;
for (var index = 0; index < readFuncs.Length; index++)
{
if (canReadFuncs[index]())
{
value = readFuncs[index]();
}
if (!string.IsNullOrWhiteSpace(value))
{
break;
}
}
return value;
}
private string GetSessionId()
{
return _properties.TryGetValue(Properties.SessionIdKey, out var res)
? res.Item2
: null;
}
public void Clear()
{
_properties.Clear();
}
public IDictionary<string, string> GetAllProperties()
{
return _properties.ToImmutableDictionary(k => k.Value.Item1, v => v.Value.Item2);
}
public ISet<string> GetKeys()
{
return _properties.Values.Select(i => i.Item1).ToImmutableHashSet();
}
private string GetTemplateProperty(string value)
{
var start = 0;
while (true)
{
// try to find TEMPL_START - TEMPL_END pair
start = value.IndexOf(TemplStart, start, StringComparison.Ordinal);
if (start != -1)
{
var end = value.IndexOf(TemplEnd, start + TemplStart.Length, StringComparison.Ordinal);
if (end != -1)
{
// try to find TEMPL_NEGATION prefix
if (start >= TemplNegationLen && TemplNegation.Equals(
value.Substring(start - TemplNegationLen, start - (start - TemplNegationLen))))
{
// template negation found
// remove negation label only
value = value.Substring(0, start - TemplNegation.Length) + value.Substring(start);
start = end + TemplEnd.Length - TemplNegation.Length;
}
else
{
// template subst
var name = value.Substring(start + TemplStart.Length, end - start - TemplStart.Length);
var templVal = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrEmpty(templVal))
{
templVal = _properties.TryGetValue(name.ToLowerInvariant(), out (string, string) v) ? v.Item2 : string.Empty;
}
value = value.Substring(0, start) + templVal + value.Substring(end + TemplEnd.Length);
start += templVal.Length;
}
}
else
{
break;
}
}
else
{
break;
}
}
return value;
}
public override bool Equals(object o)
{
if (this == o)
{
return true;
}
if (o == null || GetType() != o.GetType())
{
return false;
}
var that = (TemplatePropertiesWrapper)o;
if (_properties == that._properties)
{
return true;
}
return _properties.Count == that._properties.Count && !_properties.Except(that._properties).Any();
}
public override int GetHashCode()
{
return _properties != null ? _properties.GetHashCode() : 0;
}
public bool Exists(string propertyName)
{
return _properties.ContainsKey(propertyName);
}
}
}