Как избежать JvmParameterizedTypeReference: java.util.List‹JvmUnknownTypeReference: A› при создании методов в Xtext?

В моей модели у меня есть больше объектов, из которых позже создаются классы Java. Например. в одном файле определяется

Object A {
    operation getList B
}

И в другом файле:

Object B {
    operation getList A
}

1) Из этого должны быть сгенерированы 2 интерфейса:

interface A {
    java.util.List<B> getListOfBs();
}

interface B {
    java.util.List<A> getListOfAs();
}

2) Вместо этого он создается заново следующим образом:

interface A {
    /* java.util.List<B> */ Object getListOfBs();
}

interface B {
    /* java.util.List<A> */ Object getListOfAs();
}

Это происходит только тогда, когда я запускаю действие Project -> Clean... в Eclipse.
После того, как я что-то изменил в своей модели и сохранил ее, все сгенерировано нормально, это выглядит как первый пример. Когда я отлаживал свой механизм вывода, я заметил, что возвращаемый тип созданного метода определяется как JvmParameterizedTypeReference: java.util.List‹JvmUnknownTypeReference: A›.

Есть ли способ генерировать классы сразу после очистки в Eclipse?

EDIT Теперь у меня есть пример, который дублирует мою проблему. Очень нужно иметь так много файлов модели, иначе все работает нормально. Я создал новый проект Xtext со значениями по умолчанию. Мой xtext-файл:

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
ModelDomain:
    SubPackageDeclaration
    elements+=DataModelItem*;
SubPackageDeclaration:
    'subPackage' subPackageName=ID;
DataModelItem:
    DataType | DataObject | DataEnum;
DataType:
    'DataType' name=ID type=JvmParameterizedTypeReference;
DataObject:
    'DataObject' name=ID '{'
    ('lists' '{'
    (lists+=DataAttribute)*
    '}')?
    '}';
DataEnum:
    'DataEnum' name=ID '{'
    values+=ID (',' values+=ID)*
    '}';
DataAttribute:
    (transient?='transient')? type=[DataModelItem|QualifiedName] name=ID;

и вывод:

package org.xtext.example.mydsl.jvmmodel
import com.google.inject.Inject
import org.eclipse.xtext.common.types.JvmTypeReference
import org.eclipse.xtext.common.types.TypesFactory
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.DataEnum
import org.xtext.example.mydsl.myDsl.DataModelItem
import org.xtext.example.mydsl.myDsl.DataObject
import org.xtext.example.mydsl.myDsl.DataType
import org.xtext.example.mydsl.myDsl.SubPackageDeclaration
class MyDslJvmModelInferrer extends AbstractModelInferrer {
    @Inject extension JvmTypesBuilder
    @Inject private TypesFactory typesFactory
    def dispatch void infer(DataEnum dataEnum, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
        var packageName = ""
        if (dataEnum.eContainer != null && dataEnum.eContainer instanceof SubPackageDeclaration) {
            var implDec = (dataEnum.eContainer as SubPackageDeclaration)
            packageName = "data.model." + implDec.subPackageName
        }
        val qualifiedName = packageName + "." + dataEnum.name.toFirstUpper
        acceptor.accept(
            dataEnum.toEnumerationType(qualifiedName) [
                for (value : dataEnum.values) {
                    val jvmLiteral = typesFactory.createJvmEnumerationLiteral
                    jvmLiteral.simpleName = value
                    jvmLiteral.^static = true
                    var t1 = typesFactory.createJvmParameterizedTypeReference
                    t1.type = it
                    jvmLiteral.type = t1
                    members += jvmLiteral
                }
            ])
    }
    def dispatch void infer(DataObject dataObject, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
        var packageName = ""
        if (dataObject.eContainer != null && dataObject.eContainer instanceof SubPackageDeclaration) {
            var implDec = (dataObject.eContainer as SubPackageDeclaration)
            packageName = "data.model." + implDec.subPackageName
        }
        val qualifiedName = packageName + "." + dataObject.name.toFirstUpper
        acceptor.accept(
            dataObject.toInterface(qualifiedName) [
                superTypes += dataObject.newTypeRef("data.model.core.IDataObject")
                for (list : dataObject.lists) {
                    val JvmTypeReference ^void = dataObject.newTypeRef(Void.TYPE)
                    val JvmTypeReference listTypeRef = convertToJvmType(list.type)
                    if (listTypeRef != null && !isPreIndexingPhase) {
                        val removeMethod = list.toMethod("remove" + list.name.toFirstUpper, ^void) [
                            parameters += list.toParameter(list.name, listTypeRef)
                        ]
                        members += removeMethod
                        removeMethod.setAbstract(true);
                        val returnType = dataObject.newTypeRef("java.util.List", listTypeRef)
                        val allMethod = list.toMethod("get" + list.name.toFirstUpper + "List", returnType)[]
                        allMethod.setAbstract(true);
                        members += allMethod
                    }
                }
                val denyLoading = dataObject.toMethod("denyLoading", dataObject.newTypeRef(Void.TYPE))[]
                denyLoading.setAbstract(true);
                members += denyLoading
            ])
    }
    def public JvmTypeReference convertToJvmType(DataModelItem modelItem) {
        var JvmTypeReference typeRef
        if (modelItem instanceof DataType) {
            var typeData = (modelItem as DataType)
            typeRef = typeData.type.cloneWithProxies
        } else if (modelItem instanceof DataEnum) {
            var typeDec = (modelItem.eContainer as SubPackageDeclaration)
            var typePackageName = "data.model." + typeDec.subPackageName
            val typeData = modelItem as DataEnum
            typeRef = typeData.newTypeRef(typePackageName + "." + typeData.name)
        } else if (modelItem.eContainer != null) {
            var typeDec = (modelItem.eContainer as SubPackageDeclaration)
            var typePackageName = "data.model." + typeDec.subPackageName
            var typeQName = typePackageName + "." + modelItem.name
            typeRef = modelItem.newTypeRef(typeQName)
        }
        return typeRef
    }
}

Затем у меня есть эти 6 файлов mydsl:
1. address.mydsl

subPackage address DataObject PhoneNumber { lists { PhoneNumberType types } } DataObject Contact { lists { PhoneNumber phoneNumber } } DataObject Address { lists { PhoneNumber phoneNumber } } DataEnum PhoneNumberType { BUSINESS_PHONE , BUSINESS_FAX , BUSINESS_MOBILE , HOME_PHONE , HOME_MOBILE , HOME_FAX } DataEnum Gender { FEMALE , MALE }

2. актив.mydsl

subPackage asset DataObject Asset { lists { LiabilityCase liabilityCase} } DataObject VehicleEquipment { lists { Address address } } DataObject LiabilityCase { } DataObject AssetVehicle { lists { VehicleEquipment serial VehicleEquipment extra } } DataObject Tires { } DataObject RentalObject { lists { Asset vehicle Asset mob Asset prop Asset estate Asset asset } } DataEnum AssetCondition { NEW , USED , DEMO } DataEnum VehicleColorType { STANDARD , PEARL_EFFECT , METALLIC , SPECIAL } DataEnum TiresType { SUMMER , WINTER } DataEnum AssetClass { VEHICLE , MOB , PROPERTY , ESTATE }

3. BusinessPartner.mydsl

subPackage businesspartner DataEnum BusinessPartnerType { INDIVIDUAL , ORGANISATION } DataObject BusinessPartner { lists { Address address  Contact contact BpTransactions transaction BpAdvisorRelation advisor } } DataObject BpRiskParameters { } DataObject BpTransactions { } DataObject BalanceDetails { } DataObject BpAdvisorRelation { } DataEnum AdvisorType { ADVISOR , ADVISOR_BO , ADMINISTRATOR }

4. счет-фактура.mydsl

subPackage invoice DataObject Invoice{ lists { Receipt receipt } } DataObject Receipt { lists { InvoiceItem item } } DataObject ReceiptFleet { } DataObject InvoiceItem{ } DataEnum InvoiceCategory { OBJECT, SERVICE ,INVOICES } DataEnum InvoiceType { INCOMING, OUTGOING } DataEnum InvoiceStatus { OPEN, PARTIALLY_COMPLETE, COMPLETE } DataEnum ItemUnit { UNIT, PALETTE, DOZEN, LITRE } DataEnum ItemIdType { LICENSE_PLATE, CARD_NUMBER, OBJECT_ID }

5. ценные бумаги.mydsl

subPackage securities DataObject Securities {} DataEnum SecuritiesType {PARTNER_APPLICANT, GUARANTOR,SOMETHING_ELSE}

6. типы.mydsl

subPackage types DataType String java.lang.String DataType Integer java.lang.Integer DataType Double java.lang.Double DataType Date java.util.Date DataType Boolean java.lang.Boolean DataType Void java.lang.Void

Сгенерированные интерфейсы расширяют data.model.core.IDataObject. Этот интерфейс не имеет методов. После действия clean в Eclipse я вижу, что метод getPhoneNumberList генерируется как

public abstract /* List<data.model.address.PhoneNumber> */Object getPhoneNumberList();

а также другие методы, но с другими все в порядке.
Я протестировал его с Xtext 2.5 и 2.3 с тем же результатом.

Я получаю сообщение об ошибке в консоли:

2    [Worker-4] ERROR org.eclipse.xtext.common.types.access.jdt.JdtTypeProvider  - [Working copy] PhoneNumber.java [in data.model.address [in src-gen [in test]]] does not exist
Java Model Exception: Java Model Status [[Working copy] PhoneNumber.java [in data.model.address [in src-gen [in test]]] does not exist]

но я не знаю, как это исправить.


person borism    schedule 07.03.2014    source источник


Ответы (1)


Похоже, вы пытаетесь разрешить перекрестные ссылки на этапе индексации. Пожалуйста, будьте осторожны, чтобы определить методы ваших предполагаемых типов и интерфейсов в блоке инициализатора ваших вызовов #toClass. Это должно работать. Ваш код должен выглядеть так:

myObject.toClass(myObject.name) [ clazz |
  myObject.operations.forEach [ op |
    clazz.members += op.toMethod( getMethodName(op), getMethodType(op)) [
      ..
    ]
  ]
]

Если вы хотите ссылаться на другие типы из блока инициализации, вам придется разделить инициализатор на две части. Любой код, который опирается на другие типы, которые будут выведены, должен перейти в раздел инициализации позже, например.

acceptor.accept( myObject.toClass(..) [ .. ]).initializeLater [
  // more code here
]

В вашем конкретном примере вывод работает для меня, если я использую этот подход:

def dispatch void infer(DataObject dataObject, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
    var packageName = ""
    if (dataObject.eContainer != null && dataObject.eContainer instanceof SubPackageDeclaration) {
        var implDec = (dataObject.eContainer as SubPackageDeclaration)
        packageName = "data.model." + implDec.subPackageName
    }
    val qualifiedName = packageName + "." + dataObject.name.toFirstUpper
    acceptor.accept(
        dataObject.toInterface(qualifiedName) [
            superTypes += dataObject.newTypeRef("data.model.core.IDataObject")
        ]).initializeLater[
            for (list : dataObject.lists) {
                val JvmTypeReference ^void = dataObject.newTypeRef(Void.TYPE)
                if (!isPreIndexingPhase) {
                    val JvmTypeReference listTypeRef = convertToJvmType(list.type)
                    if (listTypeRef != null) {
                        val removeMethod = list.toMethod("remove" + list.name.toFirstUpper, ^void) [
                            parameters += list.toParameter(list.name, listTypeRef)
                        ]
                        members += removeMethod
                        removeMethod.setAbstract(true);
                        val returnType = dataObject.newTypeRef("java.util.List", listTypeRef)
                        val allMethod = list.toMethod("get" + list.name.toFirstUpper + "List", returnType)[]
                        allMethod.setAbstract(true);
                        members += allMethod
                    }
                }
            }
            val denyLoading = dataObject.toMethod("denyLoading", dataObject.newTypeRef(Void.TYPE))[]
            denyLoading.setAbstract(true);
            members += denyLoading
        ]
}

Все это необходимо, потому что типы должны быть полностью собраны, прежде чем на них можно будет ссылаться.

person Sebastian Zarnekow    schedule 07.03.2014
comment
У меня не может быть никаких классов в этом проекте, мне нужны только интерфейсы. Но даже когда я пробую это с myObject.toInterface, у меня все еще есть та же проблема. Интересно, что когда я делаю это в гораздо более простом проекте, как в моем примере, все работает нормально. Затем я вижу в отладке тип возвращаемого значения: JvmParameterizedTypeReference: java.util.List‹JvmParameterizedTypeReference: (тип uri: platform:/resource/test/src/A.mydsl#xtextLink_::0.0.0.3.0.0::1::/ 0)› - person borism; 07.03.2014
comment
Привет, если у вас есть время, я обновил свой вопрос и добавил целый пример. - person borism; 13.03.2014