summaryrefslogtreecommitdiff
path: root/java/java-psi-api/src/com/intellij/psi/util/ClassUtil.java
blob: 9f7f6e2cc164e2ec69b98ae917b4cceb7a4792a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.psi.util;

import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class ClassUtil {
  private ClassUtil() {}

  public static String extractPackageName(String className) {
    if (className != null) {
      int i = className.lastIndexOf('.');
      return i == -1 ? "" : className.substring(0, i);                                

    }
    return null;
  }

  public static String extractClassName(@NotNull String fqName) {
    int i = fqName.lastIndexOf('.');
    return i == -1 ? fqName : fqName.substring(i + 1);
  }

  public static String createNewClassQualifiedName(String qualifiedName, String className) {
    if (className == null){
      return null;
    }
    if (qualifiedName == null || qualifiedName.isEmpty()){
      return className;
    }
    return qualifiedName + "." + extractClassName(className);
  }

  public static PsiDirectory sourceRoot(PsiDirectory containingDirectory) {
    while (containingDirectory != null) {
      if (JavaDirectoryService.getInstance().isSourceRoot(containingDirectory)) {
        return containingDirectory;
      }
      containingDirectory = containingDirectory.getParentDirectory();
    }
    return null;
  }

  public static void formatClassName(@NotNull final PsiClass aClass, final StringBuilder buf) {
    final String qName = aClass.getQualifiedName();
    if (qName != null) {
      buf.append(qName);
    }
    else {
      final PsiClass parentClass = getContainerClass(aClass);
      if (parentClass != null) {
        formatClassName(parentClass, buf);
        buf.append("$");
        buf.append(getNonQualifiedClassIdx(aClass));
        final String name = aClass.getName();
        if (name != null) {
          buf.append(name);
        }
      }
    }
  }

  @Nullable
  private static PsiClass getContainerClass(final PsiClass aClass) {
    PsiElement parent = aClass.getContext();
    while (parent != null && !(parent instanceof PsiClass)) {
      parent = parent.getContext();
    }
    return (PsiClass)parent;
  }

  public static int getNonQualifiedClassIdx(@NotNull final PsiClass psiClass) {
    final int[] result = {-1};
    final PsiClass containingClass = getContainerClass(psiClass);
    if (containingClass != null) {
      containingClass.accept(new JavaRecursiveElementVisitor() {
        private int myCurrentIdx = 0;

        @Override public void visitElement(PsiElement element) {
          if (result[0] == -1) {
            super.visitElement(element);
          }
        }

        @Override public void visitClass(PsiClass aClass) {
          super.visitClass(aClass);
          if (aClass.getQualifiedName() == null) {
            myCurrentIdx++;
            if (psiClass == aClass) {
              result[0] = myCurrentIdx;
            }
          }
        }
      });
    }
    return result[0];
  }

  public static PsiClass findNonQualifiedClassByIndex(final String indexName, @NotNull final PsiClass containingClass) {
    return findNonQualifiedClassByIndex(indexName, containingClass, false);
  }

  public static PsiClass findNonQualifiedClassByIndex(final String indexName, @NotNull final PsiClass containingClass,
                                                      final boolean jvmCompatible) {
    String prefix = getDigitPrefix(indexName);
    final int idx = !prefix.isEmpty() ? Integer.parseInt(prefix) : -1;
    final String name = prefix.length() < indexName.length() ? indexName.substring(prefix.length()) : null;
    final PsiClass[] result = new PsiClass[1];
    containingClass.accept(new JavaRecursiveElementVisitor() {
      private int myCurrentIdx = 0;

      @Override public void visitElement(PsiElement element) {
        if (result[0] == null) {
          super.visitElement(element);
        }
      }

      @Override public void visitClass(PsiClass aClass) {
        if (!jvmCompatible) {
          super.visitClass(aClass);
          if (aClass.getQualifiedName() == null) {
            myCurrentIdx++;
            if (myCurrentIdx == idx && Comparing.strEqual(name, aClass.getName())) {
              result[0] = aClass;
            }
          }
          return;
        }
        if (aClass == containingClass) {
          super.visitClass(aClass);
          return;
        }
        if (Comparing.strEqual(name, aClass.getName())) {
          myCurrentIdx++;
          if (myCurrentIdx == idx || idx == -1) {
            result[0] = aClass;
          }
        }
      }

      @Override public void visitTypeParameter(final PsiTypeParameter classParameter) {
        if (!jvmCompatible) {
          super.visitTypeParameter(classParameter);
        }
        else {
          visitElement(classParameter);
        }
      }
    });
    return result[0];
  }

  private static String getDigitPrefix(@NotNull String indexName) {
    int i;
    for (i = 0; i < indexName.length(); i++) {
      final char c = indexName.charAt(i);
      if (!Character.isDigit(c)) {
        break;
      }
    }
    return i == 0 ? "" : indexName.substring(0, i);
  }


  /**
   * Finds anonymous classes. Uses javac notation.
   * @param psiManager project to search
   * @param externalName class qualified name
   * @return found psiClass
   */
  @Nullable
  public static PsiClass findPsiClass(final PsiManager psiManager, String externalName){
    return findPsiClass(psiManager, externalName, null, false);
  }

  @Nullable
  public static PsiClass findPsiClass(final PsiManager psiManager,
                                      String externalName,
                                      PsiClass psiClass,
                                      boolean jvmCompatible) {
    return findPsiClass(psiManager, externalName, psiClass, jvmCompatible, GlobalSearchScope.allScope(psiManager.getProject()));
  }

  @Nullable
  public static PsiClass findPsiClass(final PsiManager psiManager,
                                      String externalName,
                                      @Nullable PsiClass psiClass,
                                      boolean jvmCompatible, 
                                      final GlobalSearchScope scope) {
    for (int pos = 0; pos < externalName.length(); pos++) {
      if (externalName.charAt(pos) == '$') {
        PsiClass parentClass = psiClass;
        if (parentClass == null) {
          parentClass = JavaPsiFacade.getInstance(psiManager.getProject())
            .findClass(externalName.substring(0, pos), scope);
        }
        if (parentClass == null) continue;
        PsiClass res = findSubclass(psiManager, externalName.substring(pos + 1), parentClass, jvmCompatible);
        if (res != null) return res;
      }
    }
    return JavaPsiFacade.getInstance(psiManager.getProject()).findClass(externalName, scope);
  }

  @Nullable
  private static PsiClass findSubclass(final PsiManager psiManager,
                                       final String externalName,
                                       final PsiClass psiClass,
                                       final boolean jvmCompatible) {
    for (int pos = 0; pos < externalName.length(); pos++) {
      if (externalName.charAt(pos) == '$') {
        PsiClass anonymousClass = findNonQualifiedClassByIndex(externalName.substring(0, pos), psiClass, jvmCompatible);
        if (anonymousClass == null) return null;
        PsiClass res = findPsiClass(psiManager, externalName.substring(pos), anonymousClass, jvmCompatible);
        if (res != null) return res;
      }
    }
    return findNonQualifiedClassByIndex(externalName, psiClass, jvmCompatible);
  }

  @Nullable
  public static String getJVMClassName(@NotNull PsiClass aClass) {
    final PsiClass containingClass = aClass.getContainingClass();
    if (containingClass != null) {
      String parentName = getJVMClassName(containingClass);
      if (parentName == null) {
        return null;
      }
      
      return parentName + "$" + aClass.getName();
    }
    return aClass.getQualifiedName();
  }


  @Nullable
  public static PsiClass findPsiClassByJVMName(final PsiManager manager, final String jvmClassName) {
    return findPsiClass(manager, jvmClassName.replace('/', '.'), null, true);
  }
}