Error while executing EncapsulateFieldsProcessor
Hello. I want to build a plugin that finds fields and encapsulate them (hide field and generate getter,setter, replacing direct access to getter, setter).
I found that Intellij IDEA has a nice functionality called "Encapsulate Fields"
It hides variable field and replace field usages(direct access) to getter / setter.But because it works only on single file, I wanted to make it work on multiple files.
To accomplish this, I wrote codes below:
Action class:
ApplicationManager.getApplication().executeOnPooledThread {
val psiFiles = ApplicationManager.getApplication().runReadAction<List<PsiFile>> {
FilenameIndex.getAllFilesByExt(project, "java")
.filter { !it.isDirectory && it.path.startsWith(currentFile.path) }
.map { manager.findFile(it)!! }
}
val searchers = mutableSetOf<ClassSearcher>()
ProgressManager.getInstance().runInReadActionWithWriteActionPriority({
for (psiFile in psiFiles) {
progressWindow.text = "Searching class ${psiFile.name}"
val searcher = ClassSearcher(psiFile)
searcher.processSearch()
searchers.add(searcher)
}
}, progressWindow)
progressWindow.pushState()
ApplicationManager.getApplication().invokeLaterOnWriteThread({
for (searcher in searchers) {
try{
searcher.classes.forEach { cls ->
progressWindow.text = "Processing ${cls.name}"
val descriptor = ApplicationManager.getApplication().runReadAction<GSetterizableFieldDescriptor> { GSetterizableFieldDescriptor(cls) }
val processor = EncapsulateFieldsProcessor(project, descriptor)
processor.run()
}
}catch (e: ProcessCanceledException){
break
}
}
}, ModalityState.NON_MODAL)
}
These codes search java files under current location and classes. Also, creates simple implementation class of EncapsulateFieldsDescriptor,GSetterizableFieldsDescriptor, to create EncapsulateFieldProcessor instance.
code of GSetterizableFieldsDescriptor:
class GSetterizableFieldDescriptor(private val javaClass:PsiClass) : EncapsulateFieldsDescriptor {
private val fieldDescriptors:Array<FieldDescriptor> = javaClass.allFields.filter{ it.hasModifierProperty(PsiModifier.PUBLIC) }.map { SimpleFieldDescriptorImpl(it) }.toTypedArray()
override fun getSelectedFields(): Array<FieldDescriptor> = fieldDescriptors
override fun isToEncapsulateGet(): Boolean = true
override fun isToEncapsulateSet(): Boolean = true
override fun isToUseAccessorsWhenAccessible(): Boolean = true
override fun getFieldsVisibility(): String = PsiModifier.PRIVATE
override fun getAccessorsVisibility(): String = PsiModifier.PRIVATE
override fun getJavadocPolicy(): Int = DocCommentPolicy.COPY
override fun getTargetClass(): PsiClass = javaClass
}
and SimpleFieldDescriptorImpl to create FieldDescriptorImpl more efficiently:
class SimpleFieldDescriptorImpl(psiField: PsiField) : FieldDescriptorImpl(
psiField,
GenerateMembersUtil.suggestGetterName(psiField),
GenerateMembersUtil.suggestSetterName(psiField),
GenerateMembersUtil.generateGetterPrototype(psiField),
GenerateMembersUtil.generateSetterPrototype(psiField)
) {
}
But I got this error:
java.lang.IllegalArgumentException: Argument for @NotNull parameter 'newElement' of com/intellij/psi/impl/source/tree/CompositePsiElement.replace must not be null
at com.intellij.psi.impl.source.tree.CompositePsiElement.$$$reportNull$$$0(CompositePsiElement.java)
at com.intellij.psi.impl.source.tree.CompositePsiElement.replace(CompositePsiElement.java)
at com.intellij.refactoring.encapsulateFields.JavaEncapsulateFieldHelper.processUsage(JavaEncapsulateFieldHelper.java:214)
at com.intellij.refactoring.encapsulateFields.EncapsulateFieldsProcessor.processUsagesPerFile(EncapsulateFieldsProcessor.java:336)
at com.intellij.refactoring.encapsulateFields.EncapsulateFieldsProcessor.performRefactoring(EncapsulateFieldsProcessor.java:271)
at com.intellij.refactoring.BaseRefactoringProcessor.lambda$doRefactoring$10(BaseRefactoringProcessor.java:520)
at com.intellij.refactoring.BaseRefactoringProcessor.callPerformRefactoring(BaseRefactoringProcessor.java:573)
at com.intellij.refactoring.BaseRefactoringProcessor.lambda$doRefactoring$11(BaseRefactoringProcessor.java:519)
at com.intellij.openapi.application.impl.ApplicationImpl.lambda$runEdtProgressWriteAction$9(ApplicationImpl.java:979)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:189)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:608)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:683)
at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:639)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:607)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:176)
at com.intellij.openapi.progress.util.PotemkinProgress.runInSwingThread(PotemkinProgress.java:164)
at com.intellij.openapi.application.impl.ApplicationImpl.lambda$runEdtProgressWriteAction$10(ApplicationImpl.java:979)
at com.intellij.openapi.application.impl.ApplicationImpl.runWriteActionWithClass(ApplicationImpl.java:1003)
at com.intellij.openapi.application.impl.ApplicationImpl.runEdtProgressWriteAction(ApplicationImpl.java:977)
at com.intellij.openapi.application.impl.ApplicationImpl.runWriteActionWithNonCancellableProgressInDispatchThread(ApplicationImpl.java:958)
at com.intellij.refactoring.BaseRefactoringProcessor.doRefactoring(BaseRefactoringProcessor.java:518)
at com.intellij.refactoring.BaseRefactoringProcessor.lambda$execute$2(BaseRefactoringProcessor.java:332)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:219)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:174)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:164)
at com.intellij.refactoring.BaseRefactoringProcessor.execute(BaseRefactoringProcessor.java:330)
at com.intellij.refactoring.BaseRefactoringProcessor.doRun(BaseRefactoringProcessor.java:253)
at com.intellij.util.SlowOperations.allowSlowOperations(SlowOperations.java:147)
at com.intellij.refactoring.BaseRefactoringProcessor.lambda$run$19(BaseRefactoringProcessor.java:624)
at com.intellij.refactoring.BaseRefactoringProcessor.run(BaseRefactoringProcessor.java:638)
at io.github.singlerr.gsetterizer.actions.GSetterizeAction.actionPerformed$lambda-7$lambda-6(GSetterizeAction.kt:69)
at com.intellij.openapi.application.TransactionGuardImpl.runWithWritingAllowed(TransactionGuardImpl.java:215)
at com.intellij.openapi.application.TransactionGuardImpl.access$100(TransactionGuardImpl.java:22)
at com.intellij.openapi.application.TransactionGuardImpl$1.run(TransactionGuardImpl.java:197)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:873)
at com.intellij.openapi.application.impl.ApplicationImpl$3.run(ApplicationImpl.java:511)
at com.intellij.openapi.application.impl.FlushQueue.doRun(FlushQueue.java:69)
at com.intellij.openapi.application.impl.FlushQueue.runNextEvent(FlushQueue.java:112)
at com.intellij.openapi.application.impl.FlushQueue.flushNow(FlushQueue.java:42)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:746)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:898)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:746)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$6(IdeEventQueue.java:439)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:803)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$7(IdeEventQueue.java:438)
at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:106)
at com.intellij.ide.IdeEventQueue.performActivity(IdeEventQueue.java:604)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:436)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:873)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:484)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:207)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:92)
2022-09-04 21:36:39,434 [ 150332] SEVERE - #c.i.o.a.i.FlushQueue - IntelliJ IDEA 2022.1 Build #IC-221.5080.210
2022-09-04 21:36:39,434 [ 150332] SEVERE - #c.i.o.a.i.FlushQueue - JDK: 11.0.14.1; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o.
2022-09-04 21:36:39,434 [ 150332] SEVERE - #c.i.o.a.i.FlushQueue - OS: Windows 10
2022-09-04 21:36:39,435 [ 150333] SEVERE - #c.i.o.a.i.FlushQueue - Plugin to blame: GSetterizer version: 1.2-SNAPSHOT
2022-09-04 21:36:39,452 [ 150350] WARN - #c.i.d.PerformanceWatcherImpl - UI was frozen for 6469ms, details saved to D:\ProjectStorage\IdeaProjects\GSetterizer\build\idea-sandbox\system\log\threadDumps-freeze-20220904-213637-IC-221.5080.210-6sec
According to stacktrace, I found that JavaEncapsulateFieldHelper#checkMethodResolvable returns null. In that method,
methodCall.getMethodExpression().resolve()
returns null.
It occurrs only specific variable fields, not all of fields. How can i fix this?
Thank you.
Please sign in to leave a comment.
Hm... I don't know this code pretty well but from what I see, you are creating the setters private (getAccessorsVisibility(): String = PsiModifier.PRIVATE) but you are encapsulating public fields (javaClass.allFields.filter{ it.hasModifierProperty(PsiModifier.PUBLIC) }...). If a public field is accessed from another file, and you replace this access with a private setter, it may not resolve anymore. How about returning PsiModifier.PUBLIC from getAccessorsVisibility()? Note that it's just a guess, I did not try to actually debug this code.