public bool Build(IIncludeContext includeContext)
        {
            CleanPreviousOutput(includeContext);

            _logMessageSink.Message($"{nameof(includeContext.OutputRoot)}:{Path.GetFullPath(includeContext.OutputRoot)}");
            _logMessageSink.Message($"{nameof(includeContext.ConfigRoot)}:{Path.GetFullPath(includeContext.ConfigRoot)}");
            _logMessageSink.Message($"{nameof(includeContext.IncludeRoot)}:{Path.GetFullPath(includeContext.IncludeRoot)}");
            _logMessageSink.Message($"Resolved include paths: \n{string.Join("\n", includeContext.ResolvedIncludeRoots)}");

            Stopwatch sw = new Stopwatch();

            _logMessageSink.Message("Beginning generation process...");

            sw.Start();

            List <Task> tasks = new List <Task>();

            // The TransformationContext has caching capabilities that are based on a concurrent dictionary.
            // Create it here to share things like resolved files while applying transformations to different output files
            ConcurrentDictionary <string, object> objectCache = new ConcurrentDictionary <string, object>();

            foreach (string role in _environmentDescription.Roles)
            {
                foreach (StageDefinition stage in _environmentDescription.Stages)
                {
                    foreach (ServerDefinition server in stage.Servers)
                    {
                        if (!server.Roles.Contains(role))
                        {
                            continue;
                        }

                        if (!Directory.Exists(server.OutputPath))
                        {
                            Directory.CreateDirectory(server.OutputPath);
                        }

                        if (_initConfig.Debug)
                        {
                            PerformTransformations(objectCache, includeContext, server, role, stage);
                            continue;
                        }

                        tasks.Add(Task.Run(() =>
                        {
                            PerformTransformations(objectCache, includeContext, server, role, stage);
                        }));
                    }
                }
            }

            if (!_initConfig.Debug)
            {
                Task.WaitAll(tasks.ToArray());
            }

            sw.Stop();

            return(DetermineResult(sw));
        }
        public bool TryHandle(ITransformationContext transformationContext, JToken makro, string[] makroArguments)
        {
            if (makroArguments.Length != 2 || makroArguments[0] != "PATCH")
            {
                return(false);
            }

            JArray array = (JArray)makro.Parent;

            int index = array.IndexOf(makro);

            array.RemoveAt(index);

            string extensionToken = makroArguments[1];

            IIncludeContext includeContext = transformationContext.GetPluginPolicy <IIncludeContext>();

            ILogMessageSink logger = transformationContext.GetPluginPolicy <ILogMessageSink>();

            IEnumerable <(IPolicyPatch, IEnumerable <JObject>)> basePatches =
                _patchCollector
                .GetBasePatches(transformationContext, includeContext, $"{extensionToken}.json")
                .Select(p => (p, p.GetPolicies()));

            foreach ((IPolicyPatch, IEnumerable <JObject>)patch in basePatches)
            {
                foreach (JObject policy in patch.Item2)
                {
                    string sourceTerm = CreateSourceTerm(policy, patch.Item1.PatchSourcePath, "Base");
                    AppendPatchSource(policy, sourceTerm);
                    logger.Message(sourceTerm);

                    array.Insert(index, policy);
                    index++;
                }
            }

            IEnumerable <IPolicyPatch> targetedPatches =
                _patchCollector
                .GetTargetedPatches(transformationContext, includeContext, $"{extensionToken}.json");

            foreach (IPolicyPatch policy in targetedPatches)
            {
                ApplyPatch(logger, policy, array);
            }

            return(true);
        }
        private void ApplyAssignPatch(ILogMessageSink logger, IPolicyPatch policyPatch, List <JObject> patchPolicies, JArray policySet)
        {
            foreach (JObject patchPolicy in patchPolicies)
            {
                JObject patchTarget = FindPatchTarget(policyPatch, policySet);

                string sourceTerm = CreateSourceTerm(patchPolicy, policyPatch.PatchSourcePath, "Assign");

                if (patchTarget == null)
                {
                    logger.Warning(
                        $"Patch {sourceTerm} was not applied because a matching base patch was not found.");
                    continue;
                }

                if (policyPatch.PatchTargetQuery != null)
                {
                    patchTarget = (JObject)patchTarget.SelectToken(policyPatch.PatchTargetQuery);

                    if (patchTarget == null)
                    {
                        logger.Warning(
                            $"Patch {sourceTerm} was not applied because a matching base patch target {policyPatch.PatchTargetQuery} was not found.");
                        continue;
                    }
                }

                foreach (JProperty patchProperty in patchPolicy.Properties())
                {
                    if (patchProperty.Name.StartsWith("§"))
                    {
                        continue;
                    }

                    patchTarget[patchProperty.Name] = patchProperty.Value;
                }

                AppendPatchSource(patchTarget, sourceTerm);

                logger.Message($"{sourceTerm}\n\twhere {JsonConvert.SerializeObject(policyPatch.Conditions)}");
            }
        }
        public void Flush()
        {
            List <LogMessage> messages = this.messages;

            this.messages = new List <LogMessage>();
            messages.ForEach(l =>
            {
                if (l is ErrorMessage)
                {
                    _logMessageSink.Error(l.Message);
                    return;
                }

                if (l is WarningMessage)
                {
                    _logMessageSink.Warning(l.Message);
                    return;
                }

                _logMessageSink.Message(l.Message);
            });
            messages.Clear();
        }
        private void ApplyReplacePatch(ILogMessageSink logger, IPolicyPatch policyPatch, List <JObject> patchPolicies, JArray policySet)
        {
            foreach (JObject patchPolicy in patchPolicies)
            {
                JObject originalObject = FindPatchTarget(policyPatch, policySet);

                string sourceTerm = CreateSourceTerm(patchPolicy, policyPatch.PatchSourcePath, "Replace");

                if (originalObject == null)
                {
                    logger.Warning(
                        $"Patch {sourceTerm} was not applied because a matching base patch was not found.");
                    continue;
                }

                int index = policySet.IndexOf(originalObject);

                policySet.RemoveAt(index);

                policySet.Insert(index, patchPolicy);

                logger.Message($"{sourceTerm}\n\twhere {JsonConvert.SerializeObject(policyPatch.Conditions)}");
            }
        }