|
3 | 3 | import java.util.Collection; |
4 | 4 | import java.util.Map; |
5 | 5 | import java.util.Map.Entry; |
| 6 | +import java.util.function.Function; |
6 | 7 | import java.util.stream.Stream; |
7 | 8 |
|
8 | 9 | import com.apicatalog.tree.io.Tree; |
|
11 | 12 |
|
12 | 13 | public class MapOverlay { |
13 | 14 |
|
| 15 | + static final Function<Object, Object> REMOVED = Function.identity(); |
| 16 | + |
14 | 17 | private final Object base; |
15 | | - private final TreeAdapter baseAdapter; |
16 | 18 |
|
17 | | - private Collection<Object> keys; |
18 | | - private Map<Object, Object> overlay; |
| 19 | + private final Map<Object, Object> overlay; |
19 | 20 |
|
20 | | - private MapOverlay(Object base, TreeAdapter baseAdapter) { |
| 21 | + MapOverlay(Object base, Map<Object, Object> overlay) { |
21 | 22 | this.base = base; |
22 | | - this.baseAdapter = baseAdapter; |
23 | | - } |
24 | | - |
25 | | - public static MapOverlay with(Tree tree) { |
26 | | - return with(tree.node(), tree.adapter()); |
27 | | - } |
28 | | - |
29 | | - public static MapOverlay with(Map<Object, Object> map) { |
30 | | - return with(map, JavaAdapter.instance()); |
| 23 | + this.overlay = overlay; |
31 | 24 | } |
32 | 25 |
|
33 | | - public static MapOverlay with(Object map, TreeAdapter adapter) { |
34 | | - return null; |
| 26 | + public Collection<?> keys(TreeAdapter adapter) { |
| 27 | + return keyStream(adapter).toList(); |
35 | 28 | } |
36 | 29 |
|
37 | | - public Collection<?> keys() { |
38 | | - return keys; |
| 30 | + public Stream<?> keyStream(TreeAdapter adapter) { |
| 31 | + return Stream.concat( |
| 32 | + adapter.keyStream(base) |
| 33 | + .filter(key -> !overlay.containsKey(key)), |
| 34 | + overlay.entrySet().stream() |
| 35 | + .filter(entry -> !REMOVED.equals(entry.getValue())) |
| 36 | + .map(Entry::getKey)); |
39 | 37 | } |
40 | 38 |
|
41 | | - public Object property(Object key) { |
| 39 | + public Object property(Object key, TreeAdapter adapter) { |
42 | 40 | if (overlay.containsKey(key)) { |
43 | | - return overlay.get(key); |
| 41 | + final var value = overlay.get(key); |
| 42 | + if (REMOVED.equals(value)) { |
| 43 | + return null; |
| 44 | + } |
| 45 | + return value; |
44 | 46 | } |
45 | | - return baseAdapter.property(key, base); |
| 47 | + return adapter.property(key, base); |
46 | 48 | } |
47 | 49 |
|
48 | | - public Object property(Object key, TreeAdapter keyAdapter) { |
| 50 | + public Object property(Object key, TreeAdapter keyAdapter, TreeAdapter adapter) { |
49 | 51 | if (overlay.containsKey(key)) { |
50 | | - return overlay.get(key); |
| 52 | + final var value = overlay.get(key); |
| 53 | + if (REMOVED.equals(value)) { |
| 54 | + return null; |
| 55 | + } |
| 56 | + return value; |
51 | 57 | } |
52 | | - return baseAdapter.property(key, keyAdapter, base); |
| 58 | + return adapter.property(key, keyAdapter, base); |
53 | 59 | } |
54 | 60 |
|
55 | | - public Iterable<Entry<?, ?>> entries() { |
56 | | - return entryStream().toList(); |
| 61 | + public Iterable<Entry<?, ?>> entries(TreeAdapter adapter) { |
| 62 | + return entryStream(adapter).toList(); |
57 | 63 | } |
58 | 64 |
|
59 | | - public Stream<Entry<?, ?>> entryStream() { |
| 65 | + public Stream<Entry<?, ?>> entryStream(TreeAdapter adapter) { |
60 | 66 | return Stream.concat( |
61 | | - baseAdapter.entryStream(base) |
62 | | - .filter(entry -> !overlay.containsKey(entry.getKey()) && keys.contains(entry.getKey())), |
63 | | - overlay.entrySet().stream()); |
| 67 | + adapter.entryStream(base) |
| 68 | + .filter(entry -> !overlay.containsKey(entry.getKey())), |
| 69 | + overlay.entrySet().stream() |
| 70 | + .filter(entry -> !REMOVED.equals(entry.getValue()))); |
| 71 | + } |
| 72 | + |
| 73 | + public static Builder newBuilder(Tree tree) { |
| 74 | + return newBuilder(tree.node(), tree.adapter()); |
| 75 | + } |
| 76 | + |
| 77 | + public static Builder newBuilder(Map<Object, Object> map) { |
| 78 | + return new Builder(map, JavaAdapter.instance()); |
| 79 | + } |
| 80 | + |
| 81 | + public static Builder newBuilder(Object map, TreeAdapter adapter) { |
| 82 | + if (adapter instanceof MorphAdapter morph) { |
| 83 | + return new Builder(map, morph.base); |
| 84 | + } |
| 85 | + return new Builder(map, adapter); |
| 86 | + } |
| 87 | + |
| 88 | + public static class Builder { |
| 89 | + |
| 90 | + private final Object base; |
| 91 | + private final TreeAdapter baseAdapter; |
| 92 | + |
| 93 | + private Map<Object, Object> overlay; |
| 94 | + |
| 95 | + private Builder(Object base, TreeAdapter baseAdapter) { |
| 96 | + this.base = base; |
| 97 | + this.baseAdapter = baseAdapter; |
| 98 | + } |
| 99 | + |
| 100 | + public Builder remove(Object key) { |
| 101 | + overlay.put(key, REMOVED); |
| 102 | + return this; |
| 103 | + } |
| 104 | + |
| 105 | + public Builder put(Object key, Object node, TreeAdapter nodeAdapter) { |
| 106 | + if (node instanceof Tree tree) { |
| 107 | + return put(key, tree); |
| 108 | + } |
| 109 | + if (nodeAdapter instanceof MorphAdapter morph |
| 110 | + && baseAdapter.isEqualTo(morph.base) |
| 111 | + || baseAdapter.isEqualTo(nodeAdapter)) { |
| 112 | + overlay.put(key, node); |
| 113 | + return this; |
| 114 | + } |
| 115 | + |
| 116 | + overlay.put(key, new Tree(node, nodeAdapter)); |
| 117 | + return this; |
| 118 | + } |
| 119 | + |
| 120 | + public Builder put(Object key, Tree tree) { |
| 121 | + if (tree.adapter() instanceof MorphAdapter morph |
| 122 | + && baseAdapter.isEqualTo(morph.base) |
| 123 | + || baseAdapter.isEqualTo(tree.adapter())) { |
| 124 | + overlay.put(key, tree.node()); |
| 125 | + return this; |
| 126 | + } |
| 127 | + return put(key, tree); |
| 128 | + } |
| 129 | + |
| 130 | + public MapOverlay build() { |
| 131 | + return new MapOverlay( |
| 132 | + base, |
| 133 | + Map.copyOf(overlay)); |
| 134 | + } |
| 135 | + |
| 136 | + public Tree buildTree() { |
| 137 | + return new Tree(build(), new MorphAdapter(baseAdapter)); |
| 138 | + } |
64 | 139 | } |
65 | 140 |
|
66 | 141 | } |
0 commit comments