У меня проблема со ссылкой на класс контейнера из внутреннего класса с помощью Xtext, Xbase и логического модуля модели Java.Чтобы осветить мою проблему, позвольте мне сначала продемонстрировать рабочий пример (полученный от Реализация доменных языков с Xtext и Xtend Беттини), который я называю свободной грамматикой.Он не имеет смысла в том смысле, что сущности могут наследовать любой класс Java (а не только сущности), а атрибуты типизируются любым классом Java (а не просто сущностями).
Этот вопрос касается ужесточения этой грамматики только для сущностей.
Благодаря ответам на Грамматически ограниченное наследование JVMModelInferrer с Xtext и XBase и Xbase: поля в сгенерированном внутреннем классе не распознаны, проблем как класса в собственном файле нет,Я вполне могу достичь этого, за исключением случая ссылок на внутренние классы, содержащих классы .Этот вопрос касается только случая внутреннего класса.
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
ModelDeclaration:
importSection=XImportSection?
packageDeclaration=PackageDeclaration;
PackageDeclaration:
'package' name=QualifiedName ';'?
model=Model;
Model:
'model' name=ID '{'
(
entities+=Entity
)+
'}';
Entity:
'entity' name=ID ('extends' superType=JvmParameterizedTypeReference)? '{'
(attributes+=Attribute
|
entities+=Entity
)*
'}';
Attribute:
'attr' (type=JvmTypeReference) name=ID ';';
Следующий JvmModelInferrer отлично подделывает один класс для модели, причем сущности в качестве внутренних классов, возможно, сами содержат внутренние классы.Все сгенерированные внутренние классы являются статическими.
package org.xtext.example.mydsl.jvmmodel
import com.google.inject.Inject
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myDsl.Entity
import org.xtext.example.mydsl.myDsl.Model
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
def dispatch void infer(Model model, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(
model.toClass(model.fullyQualifiedName)
) [
model.entities.forEach [ entity |
members += entity.toClass(entity.fullyQualifiedName) [ jEntity |
forgeEntities(model, jEntity, entity)
]
]
]
}
protected def void forgeEntities(Model model, JvmGenericType it, Entity entity) {
static=true
documentation = entity.documentation
if (entity.superType !== null) {
superTypes += entity.superType
}
entity.attributes.forEach [ a |
val type = a.type
members += a.toField(a.name, type) [
documentation = a.documentation
]
members += a.toGetter(a.name, type)
members += a.toSetter(a.name, type)
]
entity.entities.forEach [ innerEntity |
members += innerEntity.toClass(innerEntity.fullyQualifiedName) [ jInnerEntity |
forgeEntities(model, jInnerEntity, innerEntity)
]
]
}
}
Например, экземпляр
package test
model Test {
entity A {}
entity B {
attr Test.A myA;
}
entity C extends test.Test.A {}
entity D {
attr Test.A myA;
entity I {}
entity J extends test.Test.D {}
entity K {
attr Test.D myD;
}
}
}
правильно выводит
package test;
@SuppressWarnings("all")
public class Test {
public static class A {
}
public static class B {
private Test.A myA;
public Test.A getMyA() {
return this.myA;
}
public void setMyA(final Test.A myA) {
this.myA = myA;
}
}
public static class C extends Test.A {
}
public static class D {
private Test.A myA;
public Test.A getMyA() {
return this.myA;
}
public void setMyA(final Test.A myA) {
this.myA = myA;
}
public static class I {
}
public static class J extends Test.D {
}
public static class K {
private Test.D myD;
public Test.D getMyD() {
return this.myD;
}
public void setMyD(final Test.D myD) {
this.myD = myD;
}
}
}
}
Теперь рассмотрим следующее ужесточение грамматики.
...
Entity:
'entity' name=ID ('extends' superType=[Entity|QualifiedName])? '{'
(attributes+=Attribute
|
entities+=Entity
)*
'}';
Attribute:
'attr' (type=[Entity|QualifiedName]) name=ID ';';
К логическому выводу модели, который я добавляю, на основе Xbase: поля в сгенерированном внутреннем классе не распознаны, нет проблем, поскольку класс в собственном файле , вспомогательные методы
def String getJavaTypeName(Entity entity) {
val parent = entity.eContainer
if (parent instanceof Model) {
return (parent.fullyQualifiedName.toString+"$"+entity.name)
}
if (parent instanceof Entity) {
return getJavaTypeName(parent)+"$"+entity.name
}
throw new RuntimeException("Impossible")
}
def JvmTypeReference getJavaTypeRef(Entity entity) {
getJavaTypeName(entity).typeRef
}
и измените метод forgeEntities на
protected def void forgeEntities(Model model, JvmGenericType it, Entity entity) {
static=true
documentation = entity.documentation
println("### FORGING [" + entity.name + "]")
if (entity.superType !== null) {
superTypes += entity.superType.javaTypeRef
}
entity.attributes.forEach [ a |
val type = a.type.javaTypeRef
members += a.toField(a.name, type) [
documentation = a.documentation
]
members += a.toGetter(a.name, type)
members += a.toSetter(a.name, type)
]
entity.entities.forEach [ innerEntity |
members += innerEntity.toClass(innerEntity.fullyQualifiedName) [ jInnerEntity |
forgeEntities(model, jInnerEntity, innerEntity)
]
]
}
Для той же модели (хотя теперь ссылки могут быть сжаты)
package test
model Test {
entity A {}
entity B {
attr A myA;
}
entity C extends A {}
entity D {
attr A myA;
entity I {}
entity J extends D {}
entity K {
attr D myD;
}
}
}
генерируется следующее.
package test;
@SuppressWarnings("all")
public class Test {
public static class A {
}
public static class B {
private Test.A myA;
public Test.A getMyA() {
return this.myA;
}
public void setMyA(final Test.A myA) {
this.myA = myA;
}
}
public static class C extends Test.A {
}
public static class D {
private Test.A myA;
public Test.A getMyA() {
return this.myA;
}
public void setMyA(final Test.A myA) {
this.myA = myA;
}
public static class I {
}
public static class J implements test.Test$D {
}
public static class K {
private test.Test$D myD;
public test.Test$D getMyD() {
return this.myD;
}
public void setMyD(final test.Test$D myD) {
this.myD = myD;
}
}
}
}
Хотя все работает, как и ожидалось, для A, B и C, демонстрируя, что "$" работает, класс D не работает.Разница в том, что внутренний класс ссылается на содержащий его класс.Это когда процесс не удается.И расширение J на D, и ссылка из myD на D в K не работают.
Хотя я могу обойти это, явно экстернализуя внутренние классы с помощью соглашения об именах и изменяя имя / область видимости?провайдер, мне интересно, есть ли простое решение.