allow to choose which @NotNull annotations to instrument (IDEA-179396)

enable only JetBrains ones by default
plus cleanup NullableNotNullManager serialization a bit
This commit is contained in:
peter
2018-06-01 13:57:36 +02:00
parent 8e2bf8d82a
commit 1bd3a97d1f
8 changed files with 289 additions and 194 deletions
@@ -157,7 +157,7 @@
<text value="Shared build process VM options:"/>
</properties>
</component>
<grid id="6ee49" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="6ee49" binding="myAssertNotNullPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -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<Setting, Collection<JComponent>> 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));
@@ -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<Element> {
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<NullableNotNullManagerImpl.StateBean> {
public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname";
private List<String> myNullables = ContainerUtil.newArrayList(DEFAULT_NULLABLES);
private List<String> 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<String> 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<String> getPredefinedNotNulls() {
return JpsJavaCompilerNotNullableSerializer.DEFAULT_NOT_NULLS;
public void setNotNulls(@NotNull String... annotations) {
LinkedHashSet<String> set = ContainerUtil.newLinkedHashSet(annotations);
Collections.addAll(set, DEFAULT_NOT_NULLS);
myNotNulls = new ArrayList<>(set);
}
@Override
public void setNullables(@NotNull String... annotations) {
LinkedHashSet<String> 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<String> getNullables() {
return Collections.unmodifiableList(myNullables);
}
@Override
@NotNull
public List<String> getNotNulls() {
return Collections.unmodifiableList(myNotNulls);
}
@NotNull
@Override
public List<String> getInstrumentedNotNulls() {
return Collections.unmodifiableList(myState.instrumentedNotNulls);
}
@Override
public void setInstrumentedNotNulls(@NotNull List<String> 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<String> 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);
}
}
@@ -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<? super String> 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<String> annotations = nullable ? getPredefinedNotNulls() : Arrays.asList(DEFAULT_NULLABLES);
List<String> 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<String> getNullables() {
return myNullables;
}
public abstract List<String> getNullables();
@NotNull
public List<String> getNotNulls() {
return myNotNulls;
}
boolean hasDefaultValues() {
List<String> 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<String> 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<String> getPredefinedNotNulls();
@NotNull
public abstract List<String> getInstrumentedNotNulls();
public abstract void setInstrumentedNotNulls(@NotNull List<String> names);
public static boolean isNullableAnnotation(@NotNull PsiAnnotation annotation) {
return getInstance(annotation.getProject()).getNullablesWithNickNames().contains(annotation.getQualifiedName());
@@ -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()
@@ -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<String> myDefaultAnnotations;
private final JBList<String> myList;
private final JBTable myTable;
private final JPanel myComponent;
private final DefaultTableModel myTableModel;
private AnnotationsPanel(final String name, final String defaultAnnotation,
final Collection<String> annotations, final String[] defaultAnnotations) {
private AnnotationsPanel(String name, String defaultAnnotation, List<String> annotations, String[] defaultAnnotations, Set<String> checkedAnnotations, boolean showInstrumentationOptions) {
myDefaultAnnotation = defaultAnnotation;
myDefaultAnnotations = new HashSet<>(Arrays.asList(defaultAnnotations));
myList = new JBList<>(annotations);
myList.setCellRenderer(new ColoredListCellRenderer<String>() {
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<String> model = (DefaultListModel<String>)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<String> model = (DefaultListModel<String>)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<String> getCheckedAnnotations() {
List<String> 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;
}
@@ -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<String> 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<String> 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
@@ -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