1 | // Copyright (C) 2008 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package com.google.caja.ancillary.linter; |
16 | |
17 | import java.util.Collection; |
18 | import java.util.Set; |
19 | |
20 | import com.google.caja.parser.AncestorChain; |
21 | import com.google.caja.parser.ParseTreeNode; |
22 | import com.google.caja.parser.Visitor; |
23 | import com.google.caja.util.Multimap; |
24 | import com.google.caja.util.Multimaps; |
25 | import com.google.caja.util.Sets; |
26 | |
27 | /** |
28 | * Groups nodes in a parse tree by type. |
29 | * |
30 | * @author mikesamuel@gmail.com |
31 | */ |
32 | final class NodeBuckets { |
33 | private final Multimap<Class<? extends ParseTreeNode>, AncestorChain<?>> |
34 | buckets = Multimaps.newListHashMultimap(); |
35 | |
36 | /** |
37 | * @param ac the root of the tree to bucket. |
38 | * @param classes the set of nodes for which {@link #get} will return |
39 | * non-null. |
40 | */ |
41 | private NodeBuckets( |
42 | AncestorChain<?> ac, |
43 | final Iterable<Class<? extends ParseTreeNode>> classes) { |
44 | ac.node.acceptPreOrder(new Visitor() { |
45 | public boolean visit(AncestorChain<?> ac) { |
46 | for (Class<? extends ParseTreeNode> cl : classes) { |
47 | if (cl.isInstance(ac.node)) { |
48 | buckets.put(cl, ac); |
49 | } |
50 | } |
51 | return true; |
52 | } |
53 | }, ac.parent); |
54 | } |
55 | |
56 | |
57 | @SuppressWarnings("unchecked") |
58 | public <T extends ParseTreeNode> |
59 | Collection<AncestorChain<T>> get(Class<T> cl) { |
60 | Collection<AncestorChain<?>> nodes = buckets.get(cl); |
61 | Collection<? extends AncestorChain<?>> wider = nodes; |
62 | // This is type-safe because the constructor only adds chains of type cl to |
63 | // the list corresponding to cl. |
64 | return (Collection<AncestorChain<T>>) wider; |
65 | } |
66 | |
67 | static class Maker { |
68 | private Set<Class<? extends ParseTreeNode>> types = Sets.newLinkedHashSet(); |
69 | private Maker() { /* no public zero-argument ctor */ } |
70 | Maker with(Class<? extends ParseTreeNode> cl) { |
71 | types.add(cl); |
72 | return this; |
73 | } |
74 | NodeBuckets under(AncestorChain<?> ac) { |
75 | return new NodeBuckets(ac, types); |
76 | } |
77 | } |
78 | |
79 | static Maker maker() { return new Maker(); } |
80 | } |