2015-01-14 4 views
1

В настоящее время я выполняю перекрестные ссылки для моего Xtext dsl. Файл dsl может содержать более одного XImportSection, а в некоторых особых случаях XImportSection не обязательно содержит все операторы импорта. Это означает, что мне нужно настроить «XImportSectionNamespaceScopeProvider», чтобы найти/построить правильный XimportSection. Во время реализации я выяснил неожиданное поведение редактора и/или некоторую проверку.Ошибка «Циклическое связывание обнаружено» при вызове ссылочного объекта в ScopeProvider

Я использовал следующий 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; 

Чтобы обеспечить более поддержка инструмента, ModifiesUnit ссылается на класс, который был изменен. Эта реализация, специфичная для Xtext, позволяет гиперссылку на класс.

В настоящее время я работаю над настраиваемым XImportSectionScopeProvider, который предоставляет все области пространства имен для ModifiesUnit. По умолчанию реализация содержит метод protected List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) предполагает, что в исходном файле есть только один классный элемент. Но для моего языка может быть больше одного. По этой причине я должен настроить его.

Моя идея в настоящее время является следующая реализация (с использованием языка программирования Xtend):

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

Перед тем, как эта работа запущен при, ссылка работала отлично и ничего неожиданного СЛУЧИЛОСЬ. Теперь моя цель - создать настраиваемый XImportSection для ModifiesUnit, который используется Xbase для разрешения ссылок на типы JVM. Для этого мне нужна копия XImportSection ссылки ClassOrInterface. Чтобы получить доступ к XImportSection, я сначала вызываю ModifiesUnit.getUnit(). Непосредственно после выполнения этого вызова редактор показывает неожиданное поведение. Минимальная реализация которых приводит к ошибке выглядит следующим образом:

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

Здесь, я не знаю, что происходит внутри страны! Но он вычисляет ошибку. Редактор показывает следующую ошибку на квалифицированном имени в (1): «Обнаружена циклическая ссылка: ModizesUnit.unit-> ModifiesUnit.unit».

Мои вопросы: что это значит? Почему Xtext показывает эту ошибку? Почему он появляется, если я обращаюсь к ссылочному объекту?

Я также выяснил странную вещь: в моем первом подходе мой код бросил NullPointerException. Хорошо, я попытался выяснить, почему, напечатав объект ci. Результат:

[email protected] (eProxyURI: platform:/resource/Test/src/My.dj#xtextLink_::0.0.0.1.1::0::/2) 
[email protected] (name: MyClass) 

Хорошо, это, кажется, что этот метод выполняется в два раза и XText разрешает прокси между первым и вторым исполнением. Это нормально для меня, пока полученный объект является правильным один раз. Я обрабатываю его с помощью инструкции if-instanceof.

Но почему у меня есть две ссылки? Опирается ли это на ParserRule ClassOrInterface (2a), который является абстрактным суперположением ClassDeclaration (2b)? Но почему Xtext не может решить ссылку для ClassOrInterface?

+0

Я задал этот вопрос на форуме сообщества Eclipse. См. Https://www.eclipse.org/forums/index.php/t/943873/ – Joko

ответ

1

ОК, теперь я нашел решение для своей проблемы.Во время экспериментов с моей реализацией я увидел, что в представлении «Проблемы» содержатся нерешенные ссылки. Это стало поводом для переосмысления моей реализации. Сначала я решил построить возвращенный список List<ImportNormalizer непосредственно, вместо того, чтобы построить XImportSection, который затем будет преобразован в этот список. Во время реализации этого я заметил, что я создал область только для ModifiesUnit элементов, а не элементов, которые нуждаются в области в пределах ModifiesUnit. Это является причиной ошибки циклического связывания. Теперь я создаю список только в том случае, если это необходимо. В результате возникает ошибка циклического связывания, и все ссылки на типы JVM корректно устраняются без ошибок в представлении проблем.

Моя реализация теперь выглядит следующим образом:

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-отца, который непосредственно содержит импорт (2).

С правильным элементом отца список имен пространства может быть рассчитан (3) для JCU (3a) и MU (3b).

 Смежные вопросы

  • Нет связанных вопросов^_^