constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / completion / ModifierChooser.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.codeInsight.completion;
3
4 import com.intellij.psi.*;
5 import com.intellij.psi.filters.FilterPositionUtil;
6 import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement;
7 import com.intellij.psi.javadoc.PsiDocComment;
8 import com.intellij.psi.util.PsiTreeUtil;
9 import com.intellij.psi.util.PsiUtil;
10 import com.intellij.util.ArrayUtilRt;
11 import com.intellij.util.containers.ContainerUtil;
12 import org.jetbrains.annotations.NotNull;
13 import org.jetbrains.annotations.Nullable;
14
15 import java.util.ArrayList;
16 import java.util.List;
17
18 /**
19  * @author ik
20  */
21 public class ModifierChooser {
22   private static final String[][] CLASS_MODIFIERS = {
23     {PsiKeyword.PUBLIC},
24     {PsiKeyword.FINAL, PsiKeyword.ABSTRACT}
25   };
26   private static final String[][] CLASS_MEMBER_MODIFIERS = {
27     {PsiKeyword.PUBLIC, PsiKeyword.PROTECTED, PsiKeyword.PRIVATE},
28     {PsiKeyword.STATIC},
29     {PsiKeyword.FINAL, PsiKeyword.ABSTRACT},
30     {PsiKeyword.NATIVE},
31     {PsiKeyword.SYNCHRONIZED},
32     {PsiKeyword.STRICTFP},
33     {PsiKeyword.VOLATILE},
34     {PsiKeyword.TRANSIENT}
35   };
36
37   private static final String[][] INTERFACE_9_MEMBER_MODIFIERS = {
38     {PsiKeyword.PUBLIC, PsiKeyword.PROTECTED, PsiKeyword.PRIVATE},
39     {PsiKeyword.STATIC, PsiKeyword.DEFAULT},
40     {PsiKeyword.FINAL, PsiKeyword.ABSTRACT}
41   };
42
43   private static final String[][] INTERFACE_8_MEMBER_MODIFIERS = {
44     {PsiKeyword.PUBLIC, PsiKeyword.PROTECTED},
45     {PsiKeyword.STATIC, PsiKeyword.DEFAULT},
46     {PsiKeyword.FINAL, PsiKeyword.ABSTRACT}
47   };
48
49   private static final String[][] INTERFACE_MEMBER_MODIFIERS = {
50     {PsiKeyword.PUBLIC, PsiKeyword.PROTECTED},
51     {PsiKeyword.FINAL, PsiKeyword.ABSTRACT}
52   };
53
54   static String[] getKeywords(@NotNull PsiElement position) {
55     final PsiModifierList list = findModifierList(position);
56     if (list == null && !shouldSuggestModifiers(position)) {
57       return ArrayUtilRt.EMPTY_STRING_ARRAY;
58     }
59
60     PsiElement scope = position.getParent();
61     while (scope != null) {
62       if (scope instanceof PsiJavaFile) {
63         return addClassModifiers(list);
64       }
65       if (scope instanceof PsiClass) {
66         return addMemberModifiers(list, ((PsiClass)scope).isInterface(), scope);
67       }
68
69       scope = scope.getParent();
70       if (scope instanceof PsiDirectory) break;
71     }
72     return ArrayUtilRt.EMPTY_STRING_ARRAY;
73   }
74
75   public static String[] addClassModifiers(PsiModifierList list) {
76     return addKeywords(list, CLASS_MODIFIERS);
77   }
78
79   public static String[] addMemberModifiers(PsiModifierList list, final boolean inInterface, @NotNull PsiElement position) {
80     return addKeywords(list, inInterface ? getInterfaceMemberModifiers(position) : CLASS_MEMBER_MODIFIERS);
81   }
82
83   private static String[][] getInterfaceMemberModifiers(@NotNull PsiElement list) {
84     if (PsiUtil.isLanguageLevel9OrHigher(list)) {
85       return INTERFACE_9_MEMBER_MODIFIERS;
86     }
87     if (PsiUtil.isLanguageLevel8OrHigher(list)) {
88       return INTERFACE_8_MEMBER_MODIFIERS;
89     }
90     return INTERFACE_MEMBER_MODIFIERS;
91   }
92
93   private static String[] addKeywords(PsiModifierList list, String[][] keywordSets) {
94     final List<String> ret = new ArrayList<>();
95     for (int i = 0; i < keywordSets.length; i++) {
96       final String[] keywords = keywordSets[keywordSets.length - i - 1];
97       boolean containModifierFlag = false;
98       if (list != null) {
99         for (@PsiModifier.ModifierConstant String keyword : keywords) {
100           if (list.hasExplicitModifier(keyword)) {
101             containModifierFlag = true;
102             break;
103           }
104         }
105       }
106       if (!containModifierFlag) {
107         ContainerUtil.addAll(ret, keywords);
108       }
109     }
110     return ArrayUtilRt.toStringArray(ret);
111   }
112
113   @Nullable
114   public static PsiModifierList findModifierList(@NotNull PsiElement element) {
115     if(element.getParent() instanceof PsiModifierList) {
116       return (PsiModifierList)element.getParent();
117     }
118
119     return PsiTreeUtil.getParentOfType(FilterPositionUtil.searchNonSpaceNonCommentBack(element), PsiModifierList.class);
120   }
121
122   private static boolean shouldSuggestModifiers(PsiElement element) {
123     PsiElement parent = element.getParent();
124     while (parent instanceof PsiJavaCodeReferenceElement ||
125            parent instanceof PsiErrorElement || parent instanceof PsiTypeElement ||
126            parent instanceof PsiMethod || parent instanceof PsiVariable ||
127            parent instanceof PsiDeclarationStatement || parent instanceof PsiImportList ||
128            parent instanceof PsiDocComment) {
129       parent = parent.getParent();
130       if (parent instanceof JspClassLevelDeclarationStatement) {
131         parent = parent.getContext();
132       }
133     }
134
135     if (parent == null) return false;
136
137     return (parent instanceof PsiJavaFile || parent instanceof PsiClass) &&
138            JavaKeywordCompletion.isEndOfBlock(element);
139   }
140 }