[WORKAROUND FOUND] How to modify java class extends list with PSI

Answered

I'm attempting to write a custom plugin for IntelliJ IDEA that simply replaces a class's existing "extends" class with another. The full plugin will be more complicated than that, but this is where my problem resides.

Right now, my plugin is a simple button that triggers actionPerformed(). I then set up a WriteCommandAction and attempt to process my PsiFile. I loop through the child psiElements and get to the class element I'm trying to change. My problem seems to exist when I'm creating the new elements to put in place.

I'm looking for the proper way to do this.

For Example, here's the class I'm trying to change:

package com.howto.intellij.refactor;

public interface SubtypeWithParameters extends TypeWithParameters<String, Integer> {

}

I would like to replace TypeWithParameters with another class in the project AnotherTypeWithParameters.

This is what I've tried:


if (psiClass.getExtendsList() != null) {
PsiClass anotherTypeClass = javaPsiFacade.findClass("com.howto.intellij.refactor.AnotherTypeWithParameters", GlobalSearchScope.projectScope(project));
PsiClass stringClass = javaPsiFacade.findClass("java.lang.String", GlobalSearchScope.everythingScope(project));
PsiClass integerClass = javaPsiFacade.findClass("java.lang.Integer", GlobalSearchScope.everythingScope(project));

PsiClassType stringClassType = psiElementFactory.createType(stringClass);
PsiClassType integerClassType = psiElementFactory.createType(integerClass);
PsiClassType fullClassType = psiElementFactory.createType(anotherTypeClass, stringClassType, integerClassType);

PsiReferenceList psiReference1 = psiElementFactory.createReferenceList(new PsiJavaCodeReferenceElement[] {
psiElementFactory.createReferenceElementByType(fullClassType)
});

psiClass.getExtendsList().replace(psiReference1);
}

The end result however, looks like this:

package com.howto.intellij.refactor;

public interface SubtypeWithParameters throws AnotherTypeWithParameters {

}

It produces invalid syntax and is missing the type parameters. I think I'm close, but I don't know what I'm doing wrong.

What am I missing?

 

 

 

 

2 comments
Comment actions Permalink

I also tried to create a new class from text and copy over the new extends list (via the replace method), but the new extends list is empty. It's like the new class was not parsed. I was sure this would work as this is what is recommended in the intellij documentation: https://jetbrains.org/intellij/sdk/docs/basics/architectural_overview/modifying_psi.html

PsiClass newTempClass = psiJavaParserFacade.createClassFromText("public interface MyTempClass extends com.howto.intellij.refactor.AnotherTypeWithParameters<java.lang.String, java.lang.Integer> {}", null);
psiClass.getExtendsList().replace(newTempClass.getExtendsList());
1
Comment actions Permalink

Well...I found a sort of work around.

Using the above approach of creating a new element and replacing the old, I decided to try one level higher. Instead of using createClassFromText() which wasn't rendering the extendsList, I tried createFileFromText() to create a whole file with a new class in it. I wrote a method to pull the first class from the new file:

private PsiClass getFirstClassFromFile(PsiFile psiFile) {
for (PsiElement psiElement: psiFile.getChildren()) {
if (psiElement instanceof PsiClass) {
return (PsiClass) psiElement;
}
}

return null;
}

Then this was how I was able to work around replacing the extends list:

PsiFile javaFile = PsiFileFactory.getInstance(project).createFileFromText(Language.findLanguageByID("JAVA"), "public interface MyTempClass extends com.howto.intellij.refactor.AnotherTypeWithParameters<java.lang.String, java.lang.Integer> {}");
PsiClass newTempClass = getFirstClassFromFile(javaFile);
psiClass.getExtendsList().replace(newTempClass.getExtendsList());

 

 

0

Please sign in to leave a comment.