mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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());
|
||||
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
|
||||
+7
-23
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user