MRI-2340 [uiDesigner]: cleanup ui-designer: always generate final bound ButtonGroup fields and constructor in form source code

When a bound ButtonGroup has member IDs that don't resolve to components
in the form (e.g., dynamically added radio buttons), the constructor
assignment was skipped, leaving the field uninitialized.

also bound ButtonGroup fields were marked as final (as other fields)

GitOrigin-RevId: 1db6c272d8eb0f6d6107329180148883ef37b01d
This commit is contained in:
Leonid Shalupov
2026-02-11 18:16:57 +00:00
committed by intellij-monorepo-bot
parent ec338add5b
commit 46ea14eacb
16 changed files with 299 additions and 3 deletions
@@ -257,7 +257,7 @@ public final class Form2SourceCompiler implements SourceInstrumentingCompiler{
private static final class MyInstrumentationItem implements ProcessingItem {
// increment it every time you want files to be regenerated
// i.e. on generation logic change
private static final long TIMESTAMP_BASE = 2;
private static final long TIMESTAMP_BASE = 3;
private final @NotNull VirtualFile mySourceFile;
private final VirtualFile myFormFile;
@@ -319,7 +319,7 @@ public final class FormSourceCodeGenerator {
PsiElement anchor;
if (myGenerateFinalFields) {
anchor = generateSourceFinalFields(newClass, module, methodText, classToBind, id2component);
anchor = generateSourceFinalFields(newClass, module, methodText, classToBind, id2component, rootContainer);
}
else {
anchor = generateSourcesNoFinalFields(rootContainer, newClass, module, methodText);
@@ -459,7 +459,7 @@ public final class FormSourceCodeGenerator {
return method;
}
private static PsiElement generateSourceFinalFields(PsiClass newClass, Module module, String methodText, PsiClass classToBind, HashMap<String, LwComponent> id2component)
private static PsiElement generateSourceFinalFields(PsiClass newClass, Module module, String methodText, PsiClass classToBind, HashMap<String, LwComponent> id2component, LwRootContainer rootContainer)
throws CodeGenerationException {
final PsiManager psiManager = PsiManager.getInstance(module.getProject());
@@ -485,6 +485,20 @@ public final class FormSourceCodeGenerator {
}
}
for (IButtonGroup group : rootContainer.getButtonGroups()) {
if (group.isBound()) {
PsiField boundField = newClass.findFieldByName(group.getName(), false);
if (boundField == null) {
throw new CodeGenerationException(null,
"Bound field '" + group.getName() + "' not found in class " + classToBind.getQualifiedName());
}
boundFields.add(boundField);
if (!boundField.hasModifierProperty(PsiModifier.FINAL)) {
Objects.requireNonNull(boundField.getModifierList()).setModifierProperty(PsiModifier.FINAL, true);
}
}
}
boundFields.sort(Comparator.comparing(PsiField::getTextOffset));
PsiField lastBoundField = boundFields.isEmpty() ? null : boundFields.getLast();
@@ -1474,6 +1488,10 @@ public final class FormSourceCodeGenerator {
endMethod();
}
}
if (!haveGroupConstructor && group.isBound()) {
append(group.getName());
append("= new javax.swing.ButtonGroup();");
}
}
}
@@ -0,0 +1,11 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private JRadioButton myRadio1;
private JRadioButton myRadio2;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
}
@@ -0,0 +1,44 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private JRadioButton myRadio1;
private JRadioButton myRadio2;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* @noinspection ALL
*/
private void $$$setupUI$$$() {
myPanel = new JPanel();
myPanel.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 1, new java.awt.Insets(0, 0, 0, 0), -1, -1));
myRadio1 = new javax.swing.JRadioButton();
myPanel.add(myRadio1, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, com.intellij.uiDesigner.core.GridConstraints.FILL_NONE, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
myRadio2 = new javax.swing.JRadioButton();
myPanel.add(myRadio2, new com.intellij.uiDesigner.core.GridConstraints(1, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, com.intellij.uiDesigner.core.GridConstraints.FILL_NONE, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
myButtonGroup = new javax.swing.ButtonGroup();
myButtonGroup.add(myRadio1);
myButtonGroup.add(myRadio2);
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return myPanel;
}
}
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="BindingTest">
<grid id="91062" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="131" y="90" width="33" height="24"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1001" class="javax.swing.JRadioButton" binding="myRadio1">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="1002" class="javax.swing.JRadioButton" binding="myRadio2">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<buttonGroups>
<group name="myButtonGroup" bound="true">
<member id="1001"/>
<member id="1002"/>
</group>
</buttonGroups>
</form>
@@ -0,0 +1,9 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
}
@@ -0,0 +1,36 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* @noinspection ALL
*/
private void $$$setupUI$$$() {
myPanel = new JPanel();
myPanel.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 1, new java.awt.Insets(0, 0, 0, 0), -1, -1));
myButtonGroup = new javax.swing.ButtonGroup();
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return myPanel;
}
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="BindingTest">
<grid id="91062" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="131" y="90" width="33" height="24"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<buttonGroups>
<group name="myButtonGroup" bound="true">
<member id="nonexistent1"/>
<member id="nonexistent2"/>
</group>
</buttonGroups>
</form>
@@ -0,0 +1,11 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private JRadioButton myRadio1;
private JRadioButton myRadio2;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
}
@@ -0,0 +1,32 @@
import javax.swing.*;
public class BindingTest {
private final JPanel myPanel;
private final JRadioButton myRadio1;
private final JRadioButton myRadio2;
private final ButtonGroup myButtonGroup;
public BindingTest() {
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
myPanel = new JPanel();
myPanel.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 1, new java.awt.Insets(0, 0, 0, 0), -1, -1));
myRadio1 = new javax.swing.JRadioButton();
myPanel.add(myRadio1, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, com.intellij.uiDesigner.core.GridConstraints.FILL_NONE, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
myRadio2 = new javax.swing.JRadioButton();
myPanel.add(myRadio2, new com.intellij.uiDesigner.core.GridConstraints(1, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, com.intellij.uiDesigner.core.GridConstraints.FILL_NONE, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
myButtonGroup = new javax.swing.ButtonGroup();
myButtonGroup.add(myRadio1);
myButtonGroup.add(myRadio2);
}
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return myPanel;
}
}
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="BindingTest">
<grid id="91062" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="131" y="90" width="33" height="24"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1001" class="javax.swing.JRadioButton" binding="myRadio1">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="1002" class="javax.swing.JRadioButton" binding="myRadio2">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<buttonGroups>
<group name="myButtonGroup" bound="true">
<member id="1001"/>
<member id="1002"/>
</group>
</buttonGroups>
</form>
@@ -0,0 +1,9 @@
import javax.swing.*;
public class BindingTest {
private JPanel myPanel;
private ButtonGroup myButtonGroup;
public BindingTest() {
}
}
@@ -0,0 +1,24 @@
import javax.swing.*;
public class BindingTest {
private final JPanel myPanel;
private final ButtonGroup myButtonGroup;
public BindingTest() {
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
myPanel = new JPanel();
myPanel.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 1, new java.awt.Insets(0, 0, 0, 0), -1, -1));
myButtonGroup = new javax.swing.ButtonGroup();
}
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return myPanel;
}
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="BindingTest">
<grid id="91062" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="131" y="90" width="33" height="24"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<buttonGroups>
<group name="myButtonGroup" bound="true">
<member id="nonexistent1"/>
<member id="nonexistent2"/>
</group>
</buttonGroups>
</form>
@@ -68,6 +68,8 @@ public class FormSourceCodeGeneratorFinalFieldsTest extends JavaPsiTestCase {
public void testNoI18nTextProperty() { doTest(); }
public void testNoI18nBorderTitle() { doTest(); }
public void testNoI18nMixed() { doTest(); }
public void testButtonGroup() { doTest(); }
public void testButtonGroupNoMembers() { doTest(); }
public void testTitledBorderInternal() {
PlatformTestUtil.withSystemProperty(ApplicationManagerEx.IS_INTERNAL_PROPERTY, "true", () -> doTest());
@@ -56,6 +56,8 @@ public class FormSourceCodeGeneratorTest extends JavaPsiTestCase {
public void testNoI18nTextProperty() { doTest(); }
public void testNoI18nBorderTitle() { doTest(); }
public void testNoI18nMixed() { doTest(); }
public void testButtonGroup() { doTest(); }
public void testButtonGroupNoMembers() { doTest(); }
public void testTitledBorderInternal() {
PlatformTestUtil.withSystemProperty(ApplicationManagerEx.IS_INTERNAL_PROPERTY, "true", () -> doTest());