| package org.eclipse.jst.jsf.common.internal.finder; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * A matcher finder that uses a visitor to traverse its INPUT and match values |
| * using its matchers. An optional value resolver can be used to get the |
| * matching value used. |
| * |
| * INPUT need not implement it's own visitation interface. Rather, an instance |
| * of MatchingVisitor must be provided to handle this. |
| * |
| * @author cbateman |
| * @param <INPUT> |
| * @param <VISITTYPE> |
| * @param <IDTYPE> |
| * |
| */ |
| public class VisitorMatcher<INPUT, VISITTYPE, IDTYPE> extends |
| AbstractMatcher<INPUT, Collection<? extends VISITTYPE>, IDTYPE> |
| { |
| private final MatchingAcceptor _acceptor; |
| |
| /** |
| * @param id |
| * @param displayName |
| * @param acceptor |
| * @param matchers |
| */ |
| public VisitorMatcher(IDTYPE id, String displayName, |
| final MatchingAcceptor<INPUT, VISITTYPE> acceptor, |
| final List<? extends IMatcher> matchers) |
| { |
| super(id, displayName, Collections.EMPTY_LIST, matchers); |
| _acceptor = acceptor; |
| } |
| |
| @Override |
| public Collection<? extends VISITTYPE> perform(final INPUT input) throws Exception |
| { |
| MatchingVisitor visitor = new MatchingVisitor(getMatchers()); |
| _acceptor.accept(input, visitor); |
| return visitor.getFoundMatches(); |
| } |
| |
| /** |
| * Call visit on each VISITTYPE. Sub-classes must provide implementations of |
| * getInputChildren and getVisitableChildren to control what gets visited |
| * from the INPUT root. |
| * |
| * @param <INPUT> |
| * @param <VISITTYPE> |
| */ |
| public abstract static class MatchingAcceptor<INPUT, VISITTYPE> |
| { |
| private void accept(final INPUT input, |
| final MatchingVisitor<VISITTYPE> visitor) |
| { |
| final Collection<? extends VISITTYPE> inputChildren = getInputChildren(input); |
| accept(visitor, inputChildren); |
| } |
| |
| private void accept(final MatchingVisitor<VISITTYPE> visitor, |
| final Collection<? extends VISITTYPE> inputChildren) |
| { |
| for (final VISITTYPE visitable : inputChildren) |
| { |
| visitor.visit(visitable); |
| accept(visitor, getVisitableChildren(visitable)); |
| } |
| } |
| |
| /** |
| * @param inputType |
| * @return the first level children of INPUT to be visited. |
| */ |
| protected abstract Collection<? extends VISITTYPE> getInputChildren( |
| INPUT inputType); |
| |
| /** |
| * @param visitType |
| * @return the visitable children of visitType. |
| */ |
| protected abstract Collection<? extends VISITTYPE> getVisitableChildren( |
| VISITTYPE visitType); |
| } |
| |
| private static final class MatchingVisitor<VISITTYPE> |
| { |
| private final List<IMatcher> _matchers; |
| private final List<VISITTYPE> _foundMatches = new ArrayList<VISITTYPE>(); |
| public MatchingVisitor(final List<IMatcher> matcher) |
| { |
| _matchers = matcher; |
| } |
| |
| public void visit(final VISITTYPE visitable) |
| { |
| MATCH_LOOP: for (final IMatcher matcher : _matchers) |
| { |
| if (matcher.matches(visitable)) |
| { |
| _foundMatches.add(visitable); |
| break MATCH_LOOP; |
| } |
| } |
| } |
| |
| protected final List<VISITTYPE> getFoundMatches() |
| { |
| return _foundMatches; |
| } |
| } |
| } |