diff options
Diffstat (limited to 'java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java')
-rw-r--r-- | java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java | 1637 |
1 files changed, 1637 insertions, 0 deletions
diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java new file mode 100644 index 000000000000..0ea857b16362 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -0,0 +1,1637 @@ +package com.intellij.structuralsearch.impl.matcher; + +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.javadoc.PsiDocTagValue; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.structuralsearch.MatchOptions; +import com.intellij.structuralsearch.MatchResult; +import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler; +import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler; +import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator; +import com.intellij.structuralsearch.impl.matcher.iterators.DocValuesIterator; +import com.intellij.structuralsearch.impl.matcher.iterators.HierarchyNodeIterator; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.structuralsearch.impl.matcher.predicates.NotPredicate; +import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate; +import com.intellij.util.containers.ContainerUtil; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +public class JavaMatchingVisitor extends JavaElementVisitor { + public static final String[] MODIFIERS = { + PsiModifier.PUBLIC, PsiModifier.PROTECTED, PsiModifier.PRIVATE, PsiModifier.STATIC, PsiModifier.ABSTRACT, PsiModifier.FINAL, + PsiModifier.NATIVE, PsiModifier.SYNCHRONIZED, PsiModifier.STRICTFP, PsiModifier.TRANSIENT, PsiModifier.VOLATILE, PsiModifier.DEFAULT + }; + public static final Key<List<PsiCatchSection>> UNMATCHED_CATCH_SECTION_CONTENT_VAR_KEY = Key.create("UnmatchedCatchSection"); + private final GlobalMatchingVisitor myMatchingVisitor; + private PsiClass myClazz; + + static { + Arrays.sort(MODIFIERS); + } + + public JavaMatchingVisitor(GlobalMatchingVisitor matchingVisitor) { + this.myMatchingVisitor = matchingVisitor; + } + + @Override + public void visitComment(PsiComment comment) { + PsiElement comment2 = null; + + if (!(myMatchingVisitor.getElement() instanceof PsiComment)) { + if (myMatchingVisitor.getElement() instanceof PsiMember) { + final PsiElement[] children = myMatchingVisitor.getElement().getChildren(); + if (children[0] instanceof PsiComment) { + comment2 = children[0]; + } + } + } + else { + comment2 = myMatchingVisitor.getElement(); + } + + if (comment2 == null) { + myMatchingVisitor.setResult(false); + return; + } + + final Object userData = comment.getUserData(CompiledPattern.HANDLER_KEY); + + if (userData instanceof String) { + String str = (String)userData; + int end = comment2.getTextLength(); + + if (((PsiComment)comment2).getTokenType() == JavaTokenType.C_STYLE_COMMENT) { + end -= 2; + } + myMatchingVisitor.setResult(((SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(str)).handle( + comment2, + 2, + end, + myMatchingVisitor.getMatchContext() + )); + } + else if (userData instanceof MatchingHandler) { + myMatchingVisitor.setResult(((MatchingHandler)userData).match(comment, comment2, myMatchingVisitor.getMatchContext())); + } + else { + myMatchingVisitor.setResult(comment.getText().equals(comment2.getText())); + } + } + + @Override + public void visitDocTagValue(final PsiDocTagValue value) { + final PsiDocTagValue value2 = (PsiDocTagValue)myMatchingVisitor.getElement(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(value); + + if (isTypedVar) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(value, value2)); + } + else { + myMatchingVisitor.setResult(value.textMatches(value2)); + } + } + + private static boolean isNotInstanceModifier(final PsiModifierList list2) { + return list2.hasModifierProperty(PsiModifier.STATIC) || + list2.hasModifierProperty(PsiModifier.ABSTRACT); + } + + @Override + public final void visitModifierList(final PsiModifierList list) { + final PsiModifierList list2 = (PsiModifierList)myMatchingVisitor.getElement(); + + for (@PsiModifier.ModifierConstant String modifier : MODIFIERS) { + if (list.hasModifierProperty(modifier) && !list2.hasModifierProperty(modifier)) { + myMatchingVisitor.setResult(false); + return; + } + } + + final PsiAnnotation[] annotations = list.getAnnotations(); + if (annotations.length > 0) { + HashSet<PsiAnnotation> set = new HashSet<PsiAnnotation>(Arrays.asList(annotations)); + + for (PsiAnnotation annotation : annotations) { + final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); + + if (nameReferenceElement != null && MatchOptions.MODIFIER_ANNOTATION_NAME.equals(nameReferenceElement.getText())) { + final PsiAnnotationParameterList parameterList = annotation.getParameterList(); + final PsiNameValuePair[] attributes = parameterList.getAttributes(); + + for (PsiNameValuePair pair : attributes) { + final PsiAnnotationMemberValue value = pair.getValue(); + if (value == null) continue; + + if (value instanceof PsiArrayInitializerMemberValue) { + boolean matchedOne = false; + + for (PsiAnnotationMemberValue v : ((PsiArrayInitializerMemberValue)value).getInitializers()) { + @PsiModifier.ModifierConstant String name = StringUtil.stripQuotesAroundValue(v.getText()); + if (MatchOptions.INSTANCE_MODIFIER_NAME.equals(name)) { + if (isNotInstanceModifier(list2)) { + myMatchingVisitor.setResult(false); + return; + } + else { + matchedOne = true; + } + } + else if (list2.hasModifierProperty(name)) { + matchedOne = true; + break; + } + } + + if (!matchedOne) { + myMatchingVisitor.setResult(false); + return; + } + } + else { + @PsiModifier.ModifierConstant String name = StringUtil.stripQuotesAroundValue(value.getText()); + if (MatchOptions.INSTANCE_MODIFIER_NAME.equals(name)) { + if (isNotInstanceModifier(list2)) { + myMatchingVisitor.setResult(false); + return; + } + } + else if (!list2.hasModifierProperty(name)) { + myMatchingVisitor.setResult(false); + return; + } + } + } + + set.remove(annotation); + } + } + + myMatchingVisitor.setResult(set.isEmpty() || myMatchingVisitor + .matchInAnyOrder(set.toArray(new PsiAnnotation[set.size()]), list2.getAnnotations())); + } + else { + myMatchingVisitor.setResult(true); + } + } + + @Override + public void visitDocTag(final PsiDocTag tag) { + final PsiDocTag tag2 = (PsiDocTag)myMatchingVisitor.getElement(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(tag.getNameElement()); + + myMatchingVisitor.setResult(isTypedVar || tag.getName().equals(tag2.getName())); + + PsiElement psiDocTagValue = tag.getValueElement(); + boolean isTypedValue = false; + + if (myMatchingVisitor.getResult() && psiDocTagValue != null) { + final PsiElement[] children = psiDocTagValue.getChildren(); + if (children.length == 1) { + psiDocTagValue = children[0]; + } + isTypedValue = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(psiDocTagValue); + + if (isTypedValue) { + if (tag2.getValueElement() != null) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(psiDocTagValue, tag2.getValueElement())); + } + else { + myMatchingVisitor.setResult(allowsAbsenceOfMatch(psiDocTagValue)); + } + } + } + + if (myMatchingVisitor.getResult() && !isTypedValue) { + myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder( + new DocValuesIterator(tag.getFirstChild()), + new DocValuesIterator(tag2.getFirstChild()) + )); + } + + if (myMatchingVisitor.getResult() && isTypedVar) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(tag.getNameElement(), tag2.getNameElement())); + } + } + + private boolean allowsAbsenceOfMatch(final PsiElement element) { + MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(element); + + if (handler instanceof SubstitutionHandler && + ((SubstitutionHandler)handler).getMinOccurs() == 0) { + return true; + } + return false; + } + + @Override + public void visitDocComment(final PsiDocComment comment) { + PsiDocComment comment2; + + if (myMatchingVisitor.getElement() instanceof PsiDocCommentOwner) { + comment2 = ((PsiDocCommentOwner)myMatchingVisitor.getElement()).getDocComment(); + + if (comment2 == null) { + // doc comment are not collapsed for inner classes! + myMatchingVisitor.setResult(false); + return; + } + } + else { + comment2 = (PsiDocComment)myMatchingVisitor.getElement(); + + if (myMatchingVisitor.getElement().getParent() instanceof PsiDocCommentOwner) { + myMatchingVisitor.setResult(false); + return; // we should matched the doc before + } + } + + if (comment.getTags().length > 0) { + myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(comment.getTags(), comment2.getTags())); + } + else { + visitComment(comment); + } + } + + @Override + public void visitElement(PsiElement el) { + myMatchingVisitor.setResult(el.textMatches(myMatchingVisitor.getElement())); + } + + @Override + public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) { + final PsiArrayInitializerExpression expr2 = (PsiArrayInitializerExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially( + new ArrayBackedNodeIterator(expression.getInitializers()), + new ArrayBackedNodeIterator(expr2.getInitializers()) + )); + } + + @Override + public void visitClassInitializer(PsiClassInitializer initializer) { + PsiClassInitializer initializer2 = (PsiClassInitializer)myMatchingVisitor.getElement(); + myMatchingVisitor.setResult(myMatchingVisitor.match(initializer.getModifierList(), initializer2.getModifierList()) && + myMatchingVisitor.match(initializer.getBody(), initializer2.getBody())); + } + + @Override + public void visitCodeBlock(PsiCodeBlock block) { + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, myMatchingVisitor.getElement())); + } + + @Override + public void visitJavaToken(final PsiJavaToken token) { + PsiElement element = myMatchingVisitor.getElement(); + boolean result; + + if (!(element instanceof PsiJavaToken)) { + result = token.textMatches(element); + } else { + final PsiJavaToken anotherToken = (PsiJavaToken)element; + + result = token.getTokenType() == anotherToken.getTokenType() && token.textMatches(anotherToken); + } + + myMatchingVisitor.setResult(result); + } + + @Override + public void visitAnnotation(PsiAnnotation annotation) { + final PsiAnnotation psiAnnotation = (PsiAnnotation)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(annotation.getNameReferenceElement(), psiAnnotation.getNameReferenceElement()) && + myMatchingVisitor + .matchInAnyOrder(annotation.getParameterList().getAttributes(), + psiAnnotation.getParameterList().getAttributes())); + } + + @Override + public void visitNameValuePair(PsiNameValuePair pair) { + final PsiIdentifier nameIdentifier = pair.getNameIdentifier(); + + final PsiNameValuePair elementNameValuePair = (PsiNameValuePair)myMatchingVisitor.getElement(); + final PsiIdentifier otherIdentifier = elementNameValuePair.getNameIdentifier(); + + final PsiAnnotationMemberValue annotationInitializer = pair.getValue(); + if (annotationInitializer != null) { + final boolean isTypedInitializer = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(annotationInitializer) && + annotationInitializer instanceof PsiReferenceExpression; + + myMatchingVisitor.setResult(myMatchingVisitor.match(annotationInitializer, elementNameValuePair.getValue()) || + (isTypedInitializer && + elementNameValuePair.getValue() == null && + allowsAbsenceOfMatch(annotationInitializer) + )); + } + if (myMatchingVisitor.getResult()) { + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(nameIdentifier); + + if (handler instanceof SubstitutionHandler) { + myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(otherIdentifier, myMatchingVisitor.getMatchContext())); + } + else if (nameIdentifier != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(nameIdentifier, otherIdentifier)); + } + else { + myMatchingVisitor.setResult(otherIdentifier == null || otherIdentifier.getText().equals("value")); + } + } + } + + private boolean checkHierarchy(PsiMember element, PsiMember patternElement) { + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(patternElement); + if (handler instanceof SubstitutionHandler) { + final SubstitutionHandler handler2 = (SubstitutionHandler)handler; + + if (!handler2.isSubtype()) { + if (handler2.isStrictSubtype()) { + // check if element is declared not in current class (in ancestors) + return element.getContainingClass() != myClazz; + } + } + else { + return true; + } + } + + // check if element is declared in current class (not in ancestors) + return element.getContainingClass() == myClazz; + } + + @Override + public void visitField(PsiField psiField) { + if (!checkHierarchy((PsiField)myMatchingVisitor.getElement(), psiField)) { + myMatchingVisitor.setResult(false); + return; + } + super.visitField(psiField); + } + + @Override + public void visitAnonymousClass(final PsiAnonymousClass clazz) { + final PsiAnonymousClass clazz2 = (PsiAnonymousClass)myMatchingVisitor.getElement(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getFirstChild()); + + myMatchingVisitor.setResult((myMatchingVisitor.match(clazz.getBaseClassReference(), clazz2.getBaseClassReference()) || isTypedVar) && + myMatchingVisitor.matchSons(clazz.getArgumentList(), clazz2.getArgumentList()) && + compareClasses(clazz, clazz2)); + + if (myMatchingVisitor.getResult() && isTypedVar) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getFirstChild(), clazz2.getFirstChild())); + } + } + + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + final PsiLambdaExpression expression2 = (PsiLambdaExpression)myMatchingVisitor.getElement(); + boolean result = true; + final PsiParameterList parameterList1 = expression.getParameterList(); + if (parameterList1.getParametersCount() != 0) { + result = myMatchingVisitor.matchSons(parameterList1, expression2.getParameterList()); + } + final PsiElement body1 = getElementToMatch(expression.getBody()); + if (body1 != null) { + result = myMatchingVisitor.matchSequentially(body1, getElementToMatch(expression2.getBody())); + } + myMatchingVisitor.setResult(result); + } + + private static PsiElement getElementToMatch(PsiElement element) { + if (element instanceof PsiCodeBlock) { + element = PsiTreeUtil.getChildOfAnyType(element, PsiStatement.class, PsiComment.class); + } + if (element instanceof PsiExpressionStatement) { + element = ((PsiExpressionStatement)element).getExpression(); + } + if (element instanceof PsiReturnStatement) { + element = ((PsiReturnStatement)element).getReturnValue(); + } + return element; + } + + protected boolean matchInAnyOrder(final PsiReferenceList elements, final PsiReferenceList elements2, GlobalMatchingVisitor visitor) { + if ((elements == null && visitor.isLeftLooseMatching()) || + elements == elements2 // null + ) { + return true; + } + + return visitor.matchInAnyOrder( + elements.getReferenceElements(), + (elements2 != null) ? elements2.getReferenceElements() : PsiElement.EMPTY_ARRAY + ); + } + + private boolean compareClasses(final PsiClass clazz, final PsiClass clazz2) { + final PsiClass saveClazz = this.myClazz; + final MatchContext.MatchedElementsListener oldListener = myMatchingVisitor.getMatchContext().getMatchedElementsListener(); + + this.myClazz = clazz2; + + final CompiledPattern pattern = myMatchingVisitor.getMatchContext().getPattern(); + assert pattern instanceof JavaCompiledPattern; + final JavaCompiledPattern javaPattern = (JavaCompiledPattern)pattern; + + final String unmatchedHandlerName = clazz.getUserData(JavaCompiledPattern.ALL_CLASS_CONTENT_VAR_NAME_KEY); + final MatchingHandler allRemainingClassContentElementHandler = unmatchedHandlerName != null ? pattern.getHandler(unmatchedHandlerName) : null; + MatchContext.MatchedElementsListener newListener = null; + + assert javaPattern instanceof JavaCompiledPattern; + if (allRemainingClassContentElementHandler != null) { + myMatchingVisitor.getMatchContext().setMatchedElementsListener( + newListener = new MatchContext.MatchedElementsListener() { + private Set<PsiElement> myMatchedElements; + + public void matchedElements(Collection<PsiElement> matchedElements) { + if (matchedElements == null) return; + if (myMatchedElements == null) { + myMatchedElements = new HashSet<PsiElement>(matchedElements); + } + else { + myMatchedElements.addAll(matchedElements); + } + } + + public void commitUnmatched() { + final SubstitutionHandler handler = (SubstitutionHandler)allRemainingClassContentElementHandler; + + for (PsiElement el = clazz2.getFirstChild(); el != null; el = el.getNextSibling()) { + if (el instanceof PsiMember && (myMatchedElements == null || !myMatchedElements.contains(el))) { + handler.handle(el, myMatchingVisitor.getMatchContext()); + } + } + } + } + ); + } + + boolean result = false; + try { + final boolean templateIsInterface = clazz.isInterface(); + if (templateIsInterface != clazz2.isInterface()) return false; + if (templateIsInterface && clazz.isAnnotationType() && !clazz2.isAnnotationType()) return false; + final boolean templateIsEnum = clazz.isEnum(); + if (templateIsEnum && !clazz2.isEnum()) return false; + + if (!matchInAnyOrder(clazz.getExtendsList(), clazz2.getExtendsList(), myMatchingVisitor)) { + return false; + } + + // check if implements is in extended classes implements + final PsiReferenceList implementsList = clazz.getImplementsList(); + if (implementsList != null) { + if (!matchInAnyOrder(implementsList, clazz2.getImplementsList(), myMatchingVisitor)) { + final PsiReferenceList anotherExtendsList = clazz2.getExtendsList(); + final PsiJavaCodeReferenceElement[] referenceElements = implementsList.getReferenceElements(); + + boolean accepted = false; + + if (referenceElements.length > 0 && anotherExtendsList != null) { + final HierarchyNodeIterator iterator = new HierarchyNodeIterator(clazz2, true, true, false); + + accepted = myMatchingVisitor.matchInAnyOrder(new ArrayBackedNodeIterator(referenceElements), iterator); + } + + if (!accepted) return false; + } + } + + final PsiField[] fields = clazz.getFields(); + + if (fields.length > 0) { + final PsiField[] fields2 = javaPattern.isRequestsSuperFields() ? + clazz2.getAllFields() : + clazz2.getFields(); + + if (!myMatchingVisitor.matchInAnyOrder(fields, fields2)) { + return false; + } + } + + final PsiMethod[] methods = clazz.getMethods(); + + if (methods.length > 0) { + final PsiMethod[] methods2 = javaPattern.isRequestsSuperMethods() ? + clazz2.getAllMethods() : + clazz2.getMethods(); + + if (!myMatchingVisitor.matchInAnyOrder(methods, methods2)) { + return false; + } + } + + final PsiClass[] nestedClasses = clazz.getInnerClasses(); + + if (nestedClasses.length > 0) { + final PsiClass[] nestedClasses2 = javaPattern.isRequestsSuperInners() ? + clazz2.getAllInnerClasses() : + clazz2.getInnerClasses(); + + if (!myMatchingVisitor.matchInAnyOrder(nestedClasses, nestedClasses2)) { + return false; + } + } + + final PsiClassInitializer[] initializers = clazz.getInitializers(); + if (initializers.length > 0) { + final PsiClassInitializer[] initializers2 = clazz2.getInitializers(); + + if (!myMatchingVisitor.matchInAnyOrder(initializers, initializers2)) { + return false; + } + } + + result = true; + return result; + } + finally { + if (result && newListener != null) newListener.commitUnmatched(); + this.myClazz = saveClazz; + myMatchingVisitor.getMatchContext().setMatchedElementsListener(oldListener); + } + } + + private boolean compareBody(final PsiElement el1, final PsiElement el2) { + PsiElement compareElement1 = el1; + PsiElement compareElement2 = el2; + + if (myMatchingVisitor.getMatchContext().getOptions().isLooseMatching()) { + if (el1 instanceof PsiBlockStatement) { + compareElement1 = ((PsiBlockStatement)el1).getCodeBlock().getFirstChild(); + } + + if (el2 instanceof PsiBlockStatement) { + compareElement2 = ((PsiBlockStatement)el2).getCodeBlock().getFirstChild(); + } + } + + return myMatchingVisitor.matchSequentially(compareElement1, compareElement2); + } + + @Override + public void visitArrayAccessExpression(final PsiArrayAccessExpression slice) { + final PsiArrayAccessExpression slice2 = (PsiArrayAccessExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(slice.getArrayExpression(), slice2.getArrayExpression()) && + myMatchingVisitor.match(slice.getIndexExpression(), slice2.getIndexExpression())); + } + + @Override + public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { + final PsiElement element = myMatchingVisitor.getElement(); + if (!(element instanceof PsiMethodReferenceExpression)) { + myMatchingVisitor.setResult(false); + return; + } + super.visitMethodReferenceExpression(expression); + } + + @Override + public void visitReferenceExpression(final PsiReferenceExpression reference) { + final PsiExpression qualifier = reference.getQualifierExpression(); + + final PsiElement nameElement = reference.getReferenceNameElement(); + final MatchContext context = myMatchingVisitor.getMatchContext(); + MatchingHandler _handler = nameElement != null ? context.getPattern().getHandlerSimple(nameElement) : null; + if (!(_handler instanceof SubstitutionHandler)) _handler = context.getPattern().getHandlerSimple(reference); + + final PsiElement element = myMatchingVisitor.getElement(); + PsiElement other = element instanceof PsiExpression && context.getOptions().isLooseMatching() ? + PsiUtil.skipParenthesizedExprDown((PsiExpression)element) : + element; + if (_handler instanceof SubstitutionHandler && + !(context.getPattern().getHandlerSimple(qualifier) instanceof SubstitutionHandler) && + !(qualifier instanceof PsiThisExpression) + ) { + if (other instanceof PsiReferenceExpression) { + final PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression)other; + + final PsiExpression qualifier2 = psiReferenceExpression.getQualifierExpression(); + if (qualifier2 == null || (context.getOptions().isLooseMatching() && qualifier2 instanceof PsiThisExpression)) { + other = psiReferenceExpression.getReferenceNameElement(); + } + } + + final SubstitutionHandler handler = (SubstitutionHandler)_handler; + if (handler.isSubtype() || handler.isStrictSubtype()) { + myMatchingVisitor.setResult(checkMatchWithingHierarchy(other, handler, reference)); + } + else { + myMatchingVisitor.setResult(handler.handle(other, context)); + } + + return; + } + + if (!(other instanceof PsiReferenceExpression)) { + myMatchingVisitor.setResult(false); + return; + } + + final PsiReferenceExpression reference2 = (PsiReferenceExpression)other; + + // just variable + final PsiExpression reference2Qualifier = reference2.getQualifierExpression(); + if (qualifier == null && reference2Qualifier == null) { + myMatchingVisitor.setResult(reference.getReferenceNameElement().textMatches(reference2.getReferenceNameElement())); + return; + } + + // handle field selection + if (!(other.getParent() instanceof PsiMethodCallExpression) && qualifier != null) { + final PsiElement referenceElement = reference.getReferenceNameElement(); + final PsiElement referenceElement2 = reference2.getReferenceNameElement(); + + if (context.getPattern().isTypedVar(referenceElement)) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(referenceElement, referenceElement2)); + } + else { + myMatchingVisitor.setResult( + (referenceElement2 != null && referenceElement != null && referenceElement.textMatches(referenceElement2)) || + referenceElement == referenceElement2); + } + + if (!myMatchingVisitor.getResult()) { + return; + } + if (reference2Qualifier != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, reference2Qualifier)); + } + else { + final PsiElement referencedElement = MatchUtils.getReferencedElement(other); + if (referencedElement instanceof PsiField) { + final PsiField field = (PsiField)referencedElement; + if (qualifier instanceof PsiThisExpression) { + myMatchingVisitor.setResult(!field.hasModifierProperty(PsiModifier.STATIC)); + return; + } + } + final MatchingHandler handler = context.getPattern().getHandler(qualifier); + matchImplicitQualifier(handler, referencedElement, context); + } + + return; + } + + myMatchingVisitor.setResult(false); + } + + private static int countCStyleArrayDeclarationDims(final PsiElement type2) { + if (type2 != null) { + final PsiElement parentElement = type2.getParent(); + + if (parentElement instanceof PsiVariable) { + final PsiIdentifier psiIdentifier = ((PsiVariable)parentElement).getNameIdentifier(); + if (psiIdentifier == null) return 0; + + int count = 0; + for (PsiElement sibling = psiIdentifier.getNextSibling(); sibling != null; sibling = sibling.getNextSibling()) { + if (sibling instanceof PsiJavaToken) { + final IElementType tokenType = ((PsiJavaToken)sibling).getTokenType(); + if (tokenType == JavaTokenType.LBRACKET) ++count; + else if (tokenType != JavaTokenType.RBRACKET) break; + } + } + + return count; + } + } + return 0; + } + + private void copyResults(final MatchResultImpl ourResult) { + if (ourResult.hasSons()) { + for (MatchResult son : ourResult.getAllSons()) { + myMatchingVisitor.getMatchContext().getResult().addSon((MatchResultImpl)son); + } + } + } + + private boolean matchType(final PsiElement _type, final PsiElement _type2) { + PsiElement el = _type; + PsiElement el2 = _type2; + PsiType type1 = null; + PsiType type2 = null; + + // check for generics + if (_type instanceof PsiTypeElement && + ((PsiTypeElement)_type).getInnermostComponentReferenceElement() != null + ) { + el = ((PsiTypeElement)_type).getInnermostComponentReferenceElement(); + type1 = ((PsiTypeElement)_type).getType(); + } + + if (_type2 instanceof PsiTypeElement && + ((PsiTypeElement)_type2).getInnermostComponentReferenceElement() != null + ) { + el2 = ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); + type2 = ((PsiTypeElement)_type2).getType(); + } + + PsiElement[] typeparams = null; + if (el2 instanceof PsiJavaCodeReferenceElement) { + typeparams = ((PsiJavaCodeReferenceElement)el2).getParameterList().getTypeParameterElements(); + if (typeparams.length > 0) { + el2 = ((PsiJavaCodeReferenceElement)el2).getReferenceNameElement(); + } + } + else if (el2 instanceof PsiTypeParameter) { + el2 = ((PsiTypeParameter)el2).getNameIdentifier(); + } + else if (el2 instanceof PsiClass && ((PsiClass)el2).hasTypeParameters() + ) { + typeparams = ((PsiClass)el2).getTypeParameters(); + el2 = ((PsiClass)el2).getNameIdentifier(); + } + else if (el2 instanceof PsiMethod && ((PsiMethod)el2).hasTypeParameters() + ) { + typeparams = ((PsiMethod)_type2).getTypeParameters(); + el2 = ((PsiMethod)_type2).getNameIdentifier(); + } + + PsiReferenceParameterList list = null; + if (el instanceof PsiJavaCodeReferenceElement) { + list = ((PsiJavaCodeReferenceElement)el).getParameterList(); + } + + if (list != null && list.getTypeParameterElements().length > 0) { + boolean result = typeparams != null && + myMatchingVisitor.matchInAnyOrder( + list.getTypeParameterElements(), + typeparams + ); + + if (!result) return false; + el = ((PsiJavaCodeReferenceElement)el).getReferenceNameElement(); + } + else { + if (_type2 instanceof PsiTypeElement) { + type2 = ((PsiTypeElement)_type2).getType(); + + if (typeparams == null || typeparams.length == 0) { + final PsiJavaCodeReferenceElement innermostComponentReferenceElement = + ((PsiTypeElement)_type2).getInnermostComponentReferenceElement(); + if (innermostComponentReferenceElement != null) el2 = innermostComponentReferenceElement; + } + else { + el2 = _type2; + } + } + } + + final int array2Dims = (type2 != null ? type2.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type2); + final int arrayDims = (type1 != null ? type1.getArrayDimensions() : 0) + countCStyleArrayDeclarationDims(_type); + + if (myMatchingVisitor.getMatchContext().getPattern().isTypedVar(el)) { + final SubstitutionHandler handler = (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(el); + + RegExpPredicate regExpPredicate = null; + + if (arrayDims != 0) { + if (arrayDims != array2Dims) { + return false; + } + } + else if (array2Dims != 0) { + regExpPredicate = MatchingHandler.getSimpleRegExpPredicate(handler); + + if (regExpPredicate != null) { + regExpPredicate.setNodeTextGenerator(new RegExpPredicate.NodeTextGenerator() { + public String getText(PsiElement element) { + StringBuilder builder = new StringBuilder(RegExpPredicate.getMeaningfulText(element)); + for (int i = 0; i < array2Dims; ++i) builder.append("[]"); + return builder.toString(); + } + }); + } + } + + try { + if (handler.isSubtype() || handler.isStrictSubtype()) { + return checkMatchWithingHierarchy(el2, handler, el); + } + else { + return handler.handle(el2, myMatchingVisitor.getMatchContext()); + } + } + finally { + if (regExpPredicate != null) regExpPredicate.setNodeTextGenerator(null); + } + } + + if (array2Dims != arrayDims) { + return false; + } + + if (el instanceof PsiIdentifier) { + final PsiElement parent = el.getParent(); + if (parent instanceof PsiJavaCodeReferenceElement) { + el = parent; + } + } + if (el2 instanceof PsiIdentifier) { + final PsiElement parent = el2.getParent(); + if (parent instanceof PsiJavaCodeReferenceElement) { + el2 = parent; + } + } + final String text = stripTypeParameters(el.getText()); + if (text.indexOf('.') == -1 || !(el2 instanceof PsiJavaReference)) { + return MatchUtils.compareWithNoDifferenceToPackage(text, stripTypeParameters(el2.getText())); + } + else { + PsiElement element2 = ((PsiJavaReference)el2).resolve(); + + if (element2 != null) { + return text.equals(((PsiClass)element2).getQualifiedName()); + } + else { + return MatchUtils.compareWithNoDifferenceToPackage(text, el2.getText()); + } + } + } + + private static String stripTypeParameters(String string) { + final int index = string.indexOf('<'); + if (index == -1) { + return string; + } + return string.substring(0, index); + } + + private boolean checkMatchWithingHierarchy(PsiElement el2, SubstitutionHandler handler, PsiElement context) { + boolean includeInterfaces = true; + boolean includeClasses = true; + final PsiElement contextParent = context.getParent(); + + if (contextParent instanceof PsiReferenceList) { + final PsiElement grandParentContext = contextParent.getParent(); + + if (grandParentContext instanceof PsiClass) { + final PsiClass psiClass = (PsiClass)grandParentContext; + + if (contextParent == psiClass.getExtendsList()) { + includeInterfaces = psiClass.isInterface(); + } + else if (contextParent == psiClass.getImplementsList()) { + includeClasses = false; + } + } + } + + // is type2 is (strict) subtype of type + final NodeIterator node = new HierarchyNodeIterator(el2, includeClasses, includeInterfaces); + + if (handler.isStrictSubtype()) { + node.advance(); + } + + final boolean notPredicate = handler.getPredicate() instanceof NotPredicate; + while (node.hasNext() && !handler.validate(node.current(), 0, -1, myMatchingVisitor.getMatchContext())) { + if (notPredicate) return false; + node.advance(); + } + + if (node.hasNext()) { + handler.addResult(el2, 0, -1, myMatchingVisitor.getMatchContext()); + return true; + } + else { + return false; + } + } + + @Override + public void visitConditionalExpression(final PsiConditionalExpression cond) { + final PsiConditionalExpression cond2 = (PsiConditionalExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(cond.getCondition(), cond2.getCondition()) && + myMatchingVisitor.matchSons(cond, cond2)); + } + + @Override + public void visitPolyadicExpression(PsiPolyadicExpression expression) { + PsiPolyadicExpression expr2 = (PsiPolyadicExpression)myMatchingVisitor.getElement(); + + boolean result = expression.getOperationTokenType().equals(expr2.getOperationTokenType()); + if (result) { + PsiExpression[] operands1 = expression.getOperands(); + PsiExpression[] operands2 = expr2.getOperands(); + if (operands1.length != operands2.length) { + result = false; + } + else { + for (int i = 0; i < operands1.length; i++) { + PsiExpression e1 = operands1[i]; + PsiExpression e2 = operands2[i]; + if (!myMatchingVisitor.match(e1, e2)) { + result = false; + break; + } + } + } + } + + myMatchingVisitor.setResult(result); + } + + @Override + public void visitVariable(final PsiVariable var) { + myMatchingVisitor.getMatchContext().pushResult(); + final PsiIdentifier nameIdentifier = var.getNameIdentifier(); + + boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(nameIdentifier); + boolean isTypedInitializer = var.getInitializer() != null && + myMatchingVisitor.getMatchContext().getPattern().isTypedVar(var.getInitializer()) && + var.getInitializer() instanceof PsiReferenceExpression; + final PsiVariable var2 = (PsiVariable)myMatchingVisitor.getElement(); + + try { + myMatchingVisitor.setResult((var.getName().equals(var2.getName()) || isTypedVar) && + ((var.getParent() instanceof PsiClass && ((PsiClass)var.getParent()).isInterface()) || + myMatchingVisitor.match(var.getModifierList(), var2.getModifierList()))); + if (myMatchingVisitor.getResult()) { + final PsiTypeElement typeElement1 = var.getTypeElement(); + if (typeElement1 != null) { + PsiTypeElement typeElement2 = var2.getTypeElement(); + if (typeElement2 == null) { + typeElement2 = JavaPsiFacade.getElementFactory(var2.getProject()).createTypeElement(var2.getType()); + } + myMatchingVisitor.setResult(myMatchingVisitor.match(typeElement1, typeElement2)); + } + } + + if (myMatchingVisitor.getResult()) { + // Check initializer + final PsiExpression var2Initializer = var2.getInitializer(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(var.getInitializer(), var2Initializer) || + (isTypedInitializer && + var2Initializer == null && + allowsAbsenceOfMatch(var.getInitializer()) + )); + } + + if (myMatchingVisitor.getResult() && var instanceof PsiParameter && var.getParent() instanceof PsiCatchSection) { + myMatchingVisitor.setResult(myMatchingVisitor.match( + ((PsiCatchSection)var.getParent()).getCatchBlock(), + ((PsiCatchSection)var2.getParent()).getCatchBlock() + )); + } + + if (myMatchingVisitor.getResult() && isTypedVar) { + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(nameIdentifier, var2.getNameIdentifier())); + } + } + finally { + saveOrDropResult(nameIdentifier, isTypedVar, var2.getNameIdentifier()); + } + } + + private void matchArrayDims(final PsiNewExpression new1, final PsiNewExpression new2) { + final PsiExpression[] arrayDims = new1.getArrayDimensions(); + final PsiExpression[] arrayDims2 = new2.getArrayDimensions(); + + if (arrayDims.length == arrayDims2.length && arrayDims.length != 0) { + for (int i = 0; i < arrayDims.length; ++i) { + myMatchingVisitor.setResult(myMatchingVisitor.match(arrayDims[i], arrayDims2[i])); + if (!myMatchingVisitor.getResult()) return; + } + } + else { + myMatchingVisitor.setResult((arrayDims == arrayDims2) && myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList())); + } + } + + private void saveOrDropResult(final PsiIdentifier methodNameNode, final boolean typedVar, final PsiIdentifier methodNameNode2) { + MatchResultImpl ourResult = myMatchingVisitor.getMatchContext().hasResult() ? myMatchingVisitor.getMatchContext().getResult() : null; + myMatchingVisitor.getMatchContext().popResult(); + + if (myMatchingVisitor.getResult()) { + if (typedVar) { + final SubstitutionHandler handler = + (SubstitutionHandler)myMatchingVisitor.getMatchContext().getPattern().getHandler(methodNameNode); + if (ourResult != null) ourResult.setScopeMatch(true); + handler.setNestedResult(ourResult); + myMatchingVisitor.setResult(handler.handle(methodNameNode2, myMatchingVisitor.getMatchContext())); + + if (handler.getNestedResult() != null) { // some constraint prevent from adding + handler.setNestedResult(null); + copyResults(ourResult); + } + } + else if (ourResult != null) { + copyResults(ourResult); + } + } + } + + private void matchImplicitQualifier(MatchingHandler matchingHandler, PsiElement target, MatchContext context) { + if (!(matchingHandler instanceof SubstitutionHandler)) { + myMatchingVisitor.setResult(false); + return; + } + final SubstitutionHandler substitutionHandler = (SubstitutionHandler)matchingHandler; + final MatchPredicate predicate = substitutionHandler.getPredicate(); + if (substitutionHandler.getMinOccurs() != 0) { + myMatchingVisitor.setResult(false); + return; + } + if (predicate == null) { + myMatchingVisitor.setResult(true); + return; + } + if (target == null) { + myMatchingVisitor.setResult(false); + return; + } + if (target instanceof PsiModifierListOwner && ((PsiModifierListOwner)target).hasModifierProperty(PsiModifier.STATIC)) { + myMatchingVisitor.setResult(predicate.match(null, PsiTreeUtil.getParentOfType(target, PsiClass.class), context)); + } else { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(target.getProject()); + final PsiExpression implicitReference = factory.createExpressionFromText("this", target); + myMatchingVisitor.setResult(predicate.match(null, implicitReference, context)); + } + } + + @Override + public void visitMethodCallExpression(final PsiMethodCallExpression mcall) { + final PsiElement element = myMatchingVisitor.getElement(); + if (!(element instanceof PsiMethodCallExpression)) { + myMatchingVisitor.setResult(false); + return; + } + final PsiMethodCallExpression mcall2 = (PsiMethodCallExpression)element; + final PsiReferenceExpression mcallRef1 = mcall.getMethodExpression(); + final PsiReferenceExpression mcallRef2 = mcall2.getMethodExpression(); + + final String mcallname1 = mcallRef1.getReferenceName(); + final String mcallname2 = mcallRef2.getReferenceName(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(mcallRef1.getReferenceNameElement()); + + if (mcallname1 != null && !mcallname1.equals(mcallname2) && !isTypedVar) { + myMatchingVisitor.setResult(false); + return; + } + + final PsiExpression qualifier = mcallRef1.getQualifierExpression(); + final PsiExpression elementQualifier = mcallRef2.getQualifierExpression(); + if (qualifier != null) { + + if (elementQualifier != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(qualifier, elementQualifier)); + if (!myMatchingVisitor.getResult()) return; + } + else { + final PsiMethod method = mcall2.resolveMethod(); + if (method != null) { + if (qualifier instanceof PsiThisExpression) { + myMatchingVisitor.setResult(!method.hasModifierProperty(PsiModifier.STATIC)); + return; + } + } + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(qualifier); + matchImplicitQualifier(handler, method, myMatchingVisitor.getMatchContext()); + if (!myMatchingVisitor.getResult()) { + return; + } + } + } + else if (elementQualifier != null) { + myMatchingVisitor.setResult(false); + return; + } + + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(mcall.getArgumentList(), mcall2.getArgumentList())); + + if (myMatchingVisitor.getResult() && isTypedVar) { + boolean res = myMatchingVisitor.getResult(); + res &= myMatchingVisitor.handleTypedElement(mcallRef1.getReferenceNameElement(), mcallRef2.getReferenceNameElement()); + myMatchingVisitor.setResult(res); + } + } + + @Override + public void visitExpressionStatement(final PsiExpressionStatement expr) { + final PsiExpressionStatement expr2 = (PsiExpressionStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getExpression(), expr2.getExpression())); + } + + @Override + public void visitLiteralExpression(final PsiLiteralExpression const1) { + final PsiLiteralExpression const2 = (PsiLiteralExpression)myMatchingVisitor.getElement(); + + MatchingHandler handler = (MatchingHandler)const1.getUserData(CompiledPattern.HANDLER_KEY); + + if (handler instanceof SubstitutionHandler) { + int offset = 0; + int length = const2.getTextLength(); + final String text = const2.getText(); + + if (length > 2 && text.charAt(0) == '"' && text.charAt(length - 1) == '"') { + length--; + offset++; + } + myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(const2, offset, length, myMatchingVisitor.getMatchContext())); + } + else if (handler != null) { + myMatchingVisitor.setResult(handler.match(const1, const2, myMatchingVisitor.getMatchContext())); + } + else { + myMatchingVisitor.setResult(const1.textMatches(const2)); + } + } + + @Override + public void visitAssignmentExpression(final PsiAssignmentExpression assign) { + final PsiAssignmentExpression assign2 = (PsiAssignmentExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(assign.getOperationTokenType().equals(assign2.getOperationTokenType()) && + myMatchingVisitor.match(assign.getLExpression(), assign2.getLExpression()) && + myMatchingVisitor.match(assign.getRExpression(), assign2.getRExpression())); + } + + @Override + public void visitIfStatement(final PsiIfStatement if1) { + final PsiIfStatement if2 = (PsiIfStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(if1.getCondition(), if2.getCondition()) && + compareBody(if1.getThenBranch(), if2.getThenBranch()) && + compareBody(if1.getElseBranch(), if2.getElseBranch())); + } + + @Override + public void visitSwitchStatement(final PsiSwitchStatement switch1) { + final PsiSwitchStatement switch2 = (PsiSwitchStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(switch1.getExpression(), switch2.getExpression()) && + myMatchingVisitor.matchSons(switch1.getBody(), switch2.getBody())); + } + + @Override + public void visitForStatement(final PsiForStatement for1) { + final PsiForStatement for2 = (PsiForStatement)myMatchingVisitor.getElement(); + + final PsiStatement initialization = for1.getInitialization(); + MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(initialization); + + myMatchingVisitor.setResult(handler.match(initialization, for2.getInitialization(), myMatchingVisitor.getMatchContext()) && + myMatchingVisitor.match(for1.getCondition(), for2.getCondition()) && + myMatchingVisitor.match(for1.getUpdate(), for2.getUpdate()) && + compareBody(for1.getBody(), for2.getBody())); + } + + @Override + public void visitForeachStatement(PsiForeachStatement for1) { + final PsiForeachStatement for2 = (PsiForeachStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(for1.getIterationParameter(), for2.getIterationParameter()) && + myMatchingVisitor.match(for1.getIteratedValue(), for2.getIteratedValue()) && + compareBody(for1.getBody(), for2.getBody())); + } + + @Override + public void visitWhileStatement(final PsiWhileStatement while1) { + final PsiWhileStatement while2 = (PsiWhileStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) && + compareBody(while1.getBody(), while2.getBody())); + } + + @Override + public void visitBlockStatement(final PsiBlockStatement block) { + if (myMatchingVisitor.getElement() instanceof PsiCodeBlock && + !(myMatchingVisitor.getElement().getParent() instanceof PsiBlockStatement) + ) { + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block.getCodeBlock(), myMatchingVisitor.getElement())); + } + else { + final PsiBlockStatement block2 = (PsiBlockStatement)myMatchingVisitor.getElement(); + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(block, block2)); + } + } + + @Override + public void visitDeclarationStatement(final PsiDeclarationStatement dcl) { + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)myMatchingVisitor.getElement(); + myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(dcl.getDeclaredElements(), declaration.getDeclaredElements())); + } + + @Override + public void visitDoWhileStatement(final PsiDoWhileStatement while1) { + final PsiDoWhileStatement while2 = (PsiDoWhileStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(while1.getCondition(), while2.getCondition()) && + compareBody(while1.getBody(), while2.getBody())); + } + + @Override + public void visitReturnStatement(final PsiReturnStatement return1) { + final PsiReturnStatement return2 = (PsiReturnStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(return1.getReturnValue(), return2.getReturnValue())); + } + + @Override + public void visitPostfixExpression(final PsiPostfixExpression postfix) { + final PsiPostfixExpression postfix2 = (PsiPostfixExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(postfix.getOperationTokenType().equals(postfix2.getOperationTokenType()) + && myMatchingVisitor.match(postfix.getOperand(), postfix2.getOperand())); + } + + @Override + public void visitPrefixExpression(final PsiPrefixExpression prefix) { + final PsiPrefixExpression prefix2 = (PsiPrefixExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(prefix.getOperationTokenType().equals(prefix2.getOperationTokenType()) + && myMatchingVisitor.match(prefix.getOperand(), prefix2.getOperand())); + } + + @Override + public void visitAssertStatement(final PsiAssertStatement assert1) { + final PsiAssertStatement assert2 = (PsiAssertStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(assert1.getAssertCondition(), assert2.getAssertCondition()) && + myMatchingVisitor.match(assert1.getAssertDescription(), assert2.getAssertDescription())); + } + + @Override + public void visitBreakStatement(final PsiBreakStatement break1) { + final PsiBreakStatement break2 = (PsiBreakStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(break1.getLabelIdentifier(), break2.getLabelIdentifier())); + } + + @Override + public void visitContinueStatement(final PsiContinueStatement continue1) { + final PsiContinueStatement continue2 = (PsiContinueStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(continue1.getLabelIdentifier(), continue2.getLabelIdentifier())); + } + + @Override + public void visitSuperExpression(final PsiSuperExpression super1) { + myMatchingVisitor.setResult(true); + } + + @Override + public void visitThisExpression(final PsiThisExpression this1) { + myMatchingVisitor.setResult(myMatchingVisitor.getElement() instanceof PsiThisExpression); + } + + @Override + public void visitSynchronizedStatement(final PsiSynchronizedStatement synchronized1) { + final PsiSynchronizedStatement synchronized2 = (PsiSynchronizedStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(synchronized1.getLockExpression(), synchronized2.getLockExpression()) && + myMatchingVisitor.matchSons(synchronized1.getBody(), synchronized2.getBody())); + } + + @Override + public void visitThrowStatement(final PsiThrowStatement throw1) { + final PsiThrowStatement throw2 = (PsiThrowStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(throw1.getException(), throw2.getException())); + } + + @Override + public void visitParenthesizedExpression(PsiParenthesizedExpression expr) { + if (myMatchingVisitor.getElement() instanceof PsiParenthesizedExpression) { + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(expr, myMatchingVisitor.getElement())); + } + else { + myMatchingVisitor.setResult(false); + } + } + + @Override + public void visitCatchSection(final PsiCatchSection section) { + final PsiCatchSection section2 = (PsiCatchSection)myMatchingVisitor.getElement(); + final PsiParameter parameter = section.getParameter(); + if (parameter != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(parameter, section2.getParameter())); + } + else { + myMatchingVisitor.setResult(myMatchingVisitor.match(section.getCatchBlock(), section2.getCatchBlock())); + } + } + + @Override + public void visitTryStatement(final PsiTryStatement try1) { + final PsiTryStatement try2 = (PsiTryStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(try1.getTryBlock(), try2.getTryBlock())); + + if (!myMatchingVisitor.getResult()) return; + + final PsiResourceList resourceList1 = try1.getResourceList(); + final PsiCatchSection[] catches1 = try1.getCatchSections(); + final PsiCodeBlock finally1 = try1.getFinallyBlock(); + + final PsiResourceList resourceList2 = try2.getResourceList(); + final PsiCatchSection[] catches2 = try2.getCatchSections(); + final PsiCodeBlock finally2 = try2.getFinallyBlock(); + + final boolean looseMatching = myMatchingVisitor.getMatchContext().getOptions().isLooseMatching(); + if (!looseMatching && + ((catches1.length == 0 && catches2.length != 0) || + (finally1 == null && finally2 != null) || + (resourceList1 == null && resourceList2 != null)) + ) { + myMatchingVisitor.setResult(false); + } + else { + if (resourceList1 != null) { + if (resourceList2 == null) { + myMatchingVisitor.setResult(false); + return; + } + final List<PsiResourceVariable> resourceVariables1 = resourceList1.getResourceVariables(); + final List<PsiResourceVariable> resourceVariables2 = resourceList2.getResourceVariables(); + myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder( + resourceVariables1.toArray(new PsiResourceVariable[resourceVariables1.size()]), + resourceVariables2.toArray(new PsiResourceVariable[resourceVariables2.size()]))); + if (!myMatchingVisitor.getResult()) return; + } + + final List<PsiCatchSection> unmatchedCatchSections = new ArrayList<PsiCatchSection>(); + + ContainerUtil.addAll(unmatchedCatchSections, catches2); + + for (int i = 0, j; i < catches1.length; ++i) { + MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(catches1[i]); + final PsiElement pinnedNode = handler.getPinnedNode(null); + + if (pinnedNode != null) { + myMatchingVisitor.setResult(handler.match(catches1[i], pinnedNode, myMatchingVisitor.getMatchContext())); + if (!myMatchingVisitor.getResult()) return; + } + else { + for (j = 0; j < unmatchedCatchSections.size(); ++j) { + if (handler.match(catches1[i], unmatchedCatchSections.get(j), myMatchingVisitor.getMatchContext())) { + unmatchedCatchSections.remove(j); + break; + } + } + + if (j == catches2.length) { + myMatchingVisitor.setResult(false); + return; + } + } + } + + if (finally1 != null) { + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(finally1, finally2)); + } + + if (myMatchingVisitor.getResult() && unmatchedCatchSections.size() > 0 && !looseMatching) { + try2.putUserData(UNMATCHED_CATCH_SECTION_CONTENT_VAR_KEY, unmatchedCatchSections); + } + } + } + + @Override + public void visitSwitchLabelStatement(final PsiSwitchLabelStatement case1) { + final PsiSwitchLabelStatement case2 = (PsiSwitchLabelStatement)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(case1.isDefaultCase() == case2.isDefaultCase() && + myMatchingVisitor.match(case1.getCaseValue(), case2.getCaseValue())); + } + + @Override + public void visitInstanceOfExpression(final PsiInstanceOfExpression instanceOf) { + final PsiInstanceOfExpression instanceOf2 = (PsiInstanceOfExpression)myMatchingVisitor.getElement(); + myMatchingVisitor.setResult(myMatchingVisitor.match(instanceOf.getOperand(), instanceOf2.getOperand()) && + matchType(instanceOf.getCheckType(), instanceOf2.getCheckType())); + } + + @Override + public void visitNewExpression(final PsiNewExpression new1) { + if (myMatchingVisitor.getElement() instanceof PsiArrayInitializerExpression && + myMatchingVisitor.getElement().getParent() instanceof PsiVariable && + new1.getArrayDimensions().length == 0 && + new1.getArrayInitializer() != null + ) { + myMatchingVisitor.setResult( + myMatchingVisitor.match(new1.getClassReference(), ((PsiVariable)myMatchingVisitor.getElement().getParent()).getTypeElement())); + myMatchingVisitor.matchSons(new1.getArrayInitializer(), myMatchingVisitor.getElement()); + return; + } + + if (!(myMatchingVisitor.getElement() instanceof PsiNewExpression)) { + myMatchingVisitor.setResult(false); + return; + } + final PsiNewExpression new2 = (PsiNewExpression)myMatchingVisitor.getElement(); + + if (new1.getClassReference() != null) { + if (new2.getClassReference() != null) { + myMatchingVisitor.setResult(myMatchingVisitor.match(new1.getClassReference(), new2.getClassReference()) && + myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer())); + + if (myMatchingVisitor.getResult()) { + // matching dims + matchArrayDims(new1, new2); + } + return; + } + else { + // match array of primitive by new 'T(); + final PsiKeyword newKeyword = PsiTreeUtil.getChildOfType(new2, PsiKeyword.class); + final PsiElement element = PsiTreeUtil.getNextSiblingOfType(newKeyword, PsiWhiteSpace.class); + + if (element != null && element.getNextSibling() instanceof PsiKeyword) { + ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true); + + myMatchingVisitor.setResult(myMatchingVisitor.match(new1.getClassReference(), element.getNextSibling()) && + myMatchingVisitor.matchSons(new1.getArrayInitializer(), new2.getArrayInitializer())); + + ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false); + if (myMatchingVisitor.getResult()) { + // matching dims + matchArrayDims(new1, new2); + } + + return; + } + } + } + + if (new1.getClassReference() == new2.getClassReference()) { + // probably anonymous class or array of primitive type + ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(true); + myMatchingVisitor.setResult(myMatchingVisitor.matchSons(new1, new2)); + ((LexicalNodesFilter)LexicalNodesFilter.getInstance()).setCareKeyWords(false); + } + else if (new1.getAnonymousClass() == null && + new1.getClassReference() != null && + new2.getAnonymousClass() != null) { + // allow matching anonymous class without pattern + myMatchingVisitor.setResult(myMatchingVisitor.match(new1.getClassReference(), new2.getAnonymousClass().getBaseClassReference()) && + myMatchingVisitor.matchSons(new1.getArgumentList(), new2.getArgumentList())); + } + else { + myMatchingVisitor.setResult(false); + } + } + + @Override + public void visitKeyword(PsiKeyword keyword) { + myMatchingVisitor.setResult(keyword.textMatches(myMatchingVisitor.getElement())); + } + + @Override + public void visitTypeCastExpression(final PsiTypeCastExpression cast) { + final PsiTypeCastExpression cast2 = (PsiTypeCastExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(cast.getCastType(), cast2.getCastType()) && + myMatchingVisitor.match(cast.getOperand(), cast2.getOperand())); + } + + @Override + public void visitClassObjectAccessExpression(final PsiClassObjectAccessExpression expr) { + final PsiClassObjectAccessExpression expr2 = (PsiClassObjectAccessExpression)myMatchingVisitor.getElement(); + + myMatchingVisitor.setResult(myMatchingVisitor.match(expr.getOperand(), expr2.getOperand())); + } + + @Override + public void visitReferenceElement(final PsiJavaCodeReferenceElement ref) { + myMatchingVisitor.setResult(matchType(ref, myMatchingVisitor.getElement())); + } + + @Override + public void visitTypeElement(final PsiTypeElement typeElement) { + myMatchingVisitor.setResult(matchType(typeElement, myMatchingVisitor.getElement())); + } + + @Override + public void visitTypeParameter(PsiTypeParameter psiTypeParameter) { + final PsiTypeParameter parameter = (PsiTypeParameter)myMatchingVisitor.getElement(); + final PsiElement[] children = psiTypeParameter.getChildren(); + final PsiElement[] children2 = parameter.getChildren(); + + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(children[0]); + + if (handler instanceof SubstitutionHandler) { + myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(children2[0], myMatchingVisitor.getMatchContext())); + } + else { + myMatchingVisitor.setResult(children[0].textMatches(children2[0])); + } + + if (myMatchingVisitor.getResult() && children.length > 2) { + // constraint present + if (children2.length == 2) { + myMatchingVisitor.setResult(false); + return; + } + + if (!children[2].getFirstChild().textMatches(children2[2].getFirstChild())) { + // constraint type (extends) + myMatchingVisitor.setResult(false); + return; + } + myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(children[2].getChildren(), children2[2].getChildren())); + } + } + + @Override + public void visitClass(PsiClass clazz) { + if (clazz.hasTypeParameters()) { + myMatchingVisitor + .setResult( + myMatchingVisitor.match(clazz.getTypeParameterList(), ((PsiClass)myMatchingVisitor.getElement()).getTypeParameterList())); + + if (!myMatchingVisitor.getResult()) return; + } + + PsiClass clazz2; + + if (myMatchingVisitor.getElement() instanceof PsiDeclarationStatement && + myMatchingVisitor.getElement().getFirstChild() instanceof PsiClass + ) { + clazz2 = (PsiClass)myMatchingVisitor.getElement().getFirstChild(); + } + else { + clazz2 = (PsiClass)myMatchingVisitor.getElement(); + } + + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(clazz.getNameIdentifier()); + + if (clazz.getModifierList().getTextLength() > 0) { + if (!myMatchingVisitor.match(clazz.getModifierList(), clazz2.getModifierList())) { + myMatchingVisitor.setResult(false); + return; + } + } + + myMatchingVisitor.setResult((clazz.getName().equals(clazz2.getName()) || isTypedVar) && + compareClasses(clazz, clazz2)); + + if (myMatchingVisitor.getResult() && isTypedVar) { + PsiElement id = clazz2.getNameIdentifier(); + if (id == null) id = clazz2; + myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(clazz.getNameIdentifier(), id)); + } + } + + @Override + public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) { + myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially( + psiTypeParameterList.getFirstChild(), + myMatchingVisitor.getElement().getFirstChild() + )); + } + + @Override + public void visitMethod(PsiMethod method) { + final PsiIdentifier methodNameNode = method.getNameIdentifier(); + final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(methodNameNode); + final PsiMethod method2 = (PsiMethod)myMatchingVisitor.getElement(); + + myMatchingVisitor.getMatchContext().pushResult(); + + try { + if (method.hasTypeParameters()) { + myMatchingVisitor.setResult( + myMatchingVisitor.match(method.getTypeParameterList(), ((PsiMethod)myMatchingVisitor.getElement()).getTypeParameterList())); + + if (!myMatchingVisitor.getResult()) return; + } + + if (!checkHierarchy(method2, method)) { + myMatchingVisitor.setResult(false); + return; + } + + myMatchingVisitor.setResult((method.getName().equals(method2.getName()) || isTypedVar) && + myMatchingVisitor.match(method.getModifierList(), method2.getModifierList()) && + myMatchingVisitor.matchSons(method.getParameterList(), method2.getParameterList()) && + myMatchingVisitor.match(method.getReturnTypeElement(), method2.getReturnTypeElement()) && + matchInAnyOrder(method.getThrowsList(), method2.getThrowsList(), myMatchingVisitor) && + myMatchingVisitor.matchSonsOptionally(method.getBody(), method2.getBody())); + } + finally { + final PsiIdentifier methodNameNode2 = method2.getNameIdentifier(); + + saveOrDropResult(methodNameNode, isTypedVar, methodNameNode2); + } + } +} |