2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.structuralsearch.impl.matcher;
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;
43 * @author Eugene.Kudelevsky
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
50 private final GlobalMatchingVisitor myMatchingVisitor;
51 private PsiClass myClazz;
54 Arrays.sort(MODIFIERS);
57 public JavaMatchingVisitor(GlobalMatchingVisitor matchingVisitor) {
58 this.myMatchingVisitor = matchingVisitor;
62 public void visitComment(PsiComment comment) {
63 PsiComment comment2 = null;
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];
74 comment2 = (PsiComment)myMatchingVisitor.getElement();
77 if (comment2 == null) {
78 myMatchingVisitor.setResult(false);
82 final Object userData = comment.getUserData(CompiledPattern.HANDLER_KEY);
84 if (userData instanceof String) {
85 final String str = (String)userData;
86 int end = comment2.getTextLength();
88 if (comment2.getTokenType() == JavaTokenType.C_STYLE_COMMENT) {
91 myMatchingVisitor.setResult(((SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(str)).handle(
95 myMatchingVisitor.getMatchContext()
98 else if (userData instanceof MatchingHandler) {
99 myMatchingVisitor.setResult(((MatchingHandler)userData).match(comment, comment2, myMatchingVisitor.getMatchContext()));
102 myMatchingVisitor.setResult(myMatchingVisitor.matchText(comment, comment2));
107 public final void visitModifierList(final PsiModifierList list) {
108 final PsiModifierList list2 = (PsiModifierList)myMatchingVisitor.getElement();
110 for (@PsiModifier.ModifierConstant String modifier : MODIFIERS) {
111 if (list.hasModifierProperty(modifier) && !list2.hasModifierProperty(modifier)) {
112 myMatchingVisitor.setResult(false);
117 final PsiAnnotation[] annotations = list.getAnnotations();
118 if (annotations.length > 0) {
119 HashSet<PsiAnnotation> set = new HashSet<>(Arrays.asList(annotations));
121 for (PsiAnnotation annotation : annotations) {
122 final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement();
124 if (nameReferenceElement != null && MatchOptions.MODIFIER_ANNOTATION_NAME.equals(nameReferenceElement.getText())) {
125 final PsiAnnotationParameterList parameterList = annotation.getParameterList();
126 final PsiNameValuePair[] attributes = parameterList.getAttributes();
128 for (PsiNameValuePair pair : attributes) {
129 final PsiAnnotationMemberValue value = pair.getValue();
130 if (value == null) continue;
132 if (value instanceof PsiArrayInitializerMemberValue) {
133 boolean matchedOne = false;
135 for (PsiAnnotationMemberValue v : ((PsiArrayInitializerMemberValue)value).getInitializers()) {
136 if (annotationValueMatchesModifierList(list2, v)) {
143 myMatchingVisitor.setResult(false);
148 if (!annotationValueMatchesModifierList(list2, value)) {
149 myMatchingVisitor.setResult(false);
155 set.remove(annotation);
159 myMatchingVisitor.setResult(set.isEmpty() ||
160 myMatchingVisitor.matchInAnyOrder(set.toArray(new PsiAnnotation[set.size()]), list2.getAnnotations()));
163 myMatchingVisitor.setResult(true);
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;
173 return list2.hasModifierProperty(name) && (!PsiModifier.PACKAGE_LOCAL.equals(name) || list2.getParent() instanceof PsiMember);
177 public void visitDocTag(final PsiDocTag tag) {
178 final PsiDocTag tag2 = (PsiDocTag)myMatchingVisitor.getElement();
179 final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(tag.getNameElement());
181 myMatchingVisitor.setResult(isTypedVar || tag.getName().equals(tag2.getName()));
183 PsiElement psiDocTagValue = tag.getValueElement();
184 boolean isTypedValue = false;
186 if (myMatchingVisitor.getResult() && psiDocTagValue != null) {
187 final PsiElement[] children = psiDocTagValue.getChildren();
188 if (children.length == 1) {
189 psiDocTagValue = children[0];
191 isTypedValue = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(psiDocTagValue);
194 if (tag2.getValueElement() != null) {
195 myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(psiDocTagValue, tag2.getValueElement()));
198 myMatchingVisitor.setResult(myMatchingVisitor.allowsAbsenceOfMatch(psiDocTagValue));
203 if (myMatchingVisitor.getResult() && !isTypedValue) {
204 myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(
205 new DocValuesIterator(tag.getFirstChild()),
206 new DocValuesIterator(tag2.getFirstChild())
210 if (myMatchingVisitor.getResult() && isTypedVar) {
211 myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(tag.getNameElement(), tag2.getNameElement()));
216 public void visitDocComment(final PsiDocComment comment) {
217 PsiDocComment comment2;
219 if (myMatchingVisitor.getElement() instanceof PsiDocCommentOwner) {
220 comment2 = ((PsiDocCommentOwner)myMatchingVisitor.getElement()).getDocComment();
222 if (comment2 == null) {
223 // doc comment are not collapsed for inner classes!
224 myMatchingVisitor.setResult(false);
229 comment2 = (PsiDocComment)myMatchingVisitor.getElement();
231 if (myMatchingVisitor.getElement().getParent() instanceof PsiDocCommentOwner) {
232 myMatchingVisitor.setResult(false);
233 return; // we should matched the doc before
237 if (comment.getTags().length > 0) {
238 myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(comment.getTags(), comment2.getTags()));
241 visitComment(comment);
246 public void visitElement(PsiElement el) {
247 myMatchingVisitor.setResult(myMatchingVisitor.matchText(el, myMatchingVisitor.getElement()));
251 public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
252 final PsiArrayInitializerExpression expr2 = (PsiArrayInitializerExpression)myMatchingVisitor.getElement();
254 myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
255 new ArrayBackedNodeIterator(expression.getInitializers()),
256 new ArrayBackedNodeIterator(expr2.getInitializers())
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()));
268 public void visitCodeBlock(PsiCodeBlock block) {
269 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, myMatchingVisitor.getElement()));
273 public void visitJavaToken(final PsiJavaToken token) {
274 PsiElement element = myMatchingVisitor.getElement();
277 if (!(element instanceof PsiJavaToken)) {
278 result = myMatchingVisitor.matchText(token, element);
280 final PsiJavaToken anotherToken = (PsiJavaToken)element;
282 result = token.getTokenType() == anotherToken.getTokenType() && myMatchingVisitor.matchText(token, anotherToken);
285 myMatchingVisitor.setResult(result);
289 public void visitAnnotation(PsiAnnotation annotation) {
290 final PsiAnnotation psiAnnotation = (PsiAnnotation)myMatchingVisitor.getElement();
292 myMatchingVisitor.setResult(myMatchingVisitor.match(annotation.getNameReferenceElement(), psiAnnotation.getNameReferenceElement()) &&
294 .matchInAnyOrder(annotation.getParameterList().getAttributes(),
295 psiAnnotation.getParameterList().getAttributes()));
299 public void visitNameValuePair(PsiNameValuePair pair) {
300 final PsiNameValuePair elementNameValuePair = (PsiNameValuePair)myMatchingVisitor.getElement();
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));
312 final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(nameIdentifier);
313 if (handler instanceof SubstitutionHandler) {
314 myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(otherIdentifier, myMatchingVisitor.getMatchContext()));
317 myMatchingVisitor.setResult(myMatchingVisitor.match(nameIdentifier, otherIdentifier));
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;
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;
339 // check if element is declared in current class (not in ancestors)
340 return myClazz == null || element.getContainingClass() == myClazz;
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;
351 if (!checkHierarchy(other, psiField)) {
352 myMatchingVisitor.setResult(false);
355 super.visitField(psiField);
359 public void visitAnonymousClass(final PsiAnonymousClass clazz) {
360 final PsiAnonymousClass clazz2 = (PsiAnonymousClass)myMatchingVisitor.getElement();
361 final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getFirstChild());
363 myMatchingVisitor.setResult((myMatchingVisitor.match(clazz.getBaseClassReference(), clazz2.getBaseClassReference()) || isTypedVar) &&
364 myMatchingVisitor.matchSons(clazz.getArgumentList(), clazz2.getArgumentList()) &&
365 compareClasses(clazz, clazz2));
367 if (myMatchingVisitor.getResult() && isTypedVar) {
368 myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getFirstChild(), clazz2.getFirstChild()));
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());
382 final PsiElement body1 = getElementToMatch(expression.getBody());
383 if (body1 != null && result) {
384 result = myMatchingVisitor.matchSequentially(body1, getElementToMatch(expression2.getBody()));
386 myMatchingVisitor.setResult(result);
389 myMatchingVisitor.setResult(false);
393 private static PsiElement getElementToMatch(PsiElement element) {
394 if (element instanceof PsiCodeBlock) {
395 element = PsiTreeUtil.getChildOfAnyType(element, PsiStatement.class, PsiComment.class);
397 if (element instanceof PsiExpressionStatement) {
398 element = ((PsiExpressionStatement)element).getExpression();
400 if (element instanceof PsiReturnStatement) {
401 element = ((PsiReturnStatement)element).getReturnValue();
406 private boolean matchInAnyOrder(final PsiReferenceList elements, final PsiReferenceList elements2) {
407 if ((elements == null && myMatchingVisitor.isLeftLooseMatching()) ||
408 elements == elements2 // null
413 return myMatchingVisitor.matchInAnyOrder(
414 elements.getReferenceElements(),
415 (elements2 != null) ? elements2.getReferenceElements() : PsiElement.EMPTY_ARRAY
419 private boolean compareClasses(final PsiClass clazz, final PsiClass clazz2) {
420 final PsiClass saveClazz = this.myClazz;
421 final MatchContext.MatchedElementsListener oldListener = myMatchingVisitor.getMatchContext().getMatchedElementsListener();
423 this.myClazz = clazz2;
425 final CompiledPattern pattern = myMatchingVisitor.getMatchContext().getPattern();
426 assert pattern instanceof JavaCompiledPattern;
427 final JavaCompiledPattern javaPattern = (JavaCompiledPattern)pattern;
429 MatchContext.MatchedElementsListener listener = new MatchContext.MatchedElementsListener() {
430 private Set<PsiElement> myMatchedElements;
433 public void matchedElements(Collection<PsiElement> matchedElements) {
434 if (matchedElements == null) return;
435 if (myMatchedElements == null) {
436 myMatchedElements = new HashSet<>(matchedElements);
439 myMatchedElements.addAll(matchedElements);
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);
455 if (unmatchedSubstitutionHandler instanceof SubstitutionHandler) {
456 final SubstitutionHandler handler = (SubstitutionHandler)unmatchedSubstitutionHandler;
457 for (PsiMember element : unmatchedElements) {
458 handler.handle(element, myMatchingVisitor.getMatchContext());
461 clazz2.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
465 myMatchingVisitor.getMatchContext().setMatchedElementsListener(listener);
467 boolean result = false;
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;
474 if (!matchInAnyOrder(clazz.getExtendsList(), clazz2.getExtendsList())) {
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();
485 boolean accepted = false;
487 if (referenceElements.length > 0 && anotherExtendsList != null) {
488 final HierarchyNodeIterator iterator = new HierarchyNodeIterator(clazz2, true, true, false);
490 accepted = myMatchingVisitor.matchInAnyOrder(new ArrayBackedNodeIterator(referenceElements), iterator);
493 if (!accepted) return false;
497 final PsiField[] fields = clazz.getFields();
499 if (fields.length > 0) {
500 final PsiField[] fields2 = javaPattern.isRequestsSuperFields() ?
501 clazz2.getAllFields() :
504 if (!myMatchingVisitor.matchInAnyOrder(fields, fields2)) {
509 final PsiMethod[] methods = clazz.getMethods();
511 if (methods.length > 0) {
512 final PsiMethod[] methods2 = javaPattern.isRequestsSuperMethods() ?
513 clazz2.getAllMethods() :
516 if (!myMatchingVisitor.matchInAnyOrder(methods, methods2)) {
521 final PsiClass[] nestedClasses = clazz.getInnerClasses();
523 if (nestedClasses.length > 0) {
524 final PsiClass[] nestedClasses2 = javaPattern.isRequestsSuperInners() ?
525 clazz2.getAllInnerClasses() :
526 clazz2.getInnerClasses();
528 if (!myMatchingVisitor.matchInAnyOrder(nestedClasses, nestedClasses2)) {
533 final PsiClassInitializer[] initializers = clazz.getInitializers();
534 if (initializers.length > 0) {
535 final PsiClassInitializer[] initializers2 = clazz2.getInitializers();
537 if (!myMatchingVisitor.matchInAnyOrder(initializers, initializers2)) {
546 if (result) listener.commitUnmatched();
547 this.myClazz = saveClazz;
548 myMatchingVisitor.getMatchContext().setMatchedElementsListener(oldListener);
552 private boolean compareBody(final PsiElement el1, final PsiElement el2) {
553 PsiElement compareElement1 = el1;
554 PsiElement compareElement2 = el2;
556 if (myMatchingVisitor.getMatchContext().getOptions().isLooseMatching()) {
557 if (el1 instanceof PsiBlockStatement) {
558 compareElement1 = ((PsiBlockStatement)el1).getCodeBlock().getFirstChild();
561 if (el2 instanceof PsiBlockStatement) {
562 compareElement2 = ((PsiBlockStatement)el2).getCodeBlock().getFirstChild();
566 return myMatchingVisitor.matchSequentially(compareElement1, compareElement2);
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()));
578 myMatchingVisitor.setResult(false);
583 public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
584 final PsiElement element = myMatchingVisitor.getElement();
585 if (!(element instanceof PsiMethodReferenceExpression)) {
586 myMatchingVisitor.setResult(false);
589 super.visitMethodReferenceExpression(expression);
593 public void visitReferenceExpression(final PsiReferenceExpression reference) {
594 final PsiExpression qualifier = reference.getQualifierExpression();
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);
601 final PsiElement element = myMatchingVisitor.getElement();
602 PsiElement other = element instanceof PsiExpression && context.getOptions().isLooseMatching() ?
603 PsiUtil.skipParenthesizedExprDown((PsiExpression)element) :
605 if (_handler instanceof SubstitutionHandler &&
606 !(context.getPattern().getHandlerSimple(qualifier) instanceof SubstitutionHandler) &&
607 !(qualifier instanceof PsiThisExpression)
609 if (other instanceof PsiReferenceExpression) {
610 final PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression)other;
612 final PsiExpression qualifier2 = psiReferenceExpression.getQualifierExpression();
613 if (qualifier2 == null || (context.getOptions().isLooseMatching() && qualifier2 instanceof PsiThisExpression)) {
614 other = psiReferenceExpression.getReferenceNameElement();
618 final SubstitutionHandler handler = (SubstitutionHandler)_handler;
619 if (handler.isSubtype() || handler.isStrictSubtype()) {
620 myMatchingVisitor.setResult(checkMatchWithingHierarchy(other, handler, reference));
623 myMatchingVisitor.setResult(handler.handle(other, context));
629 if (!(other instanceof PsiReferenceExpression)) {
630 myMatchingVisitor.setResult(false);
634 final PsiReferenceExpression reference2 = (PsiReferenceExpression)other;
637 final PsiExpression reference2Qualifier = reference2.getQualifierExpression();
638 if (qualifier == null && reference2Qualifier == null) {
639 myMatchingVisitor.setResult(myMatchingVisitor.matchText(reference.getReferenceNameElement(), reference2.getReferenceNameElement()));
643 // handle field selection
644 if (!(other.getParent() instanceof PsiMethodCallExpression) && qualifier != null) {
645 final PsiElement referenceElement = reference.getReferenceNameElement();
646 final PsiElement referenceElement2 = reference2.getReferenceNameElement();
648 if (context.getPattern().isTypedVar(referenceElement)) {
649 myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(referenceElement, referenceElement2));
652 myMatchingVisitor.setResult(myMatchingVisitor.matchText(referenceElement, referenceElement2));
655 if (!myMatchingVisitor.getResult()) {
658 if (reference2Qualifier != null) {
659 myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, reference2Qualifier));
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));
670 final MatchingHandler handler = context.getPattern().getHandler(qualifier);
671 matchImplicitQualifier(handler, referencedElement, context);
677 myMatchingVisitor.setResult(false);
680 private static int getArrayDimensions(final PsiElement element) {
681 if (element == null) {
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();
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();
695 else if (element instanceof PsiTypeElement) {
696 final PsiTypeElement typeElement = (PsiTypeElement)element;
697 final PsiType type = typeElement.getType();
698 return type.getArrayDimensions();
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();
712 private static PsiElement getInnermostComponent(PsiElement element) {
713 if (!(element instanceof PsiTypeElement)) {
716 final PsiTypeElement typeElement = (PsiTypeElement)element;
717 final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
718 return (referenceElement != null) ? referenceElement : getInnermostComponentTypeElement(typeElement);
721 private void copyResults(final MatchResultImpl ourResult) {
722 if (ourResult.hasSons()) {
723 for (MatchResult son : ourResult.getAllSons()) {
724 myMatchingVisitor.getMatchContext().getResult().addSon((MatchResultImpl)son);
728 private static PsiTypeElement[] getTypeParameters(PsiJavaCodeReferenceElement referenceElement, boolean replaceDiamondWithExplicitTypes) {
729 final PsiReferenceParameterList referenceElementParameterList = referenceElement.getParameterList();
730 if (referenceElementParameterList == null) {
733 final PsiTypeElement[] typeParameterElements = referenceElementParameterList.getTypeParameterElements();
734 if (typeParameterElements.length != 1 || !replaceDiamondWithExplicitTypes) {
735 return typeParameterElements;
737 final PsiType type = typeParameterElements[0].getType();
738 if (!(type instanceof PsiDiamondType)) {
739 return typeParameterElements;
741 final PsiDiamondType diamondType = (PsiDiamondType)type;
742 final PsiDiamondType.DiamondInferenceResult inferenceResult = diamondType.resolveInferredTypes();
743 final StringBuilder text = new StringBuilder(referenceElement.getQualifiedName());
745 boolean comma = false;
746 for (PsiType inferredType : inferenceResult.getInferredTypes()) {
753 text.append(inferredType.getCanonicalText());
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();
762 private static boolean hasDiamondTypeParameter(PsiElement element) {
763 if (!(element instanceof PsiJavaCodeReferenceElement)) {
766 final PsiJavaCodeReferenceElement javaCodeReferenceElement = (PsiJavaCodeReferenceElement)element;
767 final PsiReferenceParameterList parameterList = javaCodeReferenceElement.getParameterList();
768 if (parameterList == null) {
771 final PsiTypeElement[] elements = parameterList.getTypeParameterElements();
772 return elements.length == 1 && elements[0].getType() instanceof PsiDiamondType;
775 private boolean matchType(final PsiElement patternType, final PsiElement matchedType) {
776 PsiElement patternElement = getInnermostComponent(patternType);
777 PsiElement matchedElement = getInnermostComponent(matchedType);
779 PsiElement[] typeParameters = null;
780 if (matchedElement instanceof PsiJavaCodeReferenceElement) {
781 final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)matchedElement;
782 typeParameters = getTypeParameters(referenceElement, !hasDiamondTypeParameter(patternElement));
784 else if (matchedElement instanceof PsiTypeParameter) {
785 matchedElement = ((PsiTypeParameter)matchedElement).getNameIdentifier();
787 else if (matchedElement instanceof PsiClass && ((PsiClass)matchedElement).hasTypeParameters()) {
788 typeParameters = ((PsiClass)matchedElement).getTypeParameters();
789 matchedElement = ((PsiClass)matchedElement).getNameIdentifier();
791 else if (matchedElement instanceof PsiMethod && ((PsiMethod)matchedElement).hasTypeParameters()) {
792 typeParameters = ((PsiMethod)matchedType).getTypeParameters();
793 matchedElement = ((PsiMethod)matchedType).getNameIdentifier();
796 if (patternElement instanceof PsiJavaCodeReferenceElement) {
797 final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)patternElement;
798 final PsiReferenceParameterList list = referenceElement.getParameterList();
800 final PsiTypeElement[] elements = list.getTypeParameterElements();
801 if (elements.length > 0 && (typeParameters == null || !myMatchingVisitor.matchSequentially(elements, typeParameters))) {
805 patternElement = referenceElement.getReferenceNameElement();
808 final int matchedArrayDimensions = getArrayDimensions(matchedType);
809 final int patternArrayDimensions = getArrayDimensions(patternType);
811 if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternElement)) {
812 final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement);
814 RegExpPredicate regExpPredicate = null;
816 if (patternArrayDimensions != 0) {
817 if (patternArrayDimensions != matchedArrayDimensions) {
821 else if (matchedArrayDimensions != 0) {
822 regExpPredicate = MatchingHandler.getSimpleRegExpPredicate(handler);
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();
836 if (handler.isSubtype() || handler.isStrictSubtype()) {
837 return checkMatchWithingHierarchy(matchedElement, handler, patternElement);
840 return handler.handle(matchedElement, myMatchingVisitor.getMatchContext());
844 if (regExpPredicate != null) regExpPredicate.setNodeTextGenerator(null);
848 if (matchedArrayDimensions != patternArrayDimensions) {
852 if (patternElement instanceof PsiIdentifier) {
853 final PsiElement parent = patternElement.getParent();
854 if (parent instanceof PsiJavaCodeReferenceElement) {
855 patternElement = parent;
858 if (matchedElement instanceof PsiIdentifier) {
859 final PsiElement parent = matchedElement.getParent();
860 if (parent instanceof PsiJavaCodeReferenceElement) {
861 matchedElement = parent;
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;
872 final PsiElement element2 = ((PsiJavaReference)matchedElement).resolve();
874 if (!(element2 instanceof PsiClass)) {
877 final String name = ((PsiClass)element2).getQualifiedName();
878 return caseSensitive ? text.equals(name) : text.equalsIgnoreCase(name);
882 @Contract(pure = true)
883 private static String getText(@NotNull PsiElement element) {
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();
891 result = element.getText();
893 final int index = result.indexOf('<');
894 return index == -1 ? result : result.substring(0, index);
897 private boolean checkMatchWithingHierarchy(PsiElement el2, SubstitutionHandler handler, PsiElement context) {
898 boolean includeInterfaces = true;
899 boolean includeClasses = true;
900 final PsiElement contextParent = context.getParent();
902 if (contextParent instanceof PsiReferenceList) {
903 final PsiElement grandParentContext = contextParent.getParent();
905 if (grandParentContext instanceof PsiClass) {
906 final PsiClass psiClass = (PsiClass)grandParentContext;
908 if (contextParent == psiClass.getExtendsList()) {
909 includeInterfaces = psiClass.isInterface();
911 else if (contextParent == psiClass.getImplementsList()) {
912 includeClasses = false;
917 // is type2 is (strict) subtype of type
918 final NodeIterator node = new HierarchyNodeIterator(el2, includeClasses, includeInterfaces);
920 if (handler.isStrictSubtype()) {
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;
930 if (node.hasNext()) {
931 handler.addResult(el2, 0, -1, myMatchingVisitor.getMatchContext());
940 public void visitConditionalExpression(final PsiConditionalExpression cond) {
941 final PsiConditionalExpression cond2 = (PsiConditionalExpression)myMatchingVisitor.getElement();
943 myMatchingVisitor.setResult(myMatchingVisitor.match(cond.getCondition(), cond2.getCondition()) &&
944 myMatchingVisitor.matchSons(cond, cond2));
948 public void visitPolyadicExpression(PsiPolyadicExpression expression) {
949 final PsiPolyadicExpression expr2 = (PsiPolyadicExpression)myMatchingVisitor.getElement();
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)));
961 public void visitVariable(final PsiVariable var) {
962 myMatchingVisitor.getMatchContext().pushResult();
963 final PsiIdentifier nameIdentifier = var.getNameIdentifier();
965 final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(nameIdentifier);
966 final PsiVariable var2 = (PsiVariable)myMatchingVisitor.getElement();
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());
978 myMatchingVisitor.setResult(myMatchingVisitor.match(typeElement1, typeElement2));
982 if (myMatchingVisitor.getResult()) {
984 final PsiExpression initializer = var.getInitializer();
985 final PsiExpression var2Initializer = var2.getInitializer();
986 myMatchingVisitor.setResult(myMatchingVisitor.match(initializer, var2Initializer));
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()
997 saveOrDropResult(nameIdentifier, isTypedVar, var2.getNameIdentifier());
1001 private void matchArrayDims(final PsiNewExpression new1, final PsiNewExpression new2) {
1002 final PsiExpression[] arrayDims = new1.getArrayDimensions();
1003 final PsiExpression[] arrayDims2 = new2.getArrayDimensions();
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;
1012 myMatchingVisitor.setResult((arrayDims == arrayDims2) && myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList()));
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();
1020 if (myMatchingVisitor.getResult()) {
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()));
1028 if (handler.getNestedResult() != null) { // some constraint prevent from adding
1029 handler.setNestedResult(null);
1030 copyResults(ourResult);
1033 else if (ourResult != null) {
1034 copyResults(ourResult);
1039 private void matchImplicitQualifier(MatchingHandler matchingHandler, PsiElement target, MatchContext context) {
1040 if (!(matchingHandler instanceof SubstitutionHandler)) {
1041 myMatchingVisitor.setResult(false);
1044 final SubstitutionHandler substitutionHandler = (SubstitutionHandler)matchingHandler;
1045 final MatchPredicate predicate = substitutionHandler.getPredicate();
1046 if (substitutionHandler.getMinOccurs() != 0) {
1047 myMatchingVisitor.setResult(false);
1050 if (predicate == null) {
1051 myMatchingVisitor.setResult(true);
1054 if (target == null) {
1055 myMatchingVisitor.setResult(false);
1058 if (target instanceof PsiModifierListOwner && ((PsiModifierListOwner)target).hasModifierProperty(PsiModifier.STATIC)) {
1059 myMatchingVisitor.setResult(predicate.match(null, PsiTreeUtil.getParentOfType(target, PsiClass.class), context));
1061 final PsiElementFactory factory = JavaPsiFacade.getElementFactory(target.getProject());
1062 final PsiExpression implicitReference = factory.createExpressionFromText("this", target);
1063 myMatchingVisitor.setResult(predicate.match(null, implicitReference, context));
1068 public void visitMethodCallExpression(final PsiMethodCallExpression mcall) {
1069 final PsiElement element = myMatchingVisitor.getElement();
1070 if (!(element instanceof PsiMethodCallExpression)) {
1071 myMatchingVisitor.setResult(false);
1074 final PsiMethodCallExpression mcall2 = (PsiMethodCallExpression)element;
1075 final PsiReferenceExpression mcallRef1 = mcall.getMethodExpression();
1076 final PsiReferenceExpression mcallRef2 = mcall2.getMethodExpression();
1078 final PsiElement patternMethodName = mcallRef1.getReferenceNameElement();
1079 final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(patternMethodName);
1081 if (!isTypedVar && !myMatchingVisitor.matchText(patternMethodName, mcallRef2.getReferenceNameElement())) {
1082 myMatchingVisitor.setResult(false);
1086 final PsiExpression patternQualifier = mcallRef1.getQualifierExpression();
1087 final PsiExpression matchedQualifier = mcallRef2.getQualifierExpression();
1088 if (patternQualifier != null) {
1090 if (matchedQualifier != null) {
1091 myMatchingVisitor.setResult(myMatchingVisitor.match(patternQualifier, matchedQualifier));
1092 if (!myMatchingVisitor.getResult()) return;
1095 final PsiMethod method = mcall2.resolveMethod();
1096 if (method != null) {
1097 if (patternQualifier instanceof PsiThisExpression) {
1098 myMatchingVisitor.setResult(!method.hasModifierProperty(PsiModifier.STATIC));
1102 final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternQualifier);
1103 matchImplicitQualifier(handler, method, myMatchingVisitor.getMatchContext());
1104 if (!myMatchingVisitor.getResult()) {
1109 else if (matchedQualifier != null) {
1110 myMatchingVisitor.setResult(false);
1114 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(mcall.getArgumentList(), mcall2.getArgumentList()));
1116 if (myMatchingVisitor.getResult()) {
1117 myMatchingVisitor.setResult(matchTypeParameters(mcallRef1, mcallRef2));
1120 if (myMatchingVisitor.getResult() && isTypedVar) {
1121 boolean res = myMatchingVisitor.getResult();
1122 res &= myMatchingVisitor.handleTypedElement(patternMethodName, mcallRef2.getReferenceNameElement());
1123 myMatchingVisitor.setResult(res);
1127 private boolean matchTypeParameters(PsiJavaCodeReferenceElement mcallRef1, PsiJavaCodeReferenceElement mcallRef2) {
1128 final PsiReferenceParameterList patternParameterList = mcallRef1.getParameterList();
1129 if (patternParameterList == null) {
1132 final PsiTypeElement[] patternTypeElements = patternParameterList.getTypeParameterElements();
1133 if (patternTypeElements.length == 0) {
1136 PsiReferenceParameterList matchedParameterList = mcallRef2.getParameterList();
1137 if (matchedParameterList == null) {
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) {
1146 final PsiTypeParameterList typeParameterList = targetMethod.getTypeParameterList();
1147 if (typeParameterList == null) {
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);
1158 final PsiTypeElement matchedTypeElement = JavaPsiFacade.getElementFactory(mcallRef1.getProject()).createTypeElement(type);
1159 matchedParameterList.add(matchedTypeElement);
1162 final PsiTypeElement[] matchedTypeElements = matchedParameterList.getTypeParameterElements();
1163 return myMatchingVisitor.matchSequentially(patternTypeElements, matchedTypeElements);
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()));
1174 myMatchingVisitor.setResult(false);
1179 public void visitLiteralExpression(final PsiLiteralExpression const1) {
1180 final PsiLiteralExpression const2 = (PsiLiteralExpression)myMatchingVisitor.getElement();
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);
1190 int length = const2.getTextLength();
1191 final String text = const2.getText();
1193 if (length > 2 && text.charAt(0) == '"' && text.charAt(length - 1) == '"') {
1197 myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(const2, offset, length, myMatchingVisitor.getMatchContext()));
1200 else if (handler != null) {
1201 myMatchingVisitor.setResult(handler.match(const1, const2, myMatchingVisitor.getMatchContext()));
1204 myMatchingVisitor.setResult(myMatchingVisitor.matchText(const1, const2));
1209 public void visitAssignmentExpression(final PsiAssignmentExpression assign) {
1210 final PsiElement other = myMatchingVisitor.getElement();
1211 if (other instanceof PsiAssignmentExpression) {
1212 final PsiAssignmentExpression assign2 = (PsiAssignmentExpression)other;
1214 myMatchingVisitor.setResult(assign.getOperationTokenType().equals(assign2.getOperationTokenType()) &&
1215 myMatchingVisitor.match(assign.getLExpression(), assign2.getLExpression()) &&
1216 myMatchingVisitor.match(assign.getRExpression(), assign2.getRExpression()));
1219 myMatchingVisitor.setResult(false);
1224 public void visitIfStatement(final PsiIfStatement if1) {
1225 final PsiIfStatement if2 = (PsiIfStatement)myMatchingVisitor.getElement();
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())));
1234 public void visitSwitchStatement(final PsiSwitchStatement switch1) {
1235 final PsiSwitchStatement switch2 = (PsiSwitchStatement)myMatchingVisitor.getElement();
1237 myMatchingVisitor.setResult(myMatchingVisitor.match(switch1.getExpression(), switch2.getExpression()) &&
1238 myMatchingVisitor.matchSons(switch1.getBody(), switch2.getBody()));
1242 public void visitForStatement(final PsiForStatement for1) {
1243 final PsiForStatement for2 = (PsiForStatement)myMatchingVisitor.getElement();
1245 final PsiStatement initialization = for1.getInitialization();
1246 MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(initialization);
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()));
1255 public void visitForeachStatement(PsiForeachStatement for1) {
1256 final PsiForeachStatement for2 = (PsiForeachStatement)myMatchingVisitor.getElement();
1258 myMatchingVisitor.setResult(myMatchingVisitor.match(for1.getIterationParameter(), for2.getIterationParameter()) &&
1259 myMatchingVisitor.match(for1.getIteratedValue(), for2.getIteratedValue()) &&
1260 compareBody(for1.getBody(), for2.getBody()));
1264 public void visitWhileStatement(final PsiWhileStatement while1) {
1265 final PsiWhileStatement while2 = (PsiWhileStatement)myMatchingVisitor.getElement();
1267 myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1268 compareBody(while1.getBody(), while2.getBody()));
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));
1279 final PsiBlockStatement block2 = (PsiBlockStatement)other;
1280 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, block2));
1285 public void visitDeclarationStatement(final PsiDeclarationStatement dcl) {
1286 final PsiDeclarationStatement declaration = (PsiDeclarationStatement)myMatchingVisitor.getElement();
1287 myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(dcl.getDeclaredElements(), declaration.getDeclaredElements()));
1291 public void visitDoWhileStatement(final PsiDoWhileStatement while1) {
1292 final PsiDoWhileStatement while2 = (PsiDoWhileStatement)myMatchingVisitor.getElement();
1294 myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) &&
1295 compareBody(while1.getBody(), while2.getBody()));
1299 public void visitReturnStatement(final PsiReturnStatement return1) {
1300 final PsiReturnStatement return2 = (PsiReturnStatement)myMatchingVisitor.getElement();
1302 myMatchingVisitor.setResult(myMatchingVisitor.match(return1.getReturnValue(), return2.getReturnValue()));
1306 public void visitPostfixExpression(final PsiPostfixExpression postfix) {
1307 final PsiPostfixExpression postfix2 = (PsiPostfixExpression)myMatchingVisitor.getElement();
1309 myMatchingVisitor.setResult(postfix.getOperationTokenType().equals(postfix2.getOperationTokenType())
1310 && myMatchingVisitor.match(postfix.getOperand(), postfix2.getOperand()));
1314 public void visitPrefixExpression(final PsiPrefixExpression prefix) {
1315 final PsiPrefixExpression prefix2 = (PsiPrefixExpression)myMatchingVisitor.getElement();
1317 myMatchingVisitor.setResult(prefix.getOperationTokenType().equals(prefix2.getOperationTokenType())
1318 && myMatchingVisitor.match(prefix.getOperand(), prefix2.getOperand()));
1322 public void visitAssertStatement(final PsiAssertStatement assert1) {
1323 final PsiAssertStatement assert2 = (PsiAssertStatement)myMatchingVisitor.getElement();
1325 myMatchingVisitor.setResult(myMatchingVisitor.match(assert1.getAssertCondition(), assert2.getAssertCondition()) &&
1326 myMatchingVisitor.match(assert1.getAssertDescription(), assert2.getAssertDescription()));
1330 public void visitBreakStatement(final PsiBreakStatement break1) {
1331 final PsiBreakStatement break2 = (PsiBreakStatement)myMatchingVisitor.getElement();
1333 myMatchingVisitor.setResult(myMatchingVisitor.match(break1.getLabelIdentifier(), break2.getLabelIdentifier()));
1337 public void visitContinueStatement(final PsiContinueStatement continue1) {
1338 final PsiContinueStatement continue2 = (PsiContinueStatement)myMatchingVisitor.getElement();
1340 myMatchingVisitor.setResult(myMatchingVisitor.match(continue1.getLabelIdentifier(), continue2.getLabelIdentifier()));
1344 public void visitSuperExpression(final PsiSuperExpression super1) {
1345 myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiSuperExpression);
1349 public void visitThisExpression(final PsiThisExpression this1) {
1350 myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiThisExpression);
1354 public void visitSynchronizedStatement(final PsiSynchronizedStatement synchronized1) {
1355 final PsiSynchronizedStatement synchronized2 = (PsiSynchronizedStatement)myMatchingVisitor.getElement();
1357 myMatchingVisitor.setResult(myMatchingVisitor.match(synchronized1.getLockExpression(), synchronized2.getLockExpression()) &&
1358 myMatchingVisitor.matchSons(synchronized1.getBody(), synchronized2.getBody()));
1362 public void visitThrowStatement(final PsiThrowStatement throw1) {
1363 final PsiThrowStatement throw2 = (PsiThrowStatement)myMatchingVisitor.getElement();
1365 myMatchingVisitor.setResult(myMatchingVisitor.match(throw1.getException(), throw2.getException()));
1369 public void visitParenthesizedExpression(PsiParenthesizedExpression expr) {
1370 if (myMatchingVisitor.getElement() instanceof PsiParenthesizedExpression) {
1371 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(expr, myMatchingVisitor.getElement()));
1374 myMatchingVisitor.setResult(false);
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()));
1386 myMatchingVisitor.setResult(myMatchingVisitor.match(section.getCatchBlock(), section2.getCatchBlock()));
1391 public void visitTryStatement(final PsiTryStatement try1) {
1392 final PsiTryStatement try2 = (PsiTryStatement)myMatchingVisitor.getElement();
1394 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(try1.getTryBlock(), try2.getTryBlock()));
1395 if (!myMatchingVisitor.getResult()) return;
1397 final PsiResourceList resourceList1 = try1.getResourceList();
1398 final PsiCatchSection[] catches1 = try1.getCatchSections();
1399 final PsiCodeBlock finally1 = try1.getFinallyBlock();
1401 final PsiResourceList resourceList2 = try2.getResourceList();
1402 final PsiCatchSection[] catches2 = try2.getCatchSections();
1403 final PsiCodeBlock finally2 = try2.getFinallyBlock();
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
1411 myMatchingVisitor.setResult(false);
1414 final List<PsiElement> unmatchedElements = new ArrayList<>();
1416 if (resourceList1 != null) {
1417 if (resourceList2 == null) {
1418 myMatchingVisitor.setResult(false);
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;
1428 else if (resourceList2 != null){
1429 unmatchedElements.add(resourceList2);
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);
1437 if (pinnedNode != null) {
1438 myMatchingVisitor.setResult(handler.match(catchSection, pinnedNode, myMatchingVisitor.getMatchContext()));
1439 if (!myMatchingVisitor.getResult()) return;
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);
1451 myMatchingVisitor.setResult(false);
1457 if (finally1 != null) {
1458 myMatchingVisitor.setResult(myMatchingVisitor.matchSons(finally1, finally2));
1459 } else if (finally2 != null) {
1460 unmatchedElements.add(finally2);
1463 if (myMatchingVisitor.getResult() && unmatchedElements.size() > 0) {
1464 try2.putUserData(GlobalMatchingVisitor.UNMATCHED_ELEMENTS_KEY, unmatchedElements);
1470 public void visitSwitchLabelStatement(final PsiSwitchLabelStatement case1) {
1471 final PsiSwitchLabelStatement case2 = (PsiSwitchLabelStatement)myMatchingVisitor.getElement();
1473 myMatchingVisitor.setResult(case1.isDefaultCase() == case2.isDefaultCase() &&
1474 myMatchingVisitor.match(case1.getCaseValue(), case2.getCaseValue()));
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()));
1491 myMatchingVisitor.setResult(false);
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
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);
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));
1520 final PsiType type = new1.getType();
1521 myMatchingVisitor.setResult(type != null && type.equals(otherType));
1523 if (myMatchingVisitor.getResult()) {
1524 myMatchingVisitor.matchSons(new1.getArrayInitializer(), other);
1529 if (!(other instanceof PsiNewExpression)) {
1530 myMatchingVisitor.setResult(false);
1533 final PsiNewExpression new2 = (PsiNewExpression)other;
1535 if (classReference != null) {
1536 if (new2.getClassReference() != null) {
1537 myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, new2.getClassReference()) &&
1538 myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1540 if (myMatchingVisitor.getResult()) {
1542 matchArrayDims(new1, new2);
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);
1551 if (element != null && element.getNextSibling() instanceof PsiKeyword) {
1552 ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true);
1554 myMatchingVisitor.setResult(myMatchingVisitor.match(classReference, element.getNextSibling()) &&
1555 myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer()));
1557 ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false);
1558 if (myMatchingVisitor.getResult()) {
1560 matchArrayDims(new1, new2);
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);
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()));
1582 myMatchingVisitor.setResult(false);
1587 public void visitKeyword(PsiKeyword keyword) {
1588 myMatchingVisitor.setResult(myMatchingVisitor.matchText(keyword, myMatchingVisitor.getElement()));
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()));
1600 myMatchingVisitor.setResult(false);
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()));
1612 myMatchingVisitor.setResult(false);
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;
1625 myMatchingVisitor.setResult(matchType(ref, other));
1629 public void visitTypeElement(final PsiTypeElement typeElement) {
1630 final PsiElement other = myMatchingVisitor.getElement(); // might not be a PsiTypeElement
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;
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));
1647 myMatchingVisitor.setResult(matchType(typeElement, other));
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();
1657 final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(identifier);
1658 if (handler instanceof SubstitutionHandler) {
1659 myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(identifier2, myMatchingVisitor.getMatchContext()));
1662 myMatchingVisitor.setResult(myMatchingVisitor.matchText(identifier, identifier2));
1665 if (myMatchingVisitor.getResult()) {
1666 myMatchingVisitor.setResult(matchInAnyOrder(psiTypeParameter.getExtendsList(), parameter.getExtendsList()));
1668 if (myMatchingVisitor.getResult()) {
1669 myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(psiTypeParameter.getAnnotations(), parameter.getAnnotations()));
1674 public void visitClass(PsiClass clazz) {
1675 if (clazz.hasTypeParameters()) {
1678 myMatchingVisitor.match(clazz.getTypeParameterList(), ((PsiClass)myMatchingVisitor.getElement()).getTypeParameterList()));
1680 if (!myMatchingVisitor.getResult()) return;
1685 if (myMatchingVisitor.getElement() instanceof PsiDeclarationStatement &&
1686 myMatchingVisitor.getElement().getFirstChild() instanceof PsiClass
1688 clazz2 = (PsiClass)myMatchingVisitor.getElement().getFirstChild();
1691 clazz2 = (PsiClass)myMatchingVisitor.getElement();
1694 final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getNameIdentifier());
1696 if (clazz.getModifierList().getTextLength() > 0) {
1697 if (!myMatchingVisitor.match(clazz.getModifierList(), clazz2.getModifierList())) {
1698 myMatchingVisitor.setResult(false);
1703 myMatchingVisitor.setResult((myMatchingVisitor.matchText(clazz.getNameIdentifier(), clazz2.getNameIdentifier()) || isTypedVar) &&
1704 compareClasses(clazz, clazz2));
1706 if (myMatchingVisitor.getResult() && isTypedVar) {
1707 PsiElement id = clazz2.getNameIdentifier();
1708 if (id == null) id = clazz2;
1709 myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getNameIdentifier(), id));
1714 public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) {
1715 myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
1716 psiTypeParameterList.getFirstChild(),
1717 myMatchingVisitor.getElement().getFirstChild()
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();
1727 myMatchingVisitor.getMatchContext().pushResult();
1730 final PsiDocComment docComment = method.getDocComment();
1731 if (docComment != null) {
1732 myMatchingVisitor.setResult(myMatchingVisitor.match(docComment, method2));
1733 if (!myMatchingVisitor.getResult()) return;
1735 if (method.hasTypeParameters()) {
1736 myMatchingVisitor.setResult(
1737 myMatchingVisitor.match(method.getTypeParameterList(), ((PsiMethod)myMatchingVisitor.getElement()).getTypeParameterList()));
1739 if (!myMatchingVisitor.getResult()) return;
1742 if (!checkHierarchy(method2, method)) {
1743 myMatchingVisitor.setResult(false);
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()));
1755 saveOrDropResult(methodNameNode, isTypedVar, method2.getNameIdentifier());