summaryrefslogtreecommitdiff
path: root/java/structuralsearch-java/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'java/structuralsearch-java/src/com')
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java296
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java557
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java661
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaCompiledPattern.java90
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchPredicateProvider.java62
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java1637
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java589
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/BlockFilter.java42
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ClassFilter.java43
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/CommentFilter.java48
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ConstantFilter.java38
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/DeclarationFilter.java44
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/ExpressionFilter.java39
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaDocFilter.java33
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/JavaLexicalNodesFilter.java37
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/MethodFilter.java33
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/StatementFilter.java33
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/SymbolNodeFilter.java72
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeFilter.java42
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypeParameterFilter.java43
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/TypedSymbolNodeFilter.java44
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/filters/VariableFilter.java41
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DeclarationStatementHandler.java96
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/DocDataHandler.java70
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/ExpressionHandler.java21
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/handlers/StatementHandler.java32
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/DocValuesIterator.java64
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/iterators/HierarchyNodeIterator.java128
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ExprTypePredicate.java95
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/FormalArgTypePredicate.java38
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java28
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/WritePredicate.java33
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/CommentMatchingStrategy.java37
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java51
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/JavaDocMatchingStrategy.java32
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/MatchingStrategyBase.java42
-rw-r--r--java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java54
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;
+ }
+}