public async Task <int> Execute(CommandDefinition definition, ChangeObservable observable) { if (definition.BaseDefinition == newCommand) { Entity dataModel = entityFactory.Create(definition.Name, definition); IEnumerable <VirtualFile> files = await templateFileGenerator.InitalizeTemplate(dataModel, observable).ConfigureAwait(false); userInterface.WriteInformation($"Successfully created template '{dataModel.Template().name}' in {GetCommonPath(files,dataModel.Path)}."); } if (generateCommands.Contains(definition)) { Entity dataModel = entityFactory.Create(definition.Name, definition); userInterface.WriteInformation(definition.Name == "all" ? $"Generating all files for {dataModel.Root.Path}." : $"Generating all files with the '{definition.Name}' " + $"generator for {dataModel.Root.Path}."); SingleValueArgument singleValueArgument = definition.Argument <SingleValueArgument>(Constants.OutputArgumentName); await templateFileGenerator.GenerateFiles(dataModel.Root, definition.Name, singleValueArgument.Value, singleValueArgument.IsDefined, observable) .ConfigureAwait(false); userInterface.WriteInformation(definition.Name == "all" ? $"Successfully generated all files for {dataModel.Root.Path}." : $"Successfully generated all files with the '{definition.Name}' " + $"generator for {dataModel.Root.Path}."); } if (definition == deployCommand) { Entity dataModel = entityFactory.Create(definition.Name, definition); deployService.DeployFiles(dataModel); Entity root = dataModel.Root; TemplateDescription template = TemplateEntity.Decorate(root).Template; foreach (templateDeployPostStep deployPostStep in template.DeployPostStep ?? Enumerable.Empty <templateDeployPostStep>()) { IDeployStep step = deploySteps.FirstOrDefault(s => s.Identifier == deployPostStep.identifier); if (step != null) { step.Execute(dataModel, observable); } else { executionContext.WriteWarning( $"Deploy post step '{deployPostStep.identifier}' could not be executed because there is no implementation. " + $"Available implementations are:{Environment.NewLine}" + $"{string.Join(Environment.NewLine, deploySteps.Select(d => d.Identifier))}"); } } userInterface.WriteInformation($"Successfully deployed all files for {dataModel.Root.Path}."); } return(0); string GetCommonPath(IEnumerable <VirtualFile> generatedFiles, string fallback) { IEnumerable <IEnumerable <VirtualDirectory> > paths = generatedFiles.Select(GetPath); VirtualDirectory commonDirectory = paths.Transpose() .TakeWhile(d => d.Distinct().Count() == 1) .FirstOrDefault() ?.First(); return(commonDirectory?.FullName ?? fallback); IEnumerable <VirtualDirectory> GetPath(VirtualFile file) { VirtualDirectory current = file.Parent; while (current != null) { yield return(current); current = current.Parent; } } } }
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); } }