delete some deprecated stuff
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / PsiImplUtil.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl;
17
18 import com.intellij.codeInsight.AnnotationTargetUtil;
19 import com.intellij.codeInsight.AnnotationUtil;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.lang.FileASTNode;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Comparing;
26 import com.intellij.openapi.util.Key;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.psi.*;
30 import com.intellij.psi.filters.ElementFilter;
31 import com.intellij.psi.impl.light.LightClassReference;
32 import com.intellij.psi.impl.light.LightJavaModule;
33 import com.intellij.psi.impl.source.DummyHolder;
34 import com.intellij.psi.impl.source.PsiClassReferenceType;
35 import com.intellij.psi.impl.source.PsiImmediateClassType;
36 import com.intellij.psi.impl.source.resolve.ResolveCache;
37 import com.intellij.psi.impl.source.tree.*;
38 import com.intellij.psi.javadoc.PsiDocComment;
39 import com.intellij.psi.scope.ElementClassHint;
40 import com.intellij.psi.scope.PsiScopeProcessor;
41 import com.intellij.psi.scope.processor.FilterScopeProcessor;
42 import com.intellij.psi.scope.util.PsiScopesUtil;
43 import com.intellij.psi.search.GlobalSearchScope;
44 import com.intellij.psi.search.LocalSearchScope;
45 import com.intellij.psi.search.PackageScope;
46 import com.intellij.psi.search.SearchScope;
47 import com.intellij.psi.tree.IElementType;
48 import com.intellij.psi.tree.TokenSet;
49 import com.intellij.psi.util.PsiTreeUtil;
50 import com.intellij.psi.util.PsiUtil;
51 import com.intellij.psi.util.PsiUtilCore;
52 import com.intellij.util.IncorrectOperationException;
53 import com.intellij.util.PairFunction;
54 import com.intellij.util.containers.ContainerUtil;
55 import org.jetbrains.annotations.NonNls;
56 import org.jetbrains.annotations.NotNull;
57 import org.jetbrains.annotations.Nullable;
58
59 import java.util.Arrays;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
63
64 public class PsiImplUtil {
65   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiImplUtil");
66
67   private PsiImplUtil() { }
68
69   @NotNull
70   public static PsiMethod[] getConstructors(@NotNull PsiClass aClass) {
71     List<PsiMethod> result = null;
72     for (PsiMethod method : aClass.getMethods()) {
73       if (method.isConstructor()) {
74         if (result == null) result = ContainerUtil.newSmartList();
75         result.add(method);
76       }
77     }
78     return result == null ? PsiMethod.EMPTY_ARRAY : result.toArray(new PsiMethod[result.size()]);
79   }
80
81   @Nullable
82   public static PsiAnnotationMemberValue findDeclaredAttributeValue(@NotNull PsiAnnotation annotation, @NonNls @Nullable String attributeName) {
83     PsiNameValuePair attribute = AnnotationUtil.findDeclaredAttribute(annotation, attributeName);
84     return attribute == null ? null : attribute.getValue();
85   }
86
87   @Nullable
88   public static PsiAnnotationMemberValue findAttributeValue(@NotNull PsiAnnotation annotation, @Nullable @NonNls String attributeName) {
89     final PsiAnnotationMemberValue value = findDeclaredAttributeValue(annotation, attributeName);
90     if (value != null) return value;
91
92     if (attributeName == null) attributeName = "value";
93     final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
94     if (referenceElement != null) {
95       PsiElement resolved = referenceElement.resolve();
96       if (resolved != null) {
97         PsiMethod[] methods = ((PsiClass)resolved).findMethodsByName(attributeName, false);
98         for (PsiMethod method : methods) {
99           if (PsiUtil.isAnnotationMethod(method)) {
100             return ((PsiAnnotationMethod)method).getDefaultValue();
101           }
102         }
103       }
104     }
105     return null;
106   }
107
108   @NotNull
109   public static PsiTypeParameter[] getTypeParameters(@NotNull PsiTypeParameterListOwner owner) {
110     final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
111     if (typeParameterList != null) {
112       return typeParameterList.getTypeParameters();
113     }
114     return PsiTypeParameter.EMPTY_ARRAY;
115   }
116
117   @NotNull
118   public static PsiJavaCodeReferenceElement[] namesToPackageReferences(@NotNull PsiManager manager, @NotNull String[] names) {
119     PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[names.length];
120     for (int i = 0; i < names.length; i++) {
121       String name = names[i];
122       try {
123         refs[i] = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createPackageReferenceElement(name);
124       }
125       catch (IncorrectOperationException e) {
126         LOG.error(e);
127       }
128     }
129     return refs;
130   }
131
132   public static int getParameterIndex(@NotNull PsiParameter parameter, @NotNull PsiParameterList parameterList) {
133     PsiElement parameterParent = parameter.getParent();
134     assert parameterParent == parameterList : parameterList +"; "+parameterParent;
135     PsiParameter[] parameters = parameterList.getParameters();
136     for (int i = 0; i < parameters.length; i++) {
137       PsiParameter paramInList = parameters[i];
138       if (parameter.equals(paramInList)) return i;
139     }
140     String name = parameter.getName();
141     PsiParameter suspect = null;
142     int i;
143     for (i = parameters.length - 1; i >= 0; i--) {
144       PsiParameter paramInList = parameters[i];
145       if (Comparing.equal(name, paramInList.getName())) {
146         suspect = paramInList;
147         break;
148       }
149     }
150     String message = parameter + ":" + parameter.getClass() + " not found among parameters: " + Arrays.asList(parameters) + "." +
151                      " parameterList' parent: " + parameterList.getParent() + ";" +
152                      " parameter.isValid()=" + parameter.isValid() + ";" +
153                      " parameterList.isValid()= " + parameterList.isValid() + ";" +
154                      " parameterList stub: " + (parameterList instanceof StubBasedPsiElement ? ((StubBasedPsiElement)parameterList).getStub() : "---") + "; " +
155                      " parameter stub: "+(parameter instanceof StubBasedPsiElement ? ((StubBasedPsiElement)parameter).getStub() : "---") + ";" +
156                      " suspect: " + suspect +" (index="+i+"); " + (suspect==null?null:suspect.getClass()) +
157                      " suspect stub: "+(suspect instanceof StubBasedPsiElement ? ((StubBasedPsiElement)suspect).getStub() : suspect == null ? "-null-" : "---"+suspect.getClass()) + ";" +
158                      " parameter.equals(suspect) = " + parameter.equals(suspect) + "; " +
159                      " parameter.getNode() == suspect.getNode():  " + (parameter.getNode() == (suspect==null ? null : suspect.getNode())) + "; " +
160                      "."
161       ;
162     LOG.error(message);
163     return i;
164   }
165
166   public static int getTypeParameterIndex(@NotNull PsiTypeParameter typeParameter, @NotNull PsiTypeParameterList typeParameterList) {
167     PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
168     for (int i = 0; i < typeParameters.length; i++) {
169       if (typeParameter.equals(typeParameters[i])) return i;
170     }
171     LOG.error(typeParameter + " in " + typeParameterList);
172     return -1;
173   }
174
175   @NotNull
176   public static Object[] getReferenceVariantsByFilter(@NotNull PsiJavaCodeReferenceElement reference, @NotNull ElementFilter filter) {
177     FilterScopeProcessor processor = new FilterScopeProcessor(filter);
178     PsiScopesUtil.resolveAndWalk(processor, reference, null, true);
179     return processor.getResults().toArray();
180   }
181
182   public static boolean processDeclarationsInMethod(@NotNull final PsiMethod method,
183                                                     @NotNull final PsiScopeProcessor processor,
184                                                     @NotNull final ResolveState state,
185                                                     PsiElement lastParent,
186                                                     @NotNull final PsiElement place) {
187     if (lastParent instanceof DummyHolder) lastParent = lastParent.getFirstChild();
188     boolean fromBody = lastParent instanceof PsiCodeBlock;
189     PsiTypeParameterList typeParameterList = method.getTypeParameterList();
190     return processDeclarationsInMethodLike(method, processor, state, place, fromBody, typeParameterList);
191   }
192
193   public static boolean processDeclarationsInLambda(@NotNull final PsiLambdaExpression lambda,
194                                                     @NotNull final PsiScopeProcessor processor,
195                                                     @NotNull final ResolveState state,
196                                                     final PsiElement lastParent,
197                                                     @NotNull final PsiElement place) {
198     final boolean fromBody = lastParent != null && lastParent == lambda.getBody();
199     return processDeclarationsInMethodLike(lambda, processor, state, place, fromBody, null);
200   }
201
202   private static boolean processDeclarationsInMethodLike(@NotNull final PsiParameterListOwner element,
203                                                          @NotNull final PsiScopeProcessor processor,
204                                                          @NotNull final ResolveState state,
205                                                          @NotNull final PsiElement place,
206                                                          final boolean fromBody,
207                                                          @Nullable final PsiTypeParameterList typeParameterList) {
208     processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, element);
209
210     if (typeParameterList != null) {
211       final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
212       if (hint == null || hint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
213         if (!typeParameterList.processDeclarations(processor, state, null, place)) return false;
214       }
215     }
216
217     if (fromBody) {
218       final PsiParameter[] parameters = element.getParameterList().getParameters();
219       for (PsiParameter parameter : parameters) {
220         if (!processor.execute(parameter, state)) return false;
221       }
222     }
223
224     return true;
225   }
226
227   public static boolean processDeclarationsInResourceList(@NotNull final PsiResourceList resourceList,
228                                                           @NotNull final PsiScopeProcessor processor,
229                                                           @NotNull final ResolveState state,
230                                                           final PsiElement lastParent) {
231     final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
232     if (hint != null && !hint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
233
234     for (PsiResourceListElement resource : resourceList) {
235       if (resource == lastParent) break;
236       if (resource instanceof PsiResourceVariable && !processor.execute(resource, state)) return false;
237     }
238
239     return true;
240   }
241
242   public static boolean hasTypeParameters(@NotNull PsiTypeParameterListOwner owner) {
243     final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
244     return typeParameterList != null && typeParameterList.getTypeParameters().length != 0;
245   }
246
247   @NotNull
248   public static PsiType[] typesByReferenceParameterList(@NotNull PsiReferenceParameterList parameterList) {
249     PsiTypeElement[] typeElements = parameterList.getTypeParameterElements();
250
251     return typesByTypeElements(typeElements);
252   }
253
254   @NotNull
255   public static PsiType[] typesByTypeElements(@NotNull PsiTypeElement[] typeElements) {
256     PsiType[] types = PsiType.createArray(typeElements.length);
257     for (int i = 0; i < types.length; i++) {
258       types[i] = typeElements[i].getType();
259     }
260     if (types.length == 1 && types[0] instanceof PsiDiamondType) {
261       return ((PsiDiamondType)types[0]).resolveInferredTypes().getTypes();
262     }
263     return types;
264   }
265
266   @NotNull
267   public static PsiType getType(@NotNull PsiClassObjectAccessExpression classAccessExpression) {
268     GlobalSearchScope resolveScope = classAccessExpression.getResolveScope();
269     PsiManager manager = classAccessExpression.getManager();
270     final PsiClass classClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Class", resolveScope);
271     if (classClass == null) {
272       return new PsiClassReferenceType(new LightClassReference(manager, "Class", "java.lang.Class", resolveScope), null);
273     }
274     if (!PsiUtil.isLanguageLevel5OrHigher(classAccessExpression)) {
275       //Raw java.lang.Class
276       return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(classClass);
277     }
278
279     PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
280     PsiType operandType = classAccessExpression.getOperand().getType();
281     if (operandType instanceof PsiPrimitiveType && !PsiType.NULL.equals(operandType)) {
282       if (PsiType.VOID.equals(operandType)) {
283         operandType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory()
284             .createTypeByFQClassName("java.lang.Void", classAccessExpression.getResolveScope());
285       }
286       else {
287         operandType = ((PsiPrimitiveType)operandType).getBoxedType(classAccessExpression);
288       }
289     }
290     final PsiTypeParameter[] typeParameters = classClass.getTypeParameters();
291     if (typeParameters.length == 1) {
292       substitutor = substitutor.put(typeParameters[0], operandType);
293     }
294
295     return new PsiImmediateClassType(classClass, substitutor);
296   }
297
298   @Nullable
299   public static PsiAnnotation findAnnotation(@Nullable PsiAnnotationOwner annotationOwner, @NotNull String qualifiedName) {
300     if (annotationOwner == null) return null;
301
302     PsiAnnotation[] annotations = annotationOwner.getAnnotations();
303     if (annotations.length == 0) return null;
304
305     String shortName = StringUtil.getShortName(qualifiedName);
306     for (PsiAnnotation annotation : annotations) {
307       PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
308       if (referenceElement != null && shortName.equals(referenceElement.getReferenceName())) {
309         if (qualifiedName.equals(annotation.getQualifiedName())) {
310           return annotation;
311         }
312       }
313     }
314
315     return null;
316   }
317
318   /** @deprecated use {@link AnnotationTargetUtil#findAnnotationTarget(PsiAnnotation, PsiAnnotation.TargetType...)} (to be removed ion IDEA 17) */
319   public static PsiAnnotation.TargetType findApplicableTarget(@NotNull PsiAnnotation annotation, @NotNull PsiAnnotation.TargetType... types) {
320     return AnnotationTargetUtil.findAnnotationTarget(annotation, types);
321   }
322
323   /** @deprecated use {@link AnnotationTargetUtil#findAnnotationTarget(PsiClass, PsiAnnotation.TargetType...)} (to be removed ion IDEA 17) */
324   public static PsiAnnotation.TargetType findApplicableTarget(@NotNull PsiClass annotationType, @NotNull PsiAnnotation.TargetType... types) {
325     return AnnotationTargetUtil.findAnnotationTarget(annotationType, types);
326   }
327
328   /** @deprecated use {@link AnnotationTargetUtil#getAnnotationTargets(PsiClass)} (to be removed ion IDEA 17) */
329   public static Set<PsiAnnotation.TargetType> getAnnotationTargets(@NotNull PsiClass annotationType) {
330     return AnnotationTargetUtil.getAnnotationTargets(annotationType);
331   }
332
333   /** @deprecated use {@link AnnotationTargetUtil#getTargetsForLocation(PsiAnnotationOwner)} (to be removed ion IDEA 17) */
334   public static PsiAnnotation.TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) {
335     return AnnotationTargetUtil.getTargetsForLocation(owner);
336   }
337
338   @Nullable
339   public static ASTNode findDocComment(@NotNull CompositeElement element) {
340     TreeElement node = element.getFirstChildNode();
341     while (node != null && isWhitespaceOrComment(node) && !(node.getPsi() instanceof PsiDocComment)) {
342       node = node.getTreeNext();
343     }
344
345     return node == null || node.getElementType() != JavaDocElementType.DOC_COMMENT ? null : node;
346   }
347
348   /**
349    * Types should be proceed by the callers themselves
350    */
351   @Deprecated
352   public static PsiType normalizeWildcardTypeByPosition(@NotNull PsiType type, @NotNull PsiExpression expression) {
353     PsiUtilCore.ensureValid(expression);
354     PsiUtil.ensureValidType(type);
355
356     PsiExpression topLevel = expression;
357     while (topLevel.getParent() instanceof PsiArrayAccessExpression &&
358            ((PsiArrayAccessExpression)topLevel.getParent()).getArrayExpression() == topLevel) {
359       topLevel = (PsiExpression)topLevel.getParent();
360     }
361
362     if (topLevel instanceof PsiArrayAccessExpression && !PsiUtil.isAccessedForWriting(topLevel)) {
363       return PsiUtil.captureToplevelWildcards(type, expression);
364     }
365
366     final PsiType normalized = doNormalizeWildcardByPosition(type, expression, topLevel);
367     LOG.assertTrue(normalized.isValid(), type);
368     if (normalized instanceof PsiClassType && !PsiUtil.isAccessedForWriting(topLevel)) {
369       return PsiUtil.captureToplevelWildcards(normalized, expression);
370     }
371
372     return normalized;
373   }
374
375   private static PsiType doNormalizeWildcardByPosition(PsiType type, @NotNull PsiExpression expression, @NotNull PsiExpression topLevel) {
376     if (type instanceof PsiWildcardType) {
377       final PsiWildcardType wildcardType = (PsiWildcardType)type;
378
379       if (PsiUtil.isAccessedForWriting(topLevel)) {
380         return wildcardType.isSuper() ? wildcardType.getBound() : PsiCapturedWildcardType.create(wildcardType, expression);
381       }
382       else {
383         if (wildcardType.isExtends()) {
384           return wildcardType.getBound();
385         }
386         return PsiType.getJavaLangObject(expression.getManager(), expression.getResolveScope());
387       }
388     }
389     if (type instanceof PsiArrayType) {
390       final PsiType componentType = ((PsiArrayType)type).getComponentType();
391       final PsiType normalizedComponentType = doNormalizeWildcardByPosition(componentType, expression, topLevel);
392       if (normalizedComponentType != componentType) {
393         return normalizedComponentType.createArrayType();
394       }
395     }
396
397     return type;
398   }
399
400   @NotNull
401   public static SearchScope getMemberUseScope(@NotNull PsiMember member) {
402     PsiFile file = member.getContainingFile();
403     PsiElement topElement = file == null ? member : file;
404     Project project = topElement.getProject();
405     final GlobalSearchScope maximalUseScope = ResolveScopeManager.getInstance(project).getUseScope(topElement);
406     if (isInServerPage(file)) return maximalUseScope;
407
408     PsiClass aClass = member.getContainingClass();
409     if (aClass instanceof PsiAnonymousClass && !(aClass instanceof PsiEnumConstantInitializer &&
410                                                  member instanceof PsiMethod &&
411                                                  member.hasModifierProperty(PsiModifier.PUBLIC) &&
412                                                  ((PsiMethod)member).findSuperMethods().length > 0)) {
413       //member from anonymous class can be called from outside the class
414       PsiElement methodCallExpr = PsiUtil.isLanguageLevel8OrHigher(aClass) ? PsiTreeUtil.getTopmostParentOfType(aClass, PsiStatement.class) 
415                                                                            : PsiTreeUtil.getParentOfType(aClass, PsiMethodCallExpression.class);
416       return new LocalSearchScope(methodCallExpr != null ? methodCallExpr : aClass);
417     }
418
419     PsiModifierList modifierList = member.getModifierList();
420     int accessLevel = modifierList == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList);
421     if (accessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC || accessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
422       return maximalUseScope; // class use scope doesn't matter, since another very visible class can inherit from aClass
423     }
424     if (accessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) {
425       PsiClass topClass = PsiUtil.getTopLevelClass(member);
426       return topClass != null ? new LocalSearchScope(topClass) : file == null ? maximalUseScope : new LocalSearchScope(file);
427     }
428     if (file instanceof PsiJavaFile) {
429       PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(((PsiJavaFile)file).getPackageName());
430       if (aPackage != null) {
431         SearchScope scope = PackageScope.packageScope(aPackage, false);
432         return scope.intersectWith(maximalUseScope);
433       }
434     }
435     return maximalUseScope;
436   }
437
438   public static boolean isInServerPage(@Nullable final PsiElement element) {
439     return getServerPageFile(element) != null;
440   }
441
442   @Nullable
443   private static ServerPageFile getServerPageFile(final PsiElement element) {
444     final PsiFile psiFile = PsiUtilCore.getTemplateLanguageFile(element);
445     return psiFile instanceof ServerPageFile ? (ServerPageFile)psiFile : null;
446   }
447
448   public static PsiElement setName(@NotNull PsiElement element, @NotNull String name) throws IncorrectOperationException {
449     PsiManager manager = element.getManager();
450     PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
451     PsiIdentifier newNameIdentifier = factory.createIdentifier(name);
452     return element.replace(newNameIdentifier);
453   }
454
455   public static boolean isDeprecatedByAnnotation(@NotNull PsiModifierListOwner owner) {
456     return AnnotationUtil.findAnnotation(owner, CommonClassNames.JAVA_LANG_DEPRECATED) != null;
457   }
458
459   public static boolean isDeprecatedByDocTag(@NotNull PsiJavaDocumentedElement owner) {
460     PsiDocComment docComment = owner.getDocComment();
461     return docComment != null && docComment.findTagByName("deprecated") != null;
462   }
463
464   @Nullable
465   public static PsiJavaDocumentedElement findDocCommentOwner(@NotNull PsiDocComment comment) {
466     PsiElement parent = comment.getParent();
467     if (parent instanceof PsiJavaDocumentedElement) {
468       PsiJavaDocumentedElement owner = (PsiJavaDocumentedElement)parent;
469       if (owner.getDocComment() == comment) {
470         return owner;
471       }
472     }
473     return null;
474   }
475
476   @Nullable
477   public static PsiAnnotationMemberValue setDeclaredAttributeValue(@NotNull PsiAnnotation psiAnnotation,
478                                                                    @Nullable String attributeName,
479                                                                    @Nullable PsiAnnotationMemberValue value,
480                                                                    @NotNull PairFunction<Project, String, PsiAnnotation> annotationCreator) {
481     PsiAnnotationMemberValue existing = psiAnnotation.findDeclaredAttributeValue(attributeName);
482     if (value == null) {
483       if (existing == null) {
484         return null;
485       }
486       existing.getParent().delete();
487     }
488     else {
489       if (existing != null) {
490         ((PsiNameValuePair)existing.getParent()).setValue(value);
491       }
492       else {
493         PsiNameValuePair[] attributes = psiAnnotation.getParameterList().getAttributes();
494         if (attributes.length == 1) {
495           PsiNameValuePair attribute = attributes[0];
496           if (attribute.getName() == null) {
497             PsiAnnotationMemberValue defValue = attribute.getValue();
498             assert defValue != null : attribute;
499             attribute.replace(createNameValuePair(defValue, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME + "=", annotationCreator));
500           }
501         }
502
503         boolean allowNoName = attributes.length == 0 && ("value".equals(attributeName) || null == attributeName);
504         final String namePrefix = allowNoName ? "" : attributeName + "=";
505         psiAnnotation.getParameterList().addBefore(createNameValuePair(value, namePrefix, annotationCreator), null);
506       }
507     }
508     return psiAnnotation.findDeclaredAttributeValue(attributeName);
509   }
510
511   private static PsiNameValuePair createNameValuePair(@NotNull PsiAnnotationMemberValue value,
512                                                      @NotNull String namePrefix,
513                                                      @NotNull PairFunction<Project, String, PsiAnnotation> annotationCreator) {
514     return annotationCreator.fun(value.getProject(), "@A(" + namePrefix + value.getText() + ")").getParameterList().getAttributes()[0];
515   }
516
517   @Nullable
518   public static ASTNode skipWhitespaceAndComments(final ASTNode node) {
519     return TreeUtil.skipWhitespaceAndComments(node, true);
520   }
521
522   @Nullable
523   public static ASTNode skipWhitespaceCommentsAndTokens(final ASTNode node, @NotNull TokenSet alsoSkip) {
524     return TreeUtil.skipWhitespaceCommentsAndTokens(node, alsoSkip, true);
525   }
526
527   public static boolean isWhitespaceOrComment(ASTNode element) {
528     return TreeUtil.isWhitespaceOrComment(element);
529   }
530
531   @Nullable
532   public static ASTNode skipWhitespaceAndCommentsBack(final ASTNode node) {
533     if (node == null) return null;
534     if (!isWhitespaceOrComment(node)) return node;
535
536     ASTNode parent = node.getTreeParent();
537     ASTNode prev = node;
538     while (prev instanceof CompositeElement) {
539       if (!isWhitespaceOrComment(prev)) return prev;
540       prev = prev.getTreePrev();
541     }
542     if (prev == null) return null;
543     ASTNode firstChildNode = parent.getFirstChildNode();
544     ASTNode lastRelevant = null;
545     while (firstChildNode != prev) {
546       if (!isWhitespaceOrComment(firstChildNode)) lastRelevant = firstChildNode;
547       firstChildNode = firstChildNode.getTreeNext();
548     }
549     return lastRelevant;
550   }
551
552   @Nullable
553   public static ASTNode findStatementChild(@NotNull CompositePsiElement statement) {
554     if (DebugUtil.CHECK_INSIDE_ATOMIC_ACTION_ENABLED) {
555       ApplicationManager.getApplication().assertReadAccessAllowed();
556     }
557     for (ASTNode element = statement.getFirstChildNode(); element != null; element = element.getTreeNext()) {
558       if (element.getPsi() instanceof PsiStatement) return element;
559     }
560     return null;
561   }
562
563   public static PsiStatement[] getChildStatements(@NotNull CompositeElement psiCodeBlock) {
564     ApplicationManager.getApplication().assertReadAccessAllowed();
565     // no lock is needed because all chameleons are expanded already
566     int count = 0;
567     for (ASTNode child1 = psiCodeBlock.getFirstChildNode(); child1 != null; child1 = child1.getTreeNext()) {
568       if (child1.getPsi() instanceof PsiStatement) {
569         count++;
570       }
571     }
572
573     PsiStatement[] result = PsiStatement.ARRAY_FACTORY.create(count);
574     if (count == 0) return result;
575     int idx = 0;
576     for (ASTNode child = psiCodeBlock.getFirstChildNode(); child != null && idx < count; child = child.getTreeNext()) {
577       PsiElement element = child.getPsi();
578       if (element instanceof PsiStatement) {
579         result[idx++] = (PsiStatement)element;
580       }
581     }
582     return result;
583   }
584
585   public static boolean isVarArgs(@NotNull PsiMethod method) {
586     PsiParameter[] parameters = method.getParameterList().getParameters();
587     return parameters.length > 0 && parameters[parameters.length - 1].isVarArgs();
588   }
589
590   public static PsiElement handleMirror(PsiElement element) {
591     return element instanceof PsiMirrorElement ? ((PsiMirrorElement)element).getPrototype() : element;
592   }
593
594   @Nullable
595   public static PsiModifierList findNeighbourModifierList(@NotNull PsiJavaCodeReferenceElement ref) {
596     PsiElement parent = PsiTreeUtil.skipParentsOfType(ref, PsiJavaCodeReferenceElement.class);
597     if (parent instanceof PsiTypeElement) {
598       PsiElement grandParent = parent.getParent();
599       if (grandParent instanceof PsiModifierListOwner) {
600         return ((PsiModifierListOwner)grandParent).getModifierList();
601       }
602     }
603
604     return null;
605   }
606
607   public static boolean isTypeAnnotation(@Nullable PsiElement element) {
608     return element instanceof PsiAnnotation && AnnotationTargetUtil.isTypeAnnotation((PsiAnnotation)element);
609   }
610
611   public static void collectTypeUseAnnotations(@NotNull PsiModifierList modifierList, @NotNull List<PsiAnnotation> annotations) {
612     for (PsiAnnotation annotation : modifierList.getAnnotations()) {
613       if (AnnotationTargetUtil.isTypeAnnotation(annotation)) {
614         annotations.add(annotation);
615       }
616     }
617   }
618
619   private static final Key<Boolean> TYPE_ANNO_MARK = Key.create("type.annotation.mark");
620
621   public static void markTypeAnnotations(@NotNull PsiTypeElement typeElement) {
622     PsiElement left = PsiTreeUtil.skipSiblingsBackward(typeElement, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
623     if (left instanceof PsiModifierList) {
624       for (PsiAnnotation annotation : ((PsiModifierList)left).getAnnotations()) {
625         if (AnnotationTargetUtil.isTypeAnnotation(annotation)) {
626           annotation.putUserData(TYPE_ANNO_MARK, Boolean.TRUE);
627         }
628       }
629     }
630   }
631
632   public static void deleteTypeAnnotations(@NotNull PsiTypeElement typeElement) {
633     PsiElement left = PsiTreeUtil.skipSiblingsBackward(typeElement, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
634     if (left instanceof PsiModifierList) {
635       for (PsiAnnotation annotation : ((PsiModifierList)left).getAnnotations()) {
636         if (TYPE_ANNO_MARK.get(annotation) == Boolean.TRUE) {
637           annotation.delete();
638         }
639       }
640     }
641   }
642
643   public static boolean isLeafElementOfType(@Nullable PsiElement element, @NotNull IElementType type) {
644     return element instanceof LeafElement && ((LeafElement)element).getElementType() == type;
645   }
646
647   public static boolean isLeafElementOfType(PsiElement element, @NotNull TokenSet tokenSet) {
648     return element instanceof LeafElement && tokenSet.contains(((LeafElement)element).getElementType());
649   }
650
651   public static PsiType buildTypeFromTypeString(@NotNull final String typeName, @NotNull final PsiElement context, @NotNull final PsiFile psiFile) {
652     final PsiManager psiManager = psiFile.getManager();
653
654     if (typeName.indexOf('<') != -1 || typeName.indexOf('[') != -1 || typeName.indexOf('.') == -1) {
655       try {
656         return JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createTypeFromText(typeName, context);
657       }
658       catch(Exception ignored) { } // invalid syntax will produce unresolved class type
659     }
660
661     PsiClass aClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(typeName, context.getResolveScope());
662
663     PsiType resultType;
664     if (aClass == null) {
665       final LightClassReference ref = new LightClassReference(
666         psiManager,
667         PsiNameHelper.getShortClassName(typeName),
668         typeName,
669         PsiSubstitutor.EMPTY,
670         psiFile
671       );
672       resultType = new PsiClassReferenceType(ref, null);
673     } else {
674       PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
675       PsiSubstitutor substitutor = factory.createRawSubstitutor(aClass);
676       resultType = factory.createType(aClass, substitutor);
677     }
678
679     return resultType;
680   }
681
682   @NotNull
683   public static <T extends PsiJavaCodeReferenceElement> JavaResolveResult[] multiResolveImpl(@NotNull T element,
684                                                                                              boolean incompleteCode,
685                                                                                              @NotNull ResolveCache.PolyVariantContextResolver<? super T> resolver) {
686
687     FileASTNode fileElement = SharedImplUtil.findFileElement(element.getNode());
688     if (fileElement == null) {
689       PsiUtilCore.ensureValid(element);
690       LOG.error("fileElement == null!");
691       return JavaResolveResult.EMPTY_ARRAY;
692     }
693     PsiFile psiFile = SharedImplUtil.getContainingFile(fileElement);
694     PsiManager manager = psiFile == null ? null : psiFile.getManager();
695     if (manager == null) {
696       PsiUtilCore.ensureValid(element);
697       LOG.error("getManager() == null!");
698       return JavaResolveResult.EMPTY_ARRAY;
699     }
700     boolean valid = psiFile.isValid();
701     if (!valid) {
702       PsiUtilCore.ensureValid(element);
703       LOG.error("psiFile.isValid() == false!");
704       return JavaResolveResult.EMPTY_ARRAY;
705     }
706     if (element instanceof PsiMethodReferenceExpression) {
707       // method refs: do not cache results during parent conflict resolving, acceptable checks, etc
708       final Map<PsiElement, PsiType> map = LambdaUtil.ourFunctionTypes.get();
709       if (map != null && map.containsKey(element)) {
710         return (JavaResolveResult[])resolver.resolve(element, psiFile, incompleteCode);
711       }
712     }
713
714     return multiResolveImpl(manager.getProject(), psiFile, element, incompleteCode, resolver);
715   }
716
717   public static <T extends PsiJavaCodeReferenceElement> JavaResolveResult[] multiResolveImpl(@NotNull Project project,
718                                                                                              @NotNull PsiFile psiFile,
719                                                                                              @NotNull T element,
720                                                                                              boolean incompleteCode,
721                                                                                              @NotNull ResolveCache.PolyVariantContextResolver<? super T> resolver) {
722
723     ResolveResult[] results =
724       ResolveCache.getInstance(project).resolveWithCaching(element, resolver, true, incompleteCode, psiFile);
725     return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
726   }
727
728   public static VirtualFile getModuleVirtualFile(@NotNull PsiJavaModule module) {
729     if (module instanceof LightJavaModule) {
730       return ((LightJavaModule)module).getRootVirtualFile();
731     }
732     else {
733       return module.getContainingFile().getVirtualFile();
734     }
735   }
736 }