diff options
Diffstat (limited to 'java/structuralsearch-java/src/com/intellij')
37 files changed, 5345 insertions, 0 deletions
diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java new file mode 100644 index 000000000000..a82c941c0fa2 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java @@ -0,0 +1,296 @@ +package com.intellij.structuralsearch; + +import com.intellij.structuralsearch.plugin.ui.Configuration; + +import static com.intellij.structuralsearch.PredefinedConfigurationUtil.createSearchTemplateInfo; +import static com.intellij.structuralsearch.PredefinedConfigurationUtil.createSearchTemplateInfoSimple; + +/** +* @author Bas Leijdekkers +*/ +class JavaPredefinedConfigurations { + + private static final String EXPRESSION_TYPE = SSRBundle.message("expressions.category"); + private static final String INTERESTING_TYPE = SSRBundle.message("interesting.category"); + private static final String J2EE_TYPE = SSRBundle.message("j2ee.category"); + private static final String OPERATOR_TYPE = SSRBundle.message("operators.category"); + private static final String CLASS_TYPE = SSRBundle.message("class.category"); + private static final String METADATA_TYPE = SSRBundle.message("metadata.category"); + private static final String MISC_TYPE = SSRBundle.message("misc.category"); + private static final String GENERICS_TYPE = SSRBundle.message("generics.category"); + + public static Configuration[] createPredefinedTemplates() { + return new Configuration[] { + // Expression patterns + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.method.calls"), "'_Instance?.'MethodCall('_Parameter*)", EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.new.expressions"), "new 'Constructor('_Argument*)", EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.lambdas"), "('_Parameter) -> ", EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.field.selections"),"'_Instance?.'Field",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.array.access"),"'_Field['_Index]",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.assignments"),"'_Inst = '_Expr",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.casts"),"('_Type)'_Expr",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.instanceof"),"'_Expr instanceof '_Type",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.string.literals"),"\"'_String\"",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.all.expressions.of.some.type"),"'_Expression:[exprtype( SomeType )]",EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.sample.method.invokation.with.constant.argument"),"Integer.parseInt('_a:[script( \"com.intellij.psi.util.PsiUtil.isConstantExpression(__context__)\" )])",EXPRESSION_TYPE), + + // Operators + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.block.dcls"),"{\n '_Type+ 'Var+ = '_Init*;\n '_BlockStatements*;\n}",OPERATOR_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.trys"),"try {\n '_TryStatement+;\n} catch('_ExceptionType '_ExceptionDcl) {\n '_CatchStatement*;\n}",OPERATOR_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.ifs"),"if ('_Condition) {\n '_ThenStatement*;\n} else {\n '_ElseStatement*;\n}",OPERATOR_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.switches"),"switch('_Condition) {\n '_Statement*;\n}",OPERATOR_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.foreaches"), "for ('_Type '_Variable : '_Expression) {\n '_Statement*;\n}", OPERATOR_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.logging.without.if"),"LOG.debug('_params*:[!within( \"if('_a) { '_st*; }\" )]);",OPERATOR_TYPE), + + // Class based + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.methods.of.the.class"), + "class '_Class { \n '_ReturnType+ 'MethodName+('_ParameterType* '_Parameter*);\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.fields.of.the.class"), + "class '_Class { \n '_FieldType+ 'FieldName+ = '_Init*;\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.all.methods.of.the.class.within.hierarchy"), + "class '_ { \n '_ReturnType+ 'MethodName+:* ('_ParameterType* '_Parameter*);\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.all.fields.of.the.class"), + "class '_Class { \n '_FieldType+ 'FieldName+:* = '_Init*;\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.instance.fields.of.the.class"), + "class '_Class { \n @Modifier(\"Instance\") '_FieldType+ 'FieldName+ = '_Init*;\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.packagelocal.fields.of.the.class"), + "class '_Class { \n @Modifier(\"packageLocal\") '_FieldType+ 'FieldName+ = '_Init*;\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.constructors.of.the.class"), + "class 'Class {\n 'Class+('_ParameterType* '_ParameterName*) {\n '_Statement*;\n }\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.classes"), + "class 'Class {}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.direct.subclasses"), + "class 'Class extends '_Parent {}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.implementors.of.interface.within.hierarchy"), + "class 'Class implements 'Interface:* {}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.interfaces"), + "interface 'Interface {}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.inner.classes"), + "class '_ {\n class 'InnerClass+ {}\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.all.inner.classes.within.hierarchy"), + "class '_Class {\n class 'InnerClass+:* {}\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.anonymous.classes"), + "new 'AnonymousClass() {}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.class.implements.two.interfaces"), + "class 'A implements '_Interface1:[regex( *java\\.lang\\.Cloneable )], '_Interface2:*java\\.io\\.Serializable {\n" +"}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.class.static.blocks"), + "class '_A {\n static {\n 'Statement*;\n }\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.class.instance.initialization.blocks"), + "class '_A {\n @Modifier(\"Instance\") {\n 'Statement*;\n }\n}", + CLASS_TYPE + ), + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.class.any.initialization.blocks"), + "class '_A {\n {\n 'Statement*;\n }\n}", + CLASS_TYPE + ), + + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.enums"), + "enum 'Enum {}", + CLASS_TYPE + ), + + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.class.with.parameterless.constructors"), + "class 'Class {\n '_Method{0,0}:[ script( \"__context__.constructor\" ) ]('_ParamType+ '_ParameterName+);\n}", + CLASS_TYPE + ), + + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.static.fields.without.final"), + "class '_Class {\n static '_Type 'Variable+:[ script( \"!__context__.hasModifierProperty(\"final\")\" ) ] = '_Init?;\n}", + CLASS_TYPE + ), + + createSearchTemplateInfo( + SSRBundle.message("predefined.configuration.interfaces.having.no.descendants"), + "interface 'A:[script( \"com.intellij.psi.search.searches.ClassInheritorsSearch.search(__context__).findFirst() == null\" )] {}", + CLASS_TYPE + ), + + // Generics + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.generic.classes"),"class 'GenericClass<'_TypeParameter+> {} ", GENERICS_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.generic.methods"),"class '_Class {\n <'_TypeParameter+> '_Type+ 'Method+('_ParameterType* '_ParameterDcl*);\n}", GENERICS_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.typed.symbol"),"'Symbol <'_GenericArgument+>", GENERICS_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.generic.casts"),"( '_Type <'_GenericArgument+> ) '_Expr", GENERICS_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.type.var.substitutions.in.intanceof.with.generic.types"),"'_Expr instanceof '_Type <'Substitutions+> ", GENERICS_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.variables.of.generic.types"),"'_Type <'_GenericArgument+> 'Var = 'Init?;", GENERICS_TYPE), + + // Add comments and metadata + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.comments"),"/* 'CommentContent */", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.javadoc.annotated.class"),"/** @'_Tag+ '_TagValue* */\nclass '_Class {\n}", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.javadoc.annotated.methods"),"class '_Class {\n /** @'_Tag+ '_TagValue* */\n '_Type+ 'Method+('_ParameterType* '_ParameterDcl*);\n}", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.javadoc.annotated.fields"),"class '_Class {\n /** @'_Tag+ '_TagValue* */\n '_Type+ 'Field+ = '_Init*;\n}", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.javadoc.tags"),"/** @'Tag+ '_TagValue* */", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.xdoclet.metadata"),"/** @'Tag \n '_Property+\n*/", METADATA_TYPE), + + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotated.class"), + "@'_Annotation( )\n" + + "class 'Class {}", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotated.fields"), + "class '_Class {\n" + + " @'_Annotation+( )\n" + + " '_FieldType+ 'FieldName+ = '_FieldInitial*;\n" + + "}", METADATA_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotated.methods"), + "class '_Class {\n" + + " @'_Annotation+( )\n" + + " '_MethodType+ 'MethodName+('_ParameterType* '_ParameterName*);\n" + + "}", METADATA_TYPE), + + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.not.annotated.methods"), + "class '_Class {\n" + + " @'_Annotation{0,0}\n" + + " '_MethodType+ 'MethodName+('_ParameterType* '_ParameterName*);\n" + + "}", METADATA_TYPE), + + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotation.declarations"), + "@interface 'Interface {}", METADATA_TYPE), + + // J2EE templates + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.struts.1.1.actions"),"public class 'StrutsActionClass extends '_ParentClass*:Action {\n" + + " public ActionForward 'AnActionMethod:*execute (ActionMapping '_action,\n" + + " ActionForm '_form,\n" + + " HttpServletRequest '_request,\n" + + " HttpServletResponse '_response);\n" + + "}",J2EE_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.entity.ejb"),"class 'EntityBean implements EntityBean {\n" + + " EntityContext '_Context?;\n\n" + + " public void setEntityContext(EntityContext '_Context2);\n\n" + + " public '_RetType ejbCreate('_CreateType* '_CreateDcl*);\n" + + " public void ejbActivate();\n\n" + + " public void ejbLoad();\n\n" + + " public void ejbPassivate();\n\n" + + " public void ejbRemove();\n\n" + + " public void ejbStore();\n" + + "}", J2EE_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.session.ejb"),"class 'SessionBean implements SessionBean {\n" + + " SessionContext '_Context?;\n\n" + + " public void '_setSessionContext(SessionContext '_Context2);\n\n" + + " public '_RetType ejbCreate('_CreateParameterType* '_CreateParameterDcl*);\n" + + " public void ejbActivate();\n\n" + + " public void ejbPassivate();\n\n" + + " public void ejbRemove();\n" + + "}", J2EE_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.ejb.interface"),"interface 'EjbInterface extends EJBObject {\n" + + " 'Type+ 'Method+('ParamType* 'ParamName*);\n" + + "}", J2EE_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.servlets"),"public class 'Servlet extends '_ParentClass:*HttpServlet {\n" + + " public void '_InitServletMethod?:init ();\n" + + " public void '_DestroyServletMethod?:destroy ();\n" + + " void '_ServiceMethod?:*service (HttpServletRequest '_request, HttpServletResponse '_response);\n" + + " void '_SpecificServiceMethod*:do.* (HttpServletRequest '_request2, HttpServletResponse '_response2); \n" + + "}", J2EE_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.filters"),"public class 'Filter implements Filter {\n" + + " public void '_DestroyFilterMethod?:*destroy ();\n" + + " public void '_InitFilterMethod?:*init ();\n" + + " public void '_FilteringMethod:*doFilter (ServletRequest '_request,\n" + + " ServletResponse '_response,FilterChain '_chain);\n" + + "}", J2EE_TYPE), + + // Misc types + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.serializable.classes.and.their.serialization.implementation"), + "class '_Class implements '_Serializable:*Serializable {\n" + + " static final long 'VersionField?:serialVersionUID = '_VersionFieldInit?;\n" + + " private static final ObjectStreamField[] '_persistentFields?:serialPersistentFields = '_persistentFieldInitial?; \n" + + " private void 'SerializationWriteHandler?:writeObject (ObjectOutputStream '_stream) throws IOException;\n" + + " private void 'SerializationReadHandler?:readObject (ObjectInputStream '_stream2) throws IOException, ClassNotFoundException;\n" + + " Object 'SpecialSerializationReadHandler?:readResolve () throws ObjectStreamException;\n" + + " Object 'SpecialSerializationWriteHandler?:writeReplace () throws ObjectStreamException;\n" + + "}",MISC_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.cloneable.implementations"), + "class '_Class implements '_Interface:*Cloneable {\n" + + " Object 'CloningMethod:*clone ();\n" + + "}",MISC_TYPE), + createSearchTemplateInfoSimple(SSRBundle.message("predefined.configuration.]junit.test.cases"),"public class 'TestCase extends 'TestCaseClazz:*TestCase {\n" + + " public void '_testMethod+:test.* ();\n" + + "}", MISC_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.singletons"),"class 'Class {\n" + + " private 'Class('_ParameterType* '_ParameterDcl*) {\n" + + " '_ConstructorStatement*;\n" + + " }\n"+ + " private static '_Class:* '_Instance;\n" + + " static '_Class:* '_GetInstance() {\n" + + " '_SomeStatement*;\n" + + " return '_Instance;\n" + + " }\n"+ + "}",MISC_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.similar.methods.structure"),"class '_Class {\n" + + " '_RetType 'Method+('_ParameterType* '_Parameter) throws 'ExceptionType {\n" + + " try {\n" + + " '_OtherStatements+;\n" + + " } catch('_SomeException '_ExceptionDcl) {\n" + + " '_CatchStatement*;\n" + + " throw new 'ExceptionType('_ExceptionConstructorArgs*);\n" + + " }\n" + + " }\n" + + "}",MISC_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.bean.info.classes"),"class 'A implements '_:*java\\.beans\\.BeanInfo {\n" + + "}",MISC_TYPE), + + // interesting types + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.symbol"),"'Symbol",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.fields.variables.read"),"'Symbol:[read]",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.fields_variables.with.given.name.pattern.updated"),"'Symbol:[regex( name ) && write]",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.usage.of.derived.type.in.cast"),"('CastType:*Base ) 'Expr",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.boxing.in.declarations"),"'_Type:Integer|Boolean|Long|Character|Short|Byte 'Var = '_Value:[formal( int|boolean|long|char|short|byte )]",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.unboxing.in.declarations"),"'_Type:int|boolean|long|char|short|byte 'Var = '_Value:[formal( Integer|Boolean|Long|Character|Short|Byte )]",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.boxing.in.method.calls"),"'_Instance?.'Call('_BeforeParam*,'_Param:[ exprtype( int|boolean|long|char|short|byte ) && formal( Integer|Boolean|Long|Character|Short|Byte )],'_AfterParam*)",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.unboxing.in.method.calls"), "'_Instance?.'Call('_BeforeParam*,'_Param:[ formal( int|boolean|long|char|short|byte ) && exprtype( Integer|Boolean|Long|Character|Short|Byte )],'_AfterParam*)",INTERESTING_TYPE), + //createSearchTemplateInfo("methods called","'_?.'_:[ref('Method)] ('_*)", INTERESTING_TYPE), + //createSearchTemplateInfo("fields selected","'_?.'_:[ref('Field)] ", INTERESTING_TYPE), + //createSearchTemplateInfo("symbols used","'_:[ref('Symbol)] ", INTERESTING_TYPE), + //createSearchTemplateInfo("types used","'_:[ref('Type)] '_;", INTERESTING_TYPE), + }; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java new file mode 100644 index 000000000000..562c8aee753c --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java @@ -0,0 +1,557 @@ +package com.intellij.structuralsearch; + +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.xml.XmlText; +import com.intellij.structuralsearch.impl.matcher.JavaMatchingVisitor; +import com.intellij.structuralsearch.impl.matcher.MatcherImplUtil; +import com.intellij.structuralsearch.impl.matcher.PatternTreeContext; +import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; +import com.intellij.structuralsearch.plugin.replace.ReplacementInfo; +import com.intellij.structuralsearch.plugin.replace.impl.ReplacementContext; +import com.intellij.structuralsearch.plugin.replace.impl.Replacer; +import com.intellij.structuralsearch.plugin.replace.impl.ReplacerUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ig.psiutils.ImportUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Eugene.Kudelevsky + */ +public class JavaReplaceHandler extends StructuralReplaceHandler { + private final ReplacementContext myContext; + private PsiCodeBlock codeBlock; + + public JavaReplaceHandler(ReplacementContext context) { + this.myContext = context; + } + + private PsiCodeBlock getCodeBlock() throws IncorrectOperationException { + if (codeBlock == null) { + codeBlock = (PsiCodeBlock)MatcherImplUtil.createTreeFromText( + myContext.getOptions().getMatchOptions().getSearchPattern(), + PatternTreeContext.Block, + myContext.getOptions().getMatchOptions().getFileType(), + myContext.getProject() + )[0].getParent(); + } + return codeBlock; + } + + private static PsiElement findRealSubstitutionElement(PsiElement el) { + if (el instanceof PsiIdentifier) { + // matches are tokens, identifiers, etc + el = el.getParent(); + } + + if (el instanceof PsiReferenceExpression && + el.getParent() instanceof PsiMethodCallExpression + ) { + // method + el = el.getParent(); + } + + if (el instanceof PsiDeclarationStatement && ((PsiDeclarationStatement)el).getDeclaredElements()[0] instanceof PsiClass) { + el = ((PsiDeclarationStatement)el).getDeclaredElements()[0]; + } + return el; + } + + private static boolean isListContext(PsiElement el) { + boolean listContext = false; + final PsiElement parent = el.getParent(); + + if (parent instanceof PsiParameterList || + parent instanceof PsiExpressionList || + parent instanceof PsiCodeBlock || + parent instanceof PsiClass || + parent instanceof XmlText || + (parent instanceof PsiIfStatement && + (((PsiIfStatement)parent).getThenBranch() == el || + ((PsiIfStatement)parent).getElseBranch() == el + ) + ) || + (parent instanceof PsiLoopStatement && + ((PsiLoopStatement)parent).getBody() == el + ) + ) { + listContext = true; + } + + return listContext; + } + + @Nullable + private PsiNamedElement getSymbolReplacementTarget(final PsiElement el) + throws IncorrectOperationException { + if (myContext.getOptions().getMatchOptions().getFileType() != StdFileTypes.JAVA) return null; //? + final PsiStatement[] searchStatements = getCodeBlock().getStatements(); + if (searchStatements.length > 0 && + searchStatements[0] instanceof PsiExpressionStatement) { + final PsiExpression expression = ((PsiExpressionStatement)searchStatements[0]).getExpression(); + + if (expression instanceof PsiReferenceExpression && + ((PsiReferenceExpression)expression).getQualifierExpression() == null + ) { + // looks like symbol replacements, namely replace AAA by BBB, so lets do the best + if (el instanceof PsiNamedElement) { + return (PsiNamedElement)el; + } + } + } + + return null; + } + + private static PsiElement getMatchExpr(PsiElement replacement, PsiElement elementToReplace) { + if (replacement instanceof PsiExpressionStatement && + !(replacement.getLastChild() instanceof PsiJavaToken) && + !(replacement.getLastChild() instanceof PsiComment) + ) { + // replacement is expression (and pattern should be so) + // assert ... + replacement = ((PsiExpressionStatement)replacement).getExpression(); + } + else if (replacement instanceof PsiDeclarationStatement && + ((PsiDeclarationStatement)replacement).getDeclaredElements().length == 1 + ) { + return ((PsiDeclarationStatement)replacement).getDeclaredElements()[0]; + } + else if (replacement instanceof PsiBlockStatement && + elementToReplace instanceof PsiCodeBlock + ) { + return ((PsiBlockStatement)replacement).getCodeBlock(); + } + + return replacement; + } + + private boolean isSymbolReplacement(final PsiElement el) throws IncorrectOperationException { + return getSymbolReplacementTarget(el) != null; + } + + @SuppressWarnings({"unchecked", "ConstantConditions"}) + private void handleModifierList(final PsiElement el, final PsiElement replacement) throws IncorrectOperationException { + // We want to copy all comments, including doc comments and modifier lists + // that are present in matched nodes but not present in search/replace + + Map<String, String> newNameToSearchPatternNameMap = myContext.getNewName2PatternNameMap(); + + ModifierListOwnerCollector collector = new ModifierListOwnerCollector(); + el.accept(collector); + Map<String, PsiNamedElement> originalNamedElements = (Map<String, PsiNamedElement>)collector.namedElements.clone(); + collector.namedElements.clear(); + + replacement.accept(collector); + Map<String, PsiNamedElement> replacedNamedElements = (Map<String, PsiNamedElement>)collector.namedElements.clone(); + collector.namedElements.clear(); + + if (originalNamedElements.size() == 0 && replacedNamedElements.size() == 0) { + Replacer.handleComments(el, replacement, myContext); + return; + } + + final PsiStatement[] statements = getCodeBlock().getStatements(); + if (statements.length > 0) { + statements[0].getParent().accept(collector); + } + + Map<String, PsiNamedElement> searchedNamedElements = (Map<String, PsiNamedElement>)collector.namedElements.clone(); + collector.namedElements.clear(); + + for (String name : originalNamedElements.keySet()) { + PsiNamedElement originalNamedElement = originalNamedElements.get(name); + PsiNamedElement replacementNamedElement = replacedNamedElements.get(name); + String key = newNameToSearchPatternNameMap.get(name); + if (key == null) key = name; + PsiNamedElement searchNamedElement = searchedNamedElements.get(key); + + if (replacementNamedElement == null && originalNamedElements.size() == 1 && replacedNamedElements.size() == 1) { + replacementNamedElement = replacedNamedElements.entrySet().iterator().next().getValue(); + } + + PsiElement comment = null; + + if (originalNamedElement instanceof PsiDocCommentOwner) { + comment = ((PsiDocCommentOwner)originalNamedElement).getDocComment(); + if (comment == null) { + PsiElement prevElement = originalNamedElement.getPrevSibling(); + if (prevElement instanceof PsiWhiteSpace) { + prevElement = prevElement.getPrevSibling(); + } + if (prevElement instanceof PsiComment) { + comment = prevElement; + } + } + } + + if (replacementNamedElement != null && searchNamedElement != null) { + Replacer.handleComments(originalNamedElement, replacementNamedElement, myContext); + } + + if (comment != null && replacementNamedElement instanceof PsiDocCommentOwner && + !(replacementNamedElement.getFirstChild() instanceof PsiDocComment) + ) { + final PsiElement nextSibling = comment.getNextSibling(); + PsiElement prevSibling = comment.getPrevSibling(); + replacementNamedElement.addRangeBefore( + prevSibling instanceof PsiWhiteSpace ? prevSibling : comment, + nextSibling instanceof PsiWhiteSpace ? nextSibling : comment, + replacementNamedElement.getFirstChild() + ); + } + + if (originalNamedElement instanceof PsiModifierListOwner && + replacementNamedElement instanceof PsiModifierListOwner + ) { + PsiModifierList modifierList = ((PsiModifierListOwner)originalNamedElements.get(name)).getModifierList(); + + if (searchNamedElement instanceof PsiModifierListOwner) { + PsiModifierList modifierListOfSearchedElement = ((PsiModifierListOwner)searchNamedElement).getModifierList(); + final PsiModifierListOwner modifierListOwner = ((PsiModifierListOwner)replacementNamedElement); + PsiModifierList modifierListOfReplacement = modifierListOwner.getModifierList(); + + if (modifierListOfSearchedElement.getTextLength() == 0 && + modifierListOfReplacement.getTextLength() == 0 && + modifierList.getTextLength() > 0 + ) { + PsiElement space = modifierList.getNextSibling(); + if (!(space instanceof PsiWhiteSpace)) { + space = createWhiteSpace(space); + } + + modifierListOfReplacement.replace(modifierList); + // copy space after modifier list + if (space instanceof PsiWhiteSpace) { + modifierListOwner.addRangeAfter(space, space, modifierListOwner.getModifierList()); + } + } else if (modifierListOfSearchedElement.getTextLength() == 0 && modifierList.getTextLength() > 0) { + modifierListOfReplacement.addRange(modifierList.getFirstChild(), modifierList.getLastChild()); + } + } + } + } + } + + private PsiElement handleSymbolReplacement(PsiElement replacement, final PsiElement el) throws IncorrectOperationException { + PsiNamedElement nameElement = getSymbolReplacementTarget(el); + if (nameElement != null) { + PsiElement oldReplacement = replacement; + replacement = el.copy(); + ((PsiNamedElement)replacement).setName(oldReplacement.getText()); + } + + return replacement; + } + + public void replace(final ReplacementInfo info, ReplaceOptions options) { + PsiElement elementToReplace = info.getMatch(0); + PsiElement elementParent = elementToReplace.getParent(); + String replacementToMake = info.getReplacement(); + Project project = myContext.getProject(); + PsiElement el = findRealSubstitutionElement(elementToReplace); + boolean listContext = isListContext(el); + + if (el instanceof PsiAnnotation && !StringUtil.startsWithChar(replacementToMake, '@')) { + replacementToMake = "@" + replacementToMake; + } + + PsiElement[] statements = ReplacerUtil + .createTreeForReplacement(replacementToMake, el instanceof PsiMember && !isSymbolReplacement(el) ? + PatternTreeContext.Class : + PatternTreeContext.Block, myContext); + + if (listContext) { + if (statements.length > 1) { + elementParent.addRangeBefore(statements[0], statements[statements.length - 1], elementToReplace); + } + else if (statements.length == 1) { + PsiElement replacement = getMatchExpr(statements[0], elementToReplace); + + handleModifierList(el, replacement); + replacement = handleSymbolReplacement(replacement, el); + + if (replacement instanceof PsiTryStatement) { + final List<PsiCatchSection> unmatchedCatchSections = el.getUserData(JavaMatchingVisitor.UNMATCHED_CATCH_SECTION_CONTENT_VAR_KEY); + final PsiCatchSection[] catches = ((PsiTryStatement)replacement).getCatchSections(); + + if (unmatchedCatchSections != null) { + for (int i = unmatchedCatchSections.size() - 1; i >= 0; --i) { + final PsiParameter parameter = unmatchedCatchSections.get(i).getParameter(); + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiCatchSection catchSection = elementFactory.createCatchSection(parameter.getType(), parameter.getName(), null); + + catchSection.getCatchBlock().replace( + unmatchedCatchSections.get(i).getCatchBlock() + ); + replacement.addAfter( + catchSection, catches[catches.length - 1] + ); + replacement.addBefore(createWhiteSpace(replacement), replacement.getLastChild()); + } + } + } + + try { + final PsiElement inserted = elementParent.addBefore(replacement, elementToReplace); + + if (replacement instanceof PsiComment && + (elementParent instanceof PsiIfStatement || + elementParent instanceof PsiLoopStatement + ) + ) { + elementParent.addAfter(createSemicolon(replacement), inserted); + } + } + catch (IncorrectOperationException e) { + elementToReplace.replace(replacement); + } + } + } + else if (statements.length > 0) { + PsiElement replacement = ReplacerUtil.copySpacesAndCommentsBefore(elementToReplace, statements, replacementToMake, elementParent); + + replacement = getMatchExpr(replacement, elementToReplace); + + if (replacement instanceof PsiStatement && + !(replacement.getLastChild() instanceof PsiJavaToken) && + !(replacement.getLastChild() instanceof PsiComment) + ) { + // assert w/o ; + final PsiElement prevLastChildInParent = replacement.getLastChild().getPrevSibling(); + + if (prevLastChildInParent != null) { + elementParent.addRangeBefore(replacement.getFirstChild(), prevLastChildInParent, el); + } + else { + elementParent.addBefore(replacement.getFirstChild(), el); + } + + el.getNode().getTreeParent().removeChild(el.getNode()); + } + else { + // preserve comments + handleModifierList(el, replacement); + + if (replacement instanceof PsiClass) { + // modifier list + final PsiStatement[] searchStatements = getCodeBlock().getStatements(); + if (searchStatements.length > 0 && + searchStatements[0] instanceof PsiDeclarationStatement && + ((PsiDeclarationStatement)searchStatements[0]).getDeclaredElements()[0] instanceof PsiClass + ) { + final PsiClass replaceClazz = (PsiClass)replacement; + final PsiClass queryClazz = (PsiClass)((PsiDeclarationStatement)searchStatements[0]).getDeclaredElements()[0]; + final PsiClass clazz = (PsiClass)el; + + if (replaceClazz.getExtendsList().getTextLength() == 0 && + queryClazz.getExtendsList().getTextLength() == 0 && + clazz.getExtendsList().getTextLength() != 0 + ) { + replaceClazz.addBefore(clazz.getExtendsList().getPrevSibling(), replaceClazz.getExtendsList()); // whitespace + replaceClazz.getExtendsList().addRange( + clazz.getExtendsList().getFirstChild(), clazz.getExtendsList().getLastChild() + ); + } + + if (replaceClazz.getImplementsList().getTextLength() == 0 && + queryClazz.getImplementsList().getTextLength() == 0 && + clazz.getImplementsList().getTextLength() != 0 + ) { + replaceClazz.addBefore(clazz.getImplementsList().getPrevSibling(), replaceClazz.getImplementsList()); // whitespace + replaceClazz.getImplementsList().addRange( + clazz.getImplementsList().getFirstChild(), + clazz.getImplementsList().getLastChild() + ); + } + + if (replaceClazz.getTypeParameterList().getTextLength() == 0 && + queryClazz.getTypeParameterList().getTextLength() == 0 && + clazz.getTypeParameterList().getTextLength() != 0 + ) { + // skip < and > + replaceClazz.getTypeParameterList().replace( + clazz.getTypeParameterList() + ); + } + } + } + + replacement = handleSymbolReplacement(replacement, el); + + el.replace(replacement); + } + } + else { + final PsiElement nextSibling = el.getNextSibling(); + el.delete(); + if (nextSibling instanceof PsiWhiteSpace && nextSibling.isValid()) { + nextSibling.delete(); + } + } + + if (listContext) { + final int matchSize = info.getMatchesCount(); + + for (int i = 0; i < matchSize; ++i) { + PsiElement matchElement = info.getMatch(i); + PsiElement element = findRealSubstitutionElement(matchElement); + + if (element == null) continue; + PsiElement firstToDelete = element; + PsiElement lastToDelete = element; + PsiElement prevSibling = element.getPrevSibling(); + PsiElement nextSibling = element.getNextSibling(); + + if (prevSibling instanceof PsiWhiteSpace) { + firstToDelete = prevSibling; + prevSibling = prevSibling != null ? prevSibling.getPrevSibling() : null; + } + else if (prevSibling == null && nextSibling instanceof PsiWhiteSpace) { + lastToDelete = nextSibling; + } + + if (nextSibling instanceof XmlText && i + 1 < matchSize) { + final PsiElement next = info.getMatch(i + 1); + if (next != null && next == nextSibling.getNextSibling()) { + lastToDelete = nextSibling; + } + } + + if (element instanceof PsiExpression) { + final PsiElement parent = element.getParent().getParent(); + if ((parent instanceof PsiCall || + parent instanceof PsiAnonymousClass + ) && + prevSibling instanceof PsiJavaToken && + ((PsiJavaToken)prevSibling).getTokenType() == JavaTokenType.COMMA + ) { + firstToDelete = prevSibling; + } + } + else if (element instanceof PsiParameter && + prevSibling instanceof PsiJavaToken && + ((PsiJavaToken)prevSibling).getTokenType() == JavaTokenType.COMMA + ) { + firstToDelete = prevSibling; + } + + element.getParent().deleteChildRange(firstToDelete, lastToDelete); + } + } + } + + @Override + public void postProcess(PsiElement affectedElement, ReplaceOptions options) { + if (!affectedElement.isValid()) { + return; + } + if (options.isToUseStaticImport()) { + shortenWithStaticImports(affectedElement, 0, affectedElement.getTextLength()); + } + if (options.isToShortenFQN()) { + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(affectedElement.getProject()); + codeStyleManager.shortenClassReferences(affectedElement, 0, affectedElement.getTextLength()); + } + } + + private static void shortenWithStaticImports(PsiElement affectedElement, int startOffset, int endOffset) { + final int elementOffset = affectedElement.getTextOffset(); + final int finalStartOffset = startOffset + elementOffset; + final int finalEndOffset = endOffset + elementOffset; + final List<PsiReferenceExpression> references = new ArrayList<PsiReferenceExpression>(); + final JavaRecursiveElementVisitor collector = new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + final int offset = expression.getTextOffset(); + if (offset > finalEndOffset) { + return; + } + super.visitReferenceExpression(expression); + if (offset + expression.getTextLength() < finalStartOffset) + if (expression.getQualifierExpression() == null) { + return; + } + references.add(expression); + } + }; + affectedElement.accept(collector); + for (PsiReferenceExpression expression : references) { + final PsiElement target = expression.resolve(); + if (!(target instanceof PsiMember)) { + continue; + } + final PsiMember member = (PsiMember)target; + final PsiClass containingClass = member.getContainingClass(); + if (containingClass == null) { + continue; + } + final String className = containingClass.getQualifiedName(); + if (className == null) { + continue; + } + final String name = member.getName(); + if (name == null) { + continue; + } + if (ImportUtils.addStaticImport(className, name, expression)) { + final PsiExpression qualifierExpression = expression.getQualifierExpression(); + if (qualifierExpression != null) { + qualifierExpression.delete(); + } + } + } + } + + @Nullable + private static PsiElement createSemicolon(final PsiElement space) throws IncorrectOperationException { + final PsiStatement text = JavaPsiFacade.getInstance(space.getProject()).getElementFactory().createStatementFromText(";", null); + return text.getFirstChild(); + } + + private static PsiElement createWhiteSpace(final PsiElement space) throws IncorrectOperationException { + return PsiParserFacade.SERVICE.getInstance(space.getProject()).createWhiteSpaceFromText(" "); + } + + private static class ModifierListOwnerCollector extends JavaRecursiveElementWalkingVisitor { + HashMap<String, PsiNamedElement> namedElements = new HashMap<String, PsiNamedElement>(1); + + @Override + public void visitClass(PsiClass aClass) { + if (aClass instanceof PsiAnonymousClass) return; + handleNamedElement(aClass); + } + + private void handleNamedElement(final PsiNamedElement named) { + String name = named.getName(); + + assert name != null; + + if (StructuralSearchUtil.isTypedVariable(name)) { + name = name.substring(1, name.length() - 1); + } + + if (!namedElements.containsKey(name)) namedElements.put(name, named); + named.acceptChildren(this); + } + + @Override + public void visitVariable(PsiVariable var) { + handleNamedElement(var); + } + + @Override + public void visitMethod(PsiMethod method) { + handleNamedElement(method); + } + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java new file mode 100644 index 000000000000..869f01cc7394 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -0,0 +1,661 @@ +package com.intellij.structuralsearch; + +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.template.JavaCodeContextType; +import com.intellij.codeInsight.template.TemplateContextType; +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.lang.Language; +import com.intellij.lang.StdLanguages; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.structuralsearch.impl.matcher.*; +import com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor; +import com.intellij.structuralsearch.impl.matcher.compiler.JavaCompilingVisitor; +import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler; +import com.intellij.structuralsearch.impl.matcher.filters.JavaLexicalNodesFilter; +import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter; +import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; +import com.intellij.structuralsearch.plugin.replace.impl.ParameterInfo; +import com.intellij.structuralsearch.plugin.replace.impl.ReplacementBuilder; +import com.intellij.structuralsearch.plugin.replace.impl.ReplacementContext; +import com.intellij.structuralsearch.plugin.replace.impl.Replacer; +import com.intellij.structuralsearch.plugin.ui.Configuration; +import com.intellij.structuralsearch.plugin.ui.SearchContext; +import com.intellij.structuralsearch.plugin.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +public class JavaStructuralSearchProfile extends StructuralSearchProfile { + private JavaLexicalNodesFilter myJavaLexicalNodesFilter; + + public String getText(PsiElement match, int start,int end) { + if (match instanceof PsiIdentifier) { + PsiElement parent = match.getParent(); + if (parent instanceof PsiJavaCodeReferenceElement && !(parent instanceof PsiExpression)) { + match = parent; // care about generic + } + } + final String matchText = match.getText(); + if (start==0 && end==-1) return matchText; + return matchText.substring(start,end == -1? matchText.length():end); + } + + public Class getElementContextByPsi(PsiElement element) { + if (element instanceof PsiIdentifier) { + element = element.getParent(); + } + + if (element instanceof PsiMember) { + return PsiMember.class; + } else { + return PsiExpression.class; + } + } + + public String getTypedVarString(final PsiElement element) { + String text; + + if (element instanceof PsiNamedElement) { + text = ((PsiNamedElement)element).getName(); + } + else if (element instanceof PsiAnnotation) { + PsiJavaCodeReferenceElement referenceElement = ((PsiAnnotation)element).getNameReferenceElement(); + text = referenceElement == null ? null : referenceElement.getQualifiedName(); + } + else if (element instanceof PsiNameValuePair) { + text = ((PsiNameValuePair)element).getName(); + } + else { + text = element.getText(); + if (StringUtil.startsWithChar(text, '@')) { + text = text.substring(1); + } + if (StringUtil.endsWithChar(text, ';')) text = text.substring(0, text.length() - 1); + else if (element instanceof PsiExpressionStatement) { + int i = text.indexOf(';'); + if (i != -1) text = text.substring(0, i); + } + } + + if (text==null) text = element.getText(); + + return text; + } + + @Override + public String getMeaningfulText(PsiElement element) { + if (element instanceof PsiReferenceExpression && + ((PsiReferenceExpression)element).getQualifierExpression() != null) { + final PsiElement resolve = ((PsiReferenceExpression)element).resolve(); + if (resolve instanceof PsiClass) return element.getText(); + + final PsiElement referencedElement = ((PsiReferenceExpression)element).getReferenceNameElement(); + String text = referencedElement != null ? referencedElement.getText() : ""; + + if (resolve == null && text.length() > 0 && Character.isUpperCase(text.charAt(0))) { + return element.getText(); + } + return text; + } + return super.getMeaningfulText(element); + } + + @Override + public PsiElement updateCurrentNode(PsiElement targetNode) { + if (targetNode instanceof PsiCodeBlock && ((PsiCodeBlock)targetNode).getStatements().length == 1) { + PsiElement targetNodeParent = targetNode.getParent(); + if (targetNodeParent instanceof PsiBlockStatement) { + targetNodeParent = targetNodeParent.getParent(); + } + + if (targetNodeParent instanceof PsiIfStatement || targetNodeParent instanceof PsiLoopStatement) { + targetNode = targetNodeParent; + } + } + return targetNode; + } + + @Override + public PsiElement extendMatchedByDownUp(PsiElement targetNode) { + if (targetNode instanceof PsiIdentifier) { + targetNode = targetNode.getParent(); + final PsiElement parent = targetNode.getParent(); + if (parent instanceof PsiTypeElement || parent instanceof PsiStatement) targetNode = parent; + } + return targetNode; + } + + @Override + public PsiElement extendMatchOnePsiFile(PsiElement file) { + if (file instanceof PsiIdentifier) { + // Searching in previous results + file = file.getParent(); + } + return file; + } + + public void compile(PsiElement[] elements, @NotNull GlobalCompilingVisitor globalVisitor) { + elements[0].getParent().accept(new JavaCompilingVisitor(globalVisitor)); + } + + @NotNull + public PsiElementVisitor createMatchingVisitor(@NotNull GlobalMatchingVisitor globalVisitor) { + return new JavaMatchingVisitor(globalVisitor); + } + + @NotNull + @Override + public PsiElementVisitor getLexicalNodesFilter(@NotNull LexicalNodesFilter filter) { + if (myJavaLexicalNodesFilter == null) { + myJavaLexicalNodesFilter = new JavaLexicalNodesFilter(filter); + } + return myJavaLexicalNodesFilter; + } + + @NotNull + public CompiledPattern createCompiledPattern() { + return new JavaCompiledPattern(); + } + + @Override + public boolean canProcess(@NotNull FileType fileType) { + return fileType == StdFileTypes.JAVA; + } + + public boolean isMyLanguage(@NotNull Language language) { + return language == StdLanguages.JAVA; + } + + @Override + public StructuralReplaceHandler getReplaceHandler(@NotNull ReplacementContext context) { + return new JavaReplaceHandler(context); + } + + @NotNull + @Override + public PsiElement[] createPatternTree(@NotNull String text, + @NotNull PatternTreeContext context, + @NotNull FileType fileType, + @Nullable Language language, + String contextName, @Nullable String extension, + @NotNull Project project, + boolean physical) { + if (physical) { + throw new UnsupportedOperationException(getClass() + " cannot create physical PSI"); + } + PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + if (context == PatternTreeContext.Block) { + PsiElement element = elementFactory.createStatementFromText("{\n" + text + "\n}", null); + final PsiElement[] children = ((PsiBlockStatement)element).getCodeBlock().getChildren(); + final int extraChildCount = 4; + + if (children.length > extraChildCount) { + PsiElement[] result = new PsiElement[children.length - extraChildCount]; + final int extraChildStart = 2; + System.arraycopy(children, extraChildStart, result, 0, children.length - extraChildCount); + return result; + } + else { + return PsiElement.EMPTY_ARRAY; + } + } + else if (context == PatternTreeContext.Class) { + PsiElement element = elementFactory.createStatementFromText("class A {\n" + text + "\n}", null); + PsiClass clazz = (PsiClass)((PsiDeclarationStatement)element).getDeclaredElements()[0]; + PsiElement startChild = clazz.getLBrace(); + if (startChild != null) startChild = startChild.getNextSibling(); + + PsiElement endChild = clazz.getRBrace(); + if (endChild != null) endChild = endChild.getPrevSibling(); + if (startChild == endChild) return PsiElement.EMPTY_ARRAY; // nothing produced + + final List<PsiElement> result = new ArrayList<PsiElement>(3); + assert startChild != null; + for (PsiElement el = startChild.getNextSibling(); el != endChild && el != null; el = el.getNextSibling()) { + if (el instanceof PsiErrorElement) continue; + result.add(el); + } + + return PsiUtilCore.toPsiElementArray(result); + } + else { + return PsiFileFactory.getInstance(project).createFileFromText("__dummy.java", text).getChildren(); + } + } + + @NotNull + @Override + public Editor createEditor(@NotNull SearchContext searchContext, + @NotNull FileType fileType, + Language dialect, + String text, + boolean useLastConfiguration) { + // provides autocompletion + + PsiElement element = searchContext.getFile(); + + if (element != null && !useLastConfiguration) { + final Editor selectedEditor = FileEditorManager.getInstance(searchContext.getProject()).getSelectedTextEditor(); + + if (selectedEditor != null) { + int caretPosition = selectedEditor.getCaretModel().getOffset(); + PsiElement positionedElement = searchContext.getFile().findElementAt(caretPosition); + + if (positionedElement == null) { + positionedElement = searchContext.getFile().findElementAt(caretPosition + 1); + } + + if (positionedElement != null) { + element = PsiTreeUtil.getParentOfType( + positionedElement, + PsiClass.class, PsiCodeBlock.class + ); + } + } + } + + final PsiManager psimanager = PsiManager.getInstance(searchContext.getProject()); + final Project project = psimanager.getProject(); + final PsiCodeFragment file = createCodeFragment(project, text, element); + final Document doc = PsiDocumentManager.getInstance(searchContext.getProject()).getDocument(file); + DaemonCodeAnalyzer.getInstance(searchContext.getProject()).setHighlightingEnabled(file, false); + return UIUtil.createEditor(doc, searchContext.getProject(), true, true, getTemplateContextType()); + } + + @Override + public Class<? extends TemplateContextType> getTemplateContextTypeClass() { + return JavaCodeContextType.class; + } + + public PsiCodeFragment createCodeFragment(Project project, String text, PsiElement context) { + final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(project); + return factory.createCodeBlockCodeFragment(text, context, true); + } + + @Override + public void checkSearchPattern(Project project, MatchOptions options) { + class ValidatingVisitor extends JavaRecursiveElementWalkingVisitor { + private PsiElement myCurrent; + + @Override public void visitAnnotation(PsiAnnotation annotation) { + final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); + + if (nameReferenceElement == null || + !nameReferenceElement.getText().equals(MatchOptions.MODIFIER_ANNOTATION_NAME)) { + return; + } + + for(PsiNameValuePair pair:annotation.getParameterList().getAttributes()) { + final PsiAnnotationMemberValue value = pair.getValue(); + + if (value instanceof PsiArrayInitializerMemberValue) { + for(PsiAnnotationMemberValue v:((PsiArrayInitializerMemberValue)value).getInitializers()) { + final String name = StringUtil.stripQuotesAroundValue(v.getText()); + checkModifier(name); + } + + } else if (value != null) { + final String name = StringUtil.stripQuotesAroundValue(value.getText()); + checkModifier(name); + } + } + } + + private void checkModifier(final String name) { + if (!MatchOptions.INSTANCE_MODIFIER_NAME.equals(name) && + !PsiModifier.PACKAGE_LOCAL.equals(name) && + Arrays.binarySearch(JavaMatchingVisitor.MODIFIERS, name) < 0 + ) { + throw new MalformedPatternException(SSRBundle.message("invalid.modifier.type",name)); + } + } + + @Override + public void visitErrorElement(PsiErrorElement element) { + super.visitErrorElement(element); + //final PsiElement parent = element.getParent(); + //if (parent != myCurrent || !"';' expected".equals(element.getErrorDescription())) { + // throw new MalformedPatternException(element.getErrorDescription()); + //} + } + + public void setCurrent(PsiElement current) { + myCurrent = current; + } + } + ValidatingVisitor visitor = new ValidatingVisitor(); + final CompiledPattern compiledPattern = PatternCompiler.compilePattern(project, options); + final int nodeCount = compiledPattern.getNodeCount(); + final NodeIterator nodes = compiledPattern.getNodes(); + while (nodes.hasNext()) { + final PsiElement current = nodes.current(); + visitor.setCurrent(nodeCount == 1 && current instanceof PsiExpressionStatement ? current : null); + current.accept(visitor); + nodes.advance(); + } + nodes.reset(); + } + + @Override + public void checkReplacementPattern(Project project, ReplaceOptions options) { + MatchOptions matchOptions = options.getMatchOptions(); + FileType fileType = matchOptions.getFileType(); + PsiElement[] statements = MatcherImplUtil.createTreeFromText( + matchOptions.getSearchPattern(), + PatternTreeContext.Block, + fileType, + project + ); + final boolean searchIsExpression = statements.length == 1 && statements[0].getLastChild() instanceof PsiErrorElement; + + PsiElement[] statements2 = MatcherImplUtil.createTreeFromText( + options.getReplacement(), + PatternTreeContext.Block, + fileType, + project + ); + final boolean replaceIsExpression = statements2.length == 1 && statements2[0].getLastChild() instanceof PsiErrorElement; + + if (searchIsExpression != replaceIsExpression) { + throw new UnsupportedPatternException( + searchIsExpression ? SSRBundle.message("replacement.template.is.not.expression.error.message") : + SSRBundle.message("search.template.is.not.expression.error.message") + ); + } + } + + @Override + public LanguageFileType getDefaultFileType(LanguageFileType currentDefaultFileType) { + return StdFileTypes.JAVA; + } + + @Override + Configuration[] getPredefinedTemplates() { + return JavaPredefinedConfigurations.createPredefinedTemplates(); + } + + @Override + public void provideAdditionalReplaceOptions(@NotNull PsiElement node, final ReplaceOptions options, final ReplacementBuilder builder) { + final String templateText = TemplateManager.getInstance(node.getProject()).createTemplate("", "", options.getReplacement()).getTemplateText(); + node.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + visitElement(expression); + } + + @Override + public void visitVariable(PsiVariable field) { + super.visitVariable(field); + + final PsiExpression initializer = field.getInitializer(); + + if (initializer != null) { + final String initText = initializer.getText(); + + if (StructuralSearchUtil.isTypedVariable(initText)) { + final ParameterInfo initInfo = builder.findParameterization(Replacer.stripTypedVariableDecoration(initText)); + + if (initInfo != null) { + initInfo.setVariableInitializerContext(true); + } + } + } + } + + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + + MatchVariableConstraint constraint = + options.getMatchOptions().getVariableConstraint(CompiledPattern.ALL_CLASS_UNMATCHED_CONTENT_VAR_ARTIFICIAL_NAME); + if (constraint != null) { + ParameterInfo e = new ParameterInfo(); + e.setName(CompiledPattern.ALL_CLASS_UNMATCHED_CONTENT_VAR_ARTIFICIAL_NAME); + e.setStartIndex(templateText.lastIndexOf('}')); + builder.addParametrization(e); + } + } + + @Override + public void visitParameter(PsiParameter parameter) { + super.visitParameter(parameter); + + String name = parameter.getName(); + String type = parameter.getType().getCanonicalText(); + + if (StructuralSearchUtil.isTypedVariable(name)) { + name = Replacer.stripTypedVariableDecoration(name); + + if (StructuralSearchUtil.isTypedVariable(type)) { + type = Replacer.stripTypedVariableDecoration(type); + } + ParameterInfo nameInfo = builder.findParameterization(name); + ParameterInfo typeInfo = builder.findParameterization(type); + + if (nameInfo != null && typeInfo != null && !(parameter.getParent() instanceof PsiCatchSection)) { + nameInfo.setArgumentContext(false); + typeInfo.setArgumentContext(false); + typeInfo.setMethodParameterContext(true); + nameInfo.setMethodParameterContext(true); + typeInfo.setElement(parameter.getTypeElement()); + } + } + } + }); + } + + @Override + public int handleSubstitution(final ParameterInfo info, + MatchResult match, + StringBuilder result, + int offset, + HashMap<String, MatchResult> matchMap) { + if (info.getName().equals(match.getName())) { + String replacementString = match.getMatchImage(); + boolean forceAddingNewLine = false; + + if (info.isMethodParameterContext()) { + StringBuilder buf = new StringBuilder(); + handleMethodParameter(buf, info, matchMap); + replacementString = buf.toString(); + } + else if (match.getAllSons().size() > 0 && !match.isScopeMatch()) { + // compound matches + StringBuilder buf = new StringBuilder(); + MatchResult r = null; + + for (final MatchResult matchResult : match.getAllSons()) { + MatchResult previous = r; + r = matchResult; + + final PsiElement currentElement = r.getMatch(); + + if (buf.length() > 0) { + final PsiElement parent = currentElement.getParent(); + if (info.isStatementContext()) { + final PsiElement previousElement = previous.getMatchRef().getElement(); + + if (!(previousElement instanceof PsiComment) && + ( buf.charAt(buf.length() - 1) != '}' || + previousElement instanceof PsiDeclarationStatement + ) + ) { + buf.append(';'); + } + + final PsiElement prevSibling = currentElement.getPrevSibling(); + + if (prevSibling instanceof PsiWhiteSpace && + prevSibling.getPrevSibling() == previous.getMatch() + ) { + // consequent statements matched so preserve whitespacing + buf.append(prevSibling.getText()); + } + else { + buf.append('\n'); + } + } + else if (info.isArgumentContext()) { + buf.append(','); + } + else if (parent instanceof PsiClass) { + final PsiElement prevSibling = PsiTreeUtil.skipSiblingsBackward(currentElement, PsiWhiteSpace.class); + if (prevSibling instanceof PsiJavaToken && JavaTokenType.COMMA.equals(((PsiJavaToken)prevSibling).getTokenType())) { + buf.append(','); + } + else { + buf.append('\n'); + } + } + else if (parent instanceof PsiReferenceList) { + buf.append(','); + } + else { + buf.append(' '); + } + } + + buf.append(r.getMatchImage()); + removeExtraSemicolonForSingleVarInstanceInMultipleMatch(info, r, buf); + forceAddingNewLine = currentElement instanceof PsiComment; + } + + replacementString = buf.toString(); + } else { + StringBuilder buf = new StringBuilder(); + if (info.isStatementContext()) { + forceAddingNewLine = match.getMatch() instanceof PsiComment; + } + buf.append(replacementString); + removeExtraSemicolonForSingleVarInstanceInMultipleMatch(info, match, buf); + replacementString = buf.toString(); + } + + offset = Replacer.insertSubstitution(result, offset, info, replacementString); + offset = removeExtraSemicolon(info, offset, result, match); + if (forceAddingNewLine && info.isStatementContext()) { + result.insert(info.getStartIndex() + offset + 1, '\n'); + offset ++; + } + } + return offset; + } + + @Override + public int processAdditionalOptions(ParameterInfo info, int offset, StringBuilder result, MatchResult r) { + if (info.isStatementContext()) { + return removeExtraSemicolon(info, offset, result, r); + } + return offset; + } + + @Override + public boolean isIdentifier(PsiElement element) { + return element instanceof PsiIdentifier; + } + + @Override + public Collection<String> getReservedWords() { + return Collections.singleton(PsiModifier.PACKAGE_LOCAL); + } + + @Override + public boolean isDocCommentOwner(PsiElement match) { + return match instanceof PsiMember; + } + + private static void handleMethodParameter(StringBuilder buf, ParameterInfo info, HashMap<String, MatchResult> matchMap) { + if(info.getElement() ==null) { + // no specific handling for name of method parameter since it is handled with type + return; + } + + String name = ((PsiParameter)info.getElement().getParent()).getName(); + name = StructuralSearchUtil.isTypedVariable(name) ? Replacer.stripTypedVariableDecoration(name):name; + + final MatchResult matchResult = matchMap.get(name); + if (matchResult == null) return; + + if (matchResult.isMultipleMatch()) { + for (MatchResult result : matchResult.getAllSons()) { + if (buf.length() > 0) { + buf.append(','); + } + + appendParameter(buf, result); + } + } else { + appendParameter(buf, matchResult); + } + } + + private static void appendParameter(final StringBuilder buf, final MatchResult _matchResult) { + for(Iterator<MatchResult> j = _matchResult.getAllSons().iterator();j.hasNext();) { + buf.append(j.next().getMatchImage()).append(' ').append(j.next().getMatchImage()); + } + } + + private static void removeExtraSemicolonForSingleVarInstanceInMultipleMatch(final ParameterInfo info, MatchResult r, StringBuilder buf) { + if (info.isStatementContext()) { + final PsiElement element = r.getMatchRef().getElement(); + + // remove extra ; + if (buf.charAt(buf.length()-1)==';' && + r.getMatchImage().charAt(r.getMatchImage().length()-1)==';' && + ( element instanceof PsiReturnStatement || + element instanceof PsiDeclarationStatement || + element instanceof PsiExpressionStatement || + element instanceof PsiAssertStatement || + element instanceof PsiBreakStatement || + element instanceof PsiContinueStatement || + element instanceof PsiMember || + element instanceof PsiIfStatement && !(((PsiIfStatement)element).getThenBranch() instanceof PsiBlockStatement) || + element instanceof PsiLoopStatement && !(((PsiLoopStatement)element).getBody() instanceof PsiBlockStatement) + ) + ) { + // contains extra ; + buf.deleteCharAt(buf.length()-1); + } + } + } + + private static int removeExtraSemicolon(ParameterInfo info, int offset, StringBuilder result, MatchResult match) { + if (info.isStatementContext()) { + int index = offset+ info.getStartIndex(); + if (result.charAt(index)==';' && + ( match == null || + ( result.charAt(index-1)=='}' && + !(match.getMatch() instanceof PsiDeclarationStatement) && // array init in dcl + !(match.getMatch() instanceof PsiNewExpression) // array initializer + ) || + ( !match.isMultipleMatch() && // ; in comment + match.getMatch() instanceof PsiComment + ) || + ( match.isMultipleMatch() && // ; in comment + match.getAllSons().get( match.getAllSons().size() - 1 ).getMatch() instanceof PsiComment + ) + ) + ) { + result.deleteCharAt(index); + --offset; + } + } + + return offset; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaCompiledPattern.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaCompiledPattern.java new file mode 100644 index 000000000000..9a6330bb799b --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaCompiledPattern.java @@ -0,0 +1,90 @@ +package com.intellij.structuralsearch.impl.matcher; + +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.strategies.ExprMatchingStrategy; +import org.jetbrains.annotations.Nullable; + +/** +* @author Eugene.Kudelevsky +*/ +public class JavaCompiledPattern extends CompiledPattern { + private static final String TYPED_VAR_PREFIX = "__$_"; + + private boolean requestsSuperFields; + private boolean requestsSuperMethods; + private boolean requestsSuperInners; + + public JavaCompiledPattern() { + setStrategy(ExprMatchingStrategy.getInstance()); + } + + public String[] getTypedVarPrefixes() { + return new String[] {TYPED_VAR_PREFIX}; + } + + public boolean isTypedVar(final String str) { + if (str.charAt(0)=='@') { + return str.regionMatches(1,TYPED_VAR_PREFIX,0,TYPED_VAR_PREFIX.length()); + } else { + return str.startsWith(TYPED_VAR_PREFIX); + } + } + + @Override + public boolean isToResetHandler(PsiElement element) { + return !(element instanceof PsiJavaToken) && + !(element instanceof PsiJavaCodeReferenceElement && element.getParent() instanceof PsiAnnotation); + } + + @Nullable + @Override + public String getAlternativeTextToMatch(PsiElement node, String previousText) { + // Short class name is matched with fully qualified name + if(node instanceof PsiJavaCodeReferenceElement || node instanceof PsiClass) { + PsiElement element = (node instanceof PsiJavaCodeReferenceElement)? + ((PsiJavaCodeReferenceElement)node).resolve(): + node; + + if (element instanceof PsiClass) { + String text = ((PsiClass)element).getQualifiedName(); + if (text != null && text.equals(previousText)) { + text = ((PsiClass)element).getName(); + } + + if (text != null) { + return text; + } + } + } else if (node instanceof PsiLiteralExpression) { + return node.getText(); + } + return null; + } + + public static final Key<String> ALL_CLASS_CONTENT_VAR_NAME_KEY = Key.create("AllClassContent"); + + public boolean isRequestsSuperFields() { + return requestsSuperFields; + } + + public void setRequestsSuperFields(boolean requestsSuperFields) { + this.requestsSuperFields = requestsSuperFields; + } + + public boolean isRequestsSuperInners() { + return requestsSuperInners; + } + + public void setRequestsSuperInners(boolean requestsSuperInners) { + this.requestsSuperInners = requestsSuperInners; + } + + public boolean isRequestsSuperMethods() { + return requestsSuperMethods; + } + + public void setRequestsSuperMethods(boolean requestsSuperMethods) { + this.requestsSuperMethods = requestsSuperMethods; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchPredicateProvider.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchPredicateProvider.java new file mode 100644 index 000000000000..042912e0354c --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchPredicateProvider.java @@ -0,0 +1,62 @@ +package com.intellij.structuralsearch.impl.matcher; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.structuralsearch.MatchOptions; +import com.intellij.structuralsearch.MatchVariableConstraint; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.impl.matcher.predicates.*; + +import java.util.Set; + +public class JavaMatchPredicateProvider extends MatchPredicateProvider{ + @Override + public void collectPredicates(MatchVariableConstraint constraint, String name, MatchOptions options, Set<MatchPredicate> predicates) { + if (constraint.isReadAccess()) { + MatchPredicate predicate = new ReadPredicate(); + + if (constraint.isInvertReadAccess()) { + predicate = new NotPredicate(predicate); + } + predicates.add(predicate); + } + + if (constraint.isWriteAccess()) { + MatchPredicate predicate = new WritePredicate(); + + if (constraint.isInvertWriteAccess()) { + predicate = new NotPredicate(predicate); + } + predicates.add(predicate); + } + + if (!StringUtil.isEmptyOrSpaces(constraint.getNameOfExprType())) { + MatchPredicate predicate = new ExprTypePredicate( + constraint.getNameOfExprType(), + name, + constraint.isExprTypeWithinHierarchy(), + options.isCaseSensitiveMatch(), + constraint.isPartOfSearchResults() + ); + + if (constraint.isInvertExprType()) { + predicate = new NotPredicate(predicate); + } + predicates.add(predicate); + } + + if (!StringUtil.isEmptyOrSpaces(constraint.getNameOfFormalArgType())) { + MatchPredicate predicate = new FormalArgTypePredicate( + constraint.getNameOfFormalArgType(), + name, + constraint.isFormalArgTypeWithinHierarchy(), + options.isCaseSensitiveMatch(), + constraint.isPartOfSearchResults() + ); + if (constraint.isInvertFormalType()) { + predicate = new NotPredicate(predicate); + } + predicates.add(predicate); + } + + } +} 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); + } + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java new file mode 100644 index 000000000000..271f1c468b0b --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java @@ -0,0 +1,589 @@ +package com.intellij.structuralsearch.impl.matcher.compiler; + +import com.intellij.psi.*; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.search.*; +import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.structuralsearch.MatchOptions; +import com.intellij.structuralsearch.MatchVariableConstraint; +import com.intellij.structuralsearch.SSRBundle; +import com.intellij.structuralsearch.UnsupportedPatternException; +import com.intellij.structuralsearch.impl.matcher.CompiledPattern; +import com.intellij.structuralsearch.impl.matcher.JavaCompiledPattern; +import com.intellij.structuralsearch.impl.matcher.filters.*; +import com.intellij.structuralsearch.impl.matcher.handlers.*; +import com.intellij.structuralsearch.impl.matcher.iterators.DocValuesIterator; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate; +import com.intellij.structuralsearch.impl.matcher.strategies.*; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Eugene.Kudelevsky + */ +public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor { + private final GlobalCompilingVisitor myCompilingVisitor; + + @NonNls private static final String COMMENT = "\\s*(__\\$_\\w+)\\s*"; + private static final Pattern ourPattern = Pattern.compile("//" + COMMENT, Pattern.DOTALL); + private static final Pattern ourPattern2 = Pattern.compile("/\\*" + COMMENT + "\\*/", Pattern.DOTALL); + private static final Pattern ourPattern3 = Pattern.compile("/\\*\\*" + COMMENT + "\\*/", Pattern.DOTALL); + + public JavaCompilingVisitor(GlobalCompilingVisitor compilingVisitor) { + this.myCompilingVisitor = compilingVisitor; + } + + @Override + public void visitDocTag(PsiDocTag psiDocTag) { + super.visitDocTag(psiDocTag); + + NodeIterator sons = new DocValuesIterator(psiDocTag.getFirstChild()); + while (sons.hasNext()) { + myCompilingVisitor.setHandler(sons.current(), new DocDataHandler()); + sons.advance(); + } + } + + @Override + public void visitComment(PsiComment comment) { + super.visitComment(comment); + + final String text = comment.getText(); + Matcher matcher = ourPattern.matcher(text); + boolean matches = false; + if (!matcher.matches()) { + matcher = ourPattern2.matcher(text); + + if (!matcher.matches()) { + matcher = ourPattern3.matcher(text); + } + else { + matches = true; + } + } + else { + matches = true; + } + + if (matches || matcher.matches()) { + String str = matcher.group(1); + comment.putUserData(CompiledPattern.HANDLER_KEY, str); + + GlobalCompilingVisitor.setFilter( + myCompilingVisitor.getContext().getPattern().getHandler(comment), + CommentFilter.getInstance() + ); + + SubstitutionHandler handler = (SubstitutionHandler)myCompilingVisitor.getContext().getPattern().getHandler(str); + + if (handler.getPredicate() != null) { + ((RegExpPredicate)handler.getPredicate()).setMultiline(true); + } + + RegExpPredicate predicate = MatchingHandler.getSimpleRegExpPredicate(handler); + if (GlobalCompilingVisitor.isSuitablePredicate(predicate, handler)) { + myCompilingVisitor.processTokenizedName(predicate.getRegExp(), true, GlobalCompilingVisitor.OccurenceKind.COMMENT); + } + + matches = true; + } + + if (!matches) { + MatchingHandler handler = myCompilingVisitor.processPatternStringWithFragments(text, GlobalCompilingVisitor.OccurenceKind.COMMENT); + if (handler != null) comment.putUserData(CompiledPattern.HANDLER_KEY, handler); + } + } + + @Override + public void visitLiteralExpression(PsiLiteralExpression expression) { + String value = expression.getText(); + + if (value.length() > 2 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') { + @Nullable MatchingHandler handler = + myCompilingVisitor.processPatternStringWithFragments(value, GlobalCompilingVisitor.OccurenceKind.LITERAL); + + if (handler != null) { + expression.putUserData(CompiledPattern.HANDLER_KEY, handler); + } + } + super.visitLiteralExpression(expression); + } + + @Override + public void visitClassInitializer(final PsiClassInitializer initializer) { + super.visitClassInitializer(initializer); + PsiStatement[] psiStatements = initializer.getBody().getStatements(); + if (psiStatements.length == 1 && psiStatements[0] instanceof PsiExpressionStatement) { + MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(psiStatements[0]); + + if (handler instanceof SubstitutionHandler) { + myCompilingVisitor.getContext().getPattern().setHandler(initializer, new SubstitutionHandler((SubstitutionHandler)handler)); + } + } + } + + @Override + public void visitField(PsiField psiField) { + super.visitField(psiField); + CompiledPattern pattern = myCompilingVisitor.getContext().getPattern(); + final MatchingHandler handler = pattern.getHandler(psiField); + + if (needsSupers(psiField, handler)) { + assert pattern instanceof JavaCompiledPattern; + ((JavaCompiledPattern)pattern).setRequestsSuperFields(true); + } + } + + @Override + public void visitMethod(PsiMethod psiMethod) { + super.visitMethod(psiMethod); + CompiledPattern pattern = myCompilingVisitor.getContext().getPattern(); + final MatchingHandler handler = pattern.getHandler(psiMethod); + + if (needsSupers(psiMethod, handler)) { + assert pattern instanceof JavaCompiledPattern; + ((JavaCompiledPattern)pattern).setRequestsSuperMethods(true); + } + + GlobalCompilingVisitor.setFilter(handler, MethodFilter.getInstance()); + handleReferenceText(psiMethod.getName(), myCompilingVisitor.getContext()); + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression reference) { + visitElement(reference); + + boolean typedVarProcessed = false; + final PsiElement referenceParent = reference.getParent(); + + if ((myCompilingVisitor.getContext().getPattern().isRealTypedVar(reference)) && + reference.getQualifierExpression() == null && + !(referenceParent instanceof PsiExpressionStatement) + ) { + // typed var for expression (but not top level) + MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(reference); + GlobalCompilingVisitor.setFilter(handler, ExpressionFilter.getInstance()); + typedVarProcessed = true; + } + + if (!(referenceParent instanceof PsiMethodCallExpression)) { + handleReference(reference); + } + + MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(reference); + + // We want to merge qname related to class to find it in any form + final String referencedName = reference.getReferenceName(); + + if (!typedVarProcessed && + !(handler instanceof SubstitutionHandler)) { + final PsiElement resolve = reference.resolve(); + + PsiElement referenceQualifier = reference.getQualifier(); + if (resolve instanceof PsiClass || + (resolve == null && + ((referencedName != null && Character.isUpperCase(referencedName.charAt(0))) || + referenceQualifier == null + ) + ) + ) { + boolean hasNoNestedSubstitutionHandlers = false; + PsiExpression qualifier; + PsiReferenceExpression currentReference = reference; + + while ((qualifier = currentReference.getQualifierExpression()) != null) { + if (!(qualifier instanceof PsiReferenceExpression) || + myCompilingVisitor.getContext().getPattern().getHandler(qualifier) instanceof SubstitutionHandler + ) { + hasNoNestedSubstitutionHandlers = true; + break; + } + currentReference = (PsiReferenceExpression)qualifier; + } + if (!hasNoNestedSubstitutionHandlers) { + createAndSetSubstitutionHandlerFromReference( + reference, + resolve != null ? ((PsiClass)resolve).getQualifiedName() : reference.getText(), + referenceParent instanceof PsiExpression + ); + } + } + else if (referenceQualifier != null && reference.getParent() instanceof PsiExpressionStatement) { + //Handler qualifierHandler = context.pattern.getHandler(referenceQualifier); + //if (qualifierHandler instanceof SubstitutionHandler && + // !context.pattern.isRealTypedVar(reference) + // ) { + // createAndSetSubstitutionHandlerFromReference(reference, referencedName); + // + // SubstitutionHandler substitutionHandler = (SubstitutionHandler)qualifierHandler; + // RegExpPredicate expPredicate = Handler.getSimpleRegExpPredicate(substitutionHandler); + // //if (expPredicate != null) + // // substitutionHandler.setPredicate(new ExprTypePredicate(expPredicate.getRegExp(), null, true, true, false)); + //} + } + } + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + handleReference(expression.getMethodExpression()); + super.visitMethodCallExpression(expression); + } + + @Override + public void visitBlockStatement(PsiBlockStatement psiBlockStatement) { + super.visitBlockStatement(psiBlockStatement); + myCompilingVisitor.getContext().getPattern().getHandler(psiBlockStatement).setFilter(BlockFilter.getInstance()); + } + + @Override + public void visitVariable(PsiVariable psiVariable) { + super.visitVariable(psiVariable); + myCompilingVisitor.getContext().getPattern().getHandler(psiVariable).setFilter(VariableFilter.getInstance()); + handleReferenceText(psiVariable.getName(), myCompilingVisitor.getContext()); + } + + @Override + public void visitDeclarationStatement(PsiDeclarationStatement psiDeclarationStatement) { + super.visitDeclarationStatement(psiDeclarationStatement); + + if (psiDeclarationStatement.getFirstChild() instanceof PsiTypeElement) { + // search for expression or symbol + final PsiJavaCodeReferenceElement reference = + ((PsiTypeElement)psiDeclarationStatement.getFirstChild()).getInnermostComponentReferenceElement(); + + if (reference != null && + (myCompilingVisitor.getContext().getPattern().isRealTypedVar(reference.getReferenceNameElement())) && + reference.getParameterList().getTypeParameterElements().length > 0 + ) { + myCompilingVisitor.setHandler(psiDeclarationStatement, new TypedSymbolHandler()); + final MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(psiDeclarationStatement); + // typed symbol + handler.setFilter( + TypedSymbolNodeFilter.getInstance() + ); + + final PsiTypeElement[] params = reference.getParameterList().getTypeParameterElements(); + for (PsiTypeElement param : params) { + if (param.getInnermostComponentReferenceElement() != null && + (myCompilingVisitor.getContext().getPattern().isRealTypedVar( + param.getInnermostComponentReferenceElement().getReferenceNameElement())) + ) { + myCompilingVisitor.getContext().getPattern().getHandler(param).setFilter( + TypeParameterFilter.getInstance() + ); + } + } + + return; + } + } + + final MatchingHandler handler = new DeclarationStatementHandler(); + myCompilingVisitor.getContext().getPattern().setHandler(psiDeclarationStatement, handler); + PsiElement previousNonWhiteSpace = psiDeclarationStatement.getPrevSibling(); + + while (previousNonWhiteSpace instanceof PsiWhiteSpace) { + previousNonWhiteSpace = previousNonWhiteSpace.getPrevSibling(); + } + + if (previousNonWhiteSpace instanceof PsiComment) { + ((DeclarationStatementHandler)handler) + .setCommentHandler(myCompilingVisitor.getContext().getPattern().getHandler(previousNonWhiteSpace)); + + myCompilingVisitor.getContext().getPattern().setHandler( + previousNonWhiteSpace, + handler + ); + } + + // detect typed symbol, it will have no variable + handler.setFilter(DeclarationFilter.getInstance()); + } + + @Override + public void visitDocComment(PsiDocComment psiDocComment) { + super.visitDocComment(psiDocComment); + myCompilingVisitor.getContext().getPattern().getHandler(psiDocComment).setFilter(JavaDocFilter.getInstance()); + } + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + + if (reference.getParent() != null && + reference.getParent().getParent() instanceof PsiClass) { + GlobalCompilingVisitor.setFilter(myCompilingVisitor.getContext().getPattern().getHandler(reference), TypeFilter.getInstance()); + } + + handleReference(reference); + } + + @Override + public void visitClass(PsiClass psiClass) { + super.visitClass(psiClass); + + CompiledPattern pattern = myCompilingVisitor.getContext().getPattern(); + final MatchingHandler handler = pattern.getHandler(psiClass); + + if (needsSupers(psiClass, handler)) { + ((JavaCompiledPattern)pattern).setRequestsSuperInners(true); + } + handleReferenceText(psiClass.getName(), myCompilingVisitor.getContext()); + + GlobalCompilingVisitor.setFilter(handler, ClassFilter.getInstance()); + + boolean hasSubstitutionHandler = false; + for (PsiElement element = psiClass.getFirstChild(); element != null; element = element.getNextSibling()) { + if (element instanceof PsiTypeElement && element.getNextSibling() instanceof PsiErrorElement) { + // found match that + MatchingHandler unmatchedSubstitutionHandler = pattern.getHandler(element); + if (unmatchedSubstitutionHandler != null) { + psiClass.putUserData(JavaCompiledPattern.ALL_CLASS_CONTENT_VAR_NAME_KEY, pattern.getTypedVarString(element)); + hasSubstitutionHandler = true; + } + } + } + + if (!hasSubstitutionHandler) { + String name = CompiledPattern.ALL_CLASS_UNMATCHED_CONTENT_VAR_ARTIFICIAL_NAME; + psiClass.putUserData(JavaCompiledPattern.ALL_CLASS_CONTENT_VAR_NAME_KEY, name); + MatchOptions options = myCompilingVisitor.getContext().getOptions(); + if (options.getVariableConstraint(name) == null) { + pattern.createSubstitutionHandler(name, name, false, 0, Integer.MAX_VALUE, true); + MatchVariableConstraint constraint = new MatchVariableConstraint(true); + constraint.setName(name); + constraint.setMinCount(0); + constraint.setMaxCount(Integer.MAX_VALUE); + options.addVariableConstraint(constraint); + } + } + } + + private SubstitutionHandler createAndSetSubstitutionHandlerFromReference(final PsiElement expr, final String referenceText, + boolean classQualifier) { + final SubstitutionHandler substitutionHandler = + new SubstitutionHandler("__" + referenceText.replace('.', '_'), false, classQualifier ? 0 : 1, 1, false); + substitutionHandler.setPredicate(new RegExpPredicate(referenceText.replaceAll("\\.", "\\\\."), true, null, false, false)); + myCompilingVisitor.getContext().getPattern().setHandler(expr, substitutionHandler); + return substitutionHandler; + } + + @Override + public void visitExpressionStatement(PsiExpressionStatement expr) { + myCompilingVisitor.handle(expr); + + super.visitExpressionStatement(expr); + + final PsiElement child = expr.getLastChild(); + if (!(child instanceof PsiJavaToken) && !(child instanceof PsiComment)) { + // search for expression or symbol + final PsiElement reference = expr.getFirstChild(); + MatchingHandler referenceHandler = myCompilingVisitor.getContext().getPattern().getHandler(reference); + + if (referenceHandler instanceof SubstitutionHandler && + (reference instanceof PsiReferenceExpression) + ) { + // symbol + myCompilingVisitor.getContext().getPattern().setHandler(expr, referenceHandler); + referenceHandler.setFilter( + SymbolNodeFilter.getInstance() + ); + + myCompilingVisitor.setHandler(expr, new SymbolHandler((SubstitutionHandler)referenceHandler)); + } + else if (reference instanceof PsiLiteralExpression) { + MatchingHandler handler = new ExpressionHandler(); + myCompilingVisitor.setHandler(expr, handler); + handler.setFilter(ConstantFilter.getInstance()); + } + else { + // just expression + MatchingHandler handler; + myCompilingVisitor.setHandler(expr, handler = new ExpressionHandler()); + + handler.setFilter(ExpressionFilter.getInstance()); + } + } + else if (expr.getExpression() instanceof PsiReferenceExpression && + (myCompilingVisitor.getContext().getPattern().isRealTypedVar(expr.getExpression()))) { + // search for statement + final MatchingHandler exprHandler = myCompilingVisitor.getContext().getPattern().getHandler(expr); + if (exprHandler instanceof SubstitutionHandler) { + SubstitutionHandler handler = (SubstitutionHandler)exprHandler; + handler.setFilter(new StatementFilter()); + handler.setMatchHandler(new StatementHandler()); + } + } + } + + @Override + public void visitElement(PsiElement element) { + myCompilingVisitor.handle(element); + super.visitElement(element); + } + + + private void handleReference(PsiJavaCodeReferenceElement reference) { + handleReferenceText(reference.getReferenceName(), myCompilingVisitor.getContext()); + } + + private static void handleReferenceText(String refname, CompileContext compileContext) { + if (refname == null) return; + + if (compileContext.getPattern().isTypedVar(refname)) { + SubstitutionHandler handler = (SubstitutionHandler)compileContext.getPattern().getHandler(refname); + RegExpPredicate predicate = MatchingHandler.getSimpleRegExpPredicate(handler); + if (!GlobalCompilingVisitor.isSuitablePredicate(predicate, handler)) { + return; + } + + refname = predicate.getRegExp(); + + if (handler.isStrictSubtype() || handler.isSubtype()) { + final OptimizingSearchHelper searchHelper = compileContext.getSearchHelper(); + if (addDescendantsOf(refname, handler.isSubtype(), searchHelper, compileContext)) { + searchHelper.endTransaction(); + } + + return; + } + } + + GlobalCompilingVisitor.addFilesToSearchForGivenWord(refname, true, GlobalCompilingVisitor.OccurenceKind.CODE, compileContext); + } + + + public static boolean addDescendantsOf(final String refname, final boolean subtype, OptimizingSearchHelper searchHelper, CompileContext context) { + final List<PsiClass> classes = buildDescendants(refname, subtype, searchHelper, context); + + for (final PsiClass aClass : classes) { + if (aClass instanceof PsiAnonymousClass) { + searchHelper.addWordToSearchInCode(((PsiAnonymousClass)aClass).getBaseClassReference().getReferenceName()); + } + else { + searchHelper.addWordToSearchInCode(aClass.getName()); + } + } + + return classes.size() > 0; + } + + private static List<PsiClass> buildDescendants(String className, + boolean includeSelf, + OptimizingSearchHelper searchHelper, + CompileContext context) { + if (!searchHelper.doOptimizing()) return Collections.emptyList(); + final SearchScope scope = context.getOptions().getScope(); + if (!(scope instanceof GlobalSearchScope)) return Collections.emptyList(); + + final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(context.getProject()); + final PsiClass[] classes = cache.getClassesByName(className, (GlobalSearchScope)scope); + final List<PsiClass> results = new ArrayList<PsiClass>(); + + final PsiElementProcessor<PsiClass> processor = new PsiElementProcessor<PsiClass>() { + public boolean execute(@NotNull PsiClass element) { + results.add(element); + return true; + } + + }; + + for (PsiClass aClass : classes) { + ClassInheritorsSearch.search(aClass, scope, true).forEach(new PsiElementProcessorAdapter<PsiClass>(processor)); + } + + if (includeSelf) { + Collections.addAll(results, classes); + } + + return results; + } + + + @Override + public void visitCodeBlock(PsiCodeBlock block) { + myCompilingVisitor.setCodeBlockLevel(myCompilingVisitor.getCodeBlockLevel() + 1); + MatchingStrategy strategy = null; + + for (PsiElement el = block.getFirstChild(); el != null; el = el.getNextSibling()) { + if (GlobalCompilingVisitor.getFilter().accepts(el)) { + if (el instanceof PsiWhiteSpace) { + myCompilingVisitor.addLexicalNode(el); + } + } + else { + el.accept(this); + if (myCompilingVisitor.getCodeBlockLevel() == 1) { + MatchingStrategy newstrategy = findStrategy(el); + final MatchingHandler matchingHandler = myCompilingVisitor.getContext().getPattern().getHandler(el); + myCompilingVisitor.getContext().getPattern().setHandler(el, new TopLevelMatchingHandler(matchingHandler)); + + if (strategy == null || (strategy instanceof JavaDocMatchingStrategy)) { + strategy = newstrategy; + } + else { + if (strategy.getClass() != newstrategy.getClass()) { + if (!(strategy instanceof CommentMatchingStrategy)) { + throw new UnsupportedPatternException(SSRBundle.message("different.strategies.for.top.level.nodes.error.message")); + } + strategy = newstrategy; + } + } + } + } + } + + if (myCompilingVisitor.getCodeBlockLevel() == 1) { + if (strategy == null) { + // this should happen only for error patterns + strategy = ExprMatchingStrategy.getInstance(); + } + myCompilingVisitor.getContext().getPattern().setStrategy(strategy); + } + myCompilingVisitor.setCodeBlockLevel(myCompilingVisitor.getCodeBlockLevel() - 1); + } + + private MatchingStrategy findStrategy(PsiElement el) { + // identify matching strategy + final MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(el); + + //if (handler instanceof SubstitutionHandler) { + // final SubstitutionHandler shandler = (SubstitutionHandler) handler; + if (handler.getFilter() instanceof SymbolNodeFilter || + handler.getFilter() instanceof TypedSymbolNodeFilter + ) { + return SymbolMatchingStrategy.getInstance(); + } + //} + + if (el instanceof PsiDocComment) { + return JavaDocMatchingStrategy.getInstance(); + } + else if (el instanceof PsiComment) { + return CommentMatchingStrategy.getInstance(); + } + + return ExprMatchingStrategy.getInstance(); + } + + private static boolean needsSupers(final PsiElement element, final MatchingHandler handler) { + if (element.getParent() instanceof PsiClass && + handler instanceof SubstitutionHandler + ) { + final SubstitutionHandler handler2 = (SubstitutionHandler)handler; + + return (handler2.isStrictSubtype() || handler2.isSubtype()); + } + return false; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/BlockFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/BlockFilter.java new file mode 100644 index 000000000000..5cef7ffaf3c8 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/BlockFilter.java @@ -0,0 +1,42 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiBlockStatement; +import com.intellij.psi.PsiCodeBlock; +import com.intellij.psi.PsiElement; + +/** + * Filters block related nodes + */ +public class BlockFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } + + @Override + public void visitBlockStatement(PsiBlockStatement psiBlockStatement) { + result = true; + } + + @Override + public void visitCodeBlock(PsiCodeBlock psiCodeBlock) { + result = true; + } + + private BlockFilter() { + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new BlockFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ClassFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ClassFilter.java new file mode 100644 index 000000000000..6e68b08d70da --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ClassFilter.java @@ -0,0 +1,43 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 26.12.2003 + * Time: 19:37:13 + * To change this template use Options | File Templates. + */ +public class ClassFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitAnonymousClass(PsiAnonymousClass psiAnonymousClass) { + result = true; + } + + @Override public void visitClass(PsiClass psiClass) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new ClassFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private ClassFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/CommentFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/CommentFilter.java new file mode 100644 index 000000000000..c2d974c9754e --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/CommentFilter.java @@ -0,0 +1,48 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Created by IntelliJ IDEA. + * User: Maxim.Mossienko + * Date: Apr 22, 2004 + * Time: 9:13:12 PM + * To change this template use File | Settings | File Templates. + */ +public class CommentFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitComment(PsiComment comment) { + result = true; + } + + @Override public void visitField(PsiField field) { + result = true; + } + + @Override public void visitMethod(PsiMethod method) { + result = true; + } + + @Override public void visitClass(PsiClass clazz) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new CommentFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private CommentFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ConstantFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ConstantFilter.java new file mode 100644 index 000000000000..401e3c15e365 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ConstantFilter.java @@ -0,0 +1,38 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLiteralExpression; + +/** + * Created by IntelliJ IDEA. + * User: Maxim.Mossienko + * Date: Apr 27, 2004 + * Time: 7:55:40 PM + * To change this template use File | Settings | File Templates. + */ +public class ConstantFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitLiteralExpression(PsiLiteralExpression psiLiteral) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new ConstantFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private ConstantFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/DeclarationFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/DeclarationFilter.java new file mode 100644 index 000000000000..d5f5ef5bf36e --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/DeclarationFilter.java @@ -0,0 +1,44 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 26.12.2003 + * Time: 19:23:24 + * To change this template use Options | File Templates. + */ +public class DeclarationFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitDeclarationStatement(PsiDeclarationStatement dcl) { + result = true; + } + + @Override public void visitVariable(PsiVariable psiVar) { + result = true; + } + + @Override public void visitClass(PsiClass psiClass) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new DeclarationFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private DeclarationFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ExpressionFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ExpressionFilter.java new file mode 100644 index 000000000000..632cc403eabb --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ExpressionFilter.java @@ -0,0 +1,39 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiReferenceExpression; + +/** + * Filters expression nodes + */ +public class ExpressionFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) { + result = true; + } + + @Override public void visitExpression(PsiExpression psiExpression) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new ExpressionFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private ExpressionFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaDocFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaDocFilter.java new file mode 100644 index 000000000000..d12e9cbd0440 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaDocFilter.java @@ -0,0 +1,33 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.PsiDocCommentOwner; +import com.intellij.psi.PsiElement; +import com.intellij.psi.javadoc.PsiDocComment; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 26.12.2003 + * Time: 19:08:26 + * To change this template use Options | File Templates. + */ +public class JavaDocFilter implements NodeFilter { + protected boolean result; + + public boolean accepts(PsiElement element) { + return element instanceof PsiDocCommentOwner || + element instanceof PsiDocComment; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new JavaDocFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private JavaDocFilter() { + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaLexicalNodesFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaLexicalNodesFilter.java new file mode 100644 index 000000000000..1c86acda3e83 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaLexicalNodesFilter.java @@ -0,0 +1,37 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.psi.*; +import com.intellij.psi.javadoc.PsiDocComment; + +/** +* @author Eugene.Kudelevsky +*/ +public class JavaLexicalNodesFilter extends JavaElementVisitor { + private final LexicalNodesFilter myLexicalNodesFilter; + + public JavaLexicalNodesFilter(LexicalNodesFilter lexicalNodesFilter) { + this.myLexicalNodesFilter = lexicalNodesFilter; + } + + @Override public void visitJavaToken(final PsiJavaToken t) { + myLexicalNodesFilter.setResult(true); + } + + @Override public void visitComment(final PsiComment comment) { + } + + @Override public void visitDocComment(final PsiDocComment comment) { + } + + @Override public void visitKeyword(PsiKeyword keyword) { + myLexicalNodesFilter.setResult(!myLexicalNodesFilter.isCareKeyWords()); + } + + @Override public void visitWhiteSpace(final PsiWhiteSpace space) { + myLexicalNodesFilter.setResult(true); + } + + @Override public void visitErrorElement(final PsiErrorElement element) { + myLexicalNodesFilter.setResult(true); + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/MethodFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/MethodFilter.java new file mode 100644 index 000000000000..00888bb86c5a --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/MethodFilter.java @@ -0,0 +1,33 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; + +/** + * Filters method nodes + */ +public class MethodFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitMethod(PsiMethod psiMethod) { + result = true; + } + + private MethodFilter() {} + + private static class NodeFilterHolder { + private static final NodeFilter instance = new MethodFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/StatementFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/StatementFilter.java new file mode 100644 index 000000000000..91abc96139e1 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/StatementFilter.java @@ -0,0 +1,33 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 26.12.2003 + * Time: 17:46:10 + * To change this template use Options | File Templates. + */ +public class StatementFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) { + result = false; + } + + @Override public void visitStatement(PsiStatement psiStatement) { + result = true; + } + + @Override public void visitComment(PsiComment comment) { + result = true; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/SymbolNodeFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/SymbolNodeFilter.java new file mode 100644 index 000000000000..3e461b6738b6 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/SymbolNodeFilter.java @@ -0,0 +1,72 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Tree filter for searching symbols ('T) + */ +public class SymbolNodeFilter extends JavaElementVisitor implements NodeFilter { + private boolean result; + + @Override public void visitExpression(PsiExpression expr) { + result = true; + } + + @Override public void visitLiteralExpression(PsiLiteralExpression psiLiteralExpression) { + result = true; + } + + @Override public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) { + result = true; + } + + @Override public void visitAnnotation(final PsiAnnotation annotation) { + result = true; + } + + @Override public void visitAnnotationMethod(final PsiAnnotationMethod method) { + result = true; + } + + @Override public void visitNameValuePair(final PsiNameValuePair pair) { + result = true; + } + + @Override public void visitMethod(PsiMethod psiMethod) { + result = true; + } + + @Override public void visitClass(PsiClass psiClass) { + result = true; + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) { + result = true; + } + + @Override public void visitVariable(PsiVariable psiVar) { + result = true; + } + + @Override public void visitTypeParameter(PsiTypeParameter psiTypeParameter) { + result = true; + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new SymbolNodeFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private SymbolNodeFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeFilter.java new file mode 100644 index 000000000000..14dd452b0b7a --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeFilter.java @@ -0,0 +1,42 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiJavaCodeReferenceElement; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 23.01.2004 + * Time: 1:07:09 + * To change this template use File | Settings | File Templates. + */ +public class TypeFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitClass(PsiClass aClass) { + result = true; + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiMethod) { + result = true; + } + + private TypeFilter() {} + + private static class NodeFilterHolder { + private static final NodeFilter instance = new TypeFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeParameterFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeParameterFilter.java new file mode 100644 index 000000000000..2f81c8b3980f --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeParameterFilter.java @@ -0,0 +1,43 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 03.01.2004 + * Time: 1:06:23 + * To change this template use Options | File Templates. + */ +public class TypeParameterFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + @Override public void visitTypeElement(PsiTypeElement psiTypeElement) { + result = true; + } + + @Override public void visitTypeParameter(PsiTypeParameter psiTypeParameter) { + result = true; + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) { + result = true; + } + + private TypeParameterFilter() {} + + private static class NodeFilterHolder { + private static final NodeFilter instance = new TypeParameterFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypedSymbolNodeFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypedSymbolNodeFilter.java new file mode 100644 index 000000000000..4416b8b7db24 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypedSymbolNodeFilter.java @@ -0,0 +1,44 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Filter for typed symbols + */ +public class TypedSymbolNodeFilter extends JavaElementVisitor implements NodeFilter { + private boolean result; + + @Override public void visitMethod(PsiMethod psiMethod) { + result = psiMethod.hasTypeParameters(); + } + + @Override public void visitClass(PsiClass psiClass) { + result = psiClass.hasTypeParameters(); + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) { + result = psiJavaCodeReferenceElement.getParameterList().getTypeParameterElements().length > 0; + } + + @Override public void visitTypeParameter(PsiTypeParameter parameter) { + // we need this since TypeParameter instanceof PsiClass (?) + } + + private static class NodeFilterHolder { + private static final NodeFilter instance = new TypedSymbolNodeFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + private TypedSymbolNodeFilter() { + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/VariableFilter.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/VariableFilter.java new file mode 100644 index 000000000000..e1907ecaf556 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/VariableFilter.java @@ -0,0 +1,41 @@ +package com.intellij.structuralsearch.impl.matcher.filters; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReferenceExpression; +import com.intellij.psi.PsiVariable; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 26.12.2003 + * Time: 19:52:57 + * To change this template use Options | File Templates. + */ +public class VariableFilter extends JavaElementVisitor implements NodeFilter { + protected boolean result; + + public void visitReferenceExpression(final PsiReferenceExpression expression) { + } + + @Override public void visitVariable(PsiVariable psiVariable) { + result = true; + } + + private VariableFilter() {} + + private static class NodeFilterHolder { + private static final NodeFilter instance = new VariableFilter(); + } + + public static NodeFilter getInstance() { + return NodeFilterHolder.instance; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DeclarationStatementHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DeclarationStatementHandler.java new file mode 100644 index 000000000000..7342e96758f0 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DeclarationStatementHandler.java @@ -0,0 +1,96 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator; +import com.intellij.dupLocator.iterators.CountingNodeIterator; +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator; + +/** + * Created by IntelliJ IDEA. + * User: maxim + * Date: 31.12.2004 + * Time: 12:01:29 + * To change this template use File | Settings | File Templates. + */ +public class DeclarationStatementHandler extends MatchingHandler { + private MatchingHandler myCommentHandler; + + public boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context) { + if (patternNode instanceof PsiComment) { + //if (matchedNode instanceof PsiComment || matchedNode instanceof PsiClass || matchedNode instanceof PsiField) + return myCommentHandler.match(patternNode, matchedNode, context); + //return false; + } + + if (!super.match(patternNode,matchedNode,context)) return false; + boolean result; + PsiDeclarationStatement dcl = (PsiDeclarationStatement)patternNode; + + if (matchedNode instanceof PsiDeclarationStatement) { + result = GlobalMatchingVisitor.continueMatchingSequentially( + new SsrFilteringNodeIterator(patternNode.getFirstChild()), + new SsrFilteringNodeIterator(matchedNode.getFirstChild()), + context + ); + } else { + final PsiElement[] declared = dcl.getDeclaredElements(); + + // declaration statement could wrap class or dcl + if (declared.length >0 && + ( ( declared[0] instanceof PsiVariable && matchedNode instanceof PsiVariable) || + ( declared[0] instanceof PsiClass && matchedNode instanceof PsiClass) + ) && + !(matchedNode.getParent() instanceof PsiDeclarationStatement) // skip twice matching for child + ) { + result = GlobalMatchingVisitor.continueMatchingSequentially( + new ArrayBackedNodeIterator(declared), + new CountingNodeIterator( + declared.length, + new SsrFilteringNodeIterator(matchedNode) + ), + context + ); + + if (result && + declared[0] instanceof PsiVariable && matchedNode instanceof PsiField + ) { + // we may have comments behind to match! + final PsiElement[] children = dcl.getChildren(); + + final PsiElement lastChild = children[children.length - 1]; + if (lastChild instanceof PsiComment) { + final PsiElement[] fieldChildren = matchedNode.getChildren(); + + result = context.getPattern().getHandler(lastChild).match( + lastChild, + fieldChildren[fieldChildren.length-1], + context + ); + } + } + } else { + result = false; + } + } + + return result; + } + + public boolean shouldAdvanceTheMatchFor(PsiElement patternElement, PsiElement matchedElement) { + if (patternElement instanceof PsiComment && + ( matchedElement instanceof PsiField || + matchedElement instanceof PsiClass + ) + ) { + return false; + } + + return super.shouldAdvanceTheMatchFor(patternElement,matchedElement); + } + + public void setCommentHandler(final MatchingHandler commentHandler) { + myCommentHandler = commentHandler; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DocDataHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DocDataHandler.java new file mode 100644 index 000000000000..dbc97ce67f75 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DocDataHandler.java @@ -0,0 +1,70 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.JavaDocTokenType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.javadoc.PsiDocTagValue; +import com.intellij.psi.javadoc.PsiDocToken; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import org.jetbrains.annotations.NonNls; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Handler for doc nodes + */ +public class DocDataHandler extends MatchingHandler { + @NonNls private static final String P_STR = "^\\s*((?:\\w|_|\\-|\\$)+)\\s*(?:=\\s*\"(.*)\"\\s*)?$"; + private static final Pattern p = Pattern.compile( + P_STR, + Pattern.CASE_INSENSITIVE + ); + + public boolean match(PsiElement node, PsiElement match, MatchContext context) { + String text1 = node.getText(); + + text1 = getTextFromNode(node, text1); + + Matcher m1 = p.matcher(text1); + + String text2 = match.getText(); + text2 = getTextFromNode(match, text2); + + Matcher m2 = p.matcher(text2); + + if (m1.matches() && m2.matches()) { + String name = m1.group(1); + String name2 = m2.group(1); + boolean isTypedName = context.getPattern().isTypedVar(name); + + if (name.equals(name2) || isTypedName) { + String value = m1.group(2); + String value2 = m2.group(2); + + if (value!=null) { + if (value2 == null || !value2.matches(value)) return false; + } + if (isTypedName) { + SubstitutionHandler handler = (SubstitutionHandler) context.getPattern().getHandler(name); + return handler.handle(match,context); + } + return true; + } + } + return text1.equals(text2); + } + + // since doctag value may be inside doc comment we specially build text including skipped nodes + private static String getTextFromNode(final PsiElement node, String text1) { + PsiElement nextSibling = node.getNextSibling(); + if (nextSibling instanceof PsiDocTagValue) { + text1 += nextSibling.getText(); + + nextSibling = nextSibling.getNextSibling(); + if (nextSibling instanceof PsiDocToken && ((PsiDocToken)nextSibling).getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) { + text1 += nextSibling.getText(); + } + } + return text1; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/ExpressionHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/ExpressionHandler.java new file mode 100644 index 000000000000..6437c148dd0d --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/ExpressionHandler.java @@ -0,0 +1,21 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpressionStatement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Handler for substitution expression search + */ +public class ExpressionHandler extends MatchingHandler { + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + if (!super.match(patternNode,matchedNode,context)) { + return false; + } + + return context.getMatcher().match( + ((PsiExpressionStatement)patternNode).getExpression(), + matchedNode + ); + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/StatementHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/StatementHandler.java new file mode 100644 index 000000000000..c6361e2ad4e7 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/StatementHandler.java @@ -0,0 +1,32 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Handler for statement search + */ +public class StatementHandler extends MatchingHandler { + + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + // filtering is done on SubstituionHandler level + if (patternNode==null) return false; + patternNode = ((PsiExpressionStatement)patternNode).getExpression(); + + /*if (matchedNode instanceof PsiExpressionStatement) { + //matchedNode = ((PsiExpressionStatement)matchedNode).getExpression(); + } else*/ if (( !(matchedNode instanceof PsiStatement) && + !(matchedNode instanceof PsiComment) // comments to be matched as statements + ) || + ( matchedNode instanceof PsiBlockStatement && + !(matchedNode.getParent() instanceof PsiBlockStatement) && + !(matchedNode.getParent().getParent() instanceof PsiSwitchStatement) + )) { + // typed statement does not match this things + // (BlockStatement could be nontop level in if, etc) + return false; + } + + return context.getMatcher().match(patternNode,matchedNode); + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/DocValuesIterator.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/DocValuesIterator.java new file mode 100644 index 000000000000..c1323abd0951 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/DocValuesIterator.java @@ -0,0 +1,64 @@ +package com.intellij.structuralsearch.impl.matcher.iterators; + +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.psi.JavaDocTokenType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.javadoc.PsiDocToken; +import com.intellij.psi.javadoc.PsiDocTagValue; + +import java.util.ArrayList; + +/** + * Iterates over java doc values tag + */ +public class DocValuesIterator extends NodeIterator { + private int index; + private final ArrayList<PsiElement> tokens = new ArrayList<PsiElement>(2); + private static final IElementType tokenType = JavaDocTokenType.DOC_COMMENT_DATA; + + public DocValuesIterator(PsiElement start) { + for(PsiElement e = start; e != null; e = e.getNextSibling()) { + if (e instanceof PsiDocTagValue) tokens.add(e); + else if (e instanceof PsiDocToken && ((PsiDocToken)e).getTokenType() == tokenType) { + tokens.add(e); + e = advanceToNext(e); + } + } + } + + // since doctag value may be inside doc comment we specially skip that nodes from list + static PsiElement advanceToNext(PsiElement e) { + PsiElement nextSibling = e.getNextSibling(); + if (nextSibling instanceof PsiDocTagValue) e = nextSibling; + + nextSibling = e.getNextSibling(); + + if (nextSibling instanceof PsiDocToken && + ((PsiDocToken)nextSibling).getTokenType() == tokenType + ) { + e = nextSibling; + } + return e; + } + + public boolean hasNext() { + return index >=0 && index < tokens.size(); + } + + public PsiElement current() { + return hasNext() ? tokens.get(index) : null; + } + + public void advance() { + if (index < tokens.size()) ++ index; + } + + public void rewind() { + if (index >= 0) --index; + } + + public void reset() { + index = 0; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/HierarchyNodeIterator.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/HierarchyNodeIterator.java new file mode 100644 index 000000000000..324528435cb6 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/HierarchyNodeIterator.java @@ -0,0 +1,128 @@ +package com.intellij.structuralsearch.impl.matcher.iterators; + +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiClassImplUtil; +import com.intellij.structuralsearch.impl.matcher.MatchUtils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * Passes the hierarchy + */ +public class HierarchyNodeIterator extends NodeIterator { + private int index; + private ArrayList<PsiElement> remaining; + private boolean objectTaken; + private boolean firstElementTaken; + private final boolean acceptClasses; + private final boolean acceptInterfaces; + private final boolean acceptFirstElement; + + private void build(PsiElement current, Set<PsiElement> visited) { + + if (current!=null) { + final String str = current instanceof PsiClass ? ((PsiClass)current).getName():current.getText(); + + if (MatchUtils.compareWithNoDifferenceToPackage(str,"Object")) { + if(objectTaken) return; + objectTaken = true; + } + + PsiElement element = MatchUtils.getReferencedElement(current); + + if (element instanceof PsiClass) { + if (visited.contains(element)) return; + final PsiClass clazz = (PsiClass)element; + + if (acceptInterfaces || !clazz.isInterface() ) visited.add(element); + + if (!firstElementTaken && acceptFirstElement || firstElementTaken) remaining.add(clazz); + firstElementTaken = true; + + if (clazz instanceof PsiAnonymousClass) { + build(((PsiAnonymousClass)clazz).getBaseClassReference(),visited); + return; + } + + if (acceptClasses) { + processClasses(clazz, visited); + + if (!objectTaken) { + build(PsiClassImplUtil.getSuperClass(clazz), visited); + } + } + + if (acceptInterfaces) { + final PsiReferenceList implementsList = clazz.getImplementsList(); + + if (implementsList != null) { + final PsiElement[] implementsListElements = implementsList.getReferenceElements(); + + for (PsiElement anImplementsList : implementsListElements) { + build(anImplementsList,visited); + } + } + + if (!acceptClasses) processClasses(clazz, visited); + } + } else { + remaining.add(current); + } + } + } + + private void processClasses(final PsiClass clazz, final Set<PsiElement> visited) { + final PsiReferenceList clazzExtendsList = clazz.getExtendsList(); + final PsiElement[] extendsList = (clazzExtendsList != null)?clazzExtendsList.getReferenceElements():null; + + if (extendsList != null) { + for (PsiElement anExtendsList : extendsList) { + build(anExtendsList,visited); + } + } + } + + public HierarchyNodeIterator(PsiElement reference, boolean acceptClasses, boolean acceptInterfaces) { + this(reference, acceptClasses, acceptInterfaces, true); + } + + public HierarchyNodeIterator(PsiElement reference, boolean acceptClasses, boolean acceptInterfaces, boolean acceptFirstElement) { + remaining = new ArrayList<PsiElement>(); + this.acceptClasses = acceptClasses; + this.acceptInterfaces = acceptInterfaces; + this.acceptFirstElement = acceptFirstElement; + + if (reference instanceof PsiIdentifier) { + reference = reference.getParent(); + } + + build(reference,new HashSet<PsiElement>()); + } + + public boolean hasNext() { + return index < remaining.size(); + } + + public PsiElement current() { + return remaining.get(index); + } + + public void advance() { + if (index!=remaining.size()) { + ++index; + } + } + + public void rewind() { + if (index > 0) { + --index; + } + } + + public void reset() { + index = 0; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ExprTypePredicate.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ExprTypePredicate.java new file mode 100644 index 000000000000..d66db76c272d --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ExprTypePredicate.java @@ -0,0 +1,95 @@ +package com.intellij.structuralsearch.impl.matcher.predicates; + +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.impl.matcher.iterators.HierarchyNodeIterator; +import com.intellij.dupLocator.iterators.NodeIterator; + +/** + * Created by IntelliJ IDEA. + * User: Maxim.Mossienko + * Date: Mar 23, 2004 + * Time: 6:37:15 PM + * To change this template use File | Settings | File Templates. + */ +public class ExprTypePredicate extends MatchPredicate { + private final RegExpPredicate delegate; + private final boolean withinHierarchy; + + public ExprTypePredicate(String type, String baseName, boolean _withinHierarchy, boolean caseSensitiveMatch,boolean target) { + delegate = new RegExpPredicate(type,caseSensitiveMatch,baseName,false,target); + withinHierarchy = _withinHierarchy; + } + + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + return match(patternNode, matchedNode, 0, -1, context); + } + + public boolean match(PsiElement node, PsiElement match, int start, int end, MatchContext context) { + if (match instanceof PsiIdentifier) { + // since we pickup tokens + match = match.getParent(); + } + + if (match instanceof PsiExpression) { + final PsiType type = evalType((PsiExpression)match,context); + if (type==null) return false; + + return doMatchWithTheType(type, context, match); + } else { + return false; + } + } + + protected PsiType evalType(PsiExpression match, MatchContext context) { + PsiType type = null; + + if (match instanceof PsiReferenceExpression && + match.getParent() instanceof PsiMethodCallExpression) { + PsiMethod method = ((PsiMethodCallExpression)match.getParent()).resolveMethod(); + if (method!=null) type = method.getReturnType(); + } + + if (type==null) type = match.getType(); + return type; + } + + private boolean doMatchWithTheType(final PsiType type, MatchContext context, PsiElement matchedNode) { + if (type instanceof PsiClassType) { + PsiClass clazz = ((PsiClassType)type).resolve(); + + if (clazz!=null) return checkClass(clazz, context); + } + + if (type!=null) { + final String presentableText = type.getPresentableText(); + boolean result = delegate.doMatch(presentableText,context, matchedNode); + + if (!result && type instanceof PsiArrayType && ((PsiArrayType)type).getComponentType() instanceof PsiClassType) { + PsiClass clazz = ((PsiClassType)((PsiArrayType)type).getComponentType()).resolve(); + + if (clazz!=null) { // presentable text for array is not qualified! + result = delegate.doMatch(clazz.getQualifiedName()+"[]",context, matchedNode); + } + } + return result; + } else { + return false; + } + } + + public boolean checkClass(PsiClass clazz, MatchContext context) { + if (withinHierarchy) { + final NodeIterator parents = new HierarchyNodeIterator(clazz,true,true); + + while(parents.hasNext() && !delegate.match(null,parents.current(),context)) { + parents.advance(); + } + + return parents.hasNext(); + } else { + return delegate.match(null,clazz,context); + } + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/FormalArgTypePredicate.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/FormalArgTypePredicate.java new file mode 100644 index 000000000000..02f1ee7d08e0 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/FormalArgTypePredicate.java @@ -0,0 +1,38 @@ +package com.intellij.structuralsearch.impl.matcher.predicates; + +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; + +/** + * Created by IntelliJ IDEA. + * User: Maxim.Mossienko + * Date: Mar 23, 2004 + * Time: 6:37:15 PM + * To change this template use File | Settings | File Templates. + */ +public class FormalArgTypePredicate extends ExprTypePredicate { + + public FormalArgTypePredicate(String type, String baseName, boolean _withinHierarchy, boolean caseSensitiveMatch,boolean target) { + super(type, baseName, _withinHierarchy, caseSensitiveMatch, target); + } + + protected PsiType evalType(PsiExpression match, MatchContext context) { + final PsiMethodCallExpression expr = PsiTreeUtil.getParentOfType(match,PsiMethodCallExpression.class); + if (expr == null) return null; + + // find our parent in parameters of the method + final PsiMethod psiMethod = expr.resolveMethod(); + if (psiMethod == null) return null; + final PsiParameter[] methodParameters = psiMethod.getParameterList().getParameters(); + final PsiExpression[] expressions = expr.getArgumentList().getExpressions(); + + for(int i = 0;i < methodParameters.length; ++i) { + if (expressions[i] == match) { + if (i < methodParameters.length) return methodParameters[i].getType(); + break; + } + } + return null; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java new file mode 100644 index 000000000000..ce25b998ba9b --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java @@ -0,0 +1,28 @@ +package com.intellij.structuralsearch.impl.matcher.predicates; + +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.MatchUtils; + +/** + * Handler for value read + */ +public final class ReadPredicate extends MatchPredicate { + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + if (matchedNode instanceof PsiIdentifier) { + matchedNode = matchedNode.getParent(); + } + if (matchedNode instanceof PsiReferenceExpression && + ( !(matchedNode.getParent() instanceof PsiMethodCallExpression) && + ( !(matchedNode.getParent() instanceof PsiAssignmentExpression) || + ((PsiAssignmentExpression)matchedNode.getParent()).getLExpression() != matchedNode + ) + ) && + MatchUtils.getReferencedElement(matchedNode) instanceof PsiVariable + ) { + return true; + } + return false; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/WritePredicate.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/WritePredicate.java new file mode 100644 index 000000000000..e0d86824a920 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/WritePredicate.java @@ -0,0 +1,33 @@ +package com.intellij.structuralsearch.impl.matcher.predicates; + +import com.intellij.psi.*; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.MatchUtils; + +/** + * Handler for reading + */ +public final class WritePredicate extends MatchPredicate { + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + if (matchedNode instanceof PsiIdentifier) { + matchedNode = matchedNode.getParent(); + } + if (( matchedNode instanceof PsiReferenceExpression && + matchedNode.getParent() instanceof PsiAssignmentExpression && + ((PsiAssignmentExpression)matchedNode.getParent()).getLExpression() == matchedNode && + MatchUtils.getReferencedElement(matchedNode) instanceof PsiVariable + ) || + ( + matchedNode instanceof PsiVariable && + ((PsiVariable)matchedNode).getInitializer()!=null + ) || + matchedNode.getParent() instanceof PsiPostfixExpression || + matchedNode.getParent() instanceof PsiPrefixExpression + ) { + return true; + } else { + return false; + } + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/CommentMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/CommentMatchingStrategy.java new file mode 100644 index 000000000000..7bba3250b768 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/CommentMatchingStrategy.java @@ -0,0 +1,37 @@ +package com.intellij.structuralsearch.impl.matcher.strategies; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassInitializer; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiMethod; + +/** + * Java doc matching strategy + */ +public final class CommentMatchingStrategy extends MatchingStrategyBase { + @Override public void visitClass(final PsiClass clazz) { + result = true; + } + + @Override public void visitClassInitializer(final PsiClassInitializer clazzInit) { + result = true; + } + + @Override public void visitMethod(final PsiMethod method) { + result = true; + } + + @Override public void visitComment(final PsiComment comment) { + result = true; + } + + private CommentMatchingStrategy() {} + + private static class CommentMatchingStrategyHolder { + private static final CommentMatchingStrategy instance = new CommentMatchingStrategy(); + } + + public static MatchingStrategy getInstance() { + return CommentMatchingStrategyHolder.instance; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java new file mode 100644 index 000000000000..c892badb85be --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java @@ -0,0 +1,51 @@ +package com.intellij.structuralsearch.impl.matcher.strategies; + +import com.intellij.psi.*; + +/** + * Expression matching strategy + */ +public class ExprMatchingStrategy extends MatchingStrategyBase { + @Override public void visitExpression(final PsiExpression expr) { + result = true; + } + + @Override public void visitVariable(final PsiVariable field) { + result = true; + } + + @Override public void visitClass(final PsiClass clazz) { + result = true; + } + + @Override public void visitClassInitializer(final PsiClassInitializer clazzInit) { + result = true; + } + + @Override public void visitMethod(final PsiMethod method) { + result = true; + } + + @Override public void visitExpressionList(final PsiExpressionList list) { + result = true; + } + + @Override public void visitJavaFile(final PsiJavaFile file) { + result = true; + } + + // finding parameters + @Override public void visitParameterList(final PsiParameterList list) { + result = true; + } + + protected ExprMatchingStrategy() {} + + private static class ExprMatchingStrategyHolder { + private static final ExprMatchingStrategy instance = new ExprMatchingStrategy(); + } + + public static MatchingStrategy getInstance() { + return ExprMatchingStrategyHolder.instance; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/JavaDocMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/JavaDocMatchingStrategy.java new file mode 100644 index 000000000000..b65180eff650 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/JavaDocMatchingStrategy.java @@ -0,0 +1,32 @@ +package com.intellij.structuralsearch.impl.matcher.strategies; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassInitializer; +import com.intellij.psi.PsiMethod; + +/** + * Java doc matching strategy + */ +public final class JavaDocMatchingStrategy extends MatchingStrategyBase { + @Override public void visitClass(final PsiClass clazz) { + result = true; + } + + @Override public void visitClassInitializer(final PsiClassInitializer clazzInit) { + result = true; + } + + @Override public void visitMethod(final PsiMethod method) { + result = true; + } + + private JavaDocMatchingStrategy() {} + + private static class JavaDocMatchingStrategyHolder { + private static final JavaDocMatchingStrategy instance = new JavaDocMatchingStrategy(); + } + + public static MatchingStrategy getInstance() { + return JavaDocMatchingStrategyHolder.instance; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/MatchingStrategyBase.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/MatchingStrategyBase.java new file mode 100644 index 000000000000..df6e0fd46832 --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/MatchingStrategyBase.java @@ -0,0 +1,42 @@ +package com.intellij.structuralsearch.impl.matcher.strategies; + +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; + +/** + * Base filtering strategy to find statements + */ +public class MatchingStrategyBase extends JavaElementVisitor implements MatchingStrategy, NodeFilter { + protected boolean result; + + @Override public void visitReferenceExpression(final PsiReferenceExpression psiReferenceExpression) { + visitExpression(psiReferenceExpression); + } + + @Override public void visitCodeBlock(final PsiCodeBlock block) { + result = true; + } + + @Override public void visitCatchSection(final PsiCatchSection section) { + result = true; + } + + @Override public void visitStatement(final PsiStatement statement) { + result = true; + } + + public boolean continueMatching(final PsiElement start) { + return accepts(start); + } + + @Override + public boolean shouldSkip(PsiElement element, PsiElement elementToMatchWith) { + return false; + } + + public boolean accepts(PsiElement element) { + result = false; + if (element!=null) element.accept(this); + return result; + } +} diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java new file mode 100644 index 000000000000..f31ee37e8aed --- /dev/null +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java @@ -0,0 +1,54 @@ +package com.intellij.structuralsearch.impl.matcher.strategies; + +import com.intellij.psi.*; + +/** + * CommonStrategy to match symbols + */ +public class SymbolMatchingStrategy extends ExprMatchingStrategy { + @Override public void visitReferenceList(final PsiReferenceList list) { + result = true; + } + + @Override public void visitAnnotation(final PsiAnnotation annotation) { + result = true; + } + + @Override public void visitAnnotationParameterList(final PsiAnnotationParameterList list) { + result = true; + } + + @Override public void visitModifierList(final PsiModifierList list) { + result = true; + } + + @Override public void visitNameValuePair(final PsiNameValuePair pair) { + result = true; + } + + @Override public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) { + result = true; + } + + @Override public void visitTypeElement(PsiTypeElement psiTypeElement) { + result = true; + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) { + result = true; + } + + @Override public void visitReferenceParameterList(PsiReferenceParameterList psiReferenceParameterList) { + result = true; + } + + private SymbolMatchingStrategy() {} + + private static class SymbolMatchingStrategyHolder { + private static final SymbolMatchingStrategy instance = new SymbolMatchingStrategy(); + } + + public static MatchingStrategy getInstance() { + return SymbolMatchingStrategyHolder.instance; + } +} |