Ссылка на содержащий класс из внутреннего класса с помощью Xtext, Xbase и JvmModelInferrer

У меня возникла проблема со ссылкой на класс контейнера из внутреннего класса с Xtext, Xbase и механизмом вывода модели Java. Чтобы осветить мою проблему, позвольте мне сначала продемонстрировать рабочий пример (взятый из Implementing Domain-Specific Languages ​​with Xtext and 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: Fields в сгенерированном внутреннем классе не распознается, нет проблем, как в собственном файле, вспомогательные методы

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 не работают.

Хотя я могу обойти это, явно экстернализируя внутренние классы с соглашением об именах и изменяя имя/область? провайдер, мне интересно, есть ли простое решение.


person fundagain    schedule 28.10.2018    source источник
comment
можете ли вы сократить это до минимального примера (без атрибутов) и открыть ошибку на github.com/eclipse/xtext-extras.   -  person Christian Dietrich    schedule 28.10.2018
comment
Я не могу обещать вам какое-либо исправление в ближайшее время. внутренние типы глючат, как hello. возможно, вам следует избегать их   -  person Christian Dietrich    schedule 28.10.2018
comment
Спасибо за ваш ответ. Я опубликую сообщение об ошибке, как было предложено, и, зная, что есть проблема, я буду кодировать ее.   -  person fundagain    schedule 28.10.2018