public override Entity Resolve(Entity owner, string key, bool fallback = false)
        {
            if (key.StartsWith("is", StringComparison.OrdinalIgnoreCase))
            {
                return(owner.Create(key, IsCheck(owner, key)));
            }
            switch (key)
            {
            case EntityKeys.TemplateKey:
                TemplateDescription template = templateRepository.Template(owner.Type);
                return(template != null
                               ? owner.Create(key, owner.Type, template)
                               : ParentTemplateEntity(owner));

            case EntityKeys.RelatedKey:
                return(GetRelatedEntities(owner));

            case EntityKeys.HiearchyKey:
                return(GetHierarchyEntities(owner));

            default:
                //continue execution
                break;
            }

            IEnumerable <ForeachItemContainer> foreachItems = owner.Values <ForeachItemContainer>();
            ForeachItemContainer foreachItem = foreachItems.FirstOrDefault(i => i.Key.Equals(key,
                                                                                             StringComparison.OrdinalIgnoreCase));

            if (foreachItem != null)
            {
                return(foreachItem.Current);
            }

            if (owner.Any(o => o.Value <templateFile>()?.key?.Equals(key, StringComparison.OrdinalIgnoreCase) == true))
            {
                return(owner.First(o => o.Value <templateFile>()?.key?.Equals(key, StringComparison.OrdinalIgnoreCase) == true));
            }

            if (owner.Value <templateFile>() != null)
            {
                Entity result = owner.Create(key, new Func <string>(() => owner.Value <templateFile>().GetType()
                                                                    .GetProperty(key)?
                                                                    .GetValue(owner.Value <templateFile>())
                                                                    ?.ToString() ?? string.Empty));
                result.SetContext(FindContext(owner));
                return(result);
            }

            TemplateDescription ownerTemplate = owner.Value <TemplateDescription>();

            switch (key)
            {
            case EntityKeys.TemplateFilesKey:
                Entity[] files = ownerTemplate.File.Concat(ownerTemplate.GeneratedFile ?? Enumerable.Empty <templateGeneratedFile>())
                                 .Select(tf => owner.Create("TemplateFile", tf.template, tf))
                                 .ToArray();
                return(owner.Create("TemplateFiles", (IEnumerable <Entity>)files));

            default:
                PropertyInfo info = ownerTemplate.GetType().GetProperty(key);
                if (info != null)
                {
                    object value = info.GetValue(ownerTemplate);
                    return(owner.Create(key, value?.ToString() ?? string.Empty, value));
                }
                throw new ContentProviderException(key, owner);
            }

            Entity FindContext(Entity current)
            {
                while (current != null && current.Value <TemplateDescription>() == null)
                {
                    current = current.Owner;
                }

                return(current?.Owner);
            }

            Entity GetRelatedEntities(Entity entity)
            {
                TemplateDescription template = entity.Template();
                IType entityType             = entity.Value <IType>();

                (templateRelationship relationship, TemplateDescription template)[] relatedTemplates = GetRelatedTemplates();
                List <(string tn, IType t)> related = new List <(string, IType)>();

                if (entity.Root.HasValue <ICodeModel>())
                {
                    ICodeModel codeModel = entity.Root.Value <ICodeModel>();
                    foreach (IType type in codeModel.Types.Keys)
                    {
                        (templateRelationship rel, TemplateDescription des) = relatedTemplates.FirstOrDefault(t => type.HasAttributeWithoutValue(t.template.name));
                        bool isRelevant = false;
                        if (rel != null)
                        {
                            if (template.TemplateNames(templateRepository)
                                .Any(n => n.Equals(rel.type, StringComparison.OrdinalIgnoreCase)))
                            {
                                isRelevant = template.isRoot ||
                                             type.Attributes.Any(a => a.Name.Equals(rel.name, StringComparison.OrdinalIgnoreCase) &&
                                                                 a.Values.Any(v => IsRelatedType(entityType, v)));
                            }
                            else
                            {
                                isRelevant = des.isRoot ||
                                             entityType?.Attributes.Any(a => a.Name.Equals(rel.name, StringComparison.OrdinalIgnoreCase) &&
                                                                        a.Values.Any(v => IsRelatedType(type, v))) == true;
                            }
                        }
                        if (isRelevant)
                        {
                            related.Add((des.name, type));
                        }
                    }

                    bool IsRelatedType(IType relatedType, string name)
                    {
                        if (relatedType?.FullName.Equals(name, StringComparison.Ordinal) == true)
                        {
                            return(true);
                        }

                        return(codeModel.Type(name) == null &&
                               relatedType?.FullName.Contains(name) == true);
                    }
                }

                return(owner.Create(key,
                                    related.Select(r => owner.EntityHierarchy()
                                                   .FirstOrDefault(e => e.HasValue <IType>() &&
                                                                   e.Value <IType>() == r.t) ??
                                                   owner.Create(r.tn, r.t.FullName, r.t))));

                (templateRelationship, TemplateDescription)[] GetRelatedTemplates()
        public string Resolve(string stringToResolve, IEntityBase dataSource)
        {
            StringBuilder resolved = new StringBuilder();
            int           index    = 0;

            while (index != stringToResolve.Length - 1)
            {
                Match sequenceFound = controlSequenceFinder.Match(stringToResolve, index);
                if (!sequenceFound.Success)
                {
                    break;
                }

                resolved.Append(ResolveTemplateContent(stringToResolve.Substring(index, sequenceFound.Index - index)));

                string controlSequence = sequenceFound.Groups["expression"].Value;
                string startTag        = $"$([{controlSequence}]";
                string endTag          = $"$([end-{controlSequence}])";
                int    length          = sequenceFound.Length;
                index = sequenceFound.Index;
                int nestedSequences =
                    Count(stringToResolve.Substring(index + startTag.Length, length - startTag.Length), startTag);
                int endIndex = index + length;
                while (nestedSequences > 0)
                {
                    int newEnd = stringToResolve.Substring(endIndex)
                                 .IndexOf(endTag, StringComparison.OrdinalIgnoreCase);
                    if (newEnd < 0)
                    {
                        throw new ControlSequenceEndTagNotFoundException(controlSequence);
                    }

                    newEnd = endIndex + newEnd;

                    //Find nested tags between last end tag and this end tag
                    nestedSequences += Count(stringToResolve.Substring(endIndex, newEnd - endIndex), startTag);
                    nestedSequences--;
                    endIndex = newEnd + endTag.Length;
                }

                Match controlSequenceDataMatch = greedyContentFinder.Match(stringToResolve, index, endIndex - index);
                if (!controlSequenceDataMatch.Success)
                {
                    throw new InvalidOperationException("Should not happen");
                }

                resolved.Append(ResolveControlSequence(controlSequenceDataMatch.Groups["expression"].Value,
                                                       controlSequenceDataMatch.Groups["parameter"].Value,
                                                       controlSequenceDataMatch.Groups["content"].Value));
                if (controlSequenceDataMatch.Value.Contains('\n'))
                {
                    Match newlineMatch = newlineSearcher.Match(stringToResolve, endIndex);
                    if (newlineMatch.Success && newlineMatch.Index == endIndex)
                    {
                        endIndex += newlineMatch.Length;
                    }
                }

                index = endIndex;
            }

            resolved.Append(ResolveTemplateContent(stringToResolve.Substring(index)));
            return(resolved.ToString());

            string ResolveControlSequence(string sequence, string parameter, string content)
            {
                switch (sequence.ToUpperInvariant())
                {
                case "IF-EXIST":
                    return(IfSequence(IfExistCondition));

                case "IF-SPECIFIED":
                    return(IfSequence(IfSpecifiedCondition));

                case "FOREACH":
                    return(ForeachSequence());

                case "NO-DUPLICATE-LINES":
                    return(RemoveDuplicateLinesSequence());

                default:
                    throw new UnrecognizedControlSequenceException(sequence);
                }

                string RemoveDuplicateLinesSequence()
                {
                    if (!string.IsNullOrEmpty(parameter))
                    {
                        throw new NoDuplicateLinesParameterMismatchException();
                    }

                    string[] lines = (Resolve(content, dataSource)).Split(new[] { '\r', '\n' },
                                                                          StringSplitOptions.RemoveEmptyEntries);
                    return(string.Join(Environment.NewLine, lines.Distinct()) + Environment.NewLine);
                }

                string IfSequence(Func <bool> conditionCheck)
                {
                    if (string.IsNullOrEmpty(parameter))
                    {
                        throw new IfSequenceParameterMismatchException();
                    }

                    bool condition = conditionCheck();

                    return(IfResult(condition));
                }

                bool IfExistCondition()
                {
                    CommandDefinition definition = dataSource.Value <CommandDefinition>();
                    Argument          argument   = definition?.Argument <Argument>(parameter);

                    return(argument != null ||
                           dataSource.HasContent(parameter));
                }

                bool IfSpecifiedCondition()
                {
                    CommandDefinition definition = dataSource.Value <CommandDefinition>();
                    Argument          argument   = definition?.Argument <Argument>(parameter);
                    bool specified;

                    if (argument != null)
                    {
                        specified = argument.IsDefined;
                    }
                    else
                    {
                        specified = dataSource.HasContent(parameter) &&
                                    !string.IsNullOrEmpty(dataSource[parameter].Value <string>());
                    }

                    return(specified);
                }

                string IfResult(bool condition)
                {
                    string result = string.Empty;

                    string[] elseSplit = content.Split(new[] { "$([else])" }, StringSplitOptions.RemoveEmptyEntries);
                    if (condition)
                    {
                        result = Resolve(elseSplit[0], dataSource);
                    }
                    else if (elseSplit.Length == 2)
                    {
                        result = Resolve(elseSplit[1], dataSource);
                    }

                    return(result);
                }

                string ForeachSequence()
                {
                    StringBuilder foreachResult = new StringBuilder();

                    if ((content.StartsWith("\n", StringComparison.Ordinal) ||
                         content.StartsWith("\r\n", StringComparison.Ordinal)) &&
                        (content.EndsWith("\n", StringComparison.Ordinal) ||
                         content.EndsWith("\r\n", StringComparison.Ordinal)))
                    {
                        //This would leads to unwanted empty lines
                        content = content.TrimStart('\r').TrimStart('\n');
                    }

                    string[] nameSplit = parameter.Split(new[] { "[in]" }, StringSplitOptions.RemoveEmptyEntries);
                    if (nameSplit.Length != 2)
                    {
                        throw new ForeachSequenceParameterMismatchException(parameter);
                    }

                    string elementName = nameSplit[0].Trim();

                    string[] ofTypeSplit =
                        nameSplit[1].Split(new[] { "[of-type]" }, StringSplitOptions.RemoveEmptyEntries);
                    string splitPart = ofTypeSplit.Length == 2 ? ofTypeSplit[1] : ofTypeSplit[0];

                    string[] split      = splitPart.Split(new[] { "[split]" }, StringSplitOptions.RemoveEmptyEntries);
                    string   collection = ofTypeSplit.Length == 2 ? ofTypeSplit[0].Trim() : split[0].Trim();
                    string   filter     = string.Empty;
                    string   splitSize  = split.Length == 2 ? split[1].Trim() : string.Empty;

                    if (!int.TryParse(splitSize, out int chunksize))
                    {
                        chunksize = -1;
                    }
                    if (ofTypeSplit.Length == 2)
                    {
                        filter = split.Length == 2 ? split[0].Trim() : ofTypeSplit[1].Trim();
                    }

                    string[]             path = collection.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                    IEnumerable <Entity> data = ResolveRecursively(path);

                    if (!string.IsNullOrEmpty(filter))
                    {
                        data = data.Where(TemplateEqualsFilter);
                    }

                    ForeachItemContainer container = new ForeachItemContainer(elementName);

                    if (chunksize > 0)
                    {
                        var query = data.Select((entity, idx) => new { idx, entity });
                        IEnumerable <IEnumerable <Entity> > dataChunks = query.GroupBy(x => x.idx / chunksize, a => a.entity);
                        using (dataSource.AddTemporaryDataSource(container))
                            using (dataSource.SkipCaching(elementName))
                            {
                                foreach (IEnumerable <Entity> chunk in dataChunks)
                                {
                                    using (dataSource.SkipCaching(EntityKeys.ChunkStartKey))
                                        using (dataSource.SkipCaching(EntityKeys.ChunkEndKey))
                                        {
                                            string start = data.TakeWhile(x => !x.Equals(chunk.FirstOrDefault())).Count().ToString(CultureInfo.InvariantCulture);
                                            string end   = data.TakeWhile(x => !x.Equals(chunk.LastOrDefault())).Count().ToString(CultureInfo.InvariantCulture);

                                            if (dataSource is Entity)
                                            {
                                                Entity dataSourceEntity = dataSource as Entity;
                                                container.Current = dataSourceEntity.Create(Guid.NewGuid().ToByteString(), chunk);
                                                using (container.Current.AddTemporaryDataSource(new DataChunk(start, end)))
                                                    foreachResult.Append(Resolve(content, dataSource));
                                            }
                                            else
                                            {
                                                throw new FormattableException($"The datasource should be an entity but is of type {dataSource.GetType()}");
                                            }
                                        }
                                }
                            }
                    }
                    else
                    {
                        using (dataSource.AddTemporaryDataSource(container))
                            using (dataSource.SkipCaching(elementName))
                            {
                                foreach (Entity entity in data)
                                {
                                    container.Current = entity;
                                    foreachResult.Append(Resolve(content, dataSource));
                                }
                            }
                    }
                    return(foreachResult.ToString());

                    bool TemplateEqualsFilter(Entity entity)
                    {
                        return(entity.Template().TemplateNames(repository)
                               .Any(n => n.Equals(filter, StringComparison.OrdinalIgnoreCase)));
                    }
                }
            }

            string ResolveTemplateContent(string resolvable)
            {
                string result = resolvable;
                Match  controlSequenceMatch = templateAccessRegex.Match(resolvable);

                while (controlSequenceMatch.Success)
                {
                    string content = controlSequenceMatch.Groups["content"].Value;
                    if (content.StartsWith("[", StringComparison.Ordinal) &&
                        content.EndsWith("]", StringComparison.Ordinal))
                    {
                        throw new WildControlSequenceException(content);
                    }

                    string[] path  = content.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                    string   value = (ResolveRecursively(path)).Value <string>();
                    if (controlSequenceMatch.Groups["text_control"].Success)
                    {
                        string textControlSequence = controlSequenceMatch.Groups["text_control"].Value;
                        textControlSequence = textControlSequence.Substring(0, textControlSequence.Length - 1);
                        value = ResolveTextControlSequences(value, textControlSequence);
                    }
                    result = result.Replace(controlSequenceMatch.Value, value);
                    controlSequenceMatch = controlSequenceMatch.NextMatch();
                }

                return(result);
            }

            IEntityBase ResolveRecursively(string[] path)
            {
                IEntityBase current = dataSource;

                foreach (string part in path)
                {
                    current = current[part];
                    if (current.HasValue <string>())
                    {
                        string value = Resolve(current.Value <string>(), current);
                        current.SetValue(value);
                    }
                }

                return(current);
            }

            int Count(string data, string substring)
            {
                return((data.Length - data.Replace(substring, string.Empty).Length) / substring.Length);
            }
        }