diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form index fdaf15cc6910..edb06a0b70f0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form @@ -157,7 +157,7 @@ - + diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java index e4a93f342e7f..00651634c509 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java @@ -16,6 +16,7 @@ package com.intellij.compiler.options; import com.intellij.codeInsight.NullableNotNullDialog; +import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerConfigurationImpl; import com.intellij.compiler.CompilerWorkspaceConfiguration; @@ -83,6 +84,7 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura private JLabel myParallelCompilationLegendLabel; private JButton myConfigureAnnotations; private JLabel myWarningLabel; + private JPanel myAssertNotNullPanel; public CompilerUIConfigurable(@NotNull final Project project) { myProject = project; @@ -108,7 +110,10 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura s -> StringUtil.startsWithIgnoreCase(s, "-Xmx")) == null); } }); - myConfigureAnnotations.addActionListener(NullableNotNullDialog.createActionListener(myPanel)); + myConfigureAnnotations.addActionListener(e -> { + NullableNotNullDialog.showDialogWithInstrumentationOptions(myPanel); + myCbAssertNotNull.setSelected(!NullableNotNullManager.getInstance(myProject).getInstrumentedNotNulls().isEmpty()); + }); } private void tweakControls(@NotNull Project project) { @@ -140,7 +145,7 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura Map> controls = ContainerUtilRt.newHashMap(); controls.put(Setting.RESOURCE_PATTERNS, ContainerUtilRt.newArrayList(myResourcePatternsLabel, myResourcePatternsField, myPatternLegendLabel)); controls.put(Setting.CLEAR_OUTPUT_DIR_ON_REBUILD, Collections.singleton(myCbClearOutputDirectory)); - controls.put(Setting.ADD_NOT_NULL_ASSERTIONS, Collections.singleton(myCbAssertNotNull)); + controls.put(Setting.ADD_NOT_NULL_ASSERTIONS, Collections.singleton(myAssertNotNullPanel)); controls.put(Setting.AUTO_SHOW_FIRST_ERROR_IN_EDITOR, Collections.singleton(myCbAutoShowFirstError)); controls.put(Setting.DISPLAY_NOTIFICATION_POPUP, Collections.singleton(myCbDisplayNotificationPopup)); controls.put(Setting.AUTO_MAKE, ContainerUtilRt.newArrayList(myCbEnableAutomake, myEnableAutomakeLegendLabel)); diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index fbdbed46d538..44008727e688 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -6,9 +6,7 @@ import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.DefaultJDOMExternalizer; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; @@ -17,27 +15,97 @@ import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.Tag; +import com.intellij.util.xmlb.annotations.XCollection; import one.util.streamex.StreamEx; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer; import java.util.*; -@State(name = "NullableNotNullManager") -public class NullableNotNullManagerImpl extends NullableNotNullManager implements PersistentStateComponent { +import static com.intellij.codeInsight.AnnotationUtil.NOT_NULL; +import static com.intellij.codeInsight.AnnotationUtil.NULLABLE; +@State(name = "NullableNotNullManager") +public class NullableNotNullManagerImpl extends NullableNotNullManager implements PersistentStateComponent { public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname"; + private List myNullables = ContainerUtil.newArrayList(DEFAULT_NULLABLES); + private List myNotNulls = ContainerUtil.newArrayList(DEFAULT_NOT_NULLS); + + public static class StateBean { + @Tag("option") public Element myNullables = null; + @Tag("option") public Element myNotNulls = null; + @XCollection(style = XCollection.Style.v2) public List instrumentedNotNulls = ContainerUtil.newArrayList(NOT_NULL); + public String myDefaultNullable = NULLABLE; + public String myDefaultNotNull = NOT_NULL; + } + + private StateBean myState = new StateBean(); public NullableNotNullManagerImpl(Project project) { super(project); - myNotNulls.addAll(getPredefinedNotNulls()); } @Override - public List getPredefinedNotNulls() { - return JpsJavaCompilerNotNullableSerializer.DEFAULT_NOT_NULLS; + public void setNotNulls(@NotNull String... annotations) { + LinkedHashSet set = ContainerUtil.newLinkedHashSet(annotations); + Collections.addAll(set, DEFAULT_NOT_NULLS); + myNotNulls = new ArrayList<>(set); + } + + @Override + public void setNullables(@NotNull String... annotations) { + LinkedHashSet set = ContainerUtil.newLinkedHashSet(annotations); + Collections.addAll(set, DEFAULT_NULLABLES); + myNullables = new ArrayList<>(set); + } + + @Override + @NotNull + public String getDefaultNullable() { + return myState.myDefaultNullable; + } + + @Override + public void setDefaultNullable(@NotNull String defaultNullable) { + LOG.assertTrue(getNullables().contains(defaultNullable)); + myState.myDefaultNullable = defaultNullable; + } + + @Override + @NotNull + public String getDefaultNotNull() { + return myState.myDefaultNotNull; + } + + @Override + public void setDefaultNotNull(@NotNull String defaultNotNull) { + LOG.assertTrue(getNotNulls().contains(defaultNotNull)); + myState.myDefaultNotNull = defaultNotNull; + } + + @Override + @NotNull + public List getNullables() { + return Collections.unmodifiableList(myNullables); + } + + @Override + @NotNull + public List getNotNulls() { + return Collections.unmodifiableList(myNotNulls); + } + + @NotNull + @Override + public List getInstrumentedNotNulls() { + return Collections.unmodifiableList(myState.instrumentedNotNulls); + } + + @Override + public void setInstrumentedNotNulls(@NotNull List names) { + myState.instrumentedNotNulls = ContainerUtil.sorted(names); } @Override @@ -48,36 +116,35 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement @SuppressWarnings("deprecation") @Override - public Element getState() { - final Element component = new Element("component"); + public StateBean getState() { + StateBean state = myState; - if (hasDefaultValues()) { - return component; - } + state.myNullables = new Element("option").setAttribute("name", "myNullables").addContent(new Element("value")); + new JDOMExternalizableStringList(myNullables).writeExternal(state.myNullables.getChild("value")); - try { - DefaultJDOMExternalizer.writeExternal(this, component); - } - catch (WriteExternalException e) { - LOG.error(e); - } - return component; + state.myNotNulls = new Element("option").setAttribute("name", "myNotNulls").addContent(new Element("value")); + new JDOMExternalizableStringList(myNotNulls).writeExternal(state.myNotNulls.getChild("value")); + + return state; } - @SuppressWarnings("deprecation") @Override - public void loadState(@NotNull Element state) { - try { - DefaultJDOMExternalizer.readExternal(this, state); - if (myNullables.isEmpty()) { - Collections.addAll(myNullables, DEFAULT_NULLABLES); - } - if (myNotNulls.isEmpty()) { - myNotNulls.addAll(getPredefinedNotNulls()); - } + public void loadState(@NotNull StateBean state) { + myState = state; + + readJdomList(state.myNullables, myNullables, DEFAULT_NULLABLES); + readJdomList(state.myNotNulls, myNotNulls, DEFAULT_NOT_NULLS); + } + + private static void readJdomList(@Nullable Element src, @NotNull List to, @NotNull String[] defaults) { + to.clear(); + Element value = src != null ? src.getChild("value") : null; + if (value != null) { + //noinspection deprecation + JDOMExternalizableStringList.readList(to, value); } - catch (InvalidDataException e) { - LOG.error(e); + if (to.isEmpty()) { + Collections.addAll(to, defaults); } } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 44f713bd9589..6e53cd55045e 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -4,7 +4,6 @@ package com.intellij.codeInsight; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; import com.intellij.psi.util.TypeConversionUtil; @@ -12,7 +11,9 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import static com.intellij.codeInsight.AnnotationUtil.*; @@ -24,11 +25,6 @@ public abstract class NullableNotNullManager { protected static final Logger LOG = Logger.getInstance(NullableNotNullManager.class); protected final Project myProject; - public String myDefaultNullable = NULLABLE; - public String myDefaultNotNull = NOT_NULL; - @SuppressWarnings("deprecation") public final JDOMExternalizableStringList myNullables = new JDOMExternalizableStringList(); - @SuppressWarnings("deprecation") public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList(); - protected static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable"; protected static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; @@ -43,10 +39,20 @@ public abstract class NullableNotNullManager { "org.checkerframework.checker.nullness.compatqual.NullableDecl", "org.checkerframework.checker.nullness.compatqual.NullableType" }; + static final String[] DEFAULT_NOT_NULLS = { + NotNull.class.getName(), + "javax.annotation.Nonnull", + "javax.validation.constraints.NotNull", + "edu.umd.cs.findbugs.annotations.NonNull", + "android.support.annotation.NonNull", + "androidx.annotation.NonNull", + "org.checkerframework.checker.nullness.qual.NonNull", + "org.checkerframework.checker.nullness.compatqual.NonNullDecl", + "org.checkerframework.checker.nullness.compatqual.NonNullType" + }; - public NullableNotNullManager(Project project) { + protected NullableNotNullManager(Project project) { myProject = project; - Collections.addAll(myNullables, DEFAULT_NULLABLES); } public static NullableNotNullManager getInstance(Project project) { @@ -60,36 +66,12 @@ public abstract class NullableNotNullManager { return isNullable(owner, false) || isNotNull(owner, false); } - private static void addAllIfNotPresent(@NotNull Collection collection, @NotNull String... annotations) { - for (String annotation : annotations) { - LOG.assertTrue(annotation != null); - if (!collection.contains(annotation)) { - collection.add(annotation); - } - } - } + public abstract void setNotNulls(@NotNull String... annotations); - public void setNotNulls(@NotNull String... annotations) { - myNotNulls.clear(); - for (String annotation : getPredefinedNotNulls()) { - LOG.assertTrue(annotation != null); - if (!myNotNulls.contains(annotation)) { - myNotNulls.add(annotation); - } - } - addAllIfNotPresent(myNotNulls, annotations); - } - - public void setNullables(@NotNull String... annotations) { - myNullables.clear(); - addAllIfNotPresent(myNullables, DEFAULT_NULLABLES); - addAllIfNotPresent(myNullables, annotations); - } + public abstract void setNullables(@NotNull String... annotations); @NotNull - public String getDefaultNullable() { - return myDefaultNullable; - } + public abstract String getDefaultNullable(); @Nullable public String getNullable(@NotNull PsiModifierListOwner owner) { @@ -117,15 +99,10 @@ public abstract class NullableNotNullManager { return checkNullityDefault(anno, acceptAnyTarget, false) != null; } - public void setDefaultNullable(@NotNull String defaultNullable) { - LOG.assertTrue(getNullables().contains(defaultNullable)); - myDefaultNullable = defaultNullable; - } + public abstract void setDefaultNullable(@NotNull String defaultNullable); @NotNull - public String getDefaultNotNull() { - return myDefaultNotNull; - } + public abstract String getDefaultNotNull(); @Nullable public PsiAnnotation getNotNullAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { @@ -186,10 +163,7 @@ public abstract class NullableNotNullManager { return annotation == null ? null : annotation.getQualifiedName(); } - public void setDefaultNotNull(@NotNull String defaultNotNull) { - LOG.assertTrue(getNotNulls().contains(defaultNotNull)); - myDefaultNotNull = defaultNotNull; - } + public abstract void setDefaultNotNull(@NotNull String defaultNotNull); @Nullable private PsiAnnotation findNullityAnnotationWithDefault(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean nullable) { @@ -208,7 +182,7 @@ public abstract class NullableNotNullManager { if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null; // even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations - List annotations = nullable ? getPredefinedNotNulls() : Arrays.asList(DEFAULT_NULLABLES); + List annotations = Arrays.asList(nullable ? DEFAULT_NOT_NULLS : DEFAULT_NULLABLES); int flags = (checkBases ? CHECK_HIERARCHY : 0) | CHECK_EXTERNAL | CHECK_INFERRED | CHECK_TYPE; if (isAnnotated(owner, annotations, flags)) { return null; @@ -359,36 +333,10 @@ public abstract class NullableNotNullManager { protected abstract NullityDefault isJsr305Default(@NotNull PsiAnnotation annotation, @NotNull PsiAnnotation.TargetType[] placeTargetTypes); @NotNull - public List getNullables() { - return myNullables; - } + public abstract List getNullables(); @NotNull - public List getNotNulls() { - return myNotNulls; - } - - boolean hasDefaultValues() { - List predefinedNotNulls = getPredefinedNotNulls(); - if (DEFAULT_NULLABLES.length != getNullables().size() || predefinedNotNulls.size() != getNotNulls().size()) { - return false; - } - if (!myDefaultNotNull.equals(NOT_NULL) || !myDefaultNullable.equals(NULLABLE)) { - return false; - } - for (int i = 0; i < DEFAULT_NULLABLES.length; i++) { - if (!getNullables().get(i).equals(DEFAULT_NULLABLES[i])) { - return false; - } - } - for (int i = 0; i < predefinedNotNulls.size(); i++) { - if (!getNotNulls().get(i).equals(predefinedNotNulls.get(i))) { - return false; - } - } - - return true; - } + public abstract List getNotNulls(); public static boolean isNullable(@NotNull PsiModifierListOwner owner) { return getInstance(owner.getProject()).isNullable(owner, true); @@ -398,7 +346,10 @@ public abstract class NullableNotNullManager { return getInstance(owner.getProject()).isNotNull(owner, true); } - public abstract List getPredefinedNotNulls(); + @NotNull + public abstract List getInstrumentedNotNulls(); + + public abstract void setInstrumentedNotNulls(@NotNull List names); public static boolean isNullableAnnotation(@NotNull PsiAnnotation annotation) { return getInstance(annotation.getProject()).getNullablesWithNickNames().contains(annotation.getQualifiedName()); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.groovy index 63931b5d38e4..ef4f0b1f91f4 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.groovy @@ -18,6 +18,7 @@ package com.intellij.java.codeInsight import com.intellij.codeInsight.AnnotationUtil import com.intellij.codeInsight.NullableNotNullManager import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.util.ArrayUtil /** * @author peter @@ -95,7 +96,7 @@ class Foo { @Override protected void tearDown() throws Exception { - NullableNotNullManager.getInstance(project).notNulls = NullableNotNullManager.getInstance(project).predefinedNotNulls as String[] + NullableNotNullManager.getInstance(project).notNulls = ArrayUtil.EMPTY_STRING_ARRAY NullableNotNullManager.getInstance(project).defaultNotNull = AnnotationUtil.NOT_NULL super.tearDown() diff --git a/java/openapi/src/com/intellij/codeInsight/NullableNotNullDialog.java b/java/openapi/src/com/intellij/codeInsight/NullableNotNullDialog.java index 88b97ee6ead4..ebb88eb6fe19 100644 --- a/java/openapi/src/com/intellij/codeInsight/NullableNotNullDialog.java +++ b/java/openapi/src/com/intellij/codeInsight/NullableNotNullDialog.java @@ -28,29 +28,49 @@ import com.intellij.openapi.ui.Splitter; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.*; -import com.intellij.ui.components.JBList; -import com.intellij.util.ArrayUtil; +import com.intellij.ui.table.JBTable; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; +import sun.swing.table.DefaultTableCellHeaderRenderer; import javax.swing.*; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; +import java.util.List; public class NullableNotNullDialog extends DialogWrapper { private final Project myProject; - private AnnotationsPanel myNullablePanel; - private AnnotationsPanel myNotNullPanel; + private final AnnotationsPanel myNullablePanel; + private final AnnotationsPanel myNotNullPanel; + private final boolean myShowInstrumentationOptions; public NullableNotNullDialog(@NotNull Project project) { + this(project, false); + } + + private NullableNotNullDialog(@NotNull Project project, boolean showInstrumentationOptions) { super(project, true); myProject = project; + myShowInstrumentationOptions = showInstrumentationOptions; + + NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); + myNullablePanel = new AnnotationsPanel("Nullable", + manager.getDefaultNullable(), + manager.getNullables(), NullableNotNullManager.DEFAULT_NULLABLES, + Collections.emptySet(), false); + myNotNullPanel = new AnnotationsPanel("NotNull", + manager.getDefaultNotNull(), + manager.getNotNulls(), NullableNotNullManager.DEFAULT_NOT_NULLS, + ContainerUtil.newHashSet(manager.getInstrumentedNotNulls()), showInstrumentationOptions); + init(); setTitle("Nullable/NotNull Configuration"); } @@ -70,22 +90,26 @@ public class NullableNotNullDialog extends DialogWrapper { return new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(context)); - if (project == null) project = ProjectManager.getInstance().getDefaultProject(); - new NullableNotNullDialog(project).show(); + showDialog(context, false); } }; } + public static void showDialogWithInstrumentationOptions(@NotNull Component context) { + showDialog(context, true); + } + + private static void showDialog(Component context, boolean showInstrumentationOptions) { + Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(context)); + if (project == null) project = ProjectManager.getInstance().getDefaultProject(); + NullableNotNullDialog dialog = new NullableNotNullDialog(project, showInstrumentationOptions); + dialog.show(); + } + @Override protected JComponent createCenterPanel() { - final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); final Splitter splitter = new Splitter(true); - myNullablePanel = - new AnnotationsPanel("Nullable", manager.getDefaultNullable(), manager.getNullables(), NullableNotNullManager.DEFAULT_NULLABLES); splitter.setFirstComponent(myNullablePanel.getComponent()); - myNotNullPanel = - new AnnotationsPanel("NotNull", manager.getDefaultNotNull(), manager.getNotNulls(), ArrayUtil.toStringArray(manager.getPredefinedNotNulls())); splitter.setSecondComponent(myNotNullPanel.getComponent()); splitter.setHonorComponentsMinimumSize(true); splitter.setPreferredSize(JBUI.size(300, 400)); @@ -102,78 +126,137 @@ public class NullableNotNullDialog extends DialogWrapper { manager.setNullables(myNullablePanel.getAnnotations()); manager.setDefaultNullable(myNullablePanel.getDefaultAnnotation()); + if (myShowInstrumentationOptions) { + manager.setInstrumentedNotNulls(myNotNullPanel.getCheckedAnnotations()); + } + super.doOKAction(); } private class AnnotationsPanel { private String myDefaultAnnotation; private final Set myDefaultAnnotations; - private final JBList myList; + private final JBTable myTable; private final JPanel myComponent; + private final DefaultTableModel myTableModel; - private AnnotationsPanel(final String name, final String defaultAnnotation, - final Collection annotations, final String[] defaultAnnotations) { + private AnnotationsPanel(String name, String defaultAnnotation, List annotations, String[] defaultAnnotations, Set checkedAnnotations, boolean showInstrumentationOptions) { myDefaultAnnotation = defaultAnnotation; myDefaultAnnotations = new HashSet<>(Arrays.asList(defaultAnnotations)); - myList = new JBList<>(annotations); - myList.setCellRenderer(new ColoredListCellRenderer() { + myTableModel = new DefaultTableModel() { @Override - protected void customizeCellRenderer(@NotNull JList list, String value, int index, boolean selected, boolean hasFocus) { - append(value, SimpleTextAttributes.REGULAR_ATTRIBUTES); + public boolean isCellEditable(int row, int column) { + return column == 1; + } + }; + myTableModel.setColumnCount(showInstrumentationOptions ? 2 : 1); + for (String annotation : annotations) { + addRow(annotation, checkedAnnotations.contains(annotation)); + } + + DefaultTableColumnModel columnModel = new DefaultTableColumnModel(); + columnModel.addColumn(new TableColumn(0, 100, new ColoredTableCellRenderer() { + @Override + public void acquireState(JTable table, boolean isSelected, boolean hasFocus, int row, int column) { + super.acquireState(table, isSelected, false, row, column); + } + + @Override + protected void customizeCellRenderer(JTable table, + Object value, + boolean selected, + boolean hasFocus, + int row, + int column) { + append((String)value, SimpleTextAttributes.REGULAR_ATTRIBUTES); if (value.equals(myDefaultAnnotation)) { setIcon(AllIcons.Diff.CurrentLine); } else { setIcon(EmptyIcon.ICON_16); } - //if (myDefaultAnnotations.contains(value)) { - // append(" (built in)", SimpleTextAttributes.GRAY_ATTRIBUTES); - //} } - }); + }, null)); + if (showInstrumentationOptions) { + columnModel.getColumn(0).setHeaderValue("Annotation"); + + TableColumn checkColumn = new TableColumn(1, 100, new BooleanTableCellRenderer(), new BooleanTableCellEditor()); + columnModel.addColumn(checkColumn); + checkColumn.setHeaderValue(" Instrument "); + + DefaultTableCellHeaderRenderer renderer = new DefaultTableCellHeaderRenderer(); + renderer.setToolTipText("Add runtime assertions for notnull-annotated methods and parameters"); + checkColumn.setHeaderRenderer(renderer); + checkColumn.sizeWidthToFit(); + } + + myTable = new JBTable(myTableModel, columnModel); final AnActionButton selectButton = new AnActionButton("Select annotation used for code generation", AllIcons.Actions.Checked) { @Override public void actionPerformed(AnActionEvent e) { - final String selectedValue = myList.getSelectedValue(); + String selectedValue = getSelectedAnnotation(); if (selectedValue == null) return; myDefaultAnnotation = selectedValue; - DefaultListModel model = (DefaultListModel)myList.getModel(); // to show the new default value in the ui - model.setElementAt(myList.getSelectedValue(), myList.getSelectedIndex()); + myTableModel.fireTableRowsUpdated(myTable.getSelectedRow(), myTable.getSelectedRow()); } @Override public void updateButton(AnActionEvent e) { - String selectedValue = myList.getSelectedValue(); + String selectedValue = getSelectedAnnotation(); e.getPresentation().setEnabled(selectedValue != null && !selectedValue.equals(myDefaultAnnotation)); } }; - final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myList).disableUpDownActions() - .setAddAction(b -> chooseAnnotation(name, myList)) - .setRemoveAction(new AnActionButtonRunnable() { + final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTable).disableUpDownActions() + .setAddAction(b -> chooseAnnotation(name)) + .setRemoveAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton anActionButton) { - final String selectedValue = myList.getSelectedValue(); + String selectedValue = getSelectedAnnotation(); if (selectedValue == null) return; - if (myDefaultAnnotation.equals(selectedValue)) myDefaultAnnotation = myList.getModel().getElementAt(0); + if (myDefaultAnnotation.equals(selectedValue)) myDefaultAnnotation = (String)myTable.getValueAt(0, 0); - ((DefaultListModel)myList.getModel()).removeElement(selectedValue); + myTableModel.removeRow(myTable.getSelectedRow()); } }) - .setRemoveActionUpdater(e -> !myDefaultAnnotations.contains(myList.getSelectedValue())) - .addExtraAction(selectButton); + .setRemoveActionUpdater(e -> !myDefaultAnnotations.contains(getSelectedAnnotation())) + .addExtraAction(selectButton); final JPanel panel = toolbarDecorator.createPanel(); myComponent = new JPanel(new BorderLayout()); myComponent.setBorder(IdeBorderFactory.createTitledBorder(name + " annotations", false, JBUI.insetsTop(10))); myComponent.add(panel); - myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - myList.setSelectedValue(myDefaultAnnotation, true); + myComponent.setPreferredSize(new JBDimension(myComponent.getPreferredSize().width, 200)); + + myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myTable.setRowSelectionAllowed(true); + myTable.setShowGrid(false); + + selectAnnotation(myDefaultAnnotation); } - private void chooseAnnotation(String title, JBList list) { + private void addRow(String annotation, boolean checked) { + myTableModel.addRow(new Object[]{annotation, checked}); + } + + private Integer selectAnnotation(String annotation) { + for (int i = 0; i < myTable.getRowCount(); i++) { + if (annotation.equals(myTable.getValueAt(i, 0))) { + myTable.setRowSelectionInterval(i, i); + return i; + } + } + return null; + } + + private String getSelectedAnnotation() { + int selectedRow = myTable.getSelectedRow(); + return selectedRow <0 ? null : (String)myTable.getValueAt(selectedRow, 0); + } + + private void chooseAnnotation(String title) { final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject) .createNoInnerClassesScopeChooser("Choose " + title + " annotation", GlobalSearchScope.allScope(myProject), new ClassFilter() { @Override @@ -187,30 +270,34 @@ public class NullableNotNullDialog extends DialogWrapper { return; } final String qualifiedName = selected.getQualifiedName(); - //noinspection unchecked - final DefaultListModel model = (DefaultListModel)list.getModel(); - final int index = model.indexOf(qualifiedName); - if (index < 0) { - model.addElement(qualifiedName); - } else { - myList.setSelectedIndex(index); + if (selectAnnotation(qualifiedName) == null) { + addRow(qualifiedName, false); } } - public JComponent getComponent() { + JComponent getComponent() { return myComponent; } - public String getDefaultAnnotation() { + String getDefaultAnnotation() { return myDefaultAnnotation; } - public String[] getAnnotations() { - final ListModel model = myList.getModel(); - final int size = model.getSize(); - final String[] result = new String[size]; + String[] getAnnotations() { + int size = myTable.getRowCount(); + String[] result = new String[size]; for (int i = 0; i < size; i++) { - result[i] = (String)model.getElementAt(i); + result[i] = (String)myTable.getValueAt(i, 0); + } + return result; + } + + List getCheckedAnnotations() { + List result = new ArrayList<>(); + for (int i = 0; i < myTable.getRowCount(); i++) { + if (Boolean.TRUE.equals(myTable.getValueAt(i, 1))) { + result.add((String)myTable.getValueAt(i, 0)); + } } return result; } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerNotNullableSerializer.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerNotNullableSerializer.java index f488f32af719..12c15ecc8262 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerNotNullableSerializer.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerNotNullableSerializer.java @@ -23,24 +23,13 @@ import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; import org.jetbrains.jps.model.serialization.JpsProjectExtensionSerializer; -import java.util.Arrays; +import java.util.Collections; import java.util.List; /** * @author vladimir.dolzhenko */ public class JpsJavaCompilerNotNullableSerializer extends JpsProjectExtensionSerializer { - public static final List DEFAULT_NOT_NULLS = Arrays.asList( - NotNull.class.getName(), - "javax.annotation.Nonnull", - "javax.validation.constraints.NotNull", - "edu.umd.cs.findbugs.annotations.NonNull", - "android.support.annotation.NonNull", - "androidx.annotation.NonNull", - "org.checkerframework.checker.nullness.qual.NonNull", - "org.checkerframework.checker.nullness.compatqual.NonNullDecl", - "org.checkerframework.checker.nullness.compatqual.NonNullType" - ); public JpsJavaCompilerNotNullableSerializer() { super("misc.xml", "NullableNotNullManager"); @@ -50,26 +39,21 @@ public class JpsJavaCompilerNotNullableSerializer extends JpsProjectExtensionSer public void loadExtension(@NotNull JpsProject project, @NotNull Element componentTag) { JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); List annoNames = ContainerUtil.newArrayList(); - for (Element option : componentTag.getChildren("option")) { - if ("myNotNulls".equals(option.getAttributeValue("name"))){ - for (Element value : option.getChildren("value")) { - for (Element list : value.getChildren("list")) { - for (Element item : list.getChildren("item")) { - ContainerUtil.addIfNotNull(annoNames, item.getAttributeValue("itemvalue")); - } - } - } + for (Element option : componentTag.getChildren("instrumentedNotNulls")) { + for (Element item : option.getChildren("option")) { + ContainerUtil.addIfNotNull(annoNames, item.getAttributeValue("value")); } } if (annoNames.isEmpty()) { - annoNames.addAll(DEFAULT_NOT_NULLS); + annoNames.add(NotNull.class.getName()); } configuration.setNotNullAnnotations(annoNames); } @Override public void loadExtensionWithDefaultSettings(@NotNull JpsProject project) { - JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project).setNotNullAnnotations(DEFAULT_NOT_NULLS); + JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project).setNotNullAnnotations( + Collections.singletonList(NotNull.class.getName())); } @Override diff --git a/resources-en/src/messages/CompilerBundle.properties b/resources-en/src/messages/CompilerBundle.properties index f5796a778436..d70a36e6f34b 100644 --- a/resources-en/src/messages/CompilerBundle.properties +++ b/resources-en/src/messages/CompilerBundle.properties @@ -103,7 +103,7 @@ statistics.warnings.count={0} {0,choice, 0#warnings|1#warning|2#warnings} compiler.running.dialog.title=Compiler Running warning.compiler.running.on.project.close=The compiler is running. Proceed with project closing? warning.compiler.running.on.toolwindow.close=The compiler is running. Terminate it? -add.notnull.assertions=Add runtime &assertions for not-null-annotated methods and parameters +add.notnull.assertions=Add runtime &assertions for notnull-annotated methods and parameters compiler.eclipse.name=Eclipse eclipse.options.group.title=Eclipse Options