diff options
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers')
12 files changed, 1236 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java new file mode 100644 index 000000000000..c95a85623753 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java @@ -0,0 +1,8 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +/** + * @author Eugene.Kudelevsky + */ +public interface DelegatingHandler { + MatchingHandler getDelegate(); +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java new file mode 100644 index 000000000000..34941cfa0257 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java @@ -0,0 +1,46 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.dupLocator.iterators.NodeIterator; +import org.jetbrains.annotations.NotNull; + +public final class LightTopLevelMatchingHandler extends MatchingHandler implements DelegatingHandler { + private final MatchingHandler myDelegate; + + public LightTopLevelMatchingHandler(@NotNull MatchingHandler delegate) { + myDelegate = delegate; + } + + public boolean match(final PsiElement patternNode, final PsiElement matchedNode, final MatchContext matchContext) { + return myDelegate.match(patternNode, matchedNode, matchContext); + } + + @Override + public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) { + return myDelegate.canMatch(patternNode, matchedNode); + } + + @Override + public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) { + return myDelegate.matchSequentially(nodes, nodes2, context); + } + + public boolean match(final PsiElement patternNode, + final PsiElement matchedNode, final int start, final int end, final MatchContext context) { + return myDelegate.match(patternNode, matchedNode, start, end, context); + } + + public boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) { + return true; + } + + @Override + public boolean shouldAdvanceTheMatchFor(final PsiElement patternElement, final PsiElement matchedElement) { + return myDelegate.shouldAdvanceTheMatchFor(patternElement, matchedElement); + } + + public MatchingHandler getDelegate() { + return myDelegate; + } +}
\ No newline at end of file diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java new file mode 100644 index 000000000000..78967106755f --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java @@ -0,0 +1,49 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: Maxim.Mossienko + * Date: Jun 30, 2004 + * Time: 5:07:33 PM + * To change this template use File | Settings | File Templates. + */ +public class LiteralWithSubstitutionHandler extends MatchingHandler { + private final String matchExpression; + private Matcher matcher; + private final List<SubstitutionHandler> handlers; + + public LiteralWithSubstitutionHandler(String _matchedExpression,List<SubstitutionHandler> _handlers) { + matchExpression = _matchedExpression; + handlers = _handlers; + } + + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + final String text = RegExpPredicate.getMeaningfulText(matchedNode); + int offset = matchedNode.getText().indexOf(text); + if (matcher==null) { + matcher = Pattern.compile(matchExpression).matcher(text); + } else { + matcher.reset(text); + } + + while (matcher.find()) { + for (int i = 0; i < handlers.size(); ++i) { + SubstitutionHandler handler = handlers.get(i); + + if (!handler.handle(matchedNode,offset + matcher.start(i+1), offset + matcher.end(i+1),context)) { + return false; + } + } + return true; + } + return false; + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java new file mode 100644 index 000000000000..cba96681ab71 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java @@ -0,0 +1,27 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Root of handlers for pattern node matching. Handles simpliest type of the match. + */ +public abstract class MatchPredicate { + /** + * Matches given handler node against given value. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successfull and false otherwise + */ + public boolean match(PsiElement patternNode,PsiElement matchedNode, int start, int end, MatchContext context) { + return match(patternNode,matchedNode,context); + } + + /** + * Matches given handler node against given value. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successfull and false otherwise + */ + public abstract boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context); +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java new file mode 100644 index 000000000000..c53b08412b6b --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java @@ -0,0 +1,271 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.*; +import com.intellij.structuralsearch.StructuralSearchUtil; +import com.intellij.structuralsearch.impl.matcher.CompiledPattern; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.MatchResultImpl; +import com.intellij.structuralsearch.impl.matcher.filters.DefaultFilter; +import com.intellij.structuralsearch.impl.matcher.predicates.BinaryPredicate; +import com.intellij.structuralsearch.impl.matcher.predicates.NotPredicate; +import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate; +import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy; + +import java.util.HashSet; +import java.util.Set; + +/** + * Root of handlers for pattern node matching. Handles simplest type of the match. + */ +public abstract class MatchingHandler extends MatchPredicate { + protected NodeFilter filter; + private PsiElement pinnedElement; + + public void setFilter(NodeFilter filter) { + this.filter = filter; + } + + /** + * Matches given handler node against given value. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successful and false otherwise + */ + public boolean match(PsiElement patternNode,PsiElement matchedNode, int start, int end, MatchContext context) { + return match(patternNode,matchedNode,context); + } + + /** + * Matches given handler node against given value. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successful and false otherwise + */ + public boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context) { + if (patternNode == null) { + return matchedNode == null; + } + + return canMatch(patternNode, matchedNode); + } + + public boolean canMatch(final PsiElement patternNode, final PsiElement matchedNode) { + if (filter!=null) { + return filter.accepts(matchedNode); + } else { + return DefaultFilter.accepts(patternNode, matchedNode); + } + } + + public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) { + PsiElement patternElement; + MatchingHandler handler; + MatchingStrategy strategy = context.getPattern().getStrategy(); + + skipIfNecessary(nodes, nodes2, strategy); + skipIfNecessary(nodes2, nodes, strategy); + + if (nodes2.hasNext() && + (handler = context.getPattern().getHandler(nodes.current())).match(patternElement = nodes.current(),nodes2.current(),context)) { + + nodes.advance(); + + if (shouldAdvanceTheMatchFor(patternElement, nodes2.current())) { + nodes2.advance(); + skipIfNecessary(nodes, nodes2, strategy); + } + skipIfNecessary(nodes2, nodes, strategy); + + if (nodes.hasNext()) { + final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current()); + + if (nextHandler.matchSequentially(nodes,nodes2,context)) { + // match was found! + return true; + } else { + // rewind, we was not able to match descendants + nodes.rewind(); + nodes2.rewind(); + } + } else { + // match was found + return handler.isMatchSequentiallySucceeded(nodes2); + } + } + return false; + } + + private static void skipIfNecessary(NodeIterator nodes, NodeIterator nodes2, MatchingStrategy strategy) { + while (strategy.shouldSkip(nodes2.current(), nodes.current())) { + nodes2.advance(); + } + } + + protected boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) { + return !nodes2.hasNext(); + } + + private static MatchPredicate findRegExpPredicate(MatchPredicate start) { + if (start==null) return null; + if (start instanceof RegExpPredicate) return start; + + if(start instanceof BinaryPredicate) { + BinaryPredicate binary = (BinaryPredicate)start; + final MatchPredicate result = findRegExpPredicate(binary.getFirst()); + if (result!=null) return result; + + return findRegExpPredicate(binary.getSecond()); + } else if (start instanceof NotPredicate) { + return null; + } + return null; + } + + public static RegExpPredicate getSimpleRegExpPredicate(SubstitutionHandler handler) { + if (handler == null) return null; + return (RegExpPredicate)findRegExpPredicate(handler.getPredicate()); + } + + static class ClearStateVisitor extends PsiRecursiveElementWalkingVisitor { + private CompiledPattern pattern; + + ClearStateVisitor() { + super(true); + } + + @Override public void visitElement(PsiElement element) { + // We do not reset certain handlers because they are also bound to higher level nodes + // e.g. Identifier handler in name is also bound to PsiMethod + if (pattern.isToResetHandler(element)) { + MatchingHandler handler = pattern.getHandlerSimple(element); + if (handler instanceof SubstitutionHandler) { + handler.reset(); + } + } + super.visitElement(element); + } + + synchronized void clearState(CompiledPattern _pattern, PsiElement el) { + pattern = _pattern; + el.acceptChildren(this); + pattern = null; + } + } + + protected static ClearStateVisitor clearingVisitor = new ClearStateVisitor(); + + public boolean matchInAnyOrder(NodeIterator patternNodes, NodeIterator matchedNodes, final MatchContext context) { + final MatchResultImpl saveResult = context.hasResult() ? context.getResult() : null; + context.setResult(null); + + try { + + if (patternNodes.hasNext() && !matchedNodes.hasNext()) { + return validateSatisfactionOfHandlers(patternNodes, context); + } + + Set<PsiElement> matchedElements = null; + + for(; patternNodes.hasNext(); patternNodes.advance()) { + final PsiElement patternNode = patternNodes.current(); + final CompiledPattern pattern = context.getPattern(); + final MatchingHandler handler = pattern.getHandler(patternNode); + + final PsiElement startMatching = matchedNodes.current(); + do { + final PsiElement element = handler.getPinnedNode(null); + final PsiElement matchedNode = element != null ? element : matchedNodes.current(); + + if (element == null) matchedNodes.advance(); + if (!matchedNodes.hasNext()) matchedNodes.reset(); + + if (matchedElements == null || !matchedElements.contains(matchedNode)) { + + if (handler.match(patternNode, matchedNode, context)) { + if (matchedElements == null) matchedElements = new HashSet<PsiElement>(); + matchedElements.add(matchedNode); + if (handler.shouldAdvanceThePatternFor(patternNode, matchedNode)) { + break; + } + } else if (element != null) { + return false; + } + + // clear state of dependent objects + clearingVisitor.clearState(pattern, patternNode); + } + + // passed of elements and does not found the match + if (startMatching == matchedNodes.current()) { + final boolean result = validateSatisfactionOfHandlers(patternNodes,context); + if (result && context.getMatchedElementsListener() != null) { + context.getMatchedElementsListener().matchedElements(matchedElements); + } + return result; + } + } while(true); + + if (!handler.shouldAdvanceThePatternFor(patternNode, null)) { + patternNodes.rewind(); + } + } + + final boolean result = validateSatisfactionOfHandlers(patternNodes, context); + if (result && context.getMatchedElementsListener() != null) { + context.getMatchedElementsListener().matchedElements(matchedElements); + } + return result; + } finally { + if (saveResult!=null) { + if (context.hasResult()) { + saveResult.getMatches().addAll(context.getResult().getMatches()); + } + context.setResult(saveResult); + } + } + } + + protected static boolean validateSatisfactionOfHandlers(NodeIterator nodes, MatchContext context) { + while(nodes.hasNext()) { + final PsiElement element = nodes.current(); + final MatchingHandler handler = context.getPattern().getHandler( element ); + + if (handler instanceof SubstitutionHandler) { + if (!((SubstitutionHandler)handler).validate( + context, StructuralSearchUtil.getProfileByPsiElement(element).getElementContextByPsi(element))) { + return false; + } + } else { + return false; + } + nodes.advance(); + } + return true; + } + + public NodeFilter getFilter() { + return filter; + } + + public boolean shouldAdvanceThePatternFor(PsiElement patternElement, PsiElement matchedElement) { + return true; + } + + public boolean shouldAdvanceTheMatchFor(PsiElement patternElement, PsiElement matchedElement) { + return true; + } + + public void reset() { + //pinnedElement = null; + } + + public PsiElement getPinnedNode(PsiElement context) { + return pinnedElement; + } + + public void setPinnedElement(final PsiElement pinnedElement) { + this.pinnedElement = pinnedElement; + } +}
\ No newline at end of file diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java new file mode 100644 index 000000000000..3dffe792e308 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java @@ -0,0 +1,20 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Root of handlers for pattern node matching. Handles simpliest type of the match. + */ +public final class SimpleHandler extends MatchingHandler { + /** + * Matches given handler node against given value. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successfull and false otherwise + */ + public boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context) { + if (!super.match(patternNode,matchedNode,context)) return false; + return context.getMatcher().match(patternNode,matchedNode); + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java new file mode 100644 index 000000000000..389c4a7916c2 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java @@ -0,0 +1,108 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.equivalence.EquivalenceDescriptor; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.dupLocator.util.DuplocatorUtil; +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class SkippingHandler extends MatchingHandler implements DelegatingHandler { + + private final MatchingHandler myDelegate; + + public SkippingHandler(@NotNull MatchingHandler delegate) { + myDelegate = delegate; + } + + public boolean match(PsiElement patternNode, PsiElement matchedNode, final MatchContext matchContext) { + if (patternNode == null || matchedNode == null || matchedNode.getClass() == patternNode.getClass()) { + return myDelegate.match(patternNode, matchedNode, matchContext); + } + + /*if (patternNode != null && matchedNode != null && patternNode.getClass() == matchedNode.getClass()) { + //return myDelegate.match(patternNode, matchedNode, matchContext); + }*/ + PsiElement newPatternNode = skipNodeIfNeccessary(patternNode); + matchedNode = skipNodeIfNeccessary(matchedNode); + + if (newPatternNode != patternNode) { + return matchContext.getPattern().getHandler(newPatternNode).match(newPatternNode, matchedNode, matchContext); + } + + return myDelegate.match(patternNode, matchedNode, matchContext); + } + + @Override + public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) { + return myDelegate.canMatch(patternNode, matchedNode); + } + + @Override + public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) { + return myDelegate.matchSequentially(nodes, nodes2, context); + } + + public boolean match(PsiElement patternNode, + PsiElement matchedNode, + final int start, + final int end, + final MatchContext context) { + if (patternNode == null || matchedNode == null || patternNode.getClass() == matchedNode.getClass()) { + return myDelegate.match(patternNode, matchedNode, start, end, context); + } + + PsiElement newPatternNode = skipNodeIfNeccessary(patternNode); + matchedNode = skipNodeIfNeccessary(matchedNode); + + if (newPatternNode != patternNode) { + return context.getPattern().getHandler(newPatternNode).match(newPatternNode, matchedNode, start, end, context); + } + + return myDelegate.match(patternNode, matchedNode, start, end, context); + } + + protected boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) { + return myDelegate.isMatchSequentiallySucceeded(nodes2); + } + + @Override + public boolean shouldAdvanceTheMatchFor(PsiElement patternElement, PsiElement matchedElement) { + return true; + } + + public MatchingHandler getDelegate() { + return myDelegate; + } + + @Nullable + public static PsiElement getOnlyNonWhitespaceChild(PsiElement element) { + PsiElement onlyChild = null; + for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { + if (DuplocatorUtil.isIgnoredNode(element) || child.getTextLength() == 0) { + continue; + } + if (onlyChild != null) { + return null; + } + onlyChild = child; + } + return onlyChild; + } + + @Nullable + public static PsiElement skipNodeIfNeccessary(PsiElement element) { + return skipNodeIfNeccessary(element, null, null); + } + + @Nullable + public static PsiElement skipNodeIfNeccessary(PsiElement element, EquivalenceDescriptor descriptor, NodeFilter filter) { + return DuplocatorUtil.skipNodeIfNeccessary(element, descriptor, filter != null ? filter : LexicalNodesFilter.getInstance()); + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java new file mode 100644 index 000000000000..0c84e3bb2436 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java @@ -0,0 +1,557 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.iterators.FilteringNodeIterator; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.dupLocator.util.NodeFilter; +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.MatchResult; +import com.intellij.structuralsearch.StructuralSearchProfile; +import com.intellij.structuralsearch.StructuralSearchUtil; +import com.intellij.structuralsearch.impl.matcher.CompiledPattern; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.MatchResultImpl; +import com.intellij.structuralsearch.plugin.ui.Configuration; +import com.intellij.structuralsearch.plugin.util.SmartPsiPointer; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Matching handler that manages substitutions matching + */ +public class SubstitutionHandler extends MatchingHandler { + private final String name; + private final int maxOccurs; + private final int minOccurs; + private final boolean greedy; + private boolean target; + private MatchPredicate predicate; + private MatchingHandler matchHandler; + private boolean subtype; + private boolean strictSubtype; + // matchedOccurs + 1 = number of item being matched + private int matchedOccurs; + private int totalMatchedOccurs = -1; + private MatchResultImpl myNestedResult; + + private static final NodeFilter VARS_DELIM_FILTER = new NodeFilter() { + @Override + public boolean accepts(PsiElement element) { + if (element == null) { + return false; + } + + final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(element); + if (profile == null) { + return false; + } + + return profile.canBeVarDelimeter(element); + } + }; + + public SubstitutionHandler(final String name, final boolean target, int minOccurs, + int maxOccurs, boolean greedy) { + this.name = name; + this.maxOccurs = maxOccurs; + this.minOccurs = minOccurs; + this.target = target; + this.greedy = greedy; + } + + public SubstitutionHandler(final SubstitutionHandler substitutionHandler) { + this(substitutionHandler.getName(),substitutionHandler.isTarget(), substitutionHandler.getMinOccurs(), + substitutionHandler.getMaxOccurs(), substitutionHandler.greedy); + } + + public boolean isSubtype() { + return subtype; + } + + public boolean isStrictSubtype() { + return strictSubtype; + } + + public void setStrictSubtype(boolean strictSubtype) { + this.strictSubtype = strictSubtype; + } + + public void setSubtype(boolean subtype) { + this.subtype = subtype; + } + + public void setPredicate(MatchPredicate handler) { + predicate = handler; + } + + // Matcher + + public MatchPredicate getPredicate() { + return predicate; + } + + private static boolean validateOneMatch(final PsiElement match, int start, int end, final MatchResultImpl result, final MatchContext matchContext) { + final boolean matchresult; + + if (match!=null) { + if (start==0 && end==-1 && result.getStart()==0 && result.getEnd()==-1) { + matchresult = matchContext.getMatcher().match(match,result.getMatchRef().getElement()); + } else { + matchresult = StructuralSearchUtil.getProfileByPsiElement(match).getText(match, start, end).equals( + result.getMatchImage() + ); + } + } else { + matchresult = result.isMatchImageNull(); + } + + return matchresult; + } + + public boolean validate(final PsiElement match, int start, int end, MatchContext context) { + if (predicate!=null) { + if(!predicate.match(null,match,start,end,context)) return false; + } + + if (maxOccurs==0) { + totalMatchedOccurs++; + return false; + } + + MatchResultImpl result = context.getResult().findSon(name); + + if (result == null && context.getPreviousResult() != null) { + result = context.getPreviousResult().findSon(name); + } + + if (result!=null) { + if (minOccurs == 1 && maxOccurs == 1) { + // check if they are the same + return validateOneMatch(match, start, end, result,context); + } else if (maxOccurs > 1 && totalMatchedOccurs!=-1) { + final int size = result.getAllSons().size(); + if (matchedOccurs >= size) { + return false; + } + result = size == 0 ?result:(MatchResultImpl)result.getAllSons().get(matchedOccurs); + // check if they are the same + return validateOneMatch(match, start, end, result, context); + } + } + + return true; + } + + public boolean match(final PsiElement node, final PsiElement match, MatchContext context) { + if (!super.match(node,match,context)) return false; + + return matchHandler == null ? + context.getMatcher().match(node, match): + matchHandler.match(node,match,context); + } + + public boolean handle(final PsiElement match, MatchContext context) { + return handle(match,0,-1,context); + } + + public void addResult(PsiElement match,int start, int end,MatchContext context) { + if (totalMatchedOccurs == -1) { + final MatchResultImpl matchResult = context.getResult(); + final MatchResultImpl substitution = matchResult.findSon(name); + + if (substitution == null) { + matchResult.addSon( createMatch(match,start,end) ); + } else if (maxOccurs > 1) { + final MatchResultImpl result = createMatch(match,start,end); + + if (!substitution.isMultipleMatch()) { + // adding intermediate node to contain all multiple matches + MatchResultImpl sonresult = new MatchResultImpl( + substitution.getName(), + substitution.getMatchImage(), + substitution.getMatchRef(), + substitution.getStart(), + substitution.getEnd(), + target + ); + + sonresult.setParent(substitution); + substitution.setMatchRef( + new SmartPsiPointer(match == null ? null : match) + ); + + substitution.setMultipleMatch(true); + + if (substitution.isScopeMatch()) { + substitution.setScopeMatch(false); + sonresult.setScopeMatch(true); + for(MatchResult r:substitution.getAllSons()) sonresult.addSon((MatchResultImpl)r); + substitution.clearMatches(); + } + + substitution.addSon( sonresult); + } + + result.setParent(substitution); + substitution.addSon( result ); + } + } + } + + public boolean handle(final PsiElement match, int start, int end, MatchContext context) { + if (!validate(match,start,end,context)) { + myNestedResult = null; + + //if (maxOccurs==1 && minOccurs==1) { + // if (context.hasResult()) context.getResult().removeSon(name); + //} + // @todo we may fail fast the match by throwing an exception + + return false; + } + + if (!Configuration.CONTEXT_VAR_NAME.equals(name)) addResult(match, start, end, context); + + return true; + } + + private MatchResultImpl createMatch(final PsiElement match, int start, int end) { + final String image = match == null ? null : StructuralSearchUtil.getProfileByPsiElement(match).getText(match, start, end); + final SmartPsiPointer ref = new SmartPsiPointer(match); + + final MatchResultImpl result = myNestedResult == null ? new MatchResultImpl( + name, + image, + ref, + start, + end, + target + ) : myNestedResult; + + if (myNestedResult != null) { + myNestedResult.setName( name ); + myNestedResult.setMatchImage( image ); + myNestedResult.setMatchRef( ref ); + myNestedResult.setStart( start ); + myNestedResult.setEnd( end ); + myNestedResult.setTarget( target ); + myNestedResult = null; + } + + return result; + } + + boolean validate(MatchContext context, Class elementContext) { + MatchResult substitution = context.getResult().findSon(name); + + if (minOccurs >= 1 && + ( substitution == null || + StructuralSearchUtil.getProfileByFileType(context.getOptions().getFileType()).getElementContextByPsi(substitution.getMatchRef().getElement()) != elementContext + ) + ) { + return false; + } else if (maxOccurs <= 1 && + substitution!=null && substitution.hasSons() + ) { + return false; + } else if (maxOccurs==0 && totalMatchedOccurs!=-1) { + return false; + } + return true; + } + + public int getMinOccurs() { + return minOccurs; + } + + public int getMaxOccurs() { + return maxOccurs; + } + + private void removeLastResults(int numberOfResults, MatchContext context) { + if (numberOfResults == 0) return; + final MatchResultImpl substitution = context.getResult().findSon(name); + + if (substitution!=null) { + final List<PsiElement> matchedNodes = context.getMatchedNodes(); + + if (substitution.hasSons()) { + final List<MatchResult> sons = substitution.getMatches(); + + while(numberOfResults > 0) { + --numberOfResults; + final MatchResult matchResult = sons.remove(sons.size() - 1); + if (matchedNodes != null) matchedNodes.remove(matchResult.getMatch()); + } + + if (sons.isEmpty()) { + context.getResult().removeSon(name); + } + } else { + final MatchResultImpl matchResult = context.getResult().removeSon(name); + if (matchedNodes != null) matchedNodes.remove(matchResult.getMatch()); + } + } + } + + public boolean matchInAnyOrder(NodeIterator patternNodes, NodeIterator matchedNodes, final MatchContext context) { + final MatchResultImpl saveResult = context.hasResult() ? context.getResult() : null; + context.setResult(null); + + try { + + if (patternNodes.hasNext() && !matchedNodes.hasNext()) { + return validateSatisfactionOfHandlers(patternNodes, context); + } + + Set<PsiElement> matchedElements = null; + + for(; patternNodes.hasNext(); patternNodes.advance()) { + int matchedOccurs = 0; + final PsiElement patternNode = patternNodes.current(); + final CompiledPattern pattern = context.getPattern(); + final MatchingHandler handler = pattern.getHandler(patternNode); + + final PsiElement startMatching = matchedNodes.current(); + do { + final PsiElement element = handler.getPinnedNode(null); + final PsiElement matchedNode = (element != null) ? element : matchedNodes.current(); + + if (element == null) matchedNodes.advance(); + if (!matchedNodes.hasNext()) matchedNodes.reset(); + + if (matchedOccurs <= maxOccurs && + (matchedElements == null || !matchedElements.contains(matchedNode))) { + + if (handler.match(patternNode, matchedNode, context)) { + ++matchedOccurs; + if (matchedElements == null) matchedElements = new HashSet<PsiElement>(); + matchedElements.add(matchedNode); + if (handler.shouldAdvanceThePatternFor(patternNode, matchedNode)) { + break; + } + } else if (element != null) { + return false; + } + + // clear state of dependent objects + clearingVisitor.clearState(pattern, patternNode); + } + + // passed of elements and does not found the match + if (startMatching == matchedNodes.current()) { + final boolean result = validateSatisfactionOfHandlers(patternNodes, context) && + matchedOccurs >= minOccurs && matchedOccurs <= maxOccurs; + if (result && context.getMatchedElementsListener() != null) { + context.getMatchedElementsListener().matchedElements(matchedElements); + } + return result; + } + } while(true); + + if (!handler.shouldAdvanceThePatternFor(patternNode, null)) { + patternNodes.rewind(); + } + } + + final boolean result = validateSatisfactionOfHandlers(patternNodes, context); + if (result && context.getMatchedElementsListener() != null) { + context.getMatchedElementsListener().matchedElements(matchedElements); + } + return result; + } finally { + if (saveResult!=null) { + if (context.hasResult()) { + saveResult.getMatches().addAll(context.getResult().getMatches()); + } + context.setResult(saveResult); + } + } + } + + public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) { + return doMatchSequentially(nodes, nodes2, context); + } + + protected boolean doMatchSequentiallyBySimpleHandler(NodeIterator nodes, NodeIterator nodes2, MatchContext context) { + final boolean oldValue = context.shouldRecursivelyMatch(); + context.setShouldRecursivelyMatch(false); + final boolean result = super.matchSequentially(nodes, nodes2, context); + context.setShouldRecursivelyMatch(oldValue); + return result; + } + + protected boolean doMatchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) { + final int previousMatchedOccurs = matchedOccurs; + + FilteringNodeIterator fNodes2 = new FilteringNodeIterator(nodes2, VARS_DELIM_FILTER); + + try { + MatchingHandler handler = context.getPattern().getHandler(nodes.current()); + matchedOccurs = 0; + + boolean flag = false; + + while(fNodes2.hasNext() && matchedOccurs < minOccurs) { + if (handler.match(nodes.current(), nodes2.current(), context)) { + ++matchedOccurs; + } else { + break; + } + fNodes2.advance(); + flag = true; + } + + if (matchedOccurs!=minOccurs) { + // failed even for min occurs + removeLastResults(matchedOccurs, context); + fNodes2.rewind(matchedOccurs); + return false; + } + + if (greedy) { + // go greedily to maxOccurs + + while(fNodes2.hasNext() && matchedOccurs < maxOccurs) { + if (handler.match(nodes.current(), nodes2.current(), context)) { + ++matchedOccurs; + } else { + // no more matches could take! + break; + } + fNodes2.advance(); + flag = true; + } + + if (flag) { + fNodes2.rewind(); + nodes2.advance(); + } + + nodes.advance(); + + if (nodes.hasNext()) { + final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current()); + + while(matchedOccurs >= minOccurs) { + if (nextHandler.matchSequentially(nodes, nodes2, context)) { + totalMatchedOccurs = matchedOccurs; + // match found + return true; + } + + if (matchedOccurs > 0) { + nodes2.rewind(); + removeLastResults(1,context); + } + --matchedOccurs; + } + + if (matchedOccurs > 0) { + removeLastResults(matchedOccurs, context); + } + nodes.rewind(); + return false; + } else { + // match found + if (handler.isMatchSequentiallySucceeded(nodes2)) { + return checkSameOccurrencesConstraint(); + } + removeLastResults(matchedOccurs, context); + return false; + } + } else { + nodes.advance(); + + if (flag) { + fNodes2.rewind(); + nodes2.advance(); + } + + if (nodes.hasNext()) { + final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current()); + + flag = false; + + while(nodes2.hasNext() && matchedOccurs <= maxOccurs) { + if (nextHandler.matchSequentially(nodes, nodes2, context)) { + return checkSameOccurrencesConstraint(); + } + + if (flag) { + nodes2.rewind(); + fNodes2.advance(); + } + + if (handler.match(nodes.current(), nodes2.current(), context)) { + matchedOccurs++; + } else { + nodes.rewind(); + removeLastResults(matchedOccurs,context); + return false; + } + nodes2.advance(); + flag = true; + } + + nodes.rewind(); + removeLastResults(matchedOccurs,context); + return false; + } else { + return checkSameOccurrencesConstraint(); + } + } + } finally { + matchedOccurs = previousMatchedOccurs; + } + } + + private boolean checkSameOccurrencesConstraint() { + if (totalMatchedOccurs == -1) { + totalMatchedOccurs = matchedOccurs; + return true; + } + else { + return totalMatchedOccurs == matchedOccurs; + } + } + + public void setTarget(boolean target) { + this.target = target; + } + + public MatchingHandler getMatchHandler() { + return matchHandler; + } + + public void setMatchHandler(MatchingHandler matchHandler) { + this.matchHandler = matchHandler; + } + + public boolean isTarget() { + return target; + } + + public String getName() { + return name; + } + + public void reset() { + super.reset(); + totalMatchedOccurs = -1; + } + + public boolean shouldAdvanceThePatternFor(PsiElement patternElement, PsiElement matchedElement) { + if(maxOccurs > 1) return false; + return super.shouldAdvanceThePatternFor(patternElement,matchedElement); + } + + public void setNestedResult(final MatchResultImpl nestedResult) { + myNestedResult = nestedResult; + } + + public MatchResultImpl getNestedResult() { + return myNestedResult; + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java new file mode 100644 index 000000000000..4d1f9a852940 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java @@ -0,0 +1,21 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Search handler for symbol search + */ +public class SymbolHandler extends MatchingHandler { + private final SubstitutionHandler handler; + + public SymbolHandler(SubstitutionHandler handler) { + this.handler = handler; + } + + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + // there is no need to do filtering since this is delegate of Substituion handler + + return handler.handle(matchedNode,context); + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java new file mode 100644 index 000000000000..6711bcb1fc7f --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java @@ -0,0 +1,85 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.dupLocator.iterators.SiblingNodeIterator; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.StructuralSearchUtil; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public final class TopLevelMatchingHandler extends MatchingHandler implements DelegatingHandler { + private final MatchingHandler delegate; + + public TopLevelMatchingHandler(@NotNull MatchingHandler _delegate) { + delegate = _delegate; + setFilter(_delegate.getFilter()); + } + + public boolean match(final PsiElement patternNode, final PsiElement matchedNode, final MatchContext matchContext) { + final boolean matched = delegate.match(patternNode, matchedNode, matchContext); + + if (matched) { + List<PsiElement> matchedNodes = matchContext.getMatchedNodes(); + if (matchedNodes == null) { + matchedNodes = new ArrayList<PsiElement>(); + matchContext.setMatchedNodes(matchedNodes); + } + + PsiElement elementToAdd = matchedNode; + + if (patternNode instanceof PsiComment && StructuralSearchUtil.isDocCommentOwner(matchedNode)) { + // psicomment and psidoccomment are placed inside the psimember next to them so + // simple topdown matching should do additional "dances" to cover this case. + elementToAdd = matchedNode.getFirstChild(); + assert elementToAdd instanceof PsiComment; + } + + matchedNodes.add(elementToAdd); + } + + if ((!matched || matchContext.getOptions().isRecursiveSearch()) && + matchContext.getPattern().getStrategy().continueMatching(matchedNode) && + matchContext.shouldRecursivelyMatch() + ) { + matchContext.getMatcher().matchContext( + new SsrFilteringNodeIterator( + new SiblingNodeIterator(matchedNode.getFirstChild()) + ) + ); + } + return matched; + } + + @Override + public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) { + return delegate.canMatch(patternNode, matchedNode); + } + + @Override + public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) { + return delegate.matchSequentially(nodes, nodes2, context); + } + + public boolean match(final PsiElement patternNode, + final PsiElement matchedNode, final int start, final int end, final MatchContext context) { + return match(patternNode, matchedNode, context); + } + + public boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) { + return true; + } + + @Override + public boolean shouldAdvanceTheMatchFor(final PsiElement patternElement, final PsiElement matchedElement) { + return delegate.shouldAdvanceTheMatchFor(patternElement, matchedElement); + } + + public MatchingHandler getDelegate() { + return delegate; + } +}
\ No newline at end of file diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java new file mode 100644 index 000000000000..08e335614d5a --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java @@ -0,0 +1,21 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiDeclarationStatement; +import com.intellij.structuralsearch.impl.matcher.MatchContext; + +/** + * Search handler for typed symbol ('T<a*>) + */ +public class TypedSymbolHandler extends MatchingHandler { + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + if (!super.match(patternNode,matchedNode,context)) { + return false; + } + + return context.getMatcher().match( + patternNode.getFirstChild(), + matchedNode + ); + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java new file mode 100644 index 000000000000..cc71c02ee403 --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java @@ -0,0 +1,23 @@ +package com.intellij.structuralsearch.impl.matcher.handlers; + +import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator; +import com.intellij.dupLocator.iterators.NodeIterator; +import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator; + +/** + * Root of handlers for pattern node matching. Handles simpliest type of the match. + */ +public final class XmlTextHandler extends MatchingHandler { + public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) { + final PsiElement psiElement = nodes.current(); + + return GlobalMatchingVisitor.continueMatchingSequentially( + new SsrFilteringNodeIterator( new ArrayBackedNodeIterator(psiElement.getChildren()) ), + nodes2, + context + ); + } +} |