override methods dialog with sorting by overridings

This commit is contained in:
Dmitry Batkovich
2013-05-22 14:33:19 +04:00
parent 8308cbb822
commit 5b4d579ac5
10 changed files with 819 additions and 92 deletions

View File

@@ -0,0 +1,224 @@
package com.intellij.codeInsight.generation;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.MemberChooser;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.psi.PsiElement;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Collection;
/**
* @author Dmitry Batkovich
*/
public class JavaOverrideImplementMemberChooser extends MemberChooser<PsiMethodMember> {
private static final String SORT_METHODS_BY_PERCENT_DESCRIPTION = "Sort by Percent of Classes which Overrides a Method";
@NonNls public static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";
@NonNls public static final String PROP_OVERRIDING_SORTED_OVERRIDE_IMPLEMENT = "OverrideImplement.overriding.sorted";
private ToggleAction mySortByOverridingAction;
private ToggleAction myMergeAction;
private final PsiMethodMember[] myAllElements;
private final PsiMethodMember[] myOnlyPrimaryElements;
private final NotNullLazyValue<PsiMethodWithOverridingPercentMember[]> myLazyElementsWithPercent;
private final boolean myToImplement;
private Project myProject;
private boolean myMerge;
private boolean mySortedByOverriding;
@Nullable
public static JavaOverrideImplementMemberChooser create(final PsiElement aClass,
final boolean toImplement,
final Collection<CandidateInfo> candidates,
final Collection<CandidateInfo> secondary) {
final Project project = aClass.getProject();
if (candidates.isEmpty() && secondary.isEmpty()) return null;
final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);
final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary));
final NotNullLazyValue<PsiMethodWithOverridingPercentMember[]> lazyElementsWithPercent =
new NotNullLazyValue<PsiMethodWithOverridingPercentMember[]>() {
@NotNull
@Override
protected PsiMethodWithOverridingPercentMember[] compute() {
final PsiMethodWithOverridingPercentMember[] elements =
PsiMethodWithOverridingPercentMember.calculateOverridingPercents(candidates);
Arrays.sort(elements, PsiMethodWithOverridingPercentMember.COMPARATOR);
return elements;
}
};
final boolean merge = PropertiesComponent.getInstance(project).getBoolean(PROP_COMBINED_OVERRIDE_IMPLEMENT, true);
final JavaOverrideImplementMemberChooser javaOverrideImplementMemberChooser =
new JavaOverrideImplementMemberChooser(all, onlyPrimary, lazyElementsWithPercent, project, PsiUtil.isLanguageLevel5OrHigher(aClass),
merge, toImplement, PropertiesComponent.getInstance(project)
.getBoolean(PROP_OVERRIDING_SORTED_OVERRIDE_IMPLEMENT, false));
javaOverrideImplementMemberChooser.setTitle(getChooserTitle(toImplement, merge));
javaOverrideImplementMemberChooser.setCopyJavadocVisible(true);
if (toImplement) {
javaOverrideImplementMemberChooser.selectElements(onlyPrimary);
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
if (!toImplement || onlyPrimary.length == 0) {
javaOverrideImplementMemberChooser.selectElements(all);
}
javaOverrideImplementMemberChooser.close(DialogWrapper.OK_EXIT_CODE);
return javaOverrideImplementMemberChooser;
}
return javaOverrideImplementMemberChooser;
}
private JavaOverrideImplementMemberChooser(final PsiMethodMember[] allElements,
final PsiMethodMember[] onlyPrimaryElements,
final NotNullLazyValue<PsiMethodWithOverridingPercentMember[]> lazyElementsWithPercent,
final @NotNull Project project,
final boolean isInsertOverrideVisible,
final boolean merge,
final boolean toImplement,
final boolean sortedByOverriding) {
super(false, true, project, isInsertOverrideVisible, null, null);
myAllElements = allElements;
myOnlyPrimaryElements = onlyPrimaryElements;
myLazyElementsWithPercent = lazyElementsWithPercent;
myProject = project;
myMerge = merge;
myToImplement = toImplement;
mySortedByOverriding = sortedByOverriding;
resetElements(getInitialElements(allElements, onlyPrimaryElements, lazyElementsWithPercent, merge, toImplement, sortedByOverriding));
init();
}
private static PsiMethodMember[] getInitialElements(PsiMethodMember[] allElements,
PsiMethodMember[] onlyPrimaryElements,
NotNullLazyValue<PsiMethodWithOverridingPercentMember[]> lazyElementsWithPercent,
boolean merge,
boolean toImplement,
boolean sortByOverriding) {
final boolean showElementsWithPercents = sortByOverriding && !toImplement;
final PsiMethodMember[] defaultElements = toImplement || merge ? allElements : onlyPrimaryElements;
return showElementsWithPercents ? lazyElementsWithPercent.getValue() : defaultElements;
}
@Override
protected void onAlphabeticalSortingEnabled(final AnActionEvent event) {
resetElements(myToImplement || myMerge ? myAllElements : myOnlyPrimaryElements, null, true);
if (mySortByOverridingAction != null) {
mySortByOverridingAction.setSelected(event, false);
}
}
@Override
protected void doOKAction() {
super.doOKAction();
PropertiesComponent.getInstance(myProject).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, String.valueOf(myMerge));
PropertiesComponent.getInstance(myProject).setValue(PROP_OVERRIDING_SORTED_OVERRIDE_IMPLEMENT, String.valueOf(mySortedByOverriding));
}
@Override
protected void fillToolbarActions(DefaultActionGroup group) {
super.fillToolbarActions(group);
if (myToImplement) return;
mySortByOverridingAction = new MySortByOverridingAction();
if (mySortedByOverriding) {
changeSortComparator(PsiMethodWithOverridingPercentMember.COMPARATOR);
}
group.add(mySortByOverridingAction, Constraints.FIRST);
myMergeAction = new MyMergeAction();
group.add(myMergeAction);
}
private static String getChooserTitle(final boolean toImplement, final boolean merge) {
return toImplement
? CodeInsightBundle.message("methods.to.implement.chooser.title")
: merge
? CodeInsightBundle.message("methods.to.override.implement.chooser.title")
: CodeInsightBundle.message("methods.to.override.chooser.title");
}
private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
@Override
public PsiMethodMember fun(final CandidateInfo s) {
return new PsiMethodMember(s);
}
});
}
private class MySortByOverridingAction extends ToggleAction {
public MySortByOverridingAction() {
super(SORT_METHODS_BY_PERCENT_DESCRIPTION, SORT_METHODS_BY_PERCENT_DESCRIPTION, AllIcons.ObjectBrowser.SortedByUsage);
registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_MASK)), myTree);
}
@Override
public boolean isSelected(final AnActionEvent e) {
return mySortedByOverriding;
}
@Override
public void setSelected(final AnActionEvent e, final boolean state) {
mySortedByOverriding = state;
if (state) {
if (myMerge) {
myMergeAction.setSelected(e, false);
}
disableAlphabeticalSorting(e);
final PsiMethodWithOverridingPercentMember[] elementsWithPercent = myLazyElementsWithPercent.getValue();
resetElements(elementsWithPercent, PsiMethodWithOverridingPercentMember.COMPARATOR, false);
}
else {
final PsiMethodMember[] elementsToRender = myMerge ? myAllElements : myOnlyPrimaryElements;
resetElementsWithDefaultComparator(elementsToRender, true);
}
}
}
private class MyMergeAction extends ToggleAction {
private MyMergeAction() {
super("Show methods to implement", "Show methods to implement", AllIcons.General.Show_to_implement);
registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);
final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("OverrideMethods");
registerCustomShortcutSet(new CustomShortcutSet(shortcuts), myTree);
}
@Override
public boolean isSelected(AnActionEvent e) {
return myMerge;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
myMerge = state;
if (state && mySortByOverridingAction.isSelected(e)) {
mySortByOverridingAction.setSelected(e, false);
}
resetElements(state ? myAllElements : myOnlyPrimaryElements, null, true);
setTitle(getChooserTitle(false, myMerge));
}
}
}

View File

@@ -50,7 +50,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
@@ -223,7 +222,7 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
}
annotateOnOverrideImplement(result, aClass, method, insertOverrideIfPossible);
if (CodeStyleSettingsManager.getSettings(aClass.getProject()).REPEAT_SYNCHRONIZED && method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
result.getModifierList().setModifierProperty(PsiModifier.SYNCHRONIZED, true);
}
@@ -394,9 +393,10 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), originalMethod.getProject());
if (factory == null) factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();
@NonNls String methodText;
try {
methodText = "void foo () {\n" + template.getText(properties) + "\n}";
String bodyText = template.getText(properties);
if (bodyText != null && !bodyText.isEmpty()) bodyText += "\n";
methodText = "void foo () {\n" + bodyText + "}";
methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);
} catch (Exception e) {
throw new IncorrectOperationException("Failed to parse file template",e);

View File

@@ -0,0 +1,157 @@
package com.intellij.codeInsight.generation;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.*;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class PsiMethodWithOverridingPercentMember extends PsiMethodMember {
private final int myOverridingPercent;
public PsiMethodWithOverridingPercentMember(final CandidateInfo info, final int overridingPercent) {
super(info);
myOverridingPercent = overridingPercent;
}
@Override
public void renderTreeNode(final SimpleColoredComponent component, final JTree tree) {
component.append(myOverridingPercent + "% ", SimpleTextAttributes.GRAY_ATTRIBUTES);
super.renderTreeNode(component, tree);
}
@TestOnly
public int getOverridingPercent() {
return myOverridingPercent;
}
public static final Comparator<PsiMethodMember> COMPARATOR = new Comparator<PsiMethodMember>() {
@Override
public int compare(PsiMethodMember e1, PsiMethodMember e2) {
if (!(e1 instanceof PsiMethodWithOverridingPercentMember)) {
if (!(e2 instanceof PsiMethodWithOverridingPercentMember)) {
return e1.equals(e2) ? 0 : -1;
} else {
return -1;
}
}
if (!(e2 instanceof PsiMethodWithOverridingPercentMember)) {
return 1;
}
int sub =
((PsiMethodWithOverridingPercentMember)e2).myOverridingPercent - ((PsiMethodWithOverridingPercentMember)e1).myOverridingPercent;
if (sub != 0) return sub;
return String.CASE_INSENSITIVE_ORDER.compare(e1.getText(), e2.getText());
}
};
@NotNull
public static PsiMethodWithOverridingPercentMember[] calculateOverridingPercents(@NotNull final Collection<CandidateInfo> candidateInfos) {
final List<PsiMethodWithOverridingPercentMember> result = new ArrayList<PsiMethodWithOverridingPercentMember>(candidateInfos.size());
final Map<String, Collection<PsiClass>> classShortNames2Inheritors = new HashMap<String, Collection<PsiClass>>();
for (final CandidateInfo candidateInfo : candidateInfos) {
final PsiMethod method = (PsiMethod)candidateInfo.getElement();
if (!method.hasModifierProperty(PsiModifier.FINAL) &&
!method.isConstructor() &&
!method.isDeprecated() &&
!EXCLUDED_JAVA_LANG_OBJECT_METHOD_NAMES.contains(method.getName())) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) {
continue;
}
final String classShortName = containingClass.getName();
Collection<PsiClass> allInheritors = classShortNames2Inheritors.get(classShortName);
if (allInheritors == null) {
allInheritors = ClassInheritorsSearch.search(containingClass, true).findAll();
classShortNames2Inheritors.put(classShortName, allInheritors);
}
final int allInheritorsCount = allInheritors.size() - 1;
if (allInheritorsCount > 0) {
final int percent = searchForOverridingCount(method, allInheritors) * 100 / allInheritorsCount;
if (percent > 1) {
result.add(new PsiMethodWithOverridingPercentMember(candidateInfo, percent));
}
}
}
}
return result.toArray(new PsiMethodWithOverridingPercentMember[result.size()]);
}
private static int searchForOverridingCount(final PsiMethod method, final Collection<PsiClass> containingClassInheritors) {
int counter = 0;
for (final PsiClass inheritor : containingClassInheritors) {
if (inheritor instanceof PsiExtensibleClass) {
final List<PsiMethod> ownMethods = ((PsiExtensibleClass)inheritor).getOwnMethods();
for (PsiMethod ownMethod : ownMethods) {
if (maybeSuper(method, ownMethod)) {
counter++;
break;
}
}
}
}
return counter;
}
private static boolean maybeSuper(@NotNull final PsiMethod superMethod, @NotNull final PsiMethod method) {
if (!superMethod.getName().equals(method.getName())) {
return false;
}
final PsiParameterList superMethodParameterList = superMethod.getParameterList();
final PsiParameterList methodParameterList = method.getParameterList();
if (superMethodParameterList.getParametersCount() != methodParameterList.getParametersCount()) {
return false;
}
final PsiParameter[] superMethodParameters = superMethodParameterList.getParameters();
final PsiParameter[] methodParameters = methodParameterList.getParameters();
for (int i = 0; i < methodParameters.length; i++) {
if (!StringUtil.equals(getTypeShortName(superMethodParameters[i].getType()), getTypeShortName(methodParameters[i].getType()))) {
return false;
}
}
return true;
}
@Nullable
private static String getTypeShortName(@NotNull final PsiType type) {
if (type instanceof PsiPrimitiveType) {
return ((PsiPrimitiveType)type).getBoxedTypeName();
}
if (type instanceof PsiClassType) {
return ((PsiClassType)type).getClassName();
}
if (type instanceof PsiArrayType) {
return getTypeShortName(((PsiArrayType)type).getComponentType()) + "[]";
}
return null;
}
private static final Set<String> EXCLUDED_JAVA_LANG_OBJECT_METHOD_NAMES =
ContainerUtil.newHashSet("hashCode", "finalize", "clone", "equals", "toString");
@Override
public String toString() {
return "PsiMethodWithOverridingPercentMember{" +
"myOverridingPercent=" + myOverridingPercent + ", myElement=" + getElement() +
'}';
}
}

View File

@@ -0,0 +1,30 @@
import java.lang.Override;
import java.lang.String;
class BaseClass {
public void method() {
//do nothing
}
public void method2() {
}
}
class ClassEx1 extends BaseClass {
@Override
public void method() {
}
}
class ClassEx2 extends BaseClass {
public void method() {
}
public void method(String aString) {
}
}
class MyClass extends B<caret>aseClass {
}

View File

@@ -0,0 +1,31 @@
import java.lang.Override;
import java.lang.String;
class BaseClass {
public void method() {
//do nothing
}
public void method(String s) {
//do nothing
}
}
class ClassEx1 extends BaseClass {
@Override
public void method() {
}
@Override
public void method(String s) {
}
}
class ClassEx2 extends BaseClass {
public void method() {
}
}
class MyClass extends B<caret>aseClass {
}

View File

@@ -0,0 +1,39 @@
import java.lang.Override;
import java.lang.String;
class BaseClass {
public void method() {
//do nothing
}
public void method(String s) {
//do nothing
}
}
class ClassEx1 extends BaseClass {
@Override
public void method() {
}
@Override
public void method(String s) {
}
public void method2() {
}
}
class ClassEx11 extends ClassEx1 {
public void method2() {
}
}
class ClassEx2 extends BaseClass {
public void method() {
}
}
class MyClass extends Cl<caret>assEx1 {
}

View File

@@ -0,0 +1,40 @@
import java.lang.Override;
import java.lang.String;
class BaseClass {
public void method() {
//do nothing
}
public void method(String s) {
//do nothing
}
}
class ClassEx1 extends BaseClass {
@Override
public void method(String s) {
}
public void method2() {
}
}
class ClassEx11 extends ClassEx1 {
public void method2() {
}
}
class ClassEx2 extends BaseClass {
public void method() {
}
}
class ClassEx3 extends BaseClass {
public void method() {
}
}
class MyClass extends Cl<caret>assEx1 {
}

View File

@@ -0,0 +1,63 @@
import java.lang.Override;
import java.lang.String;
class BaseClass {
public void method() {
//do nothing
}
public void method(String s) {
//do nothing
}
}
class ClassEx2 extends BaseClass {
public void method() {
}
}
class ClassEx3 extends BaseClass {
public void method() {
}
}
class ClassEx1 extends BaseClass {
@Override
public void method(String s) {
}
public void method2() {
}
}
class ClassEx11 extends ClassEx1 {
@Override
public void method(String s) {
}
public void method2() {
}
}
class ClassEx12 extends ClassEx1 {
@Override
public void method(String s) {
}
public void method2() {
}
}
class ClassEx13 extends ClassEx1 {
@Override
public void method(String s) {
}
public void method2() {
}
}
class MyClass extends Cl<caret>assEx1 {
}

View File

@@ -0,0 +1,77 @@
package com.intellij.codeInsight.generation.methodsOverridingStatistics;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.generation.OverrideImplementExploreUtil;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.generation.PsiMethodWithOverridingPercentMember;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiClass;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.util.containers.ContainerUtil;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
@SuppressWarnings("unchecked")
public class JavaMethodsOverridingStatisticsTest extends JavaCodeInsightFixtureTestCase {
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/generation/javaMethodsOverridingStatistics/";
}
public void testMethods() {
doTest(1, pair("method", 100));
}
public void testMethods2() {
doTest(2, pair("method", 50), pair("method", 100));
}
public void testMethods3() {
doTest(1, pair("method2", 100));
}
public void testMethods4() {
doTest(2, pair("method2", 100), pair("method", 50));
}
public void testMethods5() {
doTest(3, pair("method", 100), pair("method2", 100), pair("method", 33));
}
private void doTest(final int resultSize, final Pair<String, Integer>... expectedValues) {
myFixture.configureByFile(getTestName(false) + ".java");
final PsiClass contextClass =
OverrideImplementUtil.getContextClass(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), true);
assert contextClass != null;
if (OverrideImplementExploreUtil.getMethodSignaturesToOverride(contextClass).isEmpty() && expectedValues.length != 0) {
fail();
}
final Collection<CandidateInfo> candidateInfos = OverrideImplementExploreUtil.getMethodsToOverrideImplement(contextClass, false);
final PsiMethodWithOverridingPercentMember[] searchResults = PsiMethodWithOverridingPercentMember
.calculateOverridingPercents(candidateInfos);
assertSize(resultSize, searchResults);
final Set<Pair<String, Integer>> actualValues = new HashSet<Pair<String, Integer>>();
for (PsiMethodWithOverridingPercentMember searchResult : searchResults) {
actualValues.add(Pair.<String, Integer>create(searchResult.getElement().getName(), searchResult.getOverridingPercent()));
}
final Set<Pair<String, Integer>> expectedValuesSet = ContainerUtil.newHashSet(expectedValues);
assertEquals(expectedValuesSet, actualValues);
}
private static Pair<String, Integer> pair(final String methodName, final int percent) {
return Pair.create(methodName, percent);
}
}

View File

@@ -58,10 +58,11 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
protected JComponent[] myOptionControls;
private JCheckBox myCopyJavadocCheckbox;
private JCheckBox myInsertOverrideAnnotationCheckbox;
private final ArrayList<MemberNode> mySelectedNodes = new ArrayList<MemberNode>();
private boolean mySorted = false;
private final SortEmAction mySortAction;
private boolean myAlphabeticallySorted = false;
private boolean myShowClasses = true;
protected boolean myAllowEmptySelection = false;
private boolean myAllowMultiSelection;
@@ -70,29 +71,31 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
private final JComponent myHeaderPanel;
protected T[] myElements;
protected Comparator<ElementNode> myComparator = new OrderComparator();
protected final HashMap<MemberNode,ParentNode> myNodeToParentMap = new HashMap<MemberNode, ParentNode>();
protected final HashMap<ClassMember, MemberNode> myElementToNodeMap = new HashMap<ClassMember, MemberNode>();
protected final ArrayList<ContainerNode> myContainerNodes = new ArrayList<ContainerNode>();
protected LinkedHashSet<T> mySelectedElements;
@NonNls private static final String PROP_SORTED = "MemberChooser.sorted";
@NonNls private static final String PROP_SHOWCLASSES = "MemberChooser.showClasses";
@NonNls private static final String PROP_COPYJAVADOC = "MemberChooser.copyJavadoc";
public MemberChooser(T[] elements,
boolean allowEmptySelection,
boolean allowMultiSelection,
@NotNull Project project) {
this(elements, allowEmptySelection, allowMultiSelection, project, false);
}
public MemberChooser(T[] elements,
boolean allowEmptySelection,
boolean allowMultiSelection,
@NotNull Project project,
@Nullable JComponent headerPanel,
JComponent[] optionControls) {
this(elements, allowEmptySelection, allowMultiSelection, project, false, headerPanel, optionControls);
this(allowEmptySelection, allowMultiSelection, project, false, headerPanel, optionControls);
resetElements(elements);
init();
}
public MemberChooser(T[] elements, boolean allowEmptySelection, boolean allowMultiSelection, @NotNull Project project) {
this(elements, allowEmptySelection, allowMultiSelection, project, false);
}
public MemberChooser(T[] elements,
@@ -108,19 +111,19 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
boolean allowMultiSelection,
@NotNull Project project,
boolean isInsertOverrideVisible,
JComponent headerPanel
@Nullable JComponent headerPanel
) {
this(elements, allowEmptySelection, allowMultiSelection, project, isInsertOverrideVisible, headerPanel, null);
this(allowEmptySelection, allowMultiSelection, project, isInsertOverrideVisible, headerPanel, null);
resetElements(elements);
init();
}
private MemberChooser(T[] elements,
boolean allowEmptySelection,
boolean allowMultiSelection,
@NotNull Project project,
boolean isInsertOverrideVisible,
JComponent headerPanel,
@Nullable JComponent[] optionControls
) {
protected MemberChooser(boolean allowEmptySelection,
boolean allowMultiSelection,
@NotNull Project project,
boolean isInsertOverrideVisible,
@Nullable JComponent headerPanel,
@Nullable JComponent[] optionControls) {
super(project, true);
myAllowEmptySelection = allowEmptySelection;
myAllowMultiSelection = allowMultiSelection;
@@ -129,12 +132,26 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
myHeaderPanel = headerPanel;
myTree = createTree();
myOptionControls = optionControls;
resetElements(elements);
init();
mySortAction = new SortEmAction();
mySortAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.ALT_MASK)), myTree);
}
protected void resetElementsWithDefaultComparator(T[] elements, final boolean restoreSelectedElements) {
myComparator = myAlphabeticallySorted ? new AlphaComparator() : new OrderComparator();
resetElements(elements, null, restoreSelectedElements);
}
public void resetElements(T[] elements) {
resetElements(elements, null, false);
}
@SuppressWarnings("unchecked")
public void resetElements(T[] elements, final @Nullable Comparator<T> sortComparator, final boolean restoreSelectedElements) {
final List<T> selectedElements = restoreSelectedElements && mySelectedElements != null ? new ArrayList<T>(mySelectedElements) : null;
myElements = elements;
if (sortComparator != null) {
myComparator = new ElementNodeComparatorWrapper(sortComparator);
}
mySelectedNodes.clear();
myNodeToParentMap.clear();
myElementToNodeMap.clear();
@@ -150,9 +167,7 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
myTree.setModel(myTreeModel);
myTree.setRootVisible(false);
doSort();
defaultExpandTree();
restoreTree();
if (myOptionControls == null) {
myCopyJavadocCheckbox = new NonFocusableCheckBox(IdeBundle.message("checkbox.copy.javadoc"));
@@ -167,6 +182,13 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
myTree.doLayout();
setOKActionEnabled(myElements != null && myElements.length > 0);
if (selectedElements != null) {
selectElements(selectedElements.toArray(new ClassMember[selectedElements.size()]));
}
if (mySelectedElements == null || mySelectedElements.isEmpty()) {
expandFirst();
}
}
/**
@@ -175,7 +197,7 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
private DefaultTreeModel buildModel() {
final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
final Ref<Integer> count = new Ref<Integer>(0);
final FactoryMap<MemberChooserObject,ParentNode> map = new FactoryMap<MemberChooserObject,ParentNode>() {
final FactoryMap<MemberChooserObject, ParentNode> map = new FactoryMap<MemberChooserObject, ParentNode>() {
@Override
protected ParentNode create(final MemberChooserObject key) {
ParentNode node = null;
@@ -322,37 +344,10 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
BorderLayout.NORTH);
// Tree
myTree.setCellRenderer(getTreeCellRenderer());
UIUtil.setLineStyleAngled(myTree);
myTree.setRootVisible(false);
myTree.setShowsRootHandles(true);
myTree.addKeyListener(new TreeKeyListener());
myTree.addTreeSelectionListener(new MyTreeSelectionListener());
if (!myAllowMultiSelection) {
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
}
if (getRootNode().getChildCount() > 0) {
myTree.expandRow(0);
myTree.setSelectionRow(1);
}
expandFirst();
defaultExpandTree();
installSpeedSearch();
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
if (myTree.getPathForLocation(e.getX(), e.getY()) != null) {
doOKAction();
return true;
}
return false;
}
}.installOn(myTree);
TreeUtil.installActions(myTree);
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree);
scrollPane.setPreferredSize(new Dimension(350, 450));
panel.add(scrollPane, BorderLayout.CENTER);
@@ -360,8 +355,40 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
return panel;
}
private void expandFirst() {
if (getRootNode().getChildCount() > 0) {
myTree.expandRow(0);
myTree.setSelectionRow(1);
}
}
protected Tree createTree() {
return new Tree(new DefaultTreeModel(new DefaultMutableTreeNode()));
final Tree tree = new Tree(new DefaultTreeModel(new DefaultMutableTreeNode()));
tree.setCellRenderer(getTreeCellRenderer());
UIUtil.setLineStyleAngled(tree);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
tree.addKeyListener(new TreeKeyListener());
tree.addTreeSelectionListener(new MyTreeSelectionListener());
if (!myAllowMultiSelection) {
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
}
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
if (tree.getPathForLocation(e.getX(), e.getY()) != null) {
doOKAction();
return true;
}
return false;
}
}.installOn(tree);
TreeUtil.installActions(tree);
return tree;
}
protected TreeCellRenderer getTreeCellRenderer() {
@@ -411,15 +438,26 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
return new SpeedSearchComparator(false);
}
protected void disableAlphabeticalSorting(final AnActionEvent event) {
mySortAction.setSelected(event, false);
}
protected void onAlphabeticalSortingEnabled(final AnActionEvent event) {
//do nothing by default
}
protected void fillToolbarActions(DefaultActionGroup group) {
SortEmAction sortAction = new SortEmAction();
sortAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.ALT_MASK)), myTree);
setSorted(PropertiesComponent.getInstance().isTrueValue(PROP_SORTED));
group.add(sortAction);
final boolean alphabeticallySorted = PropertiesComponent.getInstance().isTrueValue(PROP_SORTED);
if (alphabeticallySorted) {
setSortComparator(new OrderComparator());
}
myAlphabeticallySorted = alphabeticallySorted;
group.add(mySortAction);
if (!supportsNestedContainers()) {
ShowContainersAction showContainersAction = getShowContainersAction();
showContainersAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK)), myTree);
showContainersAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK)),
myTree);
setShowClasses(PropertiesComponent.getInstance().isTrueValue(PROP_SHOWCLASSES));
group.add(showContainersAction);
}
@@ -469,41 +507,46 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
return myCopyJavadocCheckbox.isSelected();
}
public boolean isInsertOverrideAnnotation () {
public boolean isInsertOverrideAnnotation() {
return myIsInsertOverrideVisible && myInsertOverrideAnnotationCheckbox.isSelected();
}
private boolean isSorted() {
return mySorted;
private boolean isAlphabeticallySorted() {
return myAlphabeticallySorted;
}
private void setSorted(boolean sorted) {
if (mySorted == sorted) return;
mySorted = sorted;
@SuppressWarnings("unchecked")
protected void changeSortComparator(final Comparator<T> comparator) {
setSortComparator(new ElementNodeComparatorWrapper(comparator));
}
private void setSortComparator(Comparator<ElementNode> sortComparator) {
if (myComparator.equals(sortComparator)) return;
myComparator = sortComparator;
doSort();
}
private void doSort() {
Pair<ElementNode,List<ElementNode>> pair = storeSelection();
protected void doSort() {
Pair<ElementNode, List<ElementNode>> pair = storeSelection();
Enumeration<ParentNode> children = getRootNodeChildren();
while (children.hasMoreElements()) {
ParentNode classNode = children.nextElement();
sortNode(classNode, mySorted);
sortNode(classNode, myComparator);
myTreeModel.nodeStructureChanged(classNode);
}
restoreSelection(pair);
}
private static void sortNode(ParentNode node, boolean sorted) {
private static void sortNode(ParentNode node, final Comparator<ElementNode> sortComparator) {
ArrayList<MemberNode> arrayList = new ArrayList<MemberNode>();
Enumeration<MemberNode> children = node.children();
while (children.hasMoreElements()) {
arrayList.add(children.nextElement());
}
Collections.sort(arrayList, sorted ? new AlphaComparator() : new OrderComparator());
Collections.sort(arrayList, sortComparator);
replaceChildren(node, arrayList);
}
@@ -515,10 +558,8 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
}
}
private void setShowClasses(boolean showClasses) {
myShowClasses = showClasses;
Pair<ElementNode,List<ElementNode>> selection = storeSelection();
protected void restoreTree() {
Pair<ElementNode, List<ElementNode>> selection = storeSelection();
DefaultMutableTreeNode root = getRootNode();
if (!myShowClasses || myContainerNodes.isEmpty()) {
@@ -537,23 +578,25 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
for (MemberNode memberNode : memberNodesList) {
newRoot.add(memberNode);
}
} else {
}
else {
otherObjects.add(nextElement);
}
}
replaceChildren(root, otherObjects);
sortNode(newRoot, mySorted);
sortNode(newRoot, myComparator);
if (newRoot.children().hasMoreElements()) root.add(newRoot);
}
else {
Enumeration<ParentNode> children = getRootNodeChildren();
if (children.hasMoreElements()) {
while (children.hasMoreElements()) {
ParentNode allClassesNode = children.nextElement();
Enumeration<MemberNode> memberNodes = allClassesNode.children();
ArrayList<MemberNode> arrayList = new ArrayList<MemberNode>();
while (memberNodes.hasMoreElements()) {
arrayList.add(memberNodes.nextElement());
}
Collections.sort(arrayList, myComparator);
for (MemberNode memberNode : arrayList) {
myNodeToParentMap.get(memberNode).add(memberNode);
}
@@ -567,6 +610,11 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
restoreSelection(selection);
}
private void setShowClasses(boolean showClasses) {
myShowClasses = showClasses;
restoreTree();
}
protected String getAllContainersNodeName() {
return IdeBundle.message("node.memberchooser.all.classes");
}
@@ -618,7 +666,7 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
@Override
public void dispose() {
PropertiesComponent instance = PropertiesComponent.getInstance();
instance.setValue(PROP_SORTED, Boolean.toString(isSorted()));
instance.setValue(PROP_SORTED, Boolean.toString(isAlphabeticallySorted()));
instance.setValue(PROP_SHOWCLASSES, Boolean.toString(myShowClasses));
if (myCopyJavadocCheckbox != null) {
@@ -640,7 +688,7 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
if (mySelectedElements != null && !mySelectedElements.isEmpty()) {
T selectedElement = mySelectedElements.iterator().next();
if (selectedElement instanceof ClassMemberWithElement) {
sink.put(LangDataKeys.PSI_ELEMENT, ((ClassMemberWithElement) selectedElement).getElement());
sink.put(LangDataKeys.PSI_ELEMENT, ((ClassMemberWithElement)selectedElement).getElement());
}
}
}
@@ -773,17 +821,21 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
@Override
public boolean isSelected(AnActionEvent event) {
return isSorted();
return isAlphabeticallySorted();
}
@Override
public void setSelected(AnActionEvent event, boolean flag) {
setSorted(flag);
myAlphabeticallySorted = flag;
setSortComparator(flag ? new AlphaComparator() : new OrderComparator());
if (flag) {
MemberChooser.this.onAlphabeticalSortingEnabled(event);
}
}
}
protected ShowContainersAction getShowContainersAction() {
return new ShowContainersAction(IdeBundle.message("action.show.classes"), PlatformIcons.CLASS_ICON);
return new ShowContainersAction(IdeBundle.message("action.show.classes"), PlatformIcons.CLASS_ICON);
}
protected class ShowContainersAction extends ToggleAction {
@@ -841,16 +893,30 @@ public class MemberChooser<T extends ClassMember> extends DialogWrapper implemen
}
protected static class OrderComparator implements Comparator<ElementNode> {
public OrderComparator() {} // To make this class instanceable from the subclasses
public OrderComparator() {
} // To make this class instanceable from the subclasses
@Override
public int compare(ElementNode n1, ElementNode n2) {
if (n1.getDelegate() instanceof ClassMemberWithElement
&& n2.getDelegate() instanceof ClassMemberWithElement) {
return ((ClassMemberWithElement)n1.getDelegate()).getElement().getTextOffset()
- ((ClassMemberWithElement)n2.getDelegate()).getElement().getTextOffset();
if (n1.getDelegate() instanceof ClassMemberWithElement && n2.getDelegate() instanceof ClassMemberWithElement) {
return ((ClassMemberWithElement)n1.getDelegate()).getElement().getTextOffset() -
((ClassMemberWithElement)n2.getDelegate()).getElement().getTextOffset();
}
return n1.getOrder() - n2.getOrder();
}
}
private static class ElementNodeComparatorWrapper<T> implements Comparator<ElementNode> {
private final Comparator<T> myDelegate;
public ElementNodeComparatorWrapper(final Comparator<T> delegate) {
myDelegate = delegate;
}
@SuppressWarnings("unchecked")
@Override
public int compare(final ElementNode o1, final ElementNode o2) {
return myDelegate.compare((T) o1.getDelegate(), (T) o2.getDelegate());
}
}
}