JavaTreeCopyHandler discards information about outer class

Hi,

I am trying to add a PsiClass as superclass to another PsiClass. The superclass is contained within another class.

The usecase is actually making inner builder classes extend each other, so something like [1]. However, whenever I add an extending reference referencing B.Builder, the PsiElement:replace method shortens the reference to just Builder, which is ambiguous in this context.

Debugging this thing, I ended up in com.intellij.psi.impl.source.tree.JavaTreeCopyHandler, which throws away the original PsiElement and just resolves the whole class to the class it thinks it may fit. However, in the context of extending inner classes the behaviour is somewhat wrong.

Is there any chance to get IntelliJ not discard the information about inner classes?

Thanks,

Matthias

 

[1]:

class A {
  class Builder extends B.Builder {
  }
}
class B {
  class Builder {}
}

 

will end up in 

class A {
  class Builder extends Builder {
  }
}
0
4 comments
Avatar
Permanently deleted user

By the way - some more information on what I am doing currently: 

I am creating a PsiJavaCodeReferenceElement via PsiElementFactory::createReferenceFromText. This is the extends $$ part.

Afterwards, I pick up the extends list via PsiClass::getExtendsList and replace the second child via the newly created PsiJavaCodeReferenceElement:

extendsList.getChildren()[2]::replace

What I really want to achieve is just replace the superclass with the reference I created - incl. the class name I specified in the PsiJavaCodeReferenceElement and not the shortened one created by ::resolve in JavaTreeCopyHandler.

Matthias

Edit:

#decodeInformation gets as input parameters:

  • an Element, i.e. MyAbstractClass.Builder<BEAN, BUILDER> (TreeElement)
    element.getPsi still returns MyAbstractClass.Builder
  • treeElementToPsi
    element is still MyAbstractClass.Builder
  • it resolves to just Builder (#resolve())
  • it is shortened to Builder<BEAN, BUILDER>
  • and copied to tree, which effectively changes nothing

Edit 2: Digging deeper:

  • advancedResolve(false) correctly returns MyAbstractClass.Builder
  • Calling getElement on the ResolveResult returns the class without prefix: Builder

I'll stop here. I have no idea where to continue ... I really need help on resolving this issue.

 

0

Hi Matthias,

why do you need to replace extendsList.getChildren and not add reference to the extends list? How do you generate A.Builder class or is it already there? 

Anna

0
Avatar
Permanently deleted user

Hi Anna,

Thanks for your answer.

Adding the reference to the extends list results in the same problem, as the TreeCopyHandler is still called and #resolve still throws away the prefix for the inner class.

Yes, you're right. The superclass already exists. However, the superclass has different parameters, so I throw them away and replace the superclass with a new one.

Thanks,

Matthias

Arrays.stream(extendsList.getReferenceElements()).forEach(PsiElement::delete);
(...)
extendsList.add(createExtendingReference(superClass, builderClass, beanClazz));

private static PsiJavaCodeReferenceElement createExtendingReference(PsiClass superClass, final PsiClass conreceteBuilderClass,
final PsiClass beanClazz) throws IncorrectOperationException {
Project project = superClass.getProject();
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();

String superclassText = getPrefixedInnerClassText(superClass, beanClazz);
return elementFactory.createReferenceFromText(String.format("%1$s<%2$s, %3$s>", superclassText, beanClazz.getName(), getPrefixedInnerClassText(conreceteBuilderClass, beanClazz)), conreceteBuilderClass);
}

 

0

Sorry, I don't understand the code snippet - too many PsiClasses, I am lost which one is which in the initial sample. Anyway, would you mind not to create reference from text but create it from the class, like com.intellij.psi.PsiElementFactory#createReferenceElementByType and create reference type by class and type arguments? This way you should not lost the "class information" as tree handler should remember the class where the reference resolved before?

Anna

0

Please sign in to leave a comment.