Using Inject in an Xtend based Xtext code generator - xtext

I am brand new to Xtext and Xtend and am trying to learn Xtext using the Xtext tutorials in the Xtext documentation. I am running on Eclipse Photon under Java 10 with Xtext 2.14. I am starting the extended tutorial and have a problem very early on. Here is the code for my attempt at a code generator:
/*
* generated by Xtext 2.14.0
*/
package net.wiseoldbird.tutorial.domainmodel.generator
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import net.wiseoldbird.tutorial.domainmodel.domainmodel.Entity
import com.google.inject.Inject
#Inject extension IQualifiedNameProvider;
class DomainmodelGenerator extends AbstractGenerator {
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
for (e: resource.allContents.toIterable.filter(Entity)) {
fsa.generateFile(e.fullyQualifiedName.toString("/") + ".java", e.compile)
}
}
}
Here is my grammar file:
grammar net.wiseoldbird.tutorial.domainmodel.Domainmodel
with org.eclipse.xtext.common.Terminals
generate domainmodel "http://www.wiseoldbird.net/tutorial/domainmodel/Domainmodel"
Domainmodel :
(elements+=AbstractElement)*;
PackageDeclaration:
'package' name=QualifiedName '{'
(elements+=AbstractElement)*
'}';
AbstractElement:
PackageDeclaration | Type | Import;
QualifiedName:
ID ('.' ID)*;
Import:
'import' importedNamespace=QualifiedNameWithWildcard;
QualifiedNameWithWildcard:
QualifiedName '.*'?;
Type:
DataType | Entity;
DataType:
'datatype' name=ID;
Entity:
'entity' name=ID ('extends' superType=[Entity|QualifiedName])? '{'
(features+=Feature)*
'}';
Feature:
(many?='many')? name=ID ':' type=[Type|QualifiedName];
My problem is that Eclipse says there is a problem with the #Inject annotation. It says that Inject cannot be resolved to an annotation type. This is in an Eclipse Xtext project generated from the instructions in the tutorial.
How do I proceed from here?

you can inject only fields and post-init-method/constructor parameters
import com.google.inject.Inject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.example.domainmodel.domainmodel.Entity
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import org.eclipse.xtext.naming.IQualifiedNameProvider
class DomainmodelGenerator extends AbstractGenerator {
#Inject extension IQualifiedNameProvider
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
for (e : resource.allContents.toIterable.filter(Entity)) {
fsa.generateFile(e.fullyQualifiedName.toString("/") + ".java", e.compile)
}
}
def compile(Entity e) '''
package «e.eContainer.fullyQualifiedName»;
public class «e.name» {
}
'''
}

Related

Why does swagger codegen generates createAll java method name as create_all_using_postIntValue in python instead of create_all_using_post?

While generating swagger client through CodeGen, it is appending 2 digits character at the end of the method name. For Ex: Java rest API
HTTP POST /all
is generating the method name in python client as
create_all_using_postIntValue or create_all_using_post19
I want the generated method name as create_all_using_post. Why is it happened and how to fix this?
I am using below commands to generate the client:
swagger-codegen generate -i https://example.com/v3/api-docs -l python -o swagger_test
I got the answer. MethodName can be override by implementing OperationBuilderPlugin. Below code fixed my issue:
import com.google.common.base.CaseFormat;
import io.swagger.annotations.ApiOperation;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.Operation;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import java.util.Optional;
#Component
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class GenerateUniqueOperationName implements OperationBuilderPlugin {
#Override
public void apply(OperationContext context) {
Optional<ApiOperation> methodAnnotation = context.findControllerAnnotation(ApiOperation.class);
Operation operationBuilder = context.operationBuilder().build();
if(operationBuilder.getTags().stream().findFirst().get().isEmpty())
throw new RuntimeException("operationBuilder.getTags().stream().findFirst()");
String tag = operationBuilder.getTags().stream().findFirst().get();
String methodType = operationBuilder.getMethod().name().toUpperCase();
String methodName = operationBuilder.getSummary();
String operationId = tag + "_" + methodType + "_" + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, methodName);
context.operationBuilder().uniqueId(operationId);
context.operationBuilder().codegenMethodNameStem(operationId);
}
#Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}

parser rule not found error while defining grammar

I am new to compiler design as well as ANTLR and trying out some basic stuffs from a book.I came across this problem.Can someone help out. I am expecting the grammar validate any numbers separated by commas in between 2 braces.
Below is my java application to trigger the generated parser but it says at this line parser.init() cannot resolve method init(). I assume the init is the parser rule defined in my grammar.
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.IOException;
public class Adamant{
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
testLexer lexer = new testLexer((CharStream) input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
testParser parser = new testParser(tokens);
ParseTree tree = parser.init();
System.out.println(tree.toStringTree(parser));
}
}
Below is my grammar
grammar test;
init: '{' value (',' value)* '}';
value:
init
| INT
;
INT : [0-9]+ ;
WS : [\t\n\r] -> skip;

Rascal : NullPointerException at createM3FromEclipseProject

The following code with Rascal will not work:
module Exercise1
import IO;
import IO;
import lang::java::jdt::m3::Core;
public void testing()
{
M3 model = createM3FromEclipseProject(|project://JabberPoint/|);
for (<a,b> <- model.extends)
println("<a> extends <b>");
}
It results with:
|plugin://rascal_eclipse/src/org/rascalmpl/eclipse/library/lang/java/jdt/m3/Core.rsc|(392,187,<13,0>,<15,90>): Java("NullPointerException","")
at org.eclipse.jdt.core.dom.CompilationUnitResolver.resolve(|unknown:///CompilationUnitResolver.java|(0,0,<599,0>,<599,0>))
at org.eclipse.jdt.core.dom.ASTParser.createASTs(|unknown:///ASTParser.java|(0,0,<906,0>,<906,0>))
at org.rascalmpl.eclipse.library.lang.java.jdt.m3.internal.EclipseJavaCompiler.compileAllFiles(|unknown:///EclipseJavaCompiler.java|(0,0,<88,0>,<88,0>))....
What could be the problem?
Thanks.

How to change the delimiter of QualifiedNames

I want to change the delimiter of the QualifiedName from '.' to '#'. Below is my try. The following example from the online documentation.
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Domainmodel:
(elements+=AbstractElement)*;
PackageDeclaration:
'package' name=QualifiedName '{'
(elements+=AbstractElement)*
'}';
AbstractElement:
PackageDeclaration | Type;
QualifiedName:
ID ('#' ID)*;
Type:
DataType | Entity;
DataType:
'datatype' name=ID;
Entity:
'entity' name=ID ('extends' superType=[Entity|QualifiedName])? '{'
(features+=Feature)*
'}';
Feature:
(many?='many')? name=ID ':' type=[Type|QualifiedName];
package org.xtext.example.mydsl
import org.eclipse.xtext.naming.IQualifiedNameConverter
import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
override bindIQualifiedNameProvider() {
return MyDslQualifiedNameProvider
}
}
class MyDslQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider {
val converter = new MyDslQualifiedNameConverter();
override getConverter(){
converter
}
}
class MyDslQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
override getDelimiter() {
return "#";
}
}
I could not refer any Entity in a package such as "my#company#blog#Blog" in the following. IDE suggests the expression "my#company#blog.Blog" but that doesn't work either.
datatype String
package my#company#blog{
entity Blog{
title : String
}
}
entity Blog2 extends my#company#blog#Blog{
title : String
}
Guice usage solves. Following is how is done. There is no bind method to override in 'AbstractMyDslRuntimeModule'. Parent class, 'AbstractGenericModule', does the trick via 'getBindings' method of itself.
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
def Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
return MyDslQualifiedNameConverter;
}
}
class MyDslQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
override getDelimiter() {
return "#";
}
}

Xtext global auto-generation

In Xtext, how does one auto-generate a single file containing information from multiple model files.
Consider the following simple Xtext grammar.
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 ';';
In the launched workspace I create a project with two files, friends.people
// friends
person Alice;
person Bob;
and enemies.people
// enemies
person Malice;
person Rob;
How do I auto-generate a single file listing everyone when the global index changes?
Alice
Bob
Malice
Rob
For ease of future reference, here is the solution obtained by combining the various references given by Christian Dietrich. Note that the solution is Eclipse dependent.
Anyone who finds themselves with this requirement should perhaps try to find a better way of modelling the problem. For example a singleton model element All that generates the required list by finding everyone in the model using the standard API. This is independent of Eclipse, and requires non of the following complexity.
In the generator package of the grammar project, create an Java interface IPeopleGenerator extending 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);
}
and edit the existing generator PeopleGenerator as follows.
/*
* 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»
'''
}
and add the method
def Class<? extends IPeopleGenerator> bindIPeopleGenerator () {
return PeopleGenerator
}
to the existing runtime module PeopleRuntimeModule in the grammar project.
Work needs to be done in the UI project org.example.people.ui. Consequently this solution is Eclipse dependent.
Create a Java class org.example.people.ui.PeopleBuilderParticipant as follows (the complexity being the need to ensure that the global generated file is only created once).
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);
}
}
}
and bind this build participant by adding
override Class<? extends IXtextBuilderParticipant> bindIXtextBuilderParticipant() {
return PeopleBuilderParticipant;
}
to the existing UI module org.example.people.ui.PeopleUiModule.
I added the validation code to the answer of fundagain to eliminate invalid resources. However, this will not work when last modified resource is invalid because doGenerate is not invoked when invalid. When any valid resource is saved invalid resources will be discarded from 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)
}

Resources