Skip to main content
summaryrefslogtreecommitdiffstats
blob: 9182e7ee8bae712d71a287988cb1b45cc8061e83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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;
        }
    }
}

Back to the top