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