예제 #1
0
        private void CheckCompatibility(TemplateDescription template, Entity dataModel)
        {
            IEnumerable <TemplateDescription> roots       = FindCompatibleRoots();
            TemplateEntity             templateEntity     = TemplateEntity.Decorate(dataModel.Root);
            List <TemplateDescription> dataModelTemplates = GetTemplateHierarchy();

            if (templateEntity.HasTemplate &&
                !roots.Any(dataModelTemplates.Contains))
            {
                throw new TemplateIncompatibleException(template.name, templateEntity.Template.name);
            }

            List <TemplateDescription> GetTemplateHierarchy()
            {
                List <TemplateDescription> result = new List <TemplateDescription>();

                if (!templateEntity.HasTemplate)
                {
                    return(result);
                }

                TemplateDescription current = templateEntity.Template;

                result.Add(current);
                while (!string.IsNullOrEmpty(current?.basedOn))
                {
                    current = repository.Template(current.basedOn);
                    result.Add(current);
                }

                return(result);
            }

            IEnumerable <TemplateDescription> FindCompatibleRoots()
            {
                List <TemplateDescription> unvisited = new List <TemplateDescription>(new [] { template });
                List <TemplateDescription> visited   = new List <TemplateDescription>();
                List <TemplateDescription> result    = new List <TemplateDescription>();

                while (unvisited.Except(visited).Any())
                {
                    TemplateDescription visiting = unvisited.Except(visited).First();
                    visited.Add(visiting);
                    if (visiting.isRoot)
                    {
                        result.Add(visiting);
                    }
                    else
                    {
                        unvisited.AddRange(visiting.Relationship
                                           .Where(relationship => repository.Template(relationship.type) != null)
                                           .Select(relationship => repository.Template(relationship.type)));
                    }
                }

                return(result.Distinct());
            }
        }
        private async Task <VirtualFile> GetFile(Entity dataModel, templateFile file, bool forced, string basePath,
                                                 TemplateDescription template)
        {
            string path = await resolver.ResolveAsync(file.path ?? string.Empty, dataModel).ConfigureAwait(false);

            string name = await resolver.ResolveAsync(file.name, dataModel).ConfigureAwait(false);

            if (!forced && fileSystem.FileExists(Path.Combine(path, name), basePath))
            {
                throw new FileExistsException(name, template.name);
            }

            VirtualFile destination = fileSystem.GetFile(Path.Combine(path, name), basePath);

            return(destination);
        }
        public async Task <IEnumerable <VirtualFile> > InitalizeTemplate(Entity dataModel, ChangeObservable observable)
        {
            bool forced = dataModel.Value <CommandDefinition>()
                          .Argument <BoolArgument>(TemplateCommandBuilder.ForcedArgumentName)
                          .Value;
            TemplateDescription template = dataModel.Template();

            List <VirtualFile> generatedFiles = new List <VirtualFile>(await InitializeFiles().ConfigureAwait(false));

            generatedFiles.AddRange(await InitializeSubTemplates().ConfigureAwait(false));

            Exception e = dataModel.GetCodeExceptions();

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

            return(generatedFiles);

            async Task <IEnumerable <VirtualFile> > InitializeFiles()
            {
                string basePath             = dataModel.Root.Path;
                HashSet <VirtualFile> files = new HashSet <VirtualFile>();

                foreach (templateFile file in template.File)
                {
                    (string content, Encoding encoding) = await GetResolvedTemplateContent(dataModel, file, template).ConfigureAwait(false);

                    VirtualFile destination = await GetFile(dataModel, file, forced, basePath, template).ConfigureAwait(false);

                    observable.OnNext(new Change(() => destination.Restore(),
                                                 $"Create file {Path.GetFileName(destination.Name)} for template " +
                                                 $"{template.name} in {destination.Parent.FullName}."));

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

                    files.Add(destination);
                }

                return(files);
            }

            async Task <IEnumerable <VirtualFile> > InitializeSubTemplates()
            {
                List <VirtualFile> files = new List <VirtualFile>();

                foreach (templateReference reference in SortByRelationship(template.AddTemplate ?? Enumerable.Empty <templateReference>()))
                {
                    if (repository.Template(reference.template) == null)
                    {
                        throw new TemplateReferenceNotDefinedException(reference.template);
                    }

                    CommandDefinitionBuilder pseudoDefinition = CommandDefinitionBuilder.Create()
                                                                .SetName(reference.template);
                    foreach (templateArgumentInstance argumentInstance in reference.Arguments)
                    {
                        AddArgument(argumentInstance, pseudoDefinition);
                    }

                    IEnumerable <IGrouping <string, templateRelationshipInstance> > grouped = (reference.Relationship ?? Enumerable.Empty <templateRelationshipInstance>()).GroupBy(r => r.name);
                    foreach (IGrouping <string, templateRelationshipInstance> relationshipInstances in grouped)
                    {
                        AddRelationships(relationshipInstances, pseudoDefinition, reference);
                    }

                    pseudoDefinition.CreateArgument()
                    .SetName(TemplateCommandBuilder.ForcedArgumentName)
                    .SetValue(forced)
                    .Build();
                    Entity referencedTemplateEntity = dataModel.Create(reference.template, pseudoDefinition.Build());
                    dataModel.AddEntity(referencedTemplateEntity);
                    files.AddRange(await InitalizeTemplate(referencedTemplateEntity, observable).ConfigureAwait(false));
                }

                return(files);

                void AddArgument(templateArgumentInstance templateArgumentInstance, CommandDefinitionBuilder commandDefinitionBuilder)
                {
                    string templateArgumentValue = resolver.Resolve(templateArgumentInstance.value, dataModel);
                    bool   argumentHasNoValue    = bool.TryParse(templateArgumentValue, out bool boolValue);

                    string[] argumentSplit   = templateArgumentValue.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                    bool     isMultiArgument = argumentSplit.Length > 1;

                    if (argumentHasNoValue)
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(boolValue)
                        .Build();
                    }
                    else if (isMultiArgument)
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(argumentSplit)
                        .Build();
                    }
                    else
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(templateArgumentValue)
                        .Build();
                    }
                }

                void AddRelationships(IGrouping <string, templateRelationshipInstance> relationshipInstances,
                                      CommandDefinitionBuilder commandDefinitionBuilder, templateReference reference)
                {
                    templateRelationship relationshipDefinition = repository.Template(reference.template).Relationship
                                                                  ?.FirstOrDefault(r => r.name ==
                                                                                   relationshipInstances.Key);

                    if (relationshipDefinition == null)
                    {
                        throw new TemplateRelationshipNotFoundException(reference.template, relationshipInstances.Key);
                    }

                    bool multipleValues = relationshipDefinition.multiplicity == multiplicity.OneOrMore;

                    if (multipleValues)
                    {
                        string[] relationships = relationshipInstances.Select(r => resolver.Resolve(r.value, dataModel))
                                                 .ToArray();
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(relationshipInstances.Key)
                        .SetValue(relationships)
                        .Build();
                    }
                    else
                    {
                        if (relationshipInstances.Count() != 1)
                        {
                            throw new RelationshipMultiplicityMismatchException(relationshipInstances.Key, reference.template);
                        }
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(relationshipInstances.Key)
                        .SetValue(resolver.Resolve(relationshipInstances.Single().value, dataModel))
                        .Build();
                    }
                }

                IEnumerable <templateReference> SortByRelationship(IEnumerable <templateReference> references)
                {
                    List <templateReference> unsorted = references.ToList();
                    List <templateReference> sorted   = new List <templateReference>();

                    while (unsorted.Any())
                    {
                        Insert(unsorted[0]);
                    }

                    return(sorted);

                    void Insert(templateReference current, CycleChecker <templateReference> cycleChecker = null)
                    {
                        using (cycleChecker = cycleChecker?.SpawnChild() ?? new CycleChecker <templateReference>(
                                   ExceptionTexts.TemplateRelationshipCycle,
                                   () => cycleChecker = null))
                        {
                            cycleChecker.AddItem(current);
                            List <templateReference> dependent = new List <templateReference>();
                            foreach (templateRelationshipInstance relationshipInstance in current.Relationship ??
                                     Enumerable
                                     .Empty <
                                         templateRelationshipInstance
                                         >())
                            {
                                templateReference reference =
                                    unsorted.FirstOrDefault(r => HasRelationship(r, relationshipInstance));
                                if (reference != null)
                                {
                                    Insert(reference, cycleChecker);
                                }
                                else
                                {
                                    reference = sorted.FirstOrDefault(r => HasRelationship(r, relationshipInstance));
                                }

                                if (reference != null)
                                {
                                    dependent.Add(reference);
                                }
                            }

                            int skipping = dependent.Any() ? dependent.Select(d => sorted.IndexOf(d)).Max() : -1;
                            int index    = skipping + 1;
                            sorted.Insert(index, current);
                            unsorted.Remove(current);

                            bool HasRelationship(templateReference currentReference,
                                                 templateRelationshipInstance relationshipInstance)
                            {
                                templateArgumentInstance instance = currentReference.Arguments
                                                                    .FirstOrDefault(a => a.name
                                                                                    .Equals(
                                                                                        EntityKeys
                                                                                        .NameKey,
                                                                                        StringComparison
                                                                                        .OrdinalIgnoreCase));

                                return(instance?.value?.Equals(relationshipInstance.value,
                                                               StringComparison.OrdinalIgnoreCase) == true);
                            }
                        }
                    }
                }
            }
        }
        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));
                }
            }
        }
        private async Task <(string content, Encoding encoding)> GetResolvedTemplateContent(Entity dataModel, templateFile file, TemplateDescription template)
        {
            string   content;
            Encoding encoding;

            using (Stream fileStream = fileSystem.GetFile(file.template, repository.GetTemplateBase(template)).OpenRead())
                using (StreamReader reader = new StreamReader(fileStream, Encoding.UTF8, true))
                {
                    encoding = reader.CurrentEncoding;
                    content  = await reader.ReadToEndAsync().ConfigureAwait(false);
                }

            content = await resolver.ResolveAsync(content, dataModel).ConfigureAwait(false);

            return(content, encoding);
        }
예제 #6
0
        private async Task <(string content, Encoding encoding)> GetResolvedTemplateContent(Entity dataModel, templateFile file, TemplateDescription template)
        {
            string      content;
            Encoding    encoding;
            VirtualFile virtualFile = fileSystem.GetFile(file.template, repository.GetTemplateBase(template));

            if (!virtualFile.Exists)
            {
                return(default);