d1fa39927abf661f40076bf3af9d8fe4bc53920d
[idea/community.git] / java / structuralsearch-java / src / com / intellij / structuralsearch / impl / matcher / JavaMatchingVisitor.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.structuralsearch.impl.matcher;
17
18 import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
19 import com.intellij.dupLocator.iterators.NodeIterator;
20 import com.intellij.openapi.util.text.StringUtil;
21 import com.intellij.psi.*;
22 import com.intellij.psi.javadoc.PsiDocComment;
23 import com.intellij.psi.javadoc.PsiDocTag;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import com.intellij.psi.util.PsiUtil;
26 import com.intellij.structuralsearch.MatchOptions;
27 import com.intellij.structuralsearch.MatchResult;
28 import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
29 import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate;
30 import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
31 import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
32 import com.intellij.structuralsearch.impl.matcher.iterators.DocValuesIterator;
33 import com.intellij.structuralsearch.impl.matcher.iterators.HierarchyNodeIterator;
34 import com.intellij.structuralsearch.impl.matcher.predicates.NotPredicate;
35 import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate;
36 import com.intellij.util.containers.ContainerUtil;
37 import org.jetbrains.annotations.Contract;
38 import org.jetbrains.annotations.NotNull;
39
40 import java.util.*;
41
42 /**
43  * @author Eugene.Kudelevsky
44  */
45 public class JavaMatchingVisitor extends JavaElementVisitor {
46   public static final String[] MODIFIERS = {
47     PsiModifier.PUBLIC, PsiModifier.PROTECTED, PsiModifier.PRIVATE, PsiModifier.STATIC, PsiModifier.ABSTRACT, PsiModifier.FINAL,
48     PsiModifier.NATIVE, PsiModifier.SYNCHRONIZED, PsiModifier.STRICTFP, PsiModifier.TRANSIENT, PsiModifier.VOLATILE, PsiModifier.DEFAULT
49   };
50   private final GlobalMatchingVisitor myMatchingVisitor;
51   private PsiClass myClazz;
52
53   static {
54     Arrays.sort(MODIFIERS);
55   }
56
57   public JavaMatchingVisitor(GlobalMatchingVisitor matchingVisitor) {
58     this.myMatchingVisitor = matchingVisitor;
59   }
60
61   @Override
62   public void visitComment(PsiComment comment) {
63     PsiComment comment2 = null;
64
65     if (!(myMatchingVisitor.getElement() instanceof PsiComment)) {
66       if (myMatchingVisitor.getElement() instanceof PsiMember) {
67         final PsiElement[] children = myMatchingVisitor.getElement().getChildren();
68         if (children[0] instanceof PsiComment) {
69           comment2 = (PsiComment)children[0];
70         }
71       }
72     }
73     else {
74       comment2 = (PsiComment)myMatchingVisitor.getElement();
75     }
76
77     if (comment2 == null) {
78       myMatchingVisitor.setResult(false);
79       return;
80     }
81
82     final Object userData = comment.getUserData(CompiledPattern.HANDLER_KEY);
83
84     if (userData instanceof String) {
85       final String str = (String)userData;
86       int end = comment2.getTextLength();
87
88       if (comment2.getTokenType() == JavaTokenType.C_STYLE_COMMENT) {
89         end -= 2;
90       }
91       myMatchingVisitor.setResult(((SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(str)).handle(
92         comment2,
93         2,
94         end,
95         myMatchingVisitor.getMatchContext()
96       ));
97     }
98     else if (userData instanceof MatchingHandler) {
99       myMatchingVisitor.setResult(((MatchingHandler)userData).match(comment, comment2, myMatchingVisitor.getMatchContext()));
100     }
101     else {
102       myMatchingVisitor.setResult(myMatchingVisitor.matchText(comment, comment2));
103     }
104   }
105
106   @Override
107   public final void visitModifierList(final PsiModifierList list) {
108     final PsiModifierList list2 = (PsiModifierList)myMatchingVisitor.getElement();
109
110     for (@PsiModifier.ModifierConstant String modifier : MODIFIERS) {
111       if (list.hasModifierProperty(modifier) && !list2.hasModifierProperty(modifier)) {
112         myMatchingVisitor.setResult(false);
113         return;
114       }
115     }
116
117     final PsiAnnotation[] annotations = list.getAnnotations();
118     if (annotations.length > 0) {
119       HashSet<PsiAnnotation> set = new HashSet<>(Arrays.asList(annotations));
120
121       for (PsiAnnotation annotation : annotations) {
122         final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement();
123
124         if (nameReferenceElement != null && MatchOptions.MODIFIER_ANNOTATION_NAME.equals(nameReferenceElement.getText())) {
125           final PsiAnnotationParameterList parameterList = annotation.getParameterList();
126           final PsiNameValuePair[] attributes = parameterList.getAttributes();
127
128           for (PsiNameValuePair pair : attributes) {
129             final PsiAnnotationMemberValue value = pair.getValue();
130             if (value == null) continue;
131
132             if (value instanceof PsiArrayInitializerMemberValue) {
133               boolean matchedOne = false;
134
135               for (PsiAnnotationMemberValue v : ((PsiArrayInitializerMemberValue)value).getInitializers()) {
136                 if (annotationValueMatchesModifierList(list2, v)) {
137                   matchedOne = true;
138                   break;
139                 }
140               }
141
142               if (!matchedOne) {
143                 myMatchingVisitor.setResult(false);
144                 return;
145               }
146             }
147             else {
148               if (!annotationValueMatchesModifierList(list2, value)) {
149                 myMatchingVisitor.setResult(false);
150                 return;
151               }
152             }
153           }
154
155           set.remove(annotation);
156         }
157       }
158
159       myMatchingVisitor.setResult(set.isEmpty() ||
160                                   myMatchingVisitor.matchInAnyOrder(set.toArray(new PsiAnnotation[set.size()]), list2.getAnnotations()));
161     }
162     else {
163       myMatchingVisitor.setResult(true);
164     }
165   }
166
167   private static boolean annotationValueMatchesModifierList(PsiModifierList list2, PsiAnnotationMemberValue value) {
168     @PsiModifier.ModifierConstant final String name = StringUtil.unquoteString(value.getText());
169     if (MatchOptions.INSTANCE_MODIFIER_NAME.equals(name)) {
170       return !list2.hasModifierProperty(PsiModifier.STATIC) && !list2.hasModifierProperty(PsiModifier.ABSTRACT) &&
171              list2.getParent() instanceof PsiMember;
172     }
173     return list2.hasModifierProperty(name) && (!PsiModifier.PACKAGE_LOCAL.equals(name) || list2.getParent() instanceof PsiMember);
174   }
175
176   @Override
177   public void visitDocTag(final PsiDocTag tag) {
178     final PsiDocTag tag2 = (PsiDocTag)myMatchingVisitor.getElement();
179     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(tag.getNameElement());
180
181     myMatchingVisitor.setResult(isTypedVar || tag.getName().equals(tag2.getName()));
182
183     PsiElement psiDocTagValue = tag.getValueElement();
184     boolean isTypedValue = false;
185
186     if (myMatchingVisitor.getResult() && psiDocTagValue != null) {
187       final PsiElement[] children = psiDocTagValue.getChildren();
188       if (children.length == 1) {
189         psiDocTagValue = children[0];
190       }
191       isTypedValue = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(psiDocTagValue);
192
193       if (isTypedValue) {
194         if (tag2.getValueElement() != null) {
195           myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(psiDocTagValue, tag2.getValueElement()));
196         }
197         else {
198           myMatchingVisitor.setResult(myMatchingVisitor.allowsAbsenceOfMatch(psiDocTagValue));
199         }
200       }
201     }
202
203     if (myMatchingVisitor.getResult() && !isTypedValue) {
204       myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(
205         new DocValuesIterator(tag.getFirstChild()),
206         new DocValuesIterator(tag2.getFirstChild())
207       ));
208     }
209
210     if (myMatchingVisitor.getResult() && isTypedVar) {
211       myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(tag.getNameElement(), tag2.getNameElement()));
212     }
213   }
214
215   @Override
216   public void visitDocComment(final PsiDocComment comment) {
217     PsiDocComment comment2;
218
219     if (myMatchingVisitor.getElement() instanceof PsiDocCommentOwner) {
220       comment2 = ((PsiDocCommentOwner)myMatchingVisitor.getElement()).getDocComment();
221
222       if (comment2 == null) {
223         // doc comment are not collapsed for inner classes!
224         myMatchingVisitor.setResult(false);
225         return;
226       }
227     }
228     else {
229       comment2 = (PsiDocComment)myMatchingVisitor.getElement();
230
231       if (myMatchingVisitor.getElement().getParent() instanceof PsiDocCommentOwner) {
232         myMatchingVisitor.setResult(false);
233         return; // we should matched the doc before
234       }
235     }
236
237     if (comment.getTags().length > 0) {
238       myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(comment.getTags(), comment2.getTags()));
239     }
240     else {
241       visitComment(comment);
242     }
243   }
244
245   @Override
246   public void visitElement(PsiElement el) {
247     myMatchingVisitor.setResult(myMatchingVisitor.matchText(el, myMatchingVisitor.getElement()));
248   }
249
250   @Override
251   public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
252     final PsiArrayInitializerExpression expr2 = (PsiArrayInitializerExpression)myMatchingVisitor.getElement();
253
254     myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
255       new ArrayBackedNodeIterator(expression.getInitializers()),
256       new ArrayBackedNodeIterator(expr2.getInitializers())
257     ));
258   }
259
260   @Override
261   public void visitClassInitializer(PsiClassInitializer initializer) {
262     PsiClassInitializer initializer2 = (PsiClassInitializer)myMatchingVisitor.getElement();
263     myMatchingVisitor.setResult(myMatchingVisitor.match(initializer.getModifierList(), initializer2.getModifierList()) &&
264                                 myMatchingVisitor.match(initializer.getBody(), initializer2.getBody()));
265   }
266
267   @Override
268   public void visitCodeBlock(PsiCodeBlock block) {
269     myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, myMatchingVisitor.getElement()));
270   }
271
272   @Override
273   public void visitJavaToken(final PsiJavaToken token) {
274     PsiElement element = myMatchingVisitor.getElement();
275     boolean result;
276
277     if (!(element instanceof PsiJavaToken)) {
278       result = myMatchingVisitor.matchText(token, element);
279     } else {
280       final PsiJavaToken anotherToken = (PsiJavaToken)element;
281
282       result = token.getTokenType() == anotherToken.getTokenType() && myMatchingVisitor.matchText(token, anotherToken);
283     }
284
285     myMatchingVisitor.setResult(result);
286   }
287
288   @Override
289   public void visitAnnotation(PsiAnnotation annotation) {
290     final PsiAnnotation psiAnnotation = (PsiAnnotation)myMatchingVisitor.getElement();
291
292     myMatchingVisitor.setResult(myMatchingVisitor.match(annotation.getNameReferenceElement(), psiAnnotation.getNameReferenceElement()) &&
293                                 myMatchingVisitor
294                                   .matchInAnyOrder(annotation.getParameterList().getAttributes(),
295                                                    psiAnnotation.getParameterList().getAttributes()));
296   }
297
298   @Override
299   public void visitNameValuePair(PsiNameValuePair pair) {
300     final PsiNameValuePair elementNameValuePair = (PsiNameValuePair)myMatchingVisitor.getElement();
301
302     final PsiAnnotationMemberValue annotationInitializer = pair.getValue();
303     myMatchingVisitor.setResult(myMatchingVisitor.match(annotationInitializer, elementNameValuePair.getValue()));
304     if (myMatchingVisitor.getResult()) {
305       final PsiIdentifier nameIdentifier = pair.getNameIdentifier();
306       final PsiIdentifier otherIdentifier = elementNameValuePair.getNameIdentifier();
307       if (nameIdentifier == null) {
308         myMatchingVisitor.setResult(otherIdentifier == null ||
309                                     otherIdentifier.getText().equals(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME));
310       }
311       else {
312         final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(nameIdentifier);
313         if (handler instanceof SubstitutionHandler) {
314           myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(otherIdentifier, myMatchingVisitor.getMatchContext()));
315         }
316         else {
317           myMatchingVisitor.setResult(myMatchingVisitor.match(nameIdentifier, otherIdentifier));
318         }
319       }
320     }
321   }
322
323   private boolean checkHierarchy(PsiMember element, PsiMember patternElement) {
324     final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement);
325     if (handler instanceof SubstitutionHandler) {
326       final SubstitutionHandler handler2 = (SubstitutionHandler)handler;
327
328       if (!handler2.isSubtype()) {
329         if (handler2.isStrictSubtype()) {
330           // check if element is declared not in current class  (in ancestors)
331           return element.getContainingClass() != myClazz;
332         }
333       }
334       else {
335         return true;
336       }
337     }
338
339     // check if element is declared in current class (not in ancestors)
340     return myClazz == null || element.getContainingClass() == myClazz;
341   }
342
343   @Override
344   public void visitField(PsiField psiField) {
345     final PsiDocComment comment = psiField.getDocComment();
346     final PsiField other = (PsiField)myMatchingVisitor.getElement();
347     if (comment != null) {
348       myMatchingVisitor.setResult(myMatchingVisitor.match(comment, other));
349       if (!myMatchingVisitor.getResult()) return;
350     }
351     if (!checkHierarchy(other, psiField)) {
352       myMatchingVisitor.setResult(false);
353       return;
354     }
355     super.visitField(psiField);
356   }
357
358   @Override
359   public void visitAnonymousClass(final PsiAnonymousClass clazz) {
360     final PsiAnonymousClass clazz2 = (PsiAnonymousClass)myMatchingVisitor.getElement();
361     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getFirstChild());
362
363     myMatchingVisitor.setResult((myMatchingVisitor.match(clazz.getBaseClassReference(), clazz2.getBaseClassReference()) || isTypedVar) &&
364                                 myMatchingVisitor.matchSons(clazz.getArgumentList(), clazz2.getArgumentList()) &&
365                                 compareClasses(clazz, clazz2));
366
367     if (myMatchingVisitor.getResult() && isTypedVar) {
368       myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getFirstChild(), clazz2.getFirstChild()));
369     }
370   }
371
372   @Override
373   public void visitLambdaExpression(PsiLambdaExpression expression) {
374     final PsiElement other = myMatchingVisitor.getElement();
375     if (other instanceof PsiLambdaExpression) {
376       final PsiLambdaExpression expression2 = (PsiLambdaExpression)other;
377       boolean result = true;
378       final PsiParameterList parameterList1 = expression.getParameterList();
379       if (parameterList1.getParametersCount() != 0) {
380         result = myMatchingVisitor.matchSons(parameterList1, expression2.getParameterList());
381       }
382       final PsiElement body1 = getElementToMatch(expression.getBody());
383       if (body1 != null && result) {
384         result = myMatchingVisitor.matchSequentially(body1, getElementToMatch(expression2.getBody()));
385       }
386       myMatchingVisitor.setResult(result);
387     }
388     else {
389       myMatchingVisitor.setResult(false);
390     }
391   }
392
393   private static PsiElement getElementToMatch(PsiElement element) {
394     if (element instanceof PsiCodeBlock) {
395       element = PsiTreeUtil.getChildOfAnyType(element, PsiStatement.class, PsiComment.class);
396     }
397     if (element instanceof PsiExpressionStatement) {
398       element = ((PsiExpressionStatement)element).getExpression();
399     }
400     if (element instanceof PsiReturnStatement) {
401       element = ((PsiReturnStatement)element).getReturnValue();
402     }
403     return element;
404   }
405
406   private boolean matchInAnyOrder(final PsiReferenceList elements, final PsiReferenceList elements2) {
407     if ((elements == null && myMatchingVisitor.isLeftLooseMatching()) ||
408         elements == elements2 // null
409       ) {
410       return true;
411     }
412
413     return myMatchingVisitor.matchInAnyOrder(
414       elements.getReferenceElements(),
415       (elements2 != null) ? elements2.getReferenceElements() : PsiElement.EMPTY_ARRAY
416     );
417   }
418
419   private boolean compareClasses(final PsiClass clazz, final PsiClass clazz2) {
420     final PsiClass saveClazz = this.myClazz;
421     final MatchContext.MatchedElementsListener oldListener = myMatchingVisitor.getMatchContext().getMatchedElementsListener();
422
423     this.myClazz = clazz2;
424
425     final CompiledPattern pattern = myMatchingVisitor.getMatchContext().getPattern();
426     assert pattern instanceof JavaCompiledPattern;
427     final JavaCompiledPattern javaPattern = (JavaCompiledPattern)pattern;
428
429     MatchContext.MatchedElementsListener listener = new MatchContext.MatchedElementsListener() {
430       private Set<PsiElement> myMatchedElements;
431
432       @Override
433       public void matchedElements(Collection<PsiElement> matchedElements) {
434         if (matchedElements == null) return;
435         if (myMatchedElements == null) {
436           myMatchedElements = new HashSet<>(matchedElements);
437         }
438         else {
439           myMatchedElements.addAll(matchedElements);
440         }
441       }
442
443       @Override
444       public void commitUnmatched() {
445         final List<PsiMember> members = PsiTreeUtil.getChildrenOfTypeAsList(clazz2, PsiMember.class);
446         final List<PsiMember> unmatchedElements =
447           ContainerUtil.filter(members, a -> myMatchedElements == null || !myMatchedElements.contains(a));
448         MatchingHandler unmatchedSubstitutionHandler = null;
449         for (PsiElement element = clazz.getFirstChild(); element != null; element = element.getNextSibling()) {
450           if (element instanceof PsiTypeElement && element.getNextSibling() instanceof PsiErrorElement) {
451             unmatchedSubstitutionHandler = pattern.getHandler(element);
452             break;
453           }
454         }
455         if (unmatchedSubstitutionHandler instanceof SubstitutionHandler) {
456           final SubstitutionHandler handler = (SubstitutionHandler)unmatchedSubstitutionHandler;
457           for (PsiMember element : unmatchedElements) {
458             handler.handle(element, myMatchingVisitor.getMatchContext());
459           }
460         } else {
461           clazz2.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
462         }
463       }
464     };
465     myMatchingVisitor.getMatchContext().setMatchedElementsListener(listener);
466
467     boolean result = false;
468     try {
469       final boolean templateIsInterface = clazz.isInterface();
470       if (templateIsInterface != clazz2.isInterface()) return false;
471       if (templateIsInterface && clazz.isAnnotationType() && !clazz2.isAnnotationType()) return false;
472       if (clazz.isEnum() && !clazz2.isEnum()) return false;
473
474       if (!matchInAnyOrder(clazz.getExtendsList(), clazz2.getExtendsList())) {
475         return false;
476       }
477
478       // check if implements is in extended classes implements
479       final PsiReferenceList implementsList = clazz.getImplementsList();
480       if (implementsList != null) {
481         if (!matchInAnyOrder(implementsList, clazz2.getImplementsList())) {
482           final PsiReferenceList anotherExtendsList = clazz2.getExtendsList();
483           final PsiJavaCodeReferenceElement[] referenceElements = implementsList.getReferenceElements();
484
485           boolean accepted = false;
486
487           if (referenceElements.length > 0 && anotherExtendsList != null) {
488             final HierarchyNodeIterator iterator = new HierarchyNodeIterator(clazz2, true, true, false);
489
490             accepted = myMatchingVisitor.matchInAnyOrder(new ArrayBackedNodeIterator(referenceElements), iterator);
491           }
492
493           if (!accepted) return false;
494         }
495       }
496
497       final PsiField[] fields = clazz.getFields();
498
499       if (fields.length > 0) {
500         final PsiField[] fields2 = javaPattern.isRequestsSuperFields() ?
501                                    clazz2.getAllFields() :
502                                    clazz2.getFields();
503
504         if (!myMatchingVisitor.matchInAnyOrder(fields, fields2)) {
505           return false;
506         }
507       }
508
509       final PsiMethod[] methods = clazz.getMethods();
510
511       if (methods.length > 0) {
512         final PsiMethod[] methods2 = javaPattern.isRequestsSuperMethods() ?
513                                      clazz2.getAllMethods() :
514                                      clazz2.getMethods();
515
516         if (!myMatchingVisitor.matchInAnyOrder(methods, methods2)) {
517           return false;
518         }
519       }
520
521       final PsiClass[] nestedClasses = clazz.getInnerClasses();
522
523       if (nestedClasses.length > 0) {
524         final PsiClass[] nestedClasses2 = javaPattern.isRequestsSuperInners() ?
525                                           clazz2.getAllInnerClasses() :
526                                           clazz2.getInnerClasses();
527
528         if (!myMatchingVisitor.matchInAnyOrder(nestedClasses, nestedClasses2)) {
529           return false;
530         }
531       }
532
533       final PsiClassInitializer[] initializers = clazz.getInitializers();
534       if (initializers.length > 0) {
535         final PsiClassInitializer[] initializers2 = clazz2.getInitializers();
536
537         if (!myMatchingVisitor.matchInAnyOrder(initializers, initializers2)) {
538           return false;
539         }
540       }
541
542       result = true;
543       return true;
544     }
545     finally {
546       if (result) listener.commitUnmatched();
547       this.myClazz = saveClazz;
548       myMatchingVisitor.getMatchContext().setMatchedElementsListener(oldListener);
549     }
550   }
551
552   private boolean compareBody(final PsiElement el1, final PsiElement el2) {
553     PsiElement compareElement1 = el1;
554     PsiElement compareElement2 = el2;
555
556     if (myMatchingVisitor.getMatchContext().getOptions().isLooseMatching()) {
557       if (el1 instanceof PsiBlockStatement) {
558         compareElement1 = ((PsiBlockStatement)el1).getCodeBlock().getFirstChild();
559       }
560
561       if (el2 instanceof PsiBlockStatement) {
562         compareElement2 = ((PsiBlockStatement)el2).getCodeBlock().getFirstChild();
563       }
564     }
565
566     return myMatchingVisitor.matchSequentially(compareElement1, compareElement2);
567   }
568
569   @Override
570   public void visitArrayAccessExpression(final PsiArrayAccessExpression slice) {
571     final PsiElement other = myMatchingVisitor.getElement();
572     if (other instanceof PsiArrayAccessExpression) {
573       final PsiArrayAccessExpression slice2 = (PsiArrayAccessExpression)other;
574       myMatchingVisitor.setResult(myMatchingVisitor.match(slice.getArrayExpression(), slice2.getArrayExpression()) &&
575                                   myMatchingVisitor.match(slice.getIndexExpression(), slice2.getIndexExpression()));
576     }
577     else {
578       myMatchingVisitor.setResult(false);
579     }
580   }
581
582   @Override
583   public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
584     final PsiElement element = myMatchingVisitor.getElement();
585     if (!(element instanceof PsiMethodReferenceExpression)) {
586       myMatchingVisitor.setResult(false);
587       return;
588     }
589     super.visitMethodReferenceExpression(expression);
590   }
591
592   @Override
593   public void visitReferenceExpression(final PsiReferenceExpression reference) {
594     final PsiExpression qualifier = reference.getQualifierExpression();
595
596     final PsiElement nameElement = reference.getReferenceNameElement();
597     final MatchContext context = myMatchingVisitor.getMatchContext();
598     MatchingHandler _handler = nameElement != null ? context.getPattern().getHandlerSimple(nameElement) : null;
599     if (!(_handler instanceof SubstitutionHandler)) _handler = context.getPattern().getHandlerSimple(reference);
600
601     final PsiElement element = myMatchingVisitor.getElement();
602     PsiElement other = element instanceof PsiExpression && context.getOptions().isLooseMatching() ?
603                        PsiUtil.skipParenthesizedExprDown((PsiExpression)element) :
604                        element;
605     if (_handler instanceof SubstitutionHandler &&
606         !(context.getPattern().getHandlerSimple(qualifier) instanceof SubstitutionHandler) &&
607         !(qualifier instanceof PsiThisExpression)
608       ) {
609       if (other instanceof PsiReferenceExpression) {
610         final PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression)other;
611
612         final PsiExpression qualifier2 = psiReferenceExpression.getQualifierExpression();
613         if (qualifier2 == null || (context.getOptions().isLooseMatching() && qualifier2 instanceof PsiThisExpression)) {
614           other = psiReferenceExpression.getReferenceNameElement();
615         }
616       }
617
618       final SubstitutionHandler handler = (SubstitutionHandler)_handler;
619       if (handler.isSubtype() || handler.isStrictSubtype()) {
620         myMatchingVisitor.setResult(checkMatchWithingHierarchy(other, handler, reference));
621       }
622       else {
623         myMatchingVisitor.setResult(handler.handle(other, context));
624       }
625
626       return;
627     }
628
629     if (!(other instanceof PsiReferenceExpression)) {
630       myMatchingVisitor.setResult(false);
631       return;
632     }
633
634     final PsiReferenceExpression reference2 = (PsiReferenceExpression)other;
635
636     // just variable
637     final PsiExpression reference2Qualifier = reference2.getQualifierExpression();
638     if (qualifier == null && reference2Qualifier == null) {
639       myMatchingVisitor.setResult(myMatchingVisitor.matchText(reference.getReferenceNameElement(), reference2.getReferenceNameElement()));
640       return;
641     }
642
643     // handle field selection
644     if (!(other.getParent() instanceof PsiMethodCallExpression) && qualifier != null) {
645       final PsiElement referenceElement = reference.getReferenceNameElement();
646       final PsiElement referenceElement2 = reference2.getReferenceNameElement();
647
648       if (context.getPattern().isTypedVar(referenceElement)) {
649         myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(referenceElement, referenceElement2));
650       }
651       else {
652         myMatchingVisitor.setResult(myMatchingVisitor.matchText(referenceElement, referenceElement2));
653       }
654
655       if (!myMatchingVisitor.getResult()) {
656         return;
657       }
658       if (reference2Qualifier != null) {
659         myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, reference2Qualifier));
660       }
661       else {
662         final PsiElement referencedElement = MatchUtils.getReferencedElement(other);
663         if (referencedElement instanceof PsiField) {
664           final PsiField field = (PsiField)referencedElement;
665           if (qualifier instanceof PsiThisExpression) {
666             myMatchingVisitor.setResult(!field.hasModifierProperty(PsiModifier.STATIC));
667             return;
668           }
669         }
670         final MatchingHandler handler = context.getPattern().getHandler(qualifier);
671         matchImplicitQualifier(handler, referencedElement, context);
672       }
673
674       return;
675     }
676
677     myMatchingVisitor.setResult(false);
678   }
679
680   private static int getArrayDimensions(final PsiElement element) {
681     if (element == null) {
682       return 0;
683     }
684     final PsiElement parent = element.getParent();
685     if (parent instanceof PsiVariable) {
686       final PsiVariable variable = (PsiVariable)parent;
687       final PsiType type = variable.getType();
688       return type.getArrayDimensions();
689     }
690     else if (parent instanceof PsiMethod) {
691       final PsiMethod method = (PsiMethod)parent;
692       final PsiType type = method.getReturnType();
693       return (type == null) ? 0 : type.getArrayDimensions();
694     }
695     else if (element instanceof PsiTypeElement) {
696       final PsiTypeElement typeElement = (PsiTypeElement)element;
697       final PsiType type = typeElement.getType();
698       return type.getArrayDimensions();
699     }
700     return 0;
701   }
702
703   private static PsiTypeElement getInnermostComponentTypeElement(PsiTypeElement typeElement) {
704     PsiElement child = typeElement.getFirstChild();
705     while (child instanceof PsiTypeElement) {
706       typeElement = (PsiTypeElement)child;
707       child = typeElement.getFirstChild();
708     }
709     return typeElement;
710   }
711
712   private static PsiElement getInnermostComponent(PsiElement element) {
713     if (!(element instanceof PsiTypeElement)) {
714       return element;
715     }
716     final PsiTypeElement typeElement = (PsiTypeElement)element;
717     final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
718     return (referenceElement != null) ? referenceElement : getInnermostComponentTypeElement(typeElement);
719   }
720
721   private void copyResults(final MatchResultImpl ourResult) {
722     if (ourResult.hasSons()) {
723       for (MatchResult son : ourResult.getAllSons()) {
724         myMatchingVisitor.getMatchContext().getResult().addSon((MatchResultImpl)son);
725       }
726     }
727   }
728   private static PsiTypeElement[] getTypeParameters(PsiJavaCodeReferenceElement referenceElement, boolean replaceDiamondWithExplicitTypes) {
729     final PsiReferenceParameterList referenceElementParameterList = referenceElement.getParameterList();
730     if (referenceElementParameterList == null) {
731       return null;
732     }
733     final PsiTypeElement[] typeParameterElements = referenceElementParameterList.getTypeParameterElements();
734     if (typeParameterElements.length != 1 || !replaceDiamondWithExplicitTypes) {
735       return typeParameterElements;
736     }
737     final PsiType type = typeParameterElements[0].getType();
738     if (!(type instanceof PsiDiamondType)) {
739       return typeParameterElements;
740     }
741     final PsiDiamondType diamondType = (PsiDiamondType)type;
742     final PsiDiamondType.DiamondInferenceResult inferenceResult = diamondType.resolveInferredTypes();
743     final StringBuilder text = new StringBuilder(referenceElement.getQualifiedName());
744     text.append('<');
745     boolean comma = false;
746     for (PsiType inferredType : inferenceResult.getInferredTypes()) {
747       if (comma) {
748         text.append(',');
749       }
750       else {
751         comma = true;
752       }
753       text.append(inferredType.getCanonicalText());
754     }
755     text.append('>');
756     final PsiJavaCodeReferenceElement newReferenceElement =
757       JavaPsiFacade.getElementFactory(referenceElement.getProject()).createReferenceFromText(text.toString(), referenceElement);
758     final PsiReferenceParameterList newParameterList = newReferenceElement.getParameterList();
759     return newParameterList == null ? null : newParameterList.getTypeParameterElements();
760   }
761
762   private static boolean hasDiamondTypeParameter(PsiElement element) {
763     if (!(element instanceof PsiJavaCodeReferenceElement)) {
764       return false;
765     }
766     final PsiJavaCodeReferenceElement javaCodeReferenceElement = (PsiJavaCodeReferenceElement)element;
767     final PsiReferenceParameterList parameterList = javaCodeReferenceElement.getParameterList();
768     if (parameterList == null) {
769       return false;
770     }
771     final PsiTypeElement[] elements = parameterList.getTypeParameterElements();
772     return elements.length == 1 && elements[0].getType() instanceof PsiDiamondType;
773   }
774
775   private boolean matchType(final PsiElement patternType, final PsiElement matchedType) {
776     PsiElement patternElement = getInnermostComponent(patternType);
777     PsiElement matchedElement = getInnermostComponent(matchedType);
778
779     PsiElement[] typeParameters = null;
780     if (matchedElement instanceof PsiJavaCodeReferenceElement) {
781       final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)matchedElement;
782       typeParameters = getTypeParameters(referenceElement, !hasDiamondTypeParameter(patternElement));
783     }
784     else if (matchedElement instanceof PsiTypeParameter) {
785       matchedElement = ((PsiTypeParameter)matchedElement).getNameIdentifier();
786     }
787     else if (matchedElement instanceof PsiClass && ((PsiClass)matchedElement).hasTypeParameters()) {
788       typeParameters = ((PsiClass)matchedElement).getTypeParameters();
789       matchedElement = ((PsiClass)matchedElement).getNameIdentifier();
790     }
791     else if (matchedElement instanceof PsiMethod && ((PsiMethod)matchedElement).hasTypeParameters()) {
792       typeParameters = ((PsiMethod)matchedType).getTypeParameters();
793       matchedElement = ((PsiMethod)matchedType).getNameIdentifier();
794     }
795
796     if (patternElement instanceof PsiJavaCodeReferenceElement) {
797       final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)patternElement;
798       final PsiReferenceParameterList list = referenceElement.getParameterList();
799       if (list != null) {
800         final PsiTypeElement[] elements = list.getTypeParameterElements();
801         if (elements.length > 0 && (typeParameters == null || !myMatchingVisitor.matchSequentially(elements, typeParameters))) {
802           return false;
803         }
804       }
805       patternElement = referenceElement.getReferenceNameElement();
806     }
807
808     final int matchedArrayDimensions = getArrayDimensions(matchedType);
809     final int patternArrayDimensions = getArrayDimensions(patternType);
810
811     if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternElement)) {
812       final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement);
813
814       RegExpPredicate regExpPredicate = null;
815
816       if (patternArrayDimensions != 0) {
817         if (patternArrayDimensions != matchedArrayDimensions) {
818           return false;
819         }
820       }
821       else if (matchedArrayDimensions != 0) {
822         regExpPredicate = MatchingHandler.getSimpleRegExpPredicate(handler);
823
824         if (regExpPredicate != null) {
825           regExpPredicate.setNodeTextGenerator(new RegExpPredicate.NodeTextGenerator() {
826             public String getText(PsiElement element) {
827               StringBuilder builder = new StringBuilder(RegExpPredicate.getMeaningfulText(element));
828               for (int i = 0; i < matchedArrayDimensions; ++i) builder.append("[]");
829               return builder.toString();
830             }
831           });
832         }
833       }
834
835       try {
836         if (handler.isSubtype() || handler.isStrictSubtype()) {
837           return checkMatchWithingHierarchy(matchedElement, handler, patternElement);
838         }
839         else {
840           return handler.handle(matchedElement, myMatchingVisitor.getMatchContext());
841         }
842       }
843       finally {
844         if (regExpPredicate != null) regExpPredicate.setNodeTextGenerator(null);
845       }
846     }
847
848     if (matchedArrayDimensions != patternArrayDimensions) {
849       return false;
850     }
851
852     if (patternElement instanceof PsiIdentifier) {
853       final PsiElement parent = patternElement.getParent();
854       if (parent instanceof PsiJavaCodeReferenceElement) {
855         patternElement = parent;
856       }
857     }
858     if (matchedElement instanceof PsiIdentifier) {
859       final PsiElement parent = matchedElement.getParent();
860       if (parent instanceof PsiJavaCodeReferenceElement) {
861         matchedElement = parent;
862       }
863     }
864     final String text = getText(patternElement);
865     final String text2 = getText(matchedElement);
866     final boolean caseSensitive = myMatchingVisitor.getMatchContext().getOptions().isCaseSensitiveMatch();
867     final boolean equalsIgnorePackage = MatchUtils.compareWithNoDifferenceToPackage(text, text2, !caseSensitive);
868     if (equalsIgnorePackage || !(matchedElement instanceof PsiJavaReference)) {
869       return equalsIgnorePackage;
870     }
871     else {
872       final PsiElement element2 = ((PsiJavaReference)matchedElement).resolve();
873
874       if (!(element2 instanceof PsiClass)) {
875         return false;
876       }
877       final String name = ((PsiClass)element2).getQualifiedName();
878       return caseSensitive ? text.equals(name) : text.equalsIgnoreCase(name);
879     }
880   }
881
882   @Contract(pure = true)
883   private static String getText(@NotNull PsiElement element) {
884     String result;
885     if (element instanceof PsiClass) {
886       result = ((PsiClass)element).getQualifiedName();
887       if (result == null) result = element.getText();
888     } else if (element instanceof PsiJavaCodeReferenceElement) {
889       result = ((PsiJavaCodeReferenceElement)element).getCanonicalText();
890     } else {
891       result = element.getText();
892     }
893     final int index = result.indexOf('<');
894     return index == -1 ? result : result.substring(0, index);
895   }
896
897   private boolean checkMatchWithingHierarchy(PsiElement el2, SubstitutionHandler handler, PsiElement context) {
898     boolean includeInterfaces = true;
899     boolean includeClasses = true;
900     final PsiElement contextParent = context.getParent();
901
902     if (contextParent instanceof PsiReferenceList) {
903       final PsiElement grandParentContext = contextParent.getParent();
904
905       if (grandParentContext instanceof PsiClass) {
906         final PsiClass psiClass = (PsiClass)grandParentContext;
907
908         if (contextParent == psiClass.getExtendsList()) {
909           includeInterfaces = psiClass.isInterface();
910         }
911         else if (contextParent == psiClass.getImplementsList()) {
912           includeClasses = false;
913         }
914       }
915     }
916
917     // is type2 is (strict) subtype of type
918     final NodeIterator node = new HierarchyNodeIterator(el2, includeClasses, includeInterfaces);
919
920     if (handler.isStrictSubtype()) {
921       node.advance();
922     }
923
924     final boolean notPredicate = handler.getPredicate() instanceof NotPredicate;
925     while (node.hasNext() && !handler.validate(node.current(), 0, -1, myMatchingVisitor.getMatchContext())) {
926       if (notPredicate) return false;
927       node.advance();
928     }
929
930     if (node.hasNext()) {
931       handler.addResult(el2, 0, -1, myMatchingVisitor.getMatchContext());
932       return true;
933     }
934     else {
935       return false;
936     }
937   }
938
939   @Override
940   public void visitConditionalExpression(final PsiConditionalExpression cond) {
941     final PsiConditionalExpression cond2 = (PsiConditionalExpression)myMatchingVisitor.getElement();
942
943     myMatchingVisitor.setResult(myMatchingVisitor.match(cond.getCondition(), cond2.getCondition()) &&
944                                 myMatchingVisitor.matchSons(cond, cond2));
945   }
946
947   @Override
948   public void visitPolyadicExpression(PsiPolyadicExpression expression) {
949     final PsiPolyadicExpression expr2 = (PsiPolyadicExpression)myMatchingVisitor.getElement();
950
951     myMatchingVisitor.setResult(expression.getOperationTokenType().equals(expr2.getOperationTokenType()));
952     if (myMatchingVisitor.getResult()) {
953       final PsiExpression[] operands1 = expression.getOperands();
954       final PsiExpression[] operands2 = expr2.getOperands();
955       myMatchingVisitor.setResult(
956         myMatchingVisitor.matchSequentially(new ArrayBackedNodeIterator(operands1), new ArrayBackedNodeIterator(operands2)));
957     }
958   }
959
960   @Override
961   public void visitVariable(final PsiVariable var) {
962     myMatchingVisitor.getMatchContext().pushResult();
963     final PsiIdentifier nameIdentifier = var.getNameIdentifier();
964
965     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(nameIdentifier);
966     final PsiVariable var2 = (PsiVariable)myMatchingVisitor.getElement();
967
968     try {
969       myMatchingVisitor.setResult((myMatchingVisitor.matchText(var.getNameIdentifier(), var2.getNameIdentifier()) || isTypedVar) &&
970                                    myMatchingVisitor.match(var.getModifierList(), var2.getModifierList()));
971       if (myMatchingVisitor.getResult()) {
972         final PsiTypeElement typeElement1 = var.getTypeElement();
973         if (typeElement1 != null) {
974           PsiTypeElement typeElement2 = var2.getTypeElement();
975           if (typeElement2 == null) {
976             typeElement2 = JavaPsiFacade.getElementFactory(var2.getProject()).createTypeElement(var2.getType());
977           }
978           myMatchingVisitor.setResult(myMatchingVisitor.match(typeElement1, typeElement2));
979         }
980       }
981
982       if (myMatchingVisitor.getResult()) {
983         // Check initializer
984         final PsiExpression initializer = var.getInitializer();
985         final PsiExpression var2Initializer = var2.getInitializer();
986         myMatchingVisitor.setResult(myMatchingVisitor.match(initializer, var2Initializer));
987       }
988
989       if (myMatchingVisitor.getResult() && var instanceof PsiParameter && var.getParent() instanceof PsiCatchSection) {
990         myMatchingVisitor.setResult(myMatchingVisitor.match(
991           ((PsiCatchSection)var.getParent()).getCatchBlock(),
992           ((PsiCatchSection)var2.getParent()).getCatchBlock()
993         ));
994       }
995     }
996     finally {
997       saveOrDropResult(nameIdentifier, isTypedVar, var2.getNameIdentifier());
998     }
999   }
1000
1001   private void matchArrayDims(final PsiNewExpression new1, final PsiNewExpression new2) {
1002     final PsiExpression[] arrayDims = new1.getArrayDimensions();
1003     final PsiExpression[] arrayDims2 = new2.getArrayDimensions();
1004
1005     if (arrayDims.length == arrayDims2.length && arrayDims.length != 0) {
1006       for (int i = 0; i < arrayDims.length; ++i) {
1007         myMatchingVisitor.setResult(myMatchingVisitor.match(arrayDims[i], arrayDims2[i]));
1008         if (!myMatchingVisitor.getResult()) return;
1009       }
1010     }
1011     else {
1012       myMatchingVisitor.setResult((arrayDims == arrayDims2) && myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList()));
1013     }
1014   }
1015
1016   private void saveOrDropResult(final PsiIdentifier methodNameNode, final boolean typedVar, final PsiIdentifier methodNameNode2) {
1017     MatchResultImpl ourResult = myMatchingVisitor.getMatchContext().hasResult() ? myMatchingVisitor.getMatchContext().getResult() : null;
1018     myMatchingVisitor.getMatchContext().popResult();
1019
1020     if (myMatchingVisitor.getResult()) {
1021       if (typedVar) {
1022         final SubstitutionHandler handler =
1023           (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(methodNameNode);
1024         if (ourResult != null) ourResult.setScopeMatch(true);
1025         handler.setNestedResult(ourResult);
1026         myMatchingVisitor.setResult(handler.handle(methodNameNode2, myMatchingVisitor.getMatchContext()));
1027
1028         if (handler.getNestedResult() != null) { // some constraint prevent from adding
1029           handler.setNestedResult(null);
1030           copyResults(ourResult);
1031         }
1032       }
1033       else if (ourResult != null) {
1034         copyResults(ourResult);
1035       }
1036     }
1037   }
1038
1039   private void matchImplicitQualifier(MatchingHandler matchingHandler, PsiElement target, MatchContext context) {
1040     if (!(matchingHandler instanceof SubstitutionHandler)) {
1041       myMatchingVisitor.setResult(false);
1042       return;
1043     }
1044     final SubstitutionHandler substitutionHandler = (SubstitutionHandler)matchingHandler;
1045     final MatchPredicate predicate = substitutionHandler.getPredicate();
1046     if (substitutionHandler.getMinOccurs() != 0) {
1047       myMatchingVisitor.setResult(false);
1048       return;
1049     }
1050     if (predicate == null) {
1051       myMatchingVisitor.setResult(true);
1052       return;
1053     }
1054     if (target == null) {
1055       myMatchingVisitor.setResult(false);
1056       return;
1057     }
1058     if (target instanceof PsiModifierListOwner && ((PsiModifierListOwner)target).hasModifierProperty(PsiModifier.STATIC)) {
1059       myMatchingVisitor.setResult(predicate.match(null, PsiTreeUtil.getParentOfType(target, PsiClass.class), context));
1060     } else {
1061       final PsiElementFactory factory = JavaPsiFacade.getElementFactory(target.getProject());
1062       final PsiExpression implicitReference = factory.createExpressionFromText("this", target);
1063       myMatchingVisitor.setResult(predicate.match(null, implicitReference, context));
1064     }
1065   }
1066
1067   @Override
1068   public void visitMethodCallExpression(final PsiMethodCallExpression mcall) {
1069     final PsiElement element = myMatchingVisitor.getElement();
1070     if (!(element instanceof PsiMethodCallExpression)) {
1071       myMatchingVisitor.setResult(false);
1072       return;
1073     }
1074     final PsiMethodCallExpression mcall2 = (PsiMethodCallExpression)element;
1075     final PsiReferenceExpression mcallRef1 = mcall.getMethodExpression();
1076     final PsiReferenceExpression mcallRef2 = mcall2.getMethodExpression();
1077
1078     final PsiElement patternMethodName = mcallRef1.getReferenceNameElement();
1079     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternMethodName);
1080
1081     if (!isTypedVar && !myMatchingVisitor.matchText(patternMethodName, mcallRef2.getReferenceNameElement())) {
1082       myMatchingVisitor.setResult(false);
1083       return;
1084     }
1085
1086     final PsiExpression patternQualifier = mcallRef1.getQualifierExpression();
1087     final PsiExpression matchedQualifier = mcallRef2.getQualifierExpression();
1088     if (patternQualifier != null) {
1089
1090       if (matchedQualifier != null) {
1091         myMatchingVisitor.setResult(myMatchingVisitor.match(patternQualifier, matchedQualifier));
1092         if (!myMatchingVisitor.getResult()) return;
1093       }
1094       else {
1095         final PsiMethod method = mcall2.resolveMethod();
1096         if (method != null) {
1097           if (patternQualifier instanceof PsiThisExpression) {
1098             myMatchingVisitor.setResult(!method.hasModifierProperty(PsiModifier.STATIC));
1099             return;
1100           }
1101         }
1102         final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternQualifier);
1103         matchImplicitQualifier(handler, method, myMatchingVisitor.getMatchContext());
1104         if (!myMatchingVisitor.getResult()) {
1105           return;
1106         }
1107       }
1108     }
1109     else if (matchedQualifier != null) {
1110       myMatchingVisitor.setResult(false);
1111       return;
1112     }
1113
1114     myMatchingVisitor.setResult(myMatchingVisitor.matchSons(mcall.getArgumentList(), mcall2.getArgumentList()));
1115
1116     if (myMatchingVisitor.getResult()) {
1117       myMatchingVisitor.setResult(matchTypeParameters(mcallRef1, mcallRef2));
1118     }
1119
1120     if (myMatchingVisitor.getResult() && isTypedVar) {
1121       boolean res = myMatchingVisitor.getResult();
1122       res &= myMatchingVisitor.handleTypedElement(patternMethodName, mcallRef2.getReferenceNameElement());
1123       myMatchingVisitor.setResult(res);
1124     }
1125   }
1126
1127   private boolean matchTypeParameters(PsiJavaCodeReferenceElement mcallRef1, PsiJavaCodeReferenceElement mcallRef2) {
1128     final PsiReferenceParameterList patternParameterList = mcallRef1.getParameterList();
1129     if (patternParameterList == null) {
1130       return true;
1131     }
1132     final PsiTypeElement[] patternTypeElements = patternParameterList.getTypeParameterElements();
1133     if (patternTypeElements.length == 0) {
1134       return true;
1135     }
1136     PsiReferenceParameterList matchedParameterList = mcallRef2.getParameterList();
1137     if (matchedParameterList == null) {
1138       return false;
1139     }
1140     if (matchedParameterList.getFirstChild() == null) { // check inferred type parameters
1141       final JavaResolveResult resolveResult = mcallRef2.advancedResolve(false);
1142       final PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
1143       if (targetMethod == null) {
1144         return false;
1145       }
1146       final PsiTypeParameterList typeParameterList = targetMethod.getTypeParameterList();
1147       if (typeParameterList == null) {
1148         return false;
1149       }
1150       final PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
1151       final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
1152       matchedParameterList = (PsiReferenceParameterList)matchedParameterList.copy();
1153       for (final PsiTypeParameter typeParameter : typeParameters) {
1154         final PsiType type = substitutor.substitute(typeParameter);
1155         if (type == null) {
1156           return false;
1157         }
1158         final PsiTypeElement matchedTypeElement = JavaPsiFacade.getElementFactory(mcallRef1.getProject()).createTypeElement(type);
1159         matchedParameterList.add(matchedTypeElement);
1160       }
1161     }
1162     final PsiTypeElement[] matchedTypeElements = matchedParameterList.getTypeParameterElements();
1163     return myMatchingVisitor.matchSequentially(patternTypeElements, matchedTypeElements);
1164   }
1165
1166   @Override
1167   public void visitExpressionStatement(final PsiExpressionStatement expr) {
1168     final PsiElement other = myMatchingVisitor.getElement();
1169     if (other instanceof PsiExpressionStatement) {
1170       final PsiExpressionStatement expr2 = (PsiExpressionStatement)other;
1171       myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getExpression(), expr2.getExpression()));
1172     }
1173     else {
1174       myMatchingVisitor.setResult(false);
1175     }
1176   }
1177
1178   @Override
1179   public void visitLiteralExpression(final PsiLiteralExpression const1) {
1180     final PsiLiteralExpression const2 = (PsiLiteralExpression)myMatchingVisitor.getElement();
1181
1182     final MatchingHandler handler = (MatchingHandler)const1.getUserData(CompiledPattern.HANDLER_KEY);
1183     if (handler instanceof SubstitutionHandler) {
1184       final PsiType type1 = const1.getType();
1185       if (type1 != null && !type1.equals(const2.getType())) {
1186         myMatchingVisitor.setResult(false);
1187       }
1188       else {
1189         int offset = 0;
1190         int length = const2.getTextLength();
1191         final String text = const2.getText();
1192
1193         if (length > 2 && text.charAt(0) == '"' && text.charAt(length - 1) == '"') {
1194           length--;
1195           offset++;
1196         }
1197         myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(const2, offset, length, myMatchingVisitor.getMatchContext()));
1198       }
1199     }
1200     else if (handler != null) {
1201       myMatchingVisitor.setResult(handler.match(const1, const2, myMatchingVisitor.getMatchContext()));
1202     }
1203     else {
1204       myMatchingVisitor.setResult(myMatchingVisitor.matchText(const1, const2));
1205     }
1206   }
1207
1208   @Override
1209   public void visitAssignmentExpression(final PsiAssignmentExpression assign) {
1210     final PsiElement other = myMatchingVisitor.getElement();
1211     if (other instanceof PsiAssignmentExpression) {
1212       final PsiAssignmentExpression assign2 = (PsiAssignmentExpression)other;
1213
1214       myMatchingVisitor.setResult(assign.getOperationTokenType().equals(assign2.getOperationTokenType()) &&
1215                                   myMatchingVisitor.match(assign.getLExpression(), assign2.getLExpression()) &&
1216                                   myMatchingVisitor.match(assign.getRExpression(), assign2.getRExpression()));
1217     }
1218     else {
1219       myMatchingVisitor.setResult(false);
1220     }
1221   }
1222
1223   @Override
1224   public void visitIfStatement(final PsiIfStatement if1) {
1225     final PsiIfStatement if2 = (PsiIfStatement)myMatchingVisitor.getElement();
1226
1227     final PsiStatement elseBranch = if1.getElseBranch();
1228     myMatchingVisitor.setResult(myMatchingVisitor.match(if1.getCondition(), if2.getCondition()) &&
1229                                 compareBody(if1.getThenBranch(), if2.getThenBranch()) &&
1230                                 (elseBranch == null || compareBody(elseBranch, if2.getElseBranch())));
1231   }
1232
1233   @Override
1234   public void visitSwitchStatement(final PsiSwitchStatement switch1) {
1235     final PsiSwitchStatement switch2 = (PsiSwitchStatement)myMatchingVisitor.getElement();
1236
1237     myMatchingVisitor.setResult(myMatchingVisitor.match(switch1.getExpression(), switch2.getExpression()) &&
1238                                 myMatchingVisitor.matchSons(switch1.getBody(), switch2.getBody()));
1239   }
1240
1241   @Override
1242   public void visitForStatement(final PsiForStatement for1) {
1243     final PsiForStatement for2 = (PsiForStatement)myMatchingVisitor.getElement();
1244
1245     final PsiStatement initialization = for1.getInitialization();
1246     MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(initialization);
1247
1248     myMatchingVisitor.setResult(handler.match(initialization, for2.getInitialization(), myMatchingVisitor.getMatchContext()) &&
1249                                 myMatchingVisitor.match(for1.getCondition(), for2.getCondition()) &&
1250                                 myMatchingVisitor.match(for1.getUpdate(), for2.getUpdate()) &&
1251                                 compareBody(for1.getBody(), for2.getBody()));
1252   }
1253
1254   @Override
1255   public void visitForeachStatement(PsiForeachStatement for1) {
1256     final PsiForeachStatement for2 = (PsiForeachStatement)myMatchingVisitor.getElement();
1257
1258     myMatchingVisitor.setResult(myMatchingVisitor.match(for1.getIterationParameter(), for2.getIterationParameter()) &&
1259                                 myMatchingVisitor.match(for1.getIteratedValue(), for2.getIteratedValue()) &&
1260                                 compareBody(for1.getBody(), for2.getBody()));
1261   }
1262
1263   @Override
1264   public void visitWhileStatement(final PsiWhileStatement while1) {
1265     final PsiWhileStatement while2 = (PsiWhileStatement)myMatchingVisitor.getElement();
1266
1267     myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1268                                 compareBody(while1.getBody(), while2.getBody()));
1269   }
1270
1271   @Override
1272   public void visitBlockStatement(final PsiBlockStatement block) {
1273     final PsiElement other = myMatchingVisitor.getElement();
1274     if (other instanceof PsiCodeBlock) {
1275       myMatchingVisitor.setResult(!(other.getParent() instanceof PsiBlockStatement) &&
1276                                   myMatchingVisitor.matchSons(block.getCodeBlock(), other));
1277     }
1278     else {
1279       final PsiBlockStatement block2 = (PsiBlockStatement)other;
1280       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, block2));
1281     }
1282   }
1283
1284   @Override
1285   public void visitDeclarationStatement(final PsiDeclarationStatement dcl) {
1286     final PsiDeclarationStatement declaration = (PsiDeclarationStatement)myMatchingVisitor.getElement();
1287     myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(dcl.getDeclaredElements(), declaration.getDeclaredElements()));
1288   }
1289
1290   @Override
1291   public void visitDoWhileStatement(final PsiDoWhileStatement while1) {
1292     final PsiDoWhileStatement while2 = (PsiDoWhileStatement)myMatchingVisitor.getElement();
1293
1294     myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1295                                 compareBody(while1.getBody(), while2.getBody()));
1296   }
1297
1298   @Override
1299   public void visitReturnStatement(final PsiReturnStatement return1) {
1300     final PsiReturnStatement return2 = (PsiReturnStatement)myMatchingVisitor.getElement();
1301
1302     myMatchingVisitor.setResult(myMatchingVisitor.match(return1.getReturnValue(), return2.getReturnValue()));
1303   }
1304
1305   @Override
1306   public void visitPostfixExpression(final PsiPostfixExpression postfix) {
1307     final PsiPostfixExpression postfix2 = (PsiPostfixExpression)myMatchingVisitor.getElement();
1308
1309     myMatchingVisitor.setResult(postfix.getOperationTokenType().equals(postfix2.getOperationTokenType())
1310                                 && myMatchingVisitor.match(postfix.getOperand(), postfix2.getOperand()));
1311   }
1312
1313   @Override
1314   public void visitPrefixExpression(final PsiPrefixExpression prefix) {
1315     final PsiPrefixExpression prefix2 = (PsiPrefixExpression)myMatchingVisitor.getElement();
1316
1317     myMatchingVisitor.setResult(prefix.getOperationTokenType().equals(prefix2.getOperationTokenType())
1318                                 && myMatchingVisitor.match(prefix.getOperand(), prefix2.getOperand()));
1319   }
1320
1321   @Override
1322   public void visitAssertStatement(final PsiAssertStatement assert1) {
1323     final PsiAssertStatement assert2 = (PsiAssertStatement)myMatchingVisitor.getElement();
1324
1325     myMatchingVisitor.setResult(myMatchingVisitor.match(assert1.getAssertCondition(), assert2.getAssertCondition()) &&
1326                                 myMatchingVisitor.match(assert1.getAssertDescription(), assert2.getAssertDescription()));
1327   }
1328
1329   @Override
1330   public void visitBreakStatement(final PsiBreakStatement break1) {
1331     final PsiBreakStatement break2 = (PsiBreakStatement)myMatchingVisitor.getElement();
1332
1333     myMatchingVisitor.setResult(myMatchingVisitor.match(break1.getLabelIdentifier(), break2.getLabelIdentifier()));
1334   }
1335
1336   @Override
1337   public void visitContinueStatement(final PsiContinueStatement continue1) {
1338     final PsiContinueStatement continue2 = (PsiContinueStatement)myMatchingVisitor.getElement();
1339
1340     myMatchingVisitor.setResult(myMatchingVisitor.match(continue1.getLabelIdentifier(), continue2.getLabelIdentifier()));
1341   }
1342
1343   @Override
1344   public void visitSuperExpression(final PsiSuperExpression super1) {
1345     myMatchingVisitor.setResult(true);
1346   }
1347
1348   @Override
1349   public void visitThisExpression(final PsiThisExpression this1) {
1350     myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiThisExpression);
1351   }
1352
1353   @Override
1354   public void visitSynchronizedStatement(final PsiSynchronizedStatement synchronized1) {
1355     final PsiSynchronizedStatement synchronized2 = (PsiSynchronizedStatement)myMatchingVisitor.getElement();
1356
1357     myMatchingVisitor.setResult(myMatchingVisitor.match(synchronized1.getLockExpression(), synchronized2.getLockExpression()) &&
1358                                 myMatchingVisitor.matchSons(synchronized1.getBody(), synchronized2.getBody()));
1359   }
1360
1361   @Override
1362   public void visitThrowStatement(final PsiThrowStatement throw1) {
1363     final PsiThrowStatement throw2 = (PsiThrowStatement)myMatchingVisitor.getElement();
1364
1365     myMatchingVisitor.setResult(myMatchingVisitor.match(throw1.getException(), throw2.getException()));
1366   }
1367
1368   @Override
1369   public void visitParenthesizedExpression(PsiParenthesizedExpression expr) {
1370     if (myMatchingVisitor.getElement() instanceof PsiParenthesizedExpression) {
1371       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(expr, myMatchingVisitor.getElement()));
1372     }
1373     else {
1374       myMatchingVisitor.setResult(false);
1375     }
1376   }
1377
1378   @Override
1379   public void visitCatchSection(final PsiCatchSection section) {
1380     final PsiCatchSection section2 = (PsiCatchSection)myMatchingVisitor.getElement();
1381     final PsiParameter parameter = section.getParameter();
1382     if (parameter != null) {
1383       myMatchingVisitor.setResult(myMatchingVisitor.match(parameter, section2.getParameter()));
1384     }
1385     else {
1386       myMatchingVisitor.setResult(myMatchingVisitor.match(section.getCatchBlock(), section2.getCatchBlock()));
1387     }
1388   }
1389
1390   @Override
1391   public void visitTryStatement(final PsiTryStatement try1) {
1392     final PsiTryStatement try2 = (PsiTryStatement)myMatchingVisitor.getElement();
1393
1394     myMatchingVisitor.setResult(myMatchingVisitor.matchSons(try1.getTryBlock(), try2.getTryBlock()));
1395     if (!myMatchingVisitor.getResult()) return;
1396
1397     final PsiResourceList resourceList1 = try1.getResourceList();
1398     final PsiCatchSection[] catches1 = try1.getCatchSections();
1399     final PsiCodeBlock finally1 = try1.getFinallyBlock();
1400
1401     final PsiResourceList resourceList2 = try2.getResourceList();
1402     final PsiCatchSection[] catches2 = try2.getCatchSections();
1403     final PsiCodeBlock finally2 = try2.getFinallyBlock();
1404
1405     if (!myMatchingVisitor.getMatchContext().getOptions().isLooseMatching() &&
1406         ((catches1.length == 0 && catches2.length != 0) ||
1407          (finally1 == null && finally2 != null) ||
1408          (resourceList1 == null && resourceList2 != null)) ||
1409         catches2.length < catches1.length
1410       ) {
1411       myMatchingVisitor.setResult(false);
1412     }
1413     else {
1414       final List<PsiElement> unmatchedElements = new ArrayList<>();
1415
1416       if (resourceList1 != null) {
1417         if (resourceList2 == null) {
1418           myMatchingVisitor.setResult(false);
1419           return;
1420         }
1421         final List<PsiResourceListElement> resources1 = PsiTreeUtil.getChildrenOfTypeAsList(resourceList1, PsiResourceListElement.class);
1422         final List<PsiResourceListElement> resources2 = PsiTreeUtil.getChildrenOfTypeAsList(resourceList2, PsiResourceListElement.class);
1423         myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(
1424           resources1.toArray(new PsiResourceListElement[resources1.size()]),
1425           resources2.toArray(new PsiResourceListElement[resources2.size()])));
1426         if (!myMatchingVisitor.getResult()) return;
1427       }
1428       else if (resourceList2 != null){
1429         unmatchedElements.add(resourceList2);
1430       }
1431
1432       ContainerUtil.addAll(unmatchedElements, catches2);
1433       for (PsiCatchSection catchSection : catches1) {
1434         final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(catchSection);
1435         final PsiElement pinnedNode = handler.getPinnedNode(null);
1436
1437         if (pinnedNode != null) {
1438           myMatchingVisitor.setResult(handler.match(catchSection, pinnedNode, myMatchingVisitor.getMatchContext()));
1439           if (!myMatchingVisitor.getResult()) return;
1440         }
1441         else {
1442           boolean matched = false;
1443           for (int j = 0; j < unmatchedElements.size(); ++j) {
1444             if (handler.match(catchSection, unmatchedElements.get(j), myMatchingVisitor.getMatchContext())) {
1445               unmatchedElements.remove(j);
1446               matched = true;
1447               break;
1448             }
1449           }
1450           if (!matched) {
1451             myMatchingVisitor.setResult(false);
1452             return;
1453           }
1454         }
1455       }
1456
1457       if (finally1 != null) {
1458         myMatchingVisitor.setResult(myMatchingVisitor.matchSons(finally1, finally2));
1459       } else if (finally2 != null) {
1460         unmatchedElements.add(finally2);
1461       }
1462
1463       if (myMatchingVisitor.getResult() && unmatchedElements.size() > 0) {
1464         try2.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
1465       }
1466     }
1467   }
1468
1469   @Override
1470   public void visitSwitchLabelStatement(final PsiSwitchLabelStatement case1) {
1471     final PsiSwitchLabelStatement case2 = (PsiSwitchLabelStatement)myMatchingVisitor.getElement();
1472
1473     myMatchingVisitor.setResult(case1.isDefaultCase() == case2.isDefaultCase() &&
1474                                 myMatchingVisitor.match(case1.getCaseValue(), case2.getCaseValue()));
1475   }
1476
1477   @Override
1478   public void visitInstanceOfExpression(final PsiInstanceOfExpression instanceOf) {
1479     final PsiElement other = myMatchingVisitor.getElement();
1480     if (other instanceof PsiInstanceOfExpression) {
1481       final PsiInstanceOfExpression instanceOf2 = (PsiInstanceOfExpression)other;
1482       myMatchingVisitor.setResult(myMatchingVisitor.match(instanceOf.getOperand(), instanceOf2.getOperand()));
1483       if (myMatchingVisitor.getResult()) {
1484         final PsiTypeElement checkType = instanceOf.getCheckType();
1485         if (checkType != null) {
1486           myMatchingVisitor.setResult(myMatchingVisitor.match(checkType, instanceOf2.getCheckType()));
1487         }
1488       }
1489     }
1490     else {
1491       myMatchingVisitor.setResult(false);
1492     }
1493   }
1494
1495   @Override
1496   public void visitNewExpression(final PsiNewExpression new1) {
1497     final PsiElement other = myMatchingVisitor.getElement();
1498     final PsiJavaCodeReferenceElement classReference = new1.getClassReference();
1499     if (other instanceof PsiArrayInitializerExpression &&
1500         other.getParent() instanceof PsiVariable &&
1501         new1.getArrayDimensions().length == 0 &&
1502         new1.getArrayInitializer() != null
1503       ) {
1504       final MatchContext matchContext = myMatchingVisitor.getMatchContext();
1505       final MatchingHandler handler = matchContext.getPattern().getHandler(classReference);
1506       final boolean looseMatching = myMatchingVisitor.getMatchContext().getOptions().isLooseMatching();
1507       if ((handler instanceof SubstitutionHandler && ((SubstitutionHandler)handler).getMinOccurs() != 0) || !looseMatching) {
1508         myMatchingVisitor.setResult(false);
1509         return;
1510       }
1511       final PsiType otherType = ((PsiArrayInitializerExpression)other).getType();
1512       if (handler instanceof SubstitutionHandler && otherType != null) {
1513         final PsiElementFactory factory = JavaPsiFacade.getElementFactory(other.getProject());
1514         final PsiTypeElement otherTypeElement = factory.createTypeElement(otherType.getDeepComponentType());
1515         final SubstitutionHandler substitutionHandler = (SubstitutionHandler)handler;
1516         final MatchPredicate predicate = substitutionHandler.getPredicate();
1517         myMatchingVisitor.setResult(predicate == null || predicate.match(null, otherTypeElement, matchContext));
1518       }
1519       else {
1520         final PsiType type = new1.getType();
1521         myMatchingVisitor.setResult(type != null && type.equals(otherType));
1522       }
1523       if (myMatchingVisitor.getResult()) {
1524         myMatchingVisitor.matchSons(new1.getArrayInitializer(), other);
1525       }
1526       return;
1527     }
1528
1529     if (!(other instanceof PsiNewExpression)) {
1530       myMatchingVisitor.setResult(false);
1531       return;
1532     }
1533     final PsiNewExpression new2 = (PsiNewExpression)other;
1534
1535     if (classReference != null) {
1536       if (new2.getClassReference() != null) {
1537         myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, new2.getClassReference()) &&
1538                                     myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1539
1540         if (myMatchingVisitor.getResult()) {
1541           // matching dims
1542           matchArrayDims(new1, new2);
1543         }
1544         return;
1545       }
1546       else {
1547         // match array of primitive by new 'T();
1548         final PsiKeyword newKeyword = PsiTreeUtil.getChildOfType(new2, PsiKeyword.class);
1549         final PsiElement element = PsiTreeUtil.getNextSiblingOfType(newKeyword, PsiWhiteSpace.class);
1550
1551         if (element != null && element.getNextSibling() instanceof PsiKeyword) {
1552           ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true);
1553
1554           myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, element.getNextSibling()) &&
1555                                       myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1556
1557           ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false);
1558           if (myMatchingVisitor.getResult()) {
1559             // matching dims
1560             matchArrayDims(new1, new2);
1561           }
1562
1563           return;
1564         }
1565       }
1566     }
1567
1568     if (classReference == new2.getClassReference()) {
1569       // probably anonymous class or array of primitive type
1570       ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true);
1571       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(new1, new2));
1572       ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false);
1573     }
1574     else if (new1.getAnonymousClass() == null &&
1575              classReference != null &&
1576              new2.getAnonymousClass() != null) {
1577       // allow matching anonymous class without pattern
1578       myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, new2.getAnonymousClass().getBaseClassReference()) &&
1579                                   myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList()));
1580     }
1581     else {
1582       myMatchingVisitor.setResult(false);
1583     }
1584   }
1585
1586   @Override
1587   public void visitKeyword(PsiKeyword keyword) {
1588     myMatchingVisitor.setResult(myMatchingVisitor.matchText(keyword, myMatchingVisitor.getElement()));
1589   }
1590
1591   @Override
1592   public void visitTypeCastExpression(final PsiTypeCastExpression cast) {
1593     final PsiElement other = myMatchingVisitor.getElement();
1594     if (other instanceof PsiTypeCastExpression) {
1595       final PsiTypeCastExpression cast2 = (PsiTypeCastExpression)other;
1596       myMatchingVisitor.setResult(myMatchingVisitor.match(cast.getCastType(), cast2.getCastType()) &&
1597                                   myMatchingVisitor.match(cast.getOperand(), cast2.getOperand()));
1598     }
1599     else {
1600       myMatchingVisitor.setResult(false);
1601     }
1602   }
1603
1604   @Override
1605   public void visitClassObjectAccessExpression(final PsiClassObjectAccessExpression expr) {
1606     final PsiElement other = myMatchingVisitor.getElement();
1607     if (other instanceof PsiClassObjectAccessExpression) {
1608       final PsiClassObjectAccessExpression expr2 = (PsiClassObjectAccessExpression)other;
1609       myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getOperand(), expr2.getOperand()));
1610     }
1611     else {
1612       myMatchingVisitor.setResult(false);
1613     }
1614   }
1615
1616   @Override
1617   public void visitReferenceElement(final PsiJavaCodeReferenceElement ref) {
1618     final PsiElement other = myMatchingVisitor.getElement();
1619     final PsiAnnotation[] annotations = PsiTreeUtil.getChildrenOfType(ref, PsiAnnotation.class);
1620     if (annotations != null) {
1621       final PsiAnnotation[] otherAnnotations = PsiTreeUtil.getChildrenOfType(other, PsiAnnotation.class);
1622       myMatchingVisitor.setResult(otherAnnotations != null && myMatchingVisitor.matchInAnyOrder(annotations, otherAnnotations));
1623       if (!myMatchingVisitor.getResult()) return;
1624     }
1625     myMatchingVisitor.setResult(matchType(ref, other));
1626   }
1627
1628   @Override
1629   public void visitTypeElement(final PsiTypeElement typeElement) {
1630     final PsiElement other = myMatchingVisitor.getElement(); // might not be a PsiTypeElement
1631
1632     final PsiAnnotation[] annotations = PsiTreeUtil.getChildrenOfType(typeElement, PsiAnnotation.class);
1633     // also can't use AnnotationOwner api because it is not implemented completely yet (see e.g. ClsTypeParameterImpl)
1634     final PsiAnnotation[] annotations2 = PsiTreeUtil.getChildrenOfType(other, PsiAnnotation.class);
1635     if (annotations != null) {
1636       myMatchingVisitor.setResult(annotations2 != null && myMatchingVisitor.matchInAnyOrder(annotations, annotations2));
1637       if (!myMatchingVisitor.getResult()) return;
1638     }
1639     final PsiTypeElement[] typeElementChildren = PsiTreeUtil.getChildrenOfType(typeElement, PsiTypeElement.class);
1640     if (typeElementChildren != null && typeElementChildren.length > 1) {
1641       // multi catch type element
1642       final PsiTypeElement[] typeElementChildren2 = PsiTreeUtil.getChildrenOfType(other, PsiTypeElement.class);
1643       myMatchingVisitor.setResult(
1644         typeElementChildren2 != null && myMatchingVisitor.matchInAnyOrder(typeElementChildren, typeElementChildren2));
1645     }
1646     else {
1647       myMatchingVisitor.setResult(matchType(typeElement, other));
1648     }
1649   }
1650
1651   @Override
1652   public void visitTypeParameter(PsiTypeParameter psiTypeParameter) {
1653     final PsiTypeParameter parameter = (PsiTypeParameter)myMatchingVisitor.getElement();
1654     final PsiIdentifier identifier = psiTypeParameter.getNameIdentifier();
1655     final PsiIdentifier identifier2 = parameter.getNameIdentifier();
1656
1657     final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(identifier);
1658     if (handler instanceof SubstitutionHandler) {
1659       myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(identifier2, myMatchingVisitor.getMatchContext()));
1660     }
1661     else {
1662       myMatchingVisitor.setResult(myMatchingVisitor.matchText(identifier, identifier2));
1663     }
1664
1665     if (myMatchingVisitor.getResult()) {
1666       myMatchingVisitor.setResult(matchInAnyOrder(psiTypeParameter.getExtendsList(), parameter.getExtendsList()));
1667     }
1668     if (myMatchingVisitor.getResult()) {
1669       myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(psiTypeParameter.getAnnotations(), parameter.getAnnotations()));
1670     }
1671   }
1672
1673   @Override
1674   public void visitClass(PsiClass clazz) {
1675     if (clazz.hasTypeParameters()) {
1676       myMatchingVisitor
1677         .setResult(
1678           myMatchingVisitor.match(clazz.getTypeParameterList(), ((PsiClass)myMatchingVisitor.getElement()).getTypeParameterList()));
1679
1680       if (!myMatchingVisitor.getResult()) return;
1681     }
1682
1683     PsiClass clazz2;
1684
1685     if (myMatchingVisitor.getElement() instanceof PsiDeclarationStatement &&
1686         myMatchingVisitor.getElement().getFirstChild() instanceof PsiClass
1687       ) {
1688       clazz2 = (PsiClass)myMatchingVisitor.getElement().getFirstChild();
1689     }
1690     else {
1691       clazz2 = (PsiClass)myMatchingVisitor.getElement();
1692     }
1693
1694     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getNameIdentifier());
1695
1696     if (clazz.getModifierList().getTextLength() > 0) {
1697       if (!myMatchingVisitor.match(clazz.getModifierList(), clazz2.getModifierList())) {
1698         myMatchingVisitor.setResult(false);
1699         return;
1700       }
1701     }
1702
1703     myMatchingVisitor.setResult((myMatchingVisitor.matchText(clazz.getNameIdentifier(), clazz2.getNameIdentifier()) || isTypedVar) &&
1704                                 compareClasses(clazz, clazz2));
1705
1706     if (myMatchingVisitor.getResult() && isTypedVar) {
1707       PsiElement id = clazz2.getNameIdentifier();
1708       if (id == null) id = clazz2;
1709       myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getNameIdentifier(), id));
1710     }
1711   }
1712
1713   @Override
1714   public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) {
1715     myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
1716       psiTypeParameterList.getFirstChild(),
1717       myMatchingVisitor.getElement().getFirstChild()
1718     ));
1719   }
1720
1721   @Override
1722   public void visitMethod(PsiMethod method) {
1723     final PsiIdentifier methodNameNode = method.getNameIdentifier();
1724     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(methodNameNode);
1725     final PsiMethod method2 = (PsiMethod)myMatchingVisitor.getElement();
1726
1727     myMatchingVisitor.getMatchContext().pushResult();
1728
1729     try {
1730       final PsiDocComment docComment = method.getDocComment();
1731       if (docComment != null) {
1732         myMatchingVisitor.setResult(myMatchingVisitor.match(docComment, method2));
1733         if (!myMatchingVisitor.getResult()) return;
1734       }
1735       if (method.hasTypeParameters()) {
1736         myMatchingVisitor.setResult(
1737           myMatchingVisitor.match(method.getTypeParameterList(), ((PsiMethod)myMatchingVisitor.getElement()).getTypeParameterList()));
1738
1739         if (!myMatchingVisitor.getResult()) return;
1740       }
1741
1742       if (!checkHierarchy(method2, method)) {
1743         myMatchingVisitor.setResult(false);
1744         return;
1745       }
1746
1747       myMatchingVisitor.setResult((myMatchingVisitor.matchText(method.getNameIdentifier(), method2.getNameIdentifier()) || isTypedVar) &&
1748                                   myMatchingVisitor.match(method.getModifierList(), method2.getModifierList()) &&
1749                                   myMatchingVisitor.matchSons(method.getParameterList(), method2.getParameterList()) &&
1750                                   myMatchingVisitor.match(method.getReturnTypeElement(), method2.getReturnTypeElement()) &&
1751                                                     matchInAnyOrder(method.getThrowsList(), method2.getThrowsList()) &&
1752                                   myMatchingVisitor.matchSonsOptionally(method.getBody(), method2.getBody()));
1753     }
1754     finally {
1755       saveOrDropResult(methodNameNode, isTypedVar, method2.getNameIdentifier());
1756     }
1757   }
1758 }