public async Task GenerateFiles(Entity rootEntity, string generator, string output, bool outputDefined,
                                        ChangeObservable observable)
        {
            IEnumerable <Entity>       generatableEntities = rootEntity.Hierarchy();
            HashSet <VirtualFile>      generatedFiles      = new HashSet <VirtualFile>();
            HashSet <VirtualDirectory> rootDirectories     = new HashSet <VirtualDirectory>();

            foreach (Entity generatableEntity in generatableEntities)
            {
                await GenerateFilesForEntity(generatableEntity).ConfigureAwait(false);
            }

            Exception e = rootEntity.GetCodeExceptions();

            if (e != null)
            {
                e.CompleteCodeExceptions(fileSystem.GetDirectory(rootEntity.Path));
                throw e;
            }

            foreach (VirtualDirectory rootDirectory in rootDirectories)
            {
                DeleteRedundantFiles(rootDirectory);
            }

            void DeleteRedundantFiles(VirtualDirectory rootDirectory)
            {
                foreach (VirtualFile file in rootDirectory.Files(searchRecursive:true).Where(f => !generatedFiles.Contains(f)).ToArray())
                {
                    VirtualDirectory current = file.Parent;
                    file.Delete();
                    while (current != null && !current.Entries.Any())
                    {
                        VirtualDirectory next = current.Parent;
                        current.Delete();
                        current = next;
                    }
                }
            }

            async Task GenerateFilesForEntity(Entity generatableEntity)
            {
                TemplateDescription template = generatableEntity.Template();

                if (template == null)
                {
                    return;
                }

                foreach (templateGeneratedFile file in template.GeneratedFile ?? Enumerable.Empty <templateGeneratedFile>())
                {
                    if (generator != "all" && !file.generator.Equals(generator, StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    if (!string.IsNullOrEmpty(file.condition))
                    {
                        string condition = await resolver.ResolveAsync(file.condition, generatableEntity).ConfigureAwait(false);

                        if (condition.Equals("false", StringComparison.OrdinalIgnoreCase))
                        {
                            continue;
                        }
                    }

                    (string content, Encoding encoding) = await GetResolvedTemplateContent(generatableEntity, file, template).ConfigureAwait(false);

                    VirtualDirectory baseDirectory = GetBaseDirectory(file);
                    VirtualFile      destination   = await GetFile(generatableEntity, file, true, baseDirectory.FullName, template).ConfigureAwait(false);

                    rootDirectories.Add(baseDirectory);
                    generatedFiles.Add(destination);

                    observable.OnNext(new Change(() => destination.Restore(), $"Generated the file {destination.FullName}."));

                    using (Stream fileStream = destination.OpenComparingWriteStream())
                        using (StreamWriter writer = new StreamWriter(fileStream, encoding))
                        {
                            await writer.WriteAsync(content).ConfigureAwait(false);
                        }
                }

                VirtualDirectory GetBaseDirectory(templateGeneratedFile file)
                {
                    string generatorPath = output;

                    if (!outputDefined)
                    {
                        generatorPath = Path.Combine(Constants.IntermediateFolderName, file.generator.ToLowerInvariant());
                    }
                    else if (generator == "all")
                    {
                        generatorPath = Path.Combine(generatorPath, file.generator.ToLowerInvariant());
                    }
                    return(fileSystem.GetDirectory(generatorPath, rootEntity.Path));
                }
            }
        }