В настоящее время я реализую перекрестные ссылки для своего Xtext dsl. Файл dsl может содержать более одного XImportSection, и в некоторых особых случаях XImportSection не обязательно содержит все операторы импорта. Это означает, что мне нужно настроить «XImportSectionNamespaceScopeProvider», чтобы найти/создать правильный XimportSection. Во время реализации я обнаружил неожиданное поведение редактора и/или некоторую валидацию.
Я использовал следующий код 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): «Обнаружено циклическое связывание: ModifiedUnit.unit->ModizesUnit.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)
Хорошо, кажется, что этот метод выполняется два раза, и Xtext разрешает прокси между первым и вторым выполнением. Для меня это нормально, если полученный объект является правильным один раз. Я обрабатываю это с помощью оператора if-instanceof.
Но почему я получаю две ссылки там? Полагается ли он на ParserRule ClassOrInterface (2a), который является только абстрактным суперправилом ClassDeclaration (2b)? Но почему Xtext не может разрешить ссылку для ClassOrInterface?