SSR: javadoc matching fix and tests
[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       final SubstitutionHandler handler = (SubstitutionHandler)_handler;
609       if (handler.isSubtype() || handler.isStrictSubtype()) {
610         myMatchingVisitor.setResult(checkMatchWithinHierarchy(other, handler, reference));
611       }
612       else {
613         myMatchingVisitor.setResult(handler.handle(other, context));
614       }
615       return;
616     }
617
618     final boolean multiMatch = other != null && reference.getContainingFile() == other.getContainingFile();
619     if (!(other instanceof PsiReferenceExpression)) {
620       myMatchingVisitor.setResult(multiMatch && myMatchingVisitor.matchText(reference, other));
621       return;
622     }
623
624     final PsiReferenceExpression reference2 = (PsiReferenceExpression)other;
625
626     final PsiExpression qualifier2 = reference2.getQualifierExpression();
627     if (multiMatch &&
628         (qualifier == null || qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) &&
629         (qualifier2 == null || qualifier2 instanceof PsiThisExpression || qualifier2 instanceof PsiSuperExpression)) {
630       final PsiElement target = reference.resolve();
631       if (target != null) {
632         myMatchingVisitor.setResult(target == reference2.resolve());
633         return;
634       }
635     }
636     if (qualifier == null && qualifier2 == null) {
637       myMatchingVisitor.setResult(myMatchingVisitor.matchText(reference.getReferenceNameElement(), reference2.getReferenceNameElement()));
638       return;
639     }
640
641     // handle field selection
642     if (!(other.getParent() instanceof PsiMethodCallExpression) && qualifier != null) {
643       final PsiElement referenceElement = reference.getReferenceNameElement();
644       final PsiElement referenceElement2 = reference2.getReferenceNameElement();
645
646       if (context.getPattern().isTypedVar(referenceElement)) {
647         myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(referenceElement, referenceElement2));
648       }
649       else {
650         myMatchingVisitor.setResult(myMatchingVisitor.matchText(referenceElement, referenceElement2));
651       }
652
653       if (!myMatchingVisitor.getResult()) {
654         return;
655       }
656       if (qualifier2 != null) {
657         myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, qualifier2));
658       }
659       else {
660         final PsiElement referencedElement = MatchUtils.getReferencedElement(other);
661         if (referencedElement instanceof PsiField) {
662           final PsiField field = (PsiField)referencedElement;
663           if (qualifier instanceof PsiThisExpression) {
664             myMatchingVisitor.setResult(!field.hasModifierProperty(PsiModifier.STATIC));
665             return;
666           }
667         }
668         final MatchingHandler handler = context.getPattern().getHandler(qualifier);
669         matchImplicitQualifier(handler, referencedElement, context);
670       }
671
672       return;
673     }
674
675     myMatchingVisitor.setResult(false);
676   }
677
678   private static int getArrayDimensions(final PsiElement element) {
679     if (element == null) {
680       return 0;
681     }
682     final PsiElement parent = element.getParent();
683     if (parent instanceof PsiVariable) {
684       final PsiVariable variable = (PsiVariable)parent;
685       final PsiType type = variable.getType();
686       return type.getArrayDimensions();
687     }
688     else if (parent instanceof PsiMethod) {
689       final PsiMethod method = (PsiMethod)parent;
690       final PsiType type = method.getReturnType();
691       return (type == null) ? 0 : type.getArrayDimensions();
692     }
693     else if (element instanceof PsiTypeElement) {
694       final PsiTypeElement typeElement = (PsiTypeElement)element;
695       final PsiType type = typeElement.getType();
696       return type.getArrayDimensions();
697     }
698     return 0;
699   }
700
701   private static PsiTypeElement getInnermostComponentTypeElement(PsiTypeElement typeElement) {
702     PsiElement child = typeElement.getFirstChild();
703     while (child instanceof PsiTypeElement) {
704       typeElement = (PsiTypeElement)child;
705       child = typeElement.getFirstChild();
706     }
707     return typeElement;
708   }
709
710   private static PsiElement getInnermostComponent(PsiElement element) {
711     if (!(element instanceof PsiTypeElement)) {
712       return element;
713     }
714     final PsiTypeElement typeElement = (PsiTypeElement)element;
715     final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
716     return (referenceElement != null) ? referenceElement : getInnermostComponentTypeElement(typeElement);
717   }
718
719   private void copyResults(final MatchResultImpl ourResult) {
720     if (ourResult.hasSons()) {
721       for (MatchResult son : ourResult.getAllSons()) {
722         myMatchingVisitor.getMatchContext().getResult().addSon((MatchResultImpl)son);
723       }
724     }
725   }
726   private static PsiTypeElement[] getTypeParameters(PsiJavaCodeReferenceElement referenceElement, boolean replaceDiamondWithExplicitTypes) {
727     final PsiReferenceParameterList referenceElementParameterList = referenceElement.getParameterList();
728     if (referenceElementParameterList == null) {
729       return null;
730     }
731     final PsiTypeElement[] typeParameterElements = referenceElementParameterList.getTypeParameterElements();
732     if (typeParameterElements.length != 1 || !replaceDiamondWithExplicitTypes) {
733       return typeParameterElements;
734     }
735     final PsiType type = typeParameterElements[0].getType();
736     if (!(type instanceof PsiDiamondType)) {
737       return typeParameterElements;
738     }
739     final PsiDiamondType diamondType = (PsiDiamondType)type;
740     final PsiDiamondType.DiamondInferenceResult inferenceResult = diamondType.resolveInferredTypes();
741     final StringBuilder text = new StringBuilder(referenceElement.getQualifiedName());
742     text.append('<');
743     boolean comma = false;
744     for (PsiType inferredType : inferenceResult.getInferredTypes()) {
745       if (comma) {
746         text.append(',');
747       }
748       else {
749         comma = true;
750       }
751       text.append(inferredType.getCanonicalText());
752     }
753     text.append('>');
754     final PsiJavaCodeReferenceElement newReferenceElement =
755       JavaPsiFacade.getElementFactory(referenceElement.getProject()).createReferenceFromText(text.toString(), referenceElement);
756     final PsiReferenceParameterList newParameterList = newReferenceElement.getParameterList();
757     return newParameterList == null ? null : newParameterList.getTypeParameterElements();
758   }
759
760   private static boolean hasDiamondTypeParameter(PsiElement element) {
761     if (!(element instanceof PsiJavaCodeReferenceElement)) {
762       return false;
763     }
764     final PsiJavaCodeReferenceElement javaCodeReferenceElement = (PsiJavaCodeReferenceElement)element;
765     final PsiReferenceParameterList parameterList = javaCodeReferenceElement.getParameterList();
766     if (parameterList == null) {
767       return false;
768     }
769     final PsiTypeElement[] elements = parameterList.getTypeParameterElements();
770     return elements.length == 1 && elements[0].getType() instanceof PsiDiamondType;
771   }
772
773   private boolean matchType(final PsiElement patternType, final PsiElement matchedType) {
774     PsiElement patternElement = getInnermostComponent(patternType);
775     PsiElement matchedElement = getInnermostComponent(matchedType);
776
777     PsiElement[] typeParameters = null;
778     if (matchedElement instanceof PsiJavaCodeReferenceElement) {
779       final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)matchedElement;
780       typeParameters = getTypeParameters(referenceElement, !hasDiamondTypeParameter(patternElement));
781     }
782     else if (matchedElement instanceof PsiTypeParameter) {
783       matchedElement = ((PsiTypeParameter)matchedElement).getNameIdentifier();
784     }
785     else if (matchedElement instanceof PsiClass && ((PsiClass)matchedElement).hasTypeParameters()) {
786       typeParameters = ((PsiClass)matchedElement).getTypeParameters();
787       matchedElement = ((PsiClass)matchedElement).getNameIdentifier();
788     }
789     else if (matchedElement instanceof PsiMethod && ((PsiMethod)matchedElement).hasTypeParameters()) {
790       typeParameters = ((PsiMethod)matchedType).getTypeParameters();
791       matchedElement = ((PsiMethod)matchedType).getNameIdentifier();
792     }
793
794     if (patternElement instanceof PsiJavaCodeReferenceElement) {
795       final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)patternElement;
796       final PsiReferenceParameterList list = referenceElement.getParameterList();
797       if (list != null) {
798         final PsiTypeElement[] elements = list.getTypeParameterElements();
799         if (elements.length > 0 && (typeParameters == null || !myMatchingVisitor.matchSequentially(elements, typeParameters))) {
800           return false;
801         }
802       }
803       patternElement = referenceElement.getReferenceNameElement();
804     }
805
806     final int matchedArrayDimensions = getArrayDimensions(matchedType);
807     final int patternArrayDimensions = getArrayDimensions(patternType);
808
809     if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternElement)) {
810       final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement);
811
812       RegExpPredicate regExpPredicate = null;
813
814       if (patternArrayDimensions != 0) {
815         if (patternArrayDimensions != matchedArrayDimensions) {
816           return false;
817         }
818       }
819       else if (matchedArrayDimensions != 0) {
820         regExpPredicate = MatchingHandler.getSimpleRegExpPredicate(handler);
821
822         if (regExpPredicate != null) {
823           regExpPredicate.setNodeTextGenerator(new RegExpPredicate.NodeTextGenerator() {
824             public String getText(PsiElement element) {
825               StringBuilder builder = new StringBuilder(RegExpPredicate.getMeaningfulText(element));
826               for (int i = 0; i < matchedArrayDimensions; ++i) builder.append("[]");
827               return builder.toString();
828             }
829           });
830         }
831       }
832
833       try {
834         if (handler.isSubtype() || handler.isStrictSubtype()) {
835           return checkMatchWithinHierarchy(matchedElement, handler, patternElement);
836         }
837         else {
838           return handler.handle(matchedElement, myMatchingVisitor.getMatchContext());
839         }
840       }
841       finally {
842         if (regExpPredicate != null) regExpPredicate.setNodeTextGenerator(null);
843       }
844     }
845
846     if (matchedArrayDimensions != patternArrayDimensions) {
847       return false;
848     }
849
850     if (patternElement instanceof PsiIdentifier) {
851       final PsiElement parent = patternElement.getParent();
852       if (parent instanceof PsiJavaCodeReferenceElement) {
853         patternElement = parent;
854       }
855     }
856     if (matchedElement instanceof PsiIdentifier) {
857       final PsiElement parent = matchedElement.getParent();
858       if (parent instanceof PsiJavaCodeReferenceElement) {
859         matchedElement = parent;
860       }
861     }
862     final String text = getText(patternElement);
863     final String text2 = getText(matchedElement);
864     final boolean caseSensitive = myMatchingVisitor.getMatchContext().getOptions().isCaseSensitiveMatch();
865     final boolean equalsIgnorePackage = MatchUtils.compareWithNoDifferenceToPackage(text, text2, !caseSensitive);
866     if (equalsIgnorePackage || !(matchedElement instanceof PsiJavaReference)) {
867       return equalsIgnorePackage;
868     }
869     else {
870       final PsiElement element2 = ((PsiJavaReference)matchedElement).resolve();
871
872       if (!(element2 instanceof PsiClass)) {
873         return false;
874       }
875       final String name = ((PsiClass)element2).getQualifiedName();
876       return caseSensitive ? text.equals(name) : text.equalsIgnoreCase(name);
877     }
878   }
879
880   @Contract(pure = true)
881   private static String getText(@NotNull PsiElement element) {
882     String result;
883     if (element instanceof PsiClass) {
884       result = ((PsiClass)element).getQualifiedName();
885       if (result == null) result = element.getText();
886     } else if (element instanceof PsiJavaCodeReferenceElement) {
887       result = ((PsiJavaCodeReferenceElement)element).getCanonicalText();
888     } else {
889       result = element.getText();
890     }
891     final int index = result.indexOf('<');
892     return index == -1 ? result : result.substring(0, index);
893   }
894
895   private boolean checkMatchWithinHierarchy(PsiElement el2, SubstitutionHandler handler, PsiElement context) {
896     boolean includeInterfaces = true;
897     boolean includeClasses = true;
898     final PsiElement contextParent = context.getParent();
899
900     if (contextParent instanceof PsiReferenceList) {
901       final PsiElement grandParentContext = contextParent.getParent();
902
903       if (grandParentContext instanceof PsiClass) {
904         final PsiClass psiClass = (PsiClass)grandParentContext;
905
906         if (contextParent == psiClass.getExtendsList()) {
907           includeInterfaces = psiClass.isInterface();
908         }
909         else if (contextParent == psiClass.getImplementsList()) {
910           includeClasses = false;
911         }
912       }
913     }
914
915     // is type2 is (strict) subtype of type
916     final NodeIterator node = new HierarchyNodeIterator(el2, includeClasses, includeInterfaces);
917
918     if (handler.isStrictSubtype()) {
919       node.advance();
920     }
921
922     final boolean notPredicate = handler.getPredicate() instanceof NotPredicate;
923     while (node.hasNext() && !handler.validate(node.current(), 0, -1, myMatchingVisitor.getMatchContext())) {
924       if (notPredicate) return false;
925       node.advance();
926     }
927
928     if (node.hasNext()) {
929       handler.addResult(el2, 0, -1, myMatchingVisitor.getMatchContext());
930       return true;
931     }
932     else {
933       return false;
934     }
935   }
936
937   @Override
938   public void visitConditionalExpression(final PsiConditionalExpression cond) {
939     final PsiConditionalExpression cond2 = (PsiConditionalExpression)myMatchingVisitor.getElement();
940
941     myMatchingVisitor.setResult(myMatchingVisitor.match(cond.getCondition(), cond2.getCondition()) &&
942                                 myMatchingVisitor.matchSons(cond, cond2));
943   }
944
945   @Override
946   public void visitPolyadicExpression(PsiPolyadicExpression expression) {
947     final PsiPolyadicExpression expr2 = (PsiPolyadicExpression)myMatchingVisitor.getElement();
948
949     myMatchingVisitor.setResult(expression.getOperationTokenType().equals(expr2.getOperationTokenType()));
950     if (myMatchingVisitor.getResult()) {
951       final PsiExpression[] operands1 = expression.getOperands();
952       final PsiExpression[] operands2 = expr2.getOperands();
953       myMatchingVisitor.setResult(
954         myMatchingVisitor.matchSequentially(new ArrayBackedNodeIterator(operands1), new ArrayBackedNodeIterator(operands2)));
955     }
956   }
957
958   @Override
959   public void visitVariable(final PsiVariable var) {
960     myMatchingVisitor.getMatchContext().pushResult();
961     final PsiIdentifier nameIdentifier = var.getNameIdentifier();
962
963     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(nameIdentifier);
964     final PsiVariable var2 = (PsiVariable)myMatchingVisitor.getElement();
965
966     try {
967       myMatchingVisitor.setResult((myMatchingVisitor.matchText(var.getNameIdentifier(), var2.getNameIdentifier()) || isTypedVar) &&
968                                    myMatchingVisitor.match(var.getModifierList(), var2.getModifierList()));
969       if (myMatchingVisitor.getResult()) {
970         final PsiTypeElement typeElement1 = var.getTypeElement();
971         if (typeElement1 != null) {
972           PsiTypeElement typeElement2 = var2.getTypeElement();
973           if (typeElement2 == null) {
974             typeElement2 = JavaPsiFacade.getElementFactory(var2.getProject()).createTypeElement(var2.getType());
975           }
976           myMatchingVisitor.setResult(myMatchingVisitor.match(typeElement1, typeElement2));
977         }
978       }
979
980       if (myMatchingVisitor.getResult()) {
981         // Check initializer
982         final PsiExpression initializer = var.getInitializer();
983         final PsiExpression var2Initializer = var2.getInitializer();
984         myMatchingVisitor.setResult(myMatchingVisitor.match(initializer, var2Initializer));
985       }
986
987       if (myMatchingVisitor.getResult() && var instanceof PsiParameter && var.getParent() instanceof PsiCatchSection) {
988         myMatchingVisitor.setResult(myMatchingVisitor.match(
989           ((PsiCatchSection)var.getParent()).getCatchBlock(),
990           ((PsiCatchSection)var2.getParent()).getCatchBlock()
991         ));
992       }
993     }
994     finally {
995       saveOrDropResult(nameIdentifier, isTypedVar, var2.getNameIdentifier());
996     }
997   }
998
999   private void matchArrayDims(final PsiNewExpression new1, final PsiNewExpression new2) {
1000     final PsiExpression[] arrayDims = new1.getArrayDimensions();
1001     final PsiExpression[] arrayDims2 = new2.getArrayDimensions();
1002
1003     if (arrayDims.length == arrayDims2.length && arrayDims.length != 0) {
1004       for (int i = 0; i < arrayDims.length; ++i) {
1005         myMatchingVisitor.setResult(myMatchingVisitor.match(arrayDims[i], arrayDims2[i]));
1006         if (!myMatchingVisitor.getResult()) return;
1007       }
1008     }
1009     else {
1010       myMatchingVisitor.setResult((arrayDims == arrayDims2) && myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList()));
1011     }
1012   }
1013
1014   private void saveOrDropResult(final PsiIdentifier methodNameNode, final boolean typedVar, final PsiIdentifier methodNameNode2) {
1015     MatchResultImpl ourResult = myMatchingVisitor.getMatchContext().hasResult() ? myMatchingVisitor.getMatchContext().getResult() : null;
1016     myMatchingVisitor.getMatchContext().popResult();
1017
1018     if (myMatchingVisitor.getResult()) {
1019       if (typedVar) {
1020         final SubstitutionHandler handler =
1021           (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(methodNameNode);
1022         if (ourResult != null) ourResult.setScopeMatch(true);
1023         handler.setNestedResult(ourResult);
1024         myMatchingVisitor.setResult(handler.handle(methodNameNode2, myMatchingVisitor.getMatchContext()));
1025
1026         if (handler.getNestedResult() != null) { // some constraint prevent from adding
1027           handler.setNestedResult(null);
1028           copyResults(ourResult);
1029         }
1030       }
1031       else if (ourResult != null) {
1032         copyResults(ourResult);
1033       }
1034     }
1035   }
1036
1037   private void matchImplicitQualifier(MatchingHandler matchingHandler, PsiElement target, MatchContext context) {
1038     if (!(matchingHandler instanceof SubstitutionHandler)) {
1039       myMatchingVisitor.setResult(false);
1040       return;
1041     }
1042     final SubstitutionHandler substitutionHandler = (SubstitutionHandler)matchingHandler;
1043     final MatchPredicate predicate = substitutionHandler.getPredicate();
1044     if (substitutionHandler.getMinOccurs() != 0) {
1045       myMatchingVisitor.setResult(false);
1046       return;
1047     }
1048     if (predicate == null) {
1049       myMatchingVisitor.setResult(true);
1050       return;
1051     }
1052     if (target == null) {
1053       myMatchingVisitor.setResult(false);
1054       return;
1055     }
1056     if (target instanceof PsiModifierListOwner && ((PsiModifierListOwner)target).hasModifierProperty(PsiModifier.STATIC)) {
1057       myMatchingVisitor.setResult(predicate.match(null, PsiTreeUtil.getParentOfType(target, PsiClass.class), context));
1058     } else {
1059       final PsiElementFactory factory = JavaPsiFacade.getElementFactory(target.getProject());
1060       final PsiExpression implicitReference = factory.createExpressionFromText("this", target);
1061       myMatchingVisitor.setResult(predicate.match(null, implicitReference, context));
1062     }
1063   }
1064
1065   @Override
1066   public void visitMethodCallExpression(final PsiMethodCallExpression mcall) {
1067     final PsiElement element = myMatchingVisitor.getElement();
1068     if (!(element instanceof PsiMethodCallExpression)) {
1069       myMatchingVisitor.setResult(false);
1070       return;
1071     }
1072     final PsiMethodCallExpression mcall2 = (PsiMethodCallExpression)element;
1073     final PsiReferenceExpression mcallRef1 = mcall.getMethodExpression();
1074     final PsiReferenceExpression mcallRef2 = mcall2.getMethodExpression();
1075
1076     final PsiElement patternMethodName = mcallRef1.getReferenceNameElement();
1077     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternMethodName);
1078
1079     if (!isTypedVar && !myMatchingVisitor.matchText(patternMethodName, mcallRef2.getReferenceNameElement())) {
1080       myMatchingVisitor.setResult(false);
1081       return;
1082     }
1083
1084     final PsiExpression patternQualifier = mcallRef1.getQualifierExpression();
1085     final PsiExpression matchedQualifier = mcallRef2.getQualifierExpression();
1086     if (patternQualifier != null) {
1087
1088       if (matchedQualifier != null) {
1089         myMatchingVisitor.setResult(myMatchingVisitor.match(patternQualifier, matchedQualifier));
1090         if (!myMatchingVisitor.getResult()) return;
1091       }
1092       else {
1093         final PsiMethod method = mcall2.resolveMethod();
1094         if (method != null) {
1095           if (patternQualifier instanceof PsiThisExpression) {
1096             myMatchingVisitor.setResult(!method.hasModifierProperty(PsiModifier.STATIC));
1097             return;
1098           }
1099         }
1100         final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternQualifier);
1101         matchImplicitQualifier(handler, method, myMatchingVisitor.getMatchContext());
1102         if (!myMatchingVisitor.getResult()) {
1103           return;
1104         }
1105       }
1106     }
1107     else if (matchedQualifier != null) {
1108       myMatchingVisitor.setResult(false);
1109       return;
1110     }
1111
1112     myMatchingVisitor.setResult(myMatchingVisitor.matchSons(mcall.getArgumentList(), mcall2.getArgumentList()));
1113
1114     if (myMatchingVisitor.getResult()) {
1115       myMatchingVisitor.setResult(matchTypeParameters(mcallRef1, mcallRef2));
1116     }
1117
1118     if (myMatchingVisitor.getResult() && isTypedVar) {
1119       boolean res = myMatchingVisitor.getResult();
1120       res &= myMatchingVisitor.handleTypedElement(patternMethodName, mcallRef2.getReferenceNameElement());
1121       myMatchingVisitor.setResult(res);
1122     }
1123   }
1124
1125   private boolean matchTypeParameters(PsiJavaCodeReferenceElement mcallRef1, PsiJavaCodeReferenceElement mcallRef2) {
1126     final PsiReferenceParameterList patternParameterList = mcallRef1.getParameterList();
1127     if (patternParameterList == null) {
1128       return true;
1129     }
1130     final PsiTypeElement[] patternTypeElements = patternParameterList.getTypeParameterElements();
1131     if (patternTypeElements.length == 0) {
1132       return true;
1133     }
1134     PsiReferenceParameterList matchedParameterList = mcallRef2.getParameterList();
1135     if (matchedParameterList == null) {
1136       return false;
1137     }
1138     if (matchedParameterList.getFirstChild() == null) { // check inferred type parameters
1139       final JavaResolveResult resolveResult = mcallRef2.advancedResolve(false);
1140       final PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
1141       if (targetMethod == null) {
1142         return false;
1143       }
1144       final PsiTypeParameterList typeParameterList = targetMethod.getTypeParameterList();
1145       if (typeParameterList == null) {
1146         return false;
1147       }
1148       final PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
1149       final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
1150       matchedParameterList = (PsiReferenceParameterList)matchedParameterList.copy();
1151       for (final PsiTypeParameter typeParameter : typeParameters) {
1152         final PsiType type = substitutor.substitute(typeParameter);
1153         if (type == null) {
1154           return false;
1155         }
1156         final PsiTypeElement matchedTypeElement = JavaPsiFacade.getElementFactory(mcallRef1.getProject()).createTypeElement(type);
1157         matchedParameterList.add(matchedTypeElement);
1158       }
1159     }
1160     final PsiTypeElement[] matchedTypeElements = matchedParameterList.getTypeParameterElements();
1161     return myMatchingVisitor.matchSequentially(patternTypeElements, matchedTypeElements);
1162   }
1163
1164   @Override
1165   public void visitExpressionStatement(final PsiExpressionStatement expr) {
1166     final PsiElement other = myMatchingVisitor.getElement();
1167     if (other instanceof PsiExpressionStatement) {
1168       final PsiExpressionStatement expr2 = (PsiExpressionStatement)other;
1169       myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getExpression(), expr2.getExpression()));
1170     }
1171     else {
1172       myMatchingVisitor.setResult(false);
1173     }
1174   }
1175
1176   @Override
1177   public void visitLiteralExpression(final PsiLiteralExpression const1) {
1178     final PsiLiteralExpression const2 = (PsiLiteralExpression)myMatchingVisitor.getElement();
1179
1180     final MatchingHandler handler = (MatchingHandler)const1.getUserData(CompiledPattern.HANDLER_KEY);
1181     if (handler instanceof SubstitutionHandler) {
1182       final PsiType type1 = const1.getType();
1183       if (type1 != null && !type1.equals(const2.getType())) {
1184         myMatchingVisitor.setResult(false);
1185       }
1186       else {
1187         int offset = 0;
1188         int length = const2.getTextLength();
1189         final String text = const2.getText();
1190
1191         if (length > 2 && text.charAt(0) == '"' && text.charAt(length - 1) == '"') {
1192           length--;
1193           offset++;
1194         }
1195         myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(const2, offset, length, myMatchingVisitor.getMatchContext()));
1196       }
1197     }
1198     else if (handler != null) {
1199       myMatchingVisitor.setResult(handler.match(const1, const2, myMatchingVisitor.getMatchContext()));
1200     }
1201     else {
1202       myMatchingVisitor.setResult(myMatchingVisitor.matchText(const1, const2));
1203     }
1204   }
1205
1206   @Override
1207   public void visitAssignmentExpression(final PsiAssignmentExpression assign) {
1208     final PsiElement other = myMatchingVisitor.getElement();
1209     if (other instanceof PsiAssignmentExpression) {
1210       final PsiAssignmentExpression assign2 = (PsiAssignmentExpression)other;
1211
1212       myMatchingVisitor.setResult(assign.getOperationTokenType().equals(assign2.getOperationTokenType()) &&
1213                                   myMatchingVisitor.match(assign.getLExpression(), assign2.getLExpression()) &&
1214                                   myMatchingVisitor.match(assign.getRExpression(), assign2.getRExpression()));
1215     }
1216     else {
1217       myMatchingVisitor.setResult(false);
1218     }
1219   }
1220
1221   @Override
1222   public void visitIfStatement(final PsiIfStatement if1) {
1223     final PsiIfStatement if2 = (PsiIfStatement)myMatchingVisitor.getElement();
1224
1225     final PsiStatement elseBranch = if1.getElseBranch();
1226     myMatchingVisitor.setResult(myMatchingVisitor.match(if1.getCondition(), if2.getCondition()) &&
1227                                 compareBody(if1.getThenBranch(), if2.getThenBranch()) &&
1228                                 (elseBranch == null || compareBody(elseBranch, if2.getElseBranch())));
1229   }
1230
1231   @Override
1232   public void visitSwitchStatement(final PsiSwitchStatement switch1) {
1233     final PsiSwitchStatement switch2 = (PsiSwitchStatement)myMatchingVisitor.getElement();
1234
1235     myMatchingVisitor.setResult(myMatchingVisitor.match(switch1.getExpression(), switch2.getExpression()) &&
1236                                 myMatchingVisitor.matchSons(switch1.getBody(), switch2.getBody()));
1237   }
1238
1239   @Override
1240   public void visitForStatement(final PsiForStatement for1) {
1241     final PsiForStatement for2 = (PsiForStatement)myMatchingVisitor.getElement();
1242
1243     final PsiStatement initialization = for1.getInitialization();
1244     MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(initialization);
1245
1246     myMatchingVisitor.setResult(handler.match(initialization, for2.getInitialization(), myMatchingVisitor.getMatchContext()) &&
1247                                 myMatchingVisitor.match(for1.getCondition(), for2.getCondition()) &&
1248                                 myMatchingVisitor.match(for1.getUpdate(), for2.getUpdate()) &&
1249                                 compareBody(for1.getBody(), for2.getBody()));
1250   }
1251
1252   @Override
1253   public void visitForeachStatement(PsiForeachStatement for1) {
1254     final PsiForeachStatement for2 = (PsiForeachStatement)myMatchingVisitor.getElement();
1255
1256     myMatchingVisitor.setResult(myMatchingVisitor.match(for1.getIterationParameter(), for2.getIterationParameter()) &&
1257                                 myMatchingVisitor.match(for1.getIteratedValue(), for2.getIteratedValue()) &&
1258                                 compareBody(for1.getBody(), for2.getBody()));
1259   }
1260
1261   @Override
1262   public void visitWhileStatement(final PsiWhileStatement while1) {
1263     final PsiWhileStatement while2 = (PsiWhileStatement)myMatchingVisitor.getElement();
1264
1265     myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1266                                 compareBody(while1.getBody(), while2.getBody()));
1267   }
1268
1269   @Override
1270   public void visitBlockStatement(final PsiBlockStatement block) {
1271     final PsiElement other = myMatchingVisitor.getElement();
1272     if (other instanceof PsiCodeBlock) {
1273       myMatchingVisitor.setResult(!(other.getParent() instanceof PsiBlockStatement) &&
1274                                   myMatchingVisitor.matchSons(block.getCodeBlock(), other));
1275     }
1276     else {
1277       final PsiBlockStatement block2 = (PsiBlockStatement)other;
1278       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, block2));
1279     }
1280   }
1281
1282   @Override
1283   public void visitDeclarationStatement(final PsiDeclarationStatement dcl) {
1284     final PsiDeclarationStatement declaration = (PsiDeclarationStatement)myMatchingVisitor.getElement();
1285     myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(dcl.getDeclaredElements(), declaration.getDeclaredElements()));
1286   }
1287
1288   @Override
1289   public void visitDoWhileStatement(final PsiDoWhileStatement while1) {
1290     final PsiDoWhileStatement while2 = (PsiDoWhileStatement)myMatchingVisitor.getElement();
1291
1292     myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1293                                 compareBody(while1.getBody(), while2.getBody()));
1294   }
1295
1296   @Override
1297   public void visitReturnStatement(final PsiReturnStatement return1) {
1298     final PsiReturnStatement return2 = (PsiReturnStatement)myMatchingVisitor.getElement();
1299
1300     myMatchingVisitor.setResult(myMatchingVisitor.match(return1.getReturnValue(), return2.getReturnValue()));
1301   }
1302
1303   @Override
1304   public void visitPostfixExpression(final PsiPostfixExpression postfix) {
1305     final PsiPostfixExpression postfix2 = (PsiPostfixExpression)myMatchingVisitor.getElement();
1306
1307     myMatchingVisitor.setResult(postfix.getOperationTokenType().equals(postfix2.getOperationTokenType())
1308                                 && myMatchingVisitor.match(postfix.getOperand(), postfix2.getOperand()));
1309   }
1310
1311   @Override
1312   public void visitPrefixExpression(final PsiPrefixExpression prefix) {
1313     final PsiPrefixExpression prefix2 = (PsiPrefixExpression)myMatchingVisitor.getElement();
1314
1315     myMatchingVisitor.setResult(prefix.getOperationTokenType().equals(prefix2.getOperationTokenType())
1316                                 && myMatchingVisitor.match(prefix.getOperand(), prefix2.getOperand()));
1317   }
1318
1319   @Override
1320   public void visitAssertStatement(final PsiAssertStatement assert1) {
1321     final PsiAssertStatement assert2 = (PsiAssertStatement)myMatchingVisitor.getElement();
1322
1323     myMatchingVisitor.setResult(myMatchingVisitor.match(assert1.getAssertCondition(), assert2.getAssertCondition()) &&
1324                                 myMatchingVisitor.match(assert1.getAssertDescription(), assert2.getAssertDescription()));
1325   }
1326
1327   @Override
1328   public void visitBreakStatement(final PsiBreakStatement break1) {
1329     final PsiBreakStatement break2 = (PsiBreakStatement)myMatchingVisitor.getElement();
1330
1331     myMatchingVisitor.setResult(myMatchingVisitor.match(break1.getLabelIdentifier(), break2.getLabelIdentifier()));
1332   }
1333
1334   @Override
1335   public void visitContinueStatement(final PsiContinueStatement continue1) {
1336     final PsiContinueStatement continue2 = (PsiContinueStatement)myMatchingVisitor.getElement();
1337
1338     myMatchingVisitor.setResult(myMatchingVisitor.match(continue1.getLabelIdentifier(), continue2.getLabelIdentifier()));
1339   }
1340
1341   @Override
1342   public void visitSuperExpression(final PsiSuperExpression super1) {
1343     myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiSuperExpression);
1344   }
1345
1346   @Override
1347   public void visitThisExpression(final PsiThisExpression this1) {
1348     myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiThisExpression);
1349   }
1350
1351   @Override
1352   public void visitSynchronizedStatement(final PsiSynchronizedStatement synchronized1) {
1353     final PsiSynchronizedStatement synchronized2 = (PsiSynchronizedStatement)myMatchingVisitor.getElement();
1354
1355     myMatchingVisitor.setResult(myMatchingVisitor.match(synchronized1.getLockExpression(), synchronized2.getLockExpression()) &&
1356                                 myMatchingVisitor.matchSons(synchronized1.getBody(), synchronized2.getBody()));
1357   }
1358
1359   @Override
1360   public void visitThrowStatement(final PsiThrowStatement throw1) {
1361     final PsiThrowStatement throw2 = (PsiThrowStatement)myMatchingVisitor.getElement();
1362
1363     myMatchingVisitor.setResult(myMatchingVisitor.match(throw1.getException(), throw2.getException()));
1364   }
1365
1366   @Override
1367   public void visitParenthesizedExpression(PsiParenthesizedExpression expr) {
1368     if (myMatchingVisitor.getElement() instanceof PsiParenthesizedExpression) {
1369       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(expr, myMatchingVisitor.getElement()));
1370     }
1371     else {
1372       myMatchingVisitor.setResult(false);
1373     }
1374   }
1375
1376   @Override
1377   public void visitCatchSection(final PsiCatchSection section) {
1378     final PsiCatchSection section2 = (PsiCatchSection)myMatchingVisitor.getElement();
1379     final PsiParameter parameter = section.getParameter();
1380     if (parameter != null) {
1381       myMatchingVisitor.setResult(myMatchingVisitor.match(parameter, section2.getParameter()));
1382     }
1383     else {
1384       myMatchingVisitor.setResult(myMatchingVisitor.match(section.getCatchBlock(), section2.getCatchBlock()));
1385     }
1386   }
1387
1388   @Override
1389   public void visitTryStatement(final PsiTryStatement try1) {
1390     final PsiTryStatement try2 = (PsiTryStatement)myMatchingVisitor.getElement();
1391
1392     myMatchingVisitor.setResult(myMatchingVisitor.matchSons(try1.getTryBlock(), try2.getTryBlock()));
1393     if (!myMatchingVisitor.getResult()) return;
1394
1395     final PsiResourceList resourceList1 = try1.getResourceList();
1396     final PsiCatchSection[] catches1 = try1.getCatchSections();
1397     final PsiCodeBlock finally1 = try1.getFinallyBlock();
1398
1399     final PsiResourceList resourceList2 = try2.getResourceList();
1400     final PsiCatchSection[] catches2 = try2.getCatchSections();
1401     final PsiCodeBlock finally2 = try2.getFinallyBlock();
1402
1403     if (!myMatchingVisitor.getMatchContext().getOptions().isLooseMatching() &&
1404         ((catches1.length == 0 && catches2.length != 0) ||
1405          (finally1 == null && finally2 != null) ||
1406          (resourceList1 == null && resourceList2 != null)) ||
1407         catches2.length < catches1.length
1408       ) {
1409       myMatchingVisitor.setResult(false);
1410     }
1411     else {
1412       final List<PsiElement> unmatchedElements = new ArrayList<>();
1413
1414       if (resourceList1 != null) {
1415         if (resourceList2 == null) {
1416           myMatchingVisitor.setResult(false);
1417           return;
1418         }
1419         final List<PsiResourceListElement> resources1 = PsiTreeUtil.getChildrenOfTypeAsList(resourceList1, PsiResourceListElement.class);
1420         final List<PsiResourceListElement> resources2 = PsiTreeUtil.getChildrenOfTypeAsList(resourceList2, PsiResourceListElement.class);
1421         myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(
1422           resources1.toArray(new PsiResourceListElement[resources1.size()]),
1423           resources2.toArray(new PsiResourceListElement[resources2.size()])));
1424         if (!myMatchingVisitor.getResult()) return;
1425       }
1426       else if (resourceList2 != null){
1427         unmatchedElements.add(resourceList2);
1428       }
1429
1430       ContainerUtil.addAll(unmatchedElements, catches2);
1431       for (PsiCatchSection catchSection : catches1) {
1432         final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(catchSection);
1433         final PsiElement pinnedNode = handler.getPinnedNode(null);
1434
1435         if (pinnedNode != null) {
1436           myMatchingVisitor.setResult(handler.match(catchSection, pinnedNode, myMatchingVisitor.getMatchContext()));
1437           if (!myMatchingVisitor.getResult()) return;
1438         }
1439         else {
1440           boolean matched = false;
1441           for (int j = 0; j < unmatchedElements.size(); ++j) {
1442             if (handler.match(catchSection, unmatchedElements.get(j), myMatchingVisitor.getMatchContext())) {
1443               unmatchedElements.remove(j);
1444               matched = true;
1445               break;
1446             }
1447           }
1448           if (!matched) {
1449             myMatchingVisitor.setResult(false);
1450             return;
1451           }
1452         }
1453       }
1454
1455       if (finally1 != null) {
1456         myMatchingVisitor.setResult(myMatchingVisitor.matchSons(finally1, finally2));
1457       } else if (finally2 != null) {
1458         unmatchedElements.add(finally2);
1459       }
1460
1461       if (myMatchingVisitor.getResult() && unmatchedElements.size() > 0) {
1462         try2.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
1463       }
1464     }
1465   }
1466
1467   @Override
1468   public void visitSwitchLabelStatement(final PsiSwitchLabelStatement case1) {
1469     final PsiSwitchLabelStatement case2 = (PsiSwitchLabelStatement)myMatchingVisitor.getElement();
1470
1471     myMatchingVisitor.setResult(case1.isDefaultCase() == case2.isDefaultCase() &&
1472                                 myMatchingVisitor.match(case1.getCaseValue(), case2.getCaseValue()));
1473   }
1474
1475   @Override
1476   public void visitInstanceOfExpression(final PsiInstanceOfExpression instanceOf) {
1477     final PsiElement other = myMatchingVisitor.getElement();
1478     if (other instanceof PsiInstanceOfExpression) {
1479       final PsiInstanceOfExpression instanceOf2 = (PsiInstanceOfExpression)other;
1480       myMatchingVisitor.setResult(myMatchingVisitor.match(instanceOf.getOperand(), instanceOf2.getOperand()));
1481       if (myMatchingVisitor.getResult()) {
1482         final PsiTypeElement checkType = instanceOf.getCheckType();
1483         if (checkType != null) {
1484           myMatchingVisitor.setResult(myMatchingVisitor.match(checkType, instanceOf2.getCheckType()));
1485         }
1486       }
1487     }
1488     else {
1489       myMatchingVisitor.setResult(false);
1490     }
1491   }
1492
1493   @Override
1494   public void visitNewExpression(final PsiNewExpression new1) {
1495     final PsiElement other = myMatchingVisitor.getElement();
1496     final PsiJavaCodeReferenceElement classReference = new1.getClassReference();
1497     if (other instanceof PsiArrayInitializerExpression &&
1498         other.getParent() instanceof PsiVariable &&
1499         new1.getArrayDimensions().length == 0 &&
1500         new1.getArrayInitializer() != null
1501       ) {
1502       final MatchContext matchContext = myMatchingVisitor.getMatchContext();
1503       final MatchingHandler handler = matchContext.getPattern().getHandler(classReference);
1504       final boolean looseMatching = myMatchingVisitor.getMatchContext().getOptions().isLooseMatching();
1505       if ((handler instanceof SubstitutionHandler && ((SubstitutionHandler)handler).getMinOccurs() != 0) || !looseMatching) {
1506         myMatchingVisitor.setResult(false);
1507         return;
1508       }
1509       final PsiType otherType = ((PsiArrayInitializerExpression)other).getType();
1510       if (handler instanceof SubstitutionHandler && otherType != null) {
1511         final PsiElementFactory factory = JavaPsiFacade.getElementFactory(other.getProject());
1512         final PsiTypeElement otherTypeElement = factory.createTypeElement(otherType.getDeepComponentType());
1513         final SubstitutionHandler substitutionHandler = (SubstitutionHandler)handler;
1514         final MatchPredicate predicate = substitutionHandler.getPredicate();
1515         myMatchingVisitor.setResult(predicate == null || predicate.match(null, otherTypeElement, matchContext));
1516       }
1517       else {
1518         final PsiType type = new1.getType();
1519         myMatchingVisitor.setResult(type != null && type.equals(otherType));
1520       }
1521       if (myMatchingVisitor.getResult()) {
1522         myMatchingVisitor.matchSons(new1.getArrayInitializer(), other);
1523       }
1524       return;
1525     }
1526
1527     if (!(other instanceof PsiNewExpression)) {
1528       myMatchingVisitor.setResult(false);
1529       return;
1530     }
1531     final PsiNewExpression new2 = (PsiNewExpression)other;
1532
1533     if (classReference != null) {
1534       if (new2.getClassReference() != null) {
1535         myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, new2.getClassReference()) &&
1536                                     myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1537
1538         if (myMatchingVisitor.getResult()) {
1539           // matching dims
1540           matchArrayDims(new1, new2);
1541         }
1542         return;
1543       }
1544       else {
1545         // match array of primitive by new 'T();
1546         final PsiKeyword newKeyword = PsiTreeUtil.getChildOfType(new2, PsiKeyword.class);
1547         final PsiElement element = PsiTreeUtil.getNextSiblingOfType(newKeyword, PsiWhiteSpace.class);
1548
1549         if (element != null && element.getNextSibling() instanceof PsiKeyword) {
1550           ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true);
1551
1552           myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, element.getNextSibling()) &&
1553                                       myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1554
1555           ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false);
1556           if (myMatchingVisitor.getResult()) {
1557             // matching dims
1558             matchArrayDims(new1, new2);
1559           }
1560
1561           return;
1562         }
1563       }
1564     }
1565
1566     if (classReference == new2.getClassReference()) {
1567       // probably anonymous class or array of primitive type
1568       ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true);
1569       myMatchingVisitor.setResult(myMatchingVisitor.matchSons(new1, new2));
1570       ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false);
1571     }
1572     else if (new1.getAnonymousClass() == null &&
1573              classReference != null &&
1574              new2.getAnonymousClass() != null) {
1575       // allow matching anonymous class without pattern
1576       myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, new2.getAnonymousClass().getBaseClassReference()) &&
1577                                   myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList()));
1578     }
1579     else {
1580       myMatchingVisitor.setResult(false);
1581     }
1582   }
1583
1584   @Override
1585   public void visitKeyword(PsiKeyword keyword) {
1586     myMatchingVisitor.setResult(myMatchingVisitor.matchText(keyword, myMatchingVisitor.getElement()));
1587   }
1588
1589   @Override
1590   public void visitTypeCastExpression(final PsiTypeCastExpression cast) {
1591     final PsiElement other = myMatchingVisitor.getElement();
1592     if (other instanceof PsiTypeCastExpression) {
1593       final PsiTypeCastExpression cast2 = (PsiTypeCastExpression)other;
1594       myMatchingVisitor.setResult(myMatchingVisitor.match(cast.getCastType(), cast2.getCastType()) &&
1595                                   myMatchingVisitor.match(cast.getOperand(), cast2.getOperand()));
1596     }
1597     else {
1598       myMatchingVisitor.setResult(false);
1599     }
1600   }
1601
1602   @Override
1603   public void visitClassObjectAccessExpression(final PsiClassObjectAccessExpression expr) {
1604     final PsiElement other = myMatchingVisitor.getElement();
1605     if (other instanceof PsiClassObjectAccessExpression) {
1606       final PsiClassObjectAccessExpression expr2 = (PsiClassObjectAccessExpression)other;
1607       myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getOperand(), expr2.getOperand()));
1608     }
1609     else {
1610       myMatchingVisitor.setResult(false);
1611     }
1612   }
1613
1614   @Override
1615   public void visitReferenceElement(final PsiJavaCodeReferenceElement ref) {
1616     final PsiElement other = myMatchingVisitor.getElement();
1617     final PsiAnnotation[] annotations = PsiTreeUtil.getChildrenOfType(ref, PsiAnnotation.class);
1618     if (annotations != null) {
1619       final PsiAnnotation[] otherAnnotations = PsiTreeUtil.getChildrenOfType(other, PsiAnnotation.class);
1620       myMatchingVisitor.setResult(otherAnnotations != null && myMatchingVisitor.matchInAnyOrder(annotations, otherAnnotations));
1621       if (!myMatchingVisitor.getResult()) return;
1622     }
1623     myMatchingVisitor.setResult(matchType(ref, other));
1624   }
1625
1626   @Override
1627   public void visitTypeElement(final PsiTypeElement typeElement) {
1628     final PsiElement other = myMatchingVisitor.getElement(); // might not be a PsiTypeElement
1629
1630     final PsiAnnotation[] annotations = PsiTreeUtil.getChildrenOfType(typeElement, PsiAnnotation.class);
1631     // also can't use AnnotationOwner api because it is not implemented completely yet (see e.g. ClsTypeParameterImpl)
1632     final PsiAnnotation[] annotations2 = PsiTreeUtil.getChildrenOfType(other, PsiAnnotation.class);
1633     if (annotations != null) {
1634       myMatchingVisitor.setResult(annotations2 != null && myMatchingVisitor.matchInAnyOrder(annotations, annotations2));
1635       if (!myMatchingVisitor.getResult()) return;
1636     }
1637     final PsiTypeElement[] typeElementChildren = PsiTreeUtil.getChildrenOfType(typeElement, PsiTypeElement.class);
1638     if (typeElementChildren != null && typeElementChildren.length > 1) {
1639       // multi catch type element
1640       final PsiTypeElement[] typeElementChildren2 = PsiTreeUtil.getChildrenOfType(other, PsiTypeElement.class);
1641       myMatchingVisitor.setResult(
1642         typeElementChildren2 != null && myMatchingVisitor.matchInAnyOrder(typeElementChildren, typeElementChildren2));
1643     }
1644     else {
1645       myMatchingVisitor.setResult(matchType(typeElement, other));
1646     }
1647   }
1648
1649   @Override
1650   public void visitTypeParameter(PsiTypeParameter psiTypeParameter) {
1651     final PsiTypeParameter parameter = (PsiTypeParameter)myMatchingVisitor.getElement();
1652     final PsiIdentifier identifier = psiTypeParameter.getNameIdentifier();
1653     final PsiIdentifier identifier2 = parameter.getNameIdentifier();
1654
1655     final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(identifier);
1656     if (handler instanceof SubstitutionHandler) {
1657       myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(identifier2, myMatchingVisitor.getMatchContext()));
1658     }
1659     else {
1660       myMatchingVisitor.setResult(myMatchingVisitor.matchText(identifier, identifier2));
1661     }
1662
1663     if (myMatchingVisitor.getResult()) {
1664       myMatchingVisitor.setResult(matchInAnyOrder(psiTypeParameter.getExtendsList(), parameter.getExtendsList()));
1665     }
1666     if (myMatchingVisitor.getResult()) {
1667       myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(psiTypeParameter.getAnnotations(), parameter.getAnnotations()));
1668     }
1669   }
1670
1671   @Override
1672   public void visitClass(PsiClass clazz) {
1673     PsiClass clazz2 = (PsiClass)myMatchingVisitor.getElement();
1674     if (clazz.hasTypeParameters()) {
1675       myMatchingVisitor.setResult(myMatchingVisitor.match(clazz.getTypeParameterList(), clazz2.getTypeParameterList()));
1676       if (!myMatchingVisitor.getResult()) return;
1677     }
1678
1679     final PsiDocComment comment = clazz.getDocComment();
1680     if (comment != null) {
1681       myMatchingVisitor.setResult(myMatchingVisitor.match(comment, clazz2));
1682       if (!myMatchingVisitor.getResult()) return;
1683     }
1684
1685     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getNameIdentifier());
1686
1687     if (clazz.getModifierList().getTextLength() > 0) {
1688       if (!myMatchingVisitor.match(clazz.getModifierList(), clazz2.getModifierList())) {
1689         myMatchingVisitor.setResult(false);
1690         return;
1691       }
1692     }
1693
1694     myMatchingVisitor.setResult((myMatchingVisitor.matchText(clazz.getNameIdentifier(), clazz2.getNameIdentifier()) || isTypedVar) &&
1695                                 compareClasses(clazz, clazz2));
1696
1697     if (myMatchingVisitor.getResult() && isTypedVar) {
1698       PsiElement id = clazz2.getNameIdentifier();
1699       if (id == null) id = clazz2;
1700       myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getNameIdentifier(), id));
1701     }
1702   }
1703
1704   @Override
1705   public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) {
1706     myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
1707       psiTypeParameterList.getFirstChild(),
1708       myMatchingVisitor.getElement().getFirstChild()
1709     ));
1710   }
1711
1712   @Override
1713   public void visitMethod(PsiMethod method) {
1714     final PsiIdentifier methodNameNode = method.getNameIdentifier();
1715     final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(methodNameNode);
1716     final PsiMethod method2 = (PsiMethod)myMatchingVisitor.getElement();
1717
1718     myMatchingVisitor.getMatchContext().pushResult();
1719
1720     try {
1721       final PsiDocComment docComment = method.getDocComment();
1722       if (docComment != null) {
1723         myMatchingVisitor.setResult(myMatchingVisitor.match(docComment, method2));
1724         if (!myMatchingVisitor.getResult()) return;
1725       }
1726       if (method.hasTypeParameters()) {
1727         myMatchingVisitor.setResult(
1728           myMatchingVisitor.match(method.getTypeParameterList(), ((PsiMethod)myMatchingVisitor.getElement()).getTypeParameterList()));
1729
1730         if (!myMatchingVisitor.getResult()) return;
1731       }
1732
1733       if (!checkHierarchy(method2, method)) {
1734         myMatchingVisitor.setResult(false);
1735         return;
1736       }
1737
1738       myMatchingVisitor.setResult((myMatchingVisitor.matchText(method.getNameIdentifier(), method2.getNameIdentifier()) || isTypedVar) &&
1739                                   myMatchingVisitor.match(method.getModifierList(), method2.getModifierList()) &&
1740                                   myMatchingVisitor.matchSons(method.getParameterList(), method2.getParameterList()) &&
1741                                   myMatchingVisitor.match(method.getReturnTypeElement(), method2.getReturnTypeElement()) &&
1742                                                     matchInAnyOrder(method.getThrowsList(), method2.getThrowsList()) &&
1743                                   myMatchingVisitor.matchSonsOptionally(method.getBody(), method2.getBody()));
1744     }
1745     finally {
1746       saveOrDropResult(methodNameNode, isTypedVar, method2.getNameIdentifier());
1747     }
1748   }
1749 }