/// <summary>
        /// Add token for name
        /// </summary>
        /// <param name="key">token name</param>
        /// <param name="token"></param>
        public void Set(string key, Token token)
        {
            var refers        = _missingTokenNames.ContainsKey(key) ? _missingTokenNames[key] : new HashSet <string>();
            var missingRefees = new HashSet <string>();

            foreach (var innerTokenName in TokenReplacement.FindInnerTokenNames(token))
            {
                if (innerTokenName == key || refers.Contains(innerTokenName))
                {
                    throw new ConfigGenerationException($"detected circular reference in token replacement, token name:'{key}'");
                }

                if (_tokens.ContainsKey(innerTokenName))
                {
                    token = TokenReplacement.ReplaceTokenWithToken(token, innerTokenName, _tokens[innerTokenName]);
                    if (_tokensUnresolved.ContainsKey(innerTokenName))
                    {
                        missingRefees.UnionWith(_tokensUnresolved[innerTokenName]);
                    }
                }
                else
                {
                    missingRefees.Add(innerTokenName);
                }
            }

            foreach (var refer in refers)
            {
                _tokens[refer] = TokenReplacement.ReplaceTokenWithToken(_tokens[refer], key, token);
                if (_tokensUnresolved.ContainsKey(refer))
                {
                    _tokensUnresolved[refer].Remove(key);
                    _tokensUnresolved[refer].UnionWith(missingRefees);
                }
                else
                {
                    if (missingRefees.Count > 0)
                    {
                        _tokensUnresolved.Add(refer, missingRefees);
                    }
                }
            }

            if (missingRefees.Count > 0)
            {
                refers.Add(key);
                foreach (var refee in missingRefees)
                {
                    _missingTokenNames.MergeKeyList(refee, refers);
                }

                _tokensUnresolved[key] = missingRefees;
            }

            _missingTokenNames.Remove(key);
            _tokens[key] = token;
        }
        /// <summary>
        /// Replace tokenized placeholders in the given text with the tokens' value from this dictionary
        /// Note: the token name not found in this dictionary will not be replaced
        /// </summary>
        /// <param name="text">the given text to perform token replacement</param>
        /// <returns></returns>
        public string Resolve(string text)
        {
            var refees = TokenReplacement.GetReferencingTokenNames(text);

            refees.IntersectWith(_tokens.Keys);

            var result = text;

            foreach (var refee in refees)
            {
                var token = _tokens[refee];
                result = TokenReplacement.ReplaceStringWithToken(result, refee, token);
            }

            return(result);
        }