1

現在、Xtext DSL の相互参照を実装しています。dsl ファイルには複数の XImportSection を含めることができ、特殊なケースでは XImportSection にすべての import ステートメントが含まれているとは限りません。これは、正しい XimportSection を見つけてビルドするには、「XImportSectionNamespaceScopeProvider」をカスタマイズする必要があることを意味します。実装中に、エディターや検証の予期しない動作を見つけました。

実装をテストするために切り取った次の DSL コードを使用しました。

delta MyDelta {
    adds {
        package my.pkg;
        import java.util.List;
        public class MyClass 
                implements List
                                {
        } 
    }
    modifies my.pkg.MyClass { // (1)

        adds import java.util.ArrayList;
        adds superclass ArrayList<String>;
    }
}

DSL ソース コードは、次の文法規則によって記述されます (完全ではありません!)。

AddsUnit:
    {AddsUnit} 'adds' '{' unit=JavaCompilationUnit? '}';

ModifiesUnit:
    'modifies' unit=[ClassOrInterface|QualifiedName] '{'
    modifiesPackage=ModifiesPackage?
    modifiesImports+=ModifiesImport*
    modifiesSuperclass=ModifiesInheritance?
    '}';

JavaCompilationUnit:
    => (annotations+=Annotation*
    'package' name=QualifiedName EOL)?
    importSection=XImportSection?
    typeDeclarations+=ClassOrInterfaceDeclaration;

ClassOrInterfaceDeclaration:
    annotations+=Annotation* modifiers+=Modifier* classOrInterface=ClassOrInterface;

ClassOrInterface: // (2a)
    ClassDeclaration | InterfaceDeclaration | EnumDeclaration | AnnotationTypeDeclaration;

ClassDeclaration: // (2b)
    'class' name=QualifiedName typeParameters=TypeParameters?
    ('extends' superClass=JvmTypeReference)?
    ('implements' interfaces=Typelist)?
    body=ClassBody;

より優れたツール サポートを提供するために、aModifiesUnitは変更されたクラスを参照します。この Xtext 固有の実装により、クラスへのハイパーリンクが可能になります。

現在、のすべての名前空間スコープを提供するカスタマイズされた XImportSectionScopeProvider に取り組んでいModifiesUnitます デフォルトの実装はメソッドを含みprotected List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase)、ソース ファイルにクラスのような要素が 1 つしかないことを前提としています。しかし、私の言語では、複数存在する可能性があります。このため、カスタマイズする必要があります。

私の考えは、次の実装です (Xtend プログラミング言語を使用):

override List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
    switch (context) {
        ModifiesUnit: context.buildImportSection
        default: // ... anything else
    }
}

この作業を開始する前に、参照は正常に機能し、予期しないことは何も起こりませんでした。ModifiesUnit私の目標は、JVM タイプへの参照を解決するために Xbase によって使用される、カスタマイズされた XImportSection を構築することです。そのためには、参照先の XImportSection のコピーが必要ですClassOrInterface。XImportSection にアクセスするには、まず を呼び出しますModifiesUnit.getUnit()。この呼び出しが実行された直後に、エディターは予期しない動作を示します。エラーにつながる最小限の実装は次のようになります。

def XImportSection buildImportSection(ModifiesUnit u) {
    val ci = u.unit // Since this expression is executed, the error occurs!
    // ...
}

ここで、私は内部で何が起こっているのかわかりません!しかし、それは誤差を計算します。エディターは、(1) の修飾名に次のエラーを表示します: 「Cyclic linking detected : ModifiesUnit.unit->ModifiesUnit.unit」。

私の質問は次のとおりです。それはどういう意味ですか? Xtext でこのエラーが表示されるのはなぜですか? 参照されたオブジェクトにアクセスすると表示されるのはなぜですか?

私はそこで奇妙なことも考え出しました: 私の最初のアプローチでは、私のコードはNullPointerException. わかりました、オブジェクトを印刷して理由を理解しようとしましたci。結果は次のとおりです。

org.deltaj.scoping.deltaJ.impl.ClassOrInterfaceImpl@4642f064 (eProxyURI: platform:/resource/Test/src/My.dj#xtextLink_::0.0.0.1.1::0::/2)
org.deltaj.scoping.deltaJ.impl.ClassDeclarationImpl@1c70366 (name: MyClass)

このメソッドは 2 回実行され、Xtext は 1 回目と 2 回目の実行の間でプロキシを解決しているようです。一度受け取ったオブジェクトが正しいものであれば、私にとっては問題ありません。if-instanceof ステートメントで処理します。

しかし、なぜそこに 2 つの参照があるのでしょうか。ClassDeclaration (2b) の抽象的なスーパールールのみである ParserRule ClassOrInterface (2a) に依存していますか? しかし、Xtext が ClassOrInterface の参照を解決できないのはなぜですか?

4

1 に答える 1

1

OK、今私は私の問題の解決策を見つけました。実装を試しているときに、「問題」ビューにまだ未解決の参照が含まれていることがわかりました。これが、私の実装が何をしたかを再考する理由でした。最初は、このリストに変換される を作成する代わりに、返されたリストList<ImportNormalizerを直接作成することにしました。これを実装しているときに、 内のスコープを必要とする要素ではなく、要素XImportSectionのみのスコープを構築していることに気付きました。これがサイクリック リンク エラーの原因です。現在、必要な場合にのみリストを作成しています。その結果、循環リンク エラーが発生しなくなり、JVM タイプへのすべての参照が問題ビューでエラーなしで正しく解決されます。ModifiesUnitModifiesUnit

私の実装は次のようになります。

class DeltaJXImportSectionNamespaceScopeProvider extends XImportSectionNamespaceScopeProvider {

    override List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) {

        // A scope will only be provided for elements which really need a scope. A scope is only necessary for elements
        // which are siblings of a JavaCompilationUnit or a ModifiesUnit.
        if (context.checkElement) { // (1)
            return Collections.emptyList
        }

        // Finding the container which contains the import section
        val container = context.jvmUnit // (2)

        // For a non null container create the import normalizer list depending of returned element. If the container is
        // null, no scope is needed.
        return if (container != null) { // (3)
            switch (container) {
                JavaCompilationUnit: container.provideJcuImportNormalizerList(ignoreCase)
                ModifiesUnit: container.provideMcuImportNormalizerList(ignoreCase)
            }
        } else {
            Collections.emptyList
        }

    }

    // Iterates upwards through the AST until a ModifiesUnit or a JavaCompilationUnit is found. (2)
    def EObject jvmUnit(EObject o) {
        switch (o) {
            ModifiesUnit: o
            JavaCompilationUnit: o
            default: o.eContainer.jvmUnit
        }
    }

    // Creates the list with all imports of a JCU (3a)
    def List<ImportNormalizer> provideJcuImportNormalizerList(JavaCompilationUnit jcu, boolean ignoreCase) {
        val is = jcu.importSection
        return if (is != null) {
            is.getImportedNamespaceResolvers(ignoreCase)
        } else {
            Collections.emptyList
        }
    }

    // Creates the list of all imports of a ModifiesUnit. This implementation is similar to 
    // getImportedNamespaceResolvers(XImportSection, boolean) // (3b)
    def List<ImportNormalizer> provideMcuImportNormalizerList(ModifiesUnit mu, boolean ignoreCase) {
        val List<ImportNormalizer> result = Lists.newArrayList
        result.addAll((mu.unit.jvmUnit as JavaCompilationUnit).provideJcuImportNormalizerList(ignoreCase))
        for (imp : mu.modifiesImports) {
            if (imp instanceof AddsImport) {
                val decl = imp.importDeclaration
                if (!decl.static) {
                    result.add(decl.transform(ignoreCase))
                }
            }
        }
        result
    }

    // Creates an ImportNormalizer for a given XImportSection
    def ImportNormalizer transform(XImportDeclaration decl, boolean ignoreCase) {
        var value = decl.importedNamespace
        if (value == null) {
            value = decl.importedTypeName
        }
        return value.createImportedNamespaceResolver(ignoreCase)
    }

    // Determines whether an element needs to be processed. (1)
    def checkElement(EObject o) {
        return o instanceof DeltaJUnit || o instanceof Delta || o instanceof AddsUnit || o instanceof ModifiesUnit ||
            o instanceof RemovesUnit
    }
}

ご覧のとおり、正しいスコープのために名前空間を必要としない要素は無視されます (1)。

正しいスコープのために名前空間を必要とする可能性のある各要素について、インポートを直接含む n-father 要素が決定されます (2)。

正しい親要素を使用して、JCU (3a) および MU (3b) の名前空間リストを計算できます (3)。

于 2015-01-19T14:34:39.253 に答える