Xtext Global Auto-Generation - PullRequest
       7

Xtext Global Auto-Generation

0 голосов
/ 25 августа 2018

В Xtext как один автоматически генерирует один файл, содержащий информацию из нескольких файлов модели.

Рассмотрим следующую простую грамматику Xtext.

grammar org.example.people.People with org.eclipse.xtext.common.Terminals

generate people "http://www.example.org/people/People"

People:
    people+=Person*;

Person:
    'person' name=ID ';';

В запущенном рабочем пространстве я создаю проект с двумя файлами, friends.people

// friends
person Alice;
person Bob;

и enemies.people

// enemies
person Malice;
person Rob;

Как автоматически сгенерировать один файл со всеми при изменении глобального индекса?

Alice
Bob
Malice
Rob

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Я добавил проверочный код в ответ Fundagain, чтобы устранить недействительные ресурсы. Однако это не будет работать, когда последний измененный ресурс недействителен, поскольку doGenerate не вызывается, когда он недействителен. При сохранении любого допустимого ресурса недействительные ресурсы будут удалены из all.txt.

override doGenerate(ResourceSet rs, IFileSystemAccess2 fsa, IGeneratorContext context) {
    var valid_rs = new ArrayList<Resource>
    for(r : rs.resources)
        if (( r as XtextResource)
            .getResourceServiceProvider()
            .getResourceValidator()
            .validate(r,CheckMode.ALL, null)
            .map(issue | issue.severity)
            .filter[it === Severity.ERROR]
            .size == 0) 
                valid_rs.add(r)

    val types = valid_rs.map(r|r.allContents.toIterable.filter(Person)).flatten
    fsa.generateFile("all.txt", people.compile)
}
0 голосов
/ 25 августа 2018

Для простоты использования в будущем здесь приводится решение, полученное путем объединения различных ссылок, данных Кристианом Дитрихом.Обратите внимание, что решение зависит от Eclipse.

Любой, кто сталкивается с этим требованием, возможно, должен попытаться найти лучший способ моделирования проблемы.Например, одноэлементный элемент модели All, который генерирует требуемый список, находя всех в модели с использованием стандартного API.Это не зависит от Eclipse и требует не следующей сложности.

В пакете генератора проекта грамматики создайте интерфейс Java IPeopleGenerator, расширяющий IGenerator2.

package org.example.people.generator;

import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.generator.IFileSystemAccess2;
import org.eclipse.xtext.generator.IGenerator2;
import org.eclipse.xtext.generator.IGeneratorContext;

public interface IPeopleGenerator extends IGenerator2{
    public void doGenerate(ResourceSet input, IFileSystemAccess2 fsa, IGeneratorContext context);
}

, и отредактируйте существующий генератор PeopleGenerator следующим образом.

/*
 * generated by Xtext 2.14.0
 */
package org.example.people.generator

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import org.example.people.people.Person

/**
 * Generates code from your model files on save.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
 */
class PeopleGenerator implements IPeopleGenerator {

    override doGenerate(ResourceSet rs, IFileSystemAccess2 fsa, IGeneratorContext context) {

    val people = rs.resources.map(r|r.allContents.toIterable.filter(Person)).flatten
    fsa.generateFile("all.txt", people.compile)
    }

    override afterGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) {
    }

    override beforeGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) {
    }

    override doGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) {
    }

  def compile (Iterable<Person> entities) '''
    «FOR e : entities»
    «e.name»
    «ENDFOR»
  '''

}

и добавьте метод

def Class<? extends IPeopleGenerator> bindIPeopleGenerator () {
    return PeopleGenerator
}

к существующему модулю времени выполнения PeopleRuntimeModule в грамматическом проекте.

Работу необходимо выполнить в проекте пользовательского интерфейса org.example.people.ui,Следовательно, это решение зависит от затмения.

Создайте класс Java org.example.people.ui.PeopleBuilderParticipant следующим образом (сложность заключается в том, что глобальный сгенерированный файл должен быть создан только один раз) .

package org.example.people.ui;

import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.builder.BuilderParticipant;
import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2;
import org.eclipse.xtext.builder.MonitorBasedCancelIndicator;
import org.eclipse.xtext.generator.GeneratorContext;
import org.eclipse.xtext.resource.IContainer;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescription.Delta;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
import org.example.people.generator.IPeopleGenerator;

import com.google.inject.Inject;

public class PeopleBuilderParticipant extends BuilderParticipant {

    @Inject
    private ResourceDescriptionsProvider resourceDescriptionsProvider;

    @Inject
    private IContainer.Manager containerManager;

    @Inject(optional = true)
    private IPeopleGenerator generator;

    protected ThreadLocal<Boolean> buildSemaphor = new ThreadLocal<Boolean>();

    @Override
    public void build(IBuildContext context, IProgressMonitor monitor) throws CoreException {
        buildSemaphor.set(false);
        super.build(context, monitor);
    }

    @Override
    protected void handleChangedContents(Delta delta, IBuildContext context,
            EclipseResourceFileSystemAccess2 fileSystemAccess) throws CoreException {
        super.handleChangedContents(delta, context, fileSystemAccess);
        if (!buildSemaphor.get() && generator != null) {
            invokeGenerator(delta, context, fileSystemAccess);
        }
    }
    private void invokeGenerator(Delta delta, IBuildContext context, EclipseResourceFileSystemAccess2 access) {
        buildSemaphor.set(true);
        Resource resource = context.getResourceSet().getResource(delta.getUri(), true);
        if (shouldGenerate(resource, context)) {
            IResourceDescriptions index = resourceDescriptionsProvider.createResourceDescriptions();
            IResourceDescription resDesc = index.getResourceDescription(resource.getURI());
            List<IContainer> visibleContainers = containerManager.getVisibleContainers(resDesc, index);
            for (IContainer c : visibleContainers) {
                for (IResourceDescription rd : c.getResourceDescriptions()) {
                    context.getResourceSet().getResource(rd.getURI(), true);
                }
            }

            MonitorBasedCancelIndicator cancelIndicator = new MonitorBasedCancelIndicator(
                    new NullProgressMonitor()); //maybe use reflection to read from fsa
            GeneratorContext generatorContext = new GeneratorContext();
            generatorContext.setCancelIndicator(cancelIndicator);
            generator.doGenerate(context.getResourceSet(), access, generatorContext);
        }
    }

}

ипривязать этого участника сборки, добавив

override  Class<? extends IXtextBuilderParticipant>  bindIXtextBuilderParticipant() {       
    return PeopleBuilderParticipant;
}

к существующему модулю пользовательского интерфейса org.example.people.ui.PeopleUiModule.

...