From 46ea14eacb618fa8257dac4b8a9b226b3ae85373 Mon Sep 17 00:00:00 2001 From: Leonid Shalupov Date: Wed, 11 Feb 2026 15:05:25 +0100 Subject: [PATCH] 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 --- .../uiDesigner/make/Form2SourceCompiler.java | 2 +- .../make/FormSourceCodeGenerator.java | 22 +++++++++- .../buttonGroup/BindingTest.java | 11 +++++ .../buttonGroup/BindingTest.java.after | 44 +++++++++++++++++++ .../sourceCodeGenerator/buttonGroup/Test.form | 31 +++++++++++++ .../buttonGroupNoMembers/BindingTest.java | 9 ++++ .../BindingTest.java.after | 36 +++++++++++++++ .../buttonGroupNoMembers/Test.form | 18 ++++++++ .../buttonGroup/BindingTest.java | 11 +++++ .../buttonGroup/BindingTest.java.after | 32 ++++++++++++++ .../buttonGroup/Test.form | 31 +++++++++++++ .../buttonGroupNoMembers/BindingTest.java | 9 ++++ .../BindingTest.java.after | 24 ++++++++++ .../buttonGroupNoMembers/Test.form | 18 ++++++++ ...ormSourceCodeGeneratorFinalFieldsTest.java | 2 + .../make/FormSourceCodeGeneratorTest.java | 2 + 16 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java.after create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/Test.form create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java.after create mode 100644 plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/Test.form create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java.after create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/Test.form create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java.after create mode 100644 plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/Test.form diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2SourceCompiler.java b/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2SourceCompiler.java index 796343b20e2d..ea6ac563924b 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2SourceCompiler.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2SourceCompiler.java @@ -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; diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/make/FormSourceCodeGenerator.java b/plugins/ui-designer/src/com/intellij/uiDesigner/make/FormSourceCodeGenerator.java index 3cf39c074377..f97a4e3ec218 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/make/FormSourceCodeGenerator.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/make/FormSourceCodeGenerator.java @@ -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 id2component) + private static PsiElement generateSourceFinalFields(PsiClass newClass, Module module, String methodText, PsiClass classToBind, HashMap 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();"); + } } } diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java new file mode 100644 index 000000000000..988786c3a9ef --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java @@ -0,0 +1,11 @@ +import javax.swing.*; + +public class BindingTest { + private JPanel myPanel; + private JRadioButton myRadio1; + private JRadioButton myRadio2; + private ButtonGroup myButtonGroup; + + public BindingTest() { + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java.after b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java.after new file mode 100644 index 000000000000..420cba0abb86 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/BindingTest.java.after @@ -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; + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/Test.form b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/Test.form new file mode 100644 index 000000000000..eb3413ca90b3 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroup/Test.form @@ -0,0 +1,31 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java new file mode 100644 index 000000000000..65d8bc5f375f --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java @@ -0,0 +1,9 @@ +import javax.swing.*; + +public class BindingTest { + private JPanel myPanel; + private ButtonGroup myButtonGroup; + + public BindingTest() { + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java.after b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java.after new file mode 100644 index 000000000000..5c32c345d483 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/BindingTest.java.after @@ -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; + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/Test.form b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/Test.form new file mode 100644 index 000000000000..773163d01c0f --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGenerator/buttonGroupNoMembers/Test.form @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + + + +
diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java new file mode 100644 index 000000000000..988786c3a9ef --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java @@ -0,0 +1,11 @@ +import javax.swing.*; + +public class BindingTest { + private JPanel myPanel; + private JRadioButton myRadio1; + private JRadioButton myRadio2; + private ButtonGroup myButtonGroup; + + public BindingTest() { + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java.after b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java.after new file mode 100644 index 000000000000..cf9b19b118f3 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/BindingTest.java.after @@ -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; + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/Test.form b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/Test.form new file mode 100644 index 000000000000..eb3413ca90b3 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroup/Test.form @@ -0,0 +1,31 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java new file mode 100644 index 000000000000..65d8bc5f375f --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java @@ -0,0 +1,9 @@ +import javax.swing.*; + +public class BindingTest { + private JPanel myPanel; + private ButtonGroup myButtonGroup; + + public BindingTest() { + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java.after b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java.after new file mode 100644 index 000000000000..35dad477d431 --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/BindingTest.java.after @@ -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; + } +} diff --git a/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/Test.form b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/Test.form new file mode 100644 index 000000000000..773163d01c0f --- /dev/null +++ b/plugins/ui-designer/testData/sourceCodeGeneratorFinalFields/buttonGroupNoMembers/Test.form @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + + + +
diff --git a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorFinalFieldsTest.java b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorFinalFieldsTest.java index 0343d44e4ba9..270bcd61f43d 100644 --- a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorFinalFieldsTest.java +++ b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorFinalFieldsTest.java @@ -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()); diff --git a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorTest.java b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorTest.java index 95af911e3041..71eac965bab0 100644 --- a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorTest.java +++ b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/make/FormSourceCodeGeneratorTest.java @@ -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());