1 package com.exsoinn.util.epf;
2
3 import net.jcip.annotations.Immutable;
4
5 import java.util.*;
6 import java.util.concurrent.ConcurrentHashMap;
7 import java.util.stream.Collectors;
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 @Immutable
24 public final class SearchPath implements List<String> {
25 private final List<String> searchPath;
26 private final String searchPathAsString;
27 private final int currentNodeIndex;
28 private final boolean atEndOfSearchPath;
29 private static final String KEY_SEP = "__";
30 private static final int INIT_SP = -1;
31 private final static Map<String, SearchPath> cachedSearchPaths = new ConcurrentHashMap<>();
32
33
34
35 static String generateCacheKey(String pSearchPath, int pIdx, boolean pAtEnd) {
36 StringBuilder key = new StringBuilder();
37 key.append(pSearchPath);
38 key.append(KEY_SEP);
39 key.append(pIdx);
40 key.append(KEY_SEP);
41 key.append(pAtEnd);
42 return key.toString();
43 }
44
45 SearchPath(String pSearchPath, int pNodeIdx, boolean pAtEnd) {
46 if (pNodeIdx < 0 && pAtEnd) {
47 searchPath = Collections.emptyList();
48 } else {
49 searchPath = parseElementSearchPath(pSearchPath);
50 }
51 searchPathAsString = pSearchPath;
52 currentNodeIndex = pNodeIdx;
53 atEndOfSearchPath = pAtEnd;
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public static SearchPath valueOf(final String pSearchPath) {
69 final boolean atEnd = false;
70 String key = generateCacheKey(pSearchPath, INIT_SP, atEnd);
71 SearchPath sp = cachedSearchPaths.get(key);
72 if (null == sp) {
73 sp = new SearchPath(pSearchPath, INIT_SP, atEnd);
74 SearchPath spFromCache = cachedSearchPaths.putIfAbsent(key, sp);
75 sp = (null == spFromCache) ? sp : spFromCache;
76 }
77
78 return sp;
79 }
80
81
82
83
84
85
86
87
88 final SearchPath advanceToNextNode() {
89
90
91
92
93
94
95 int nextNodeIdx = (currentNodeIndex + 1) <= searchPath.size()-1 ? (currentNodeIndex + 1) : -1 ;
96
97
98
99
100
101
102 boolean atEnd = (nextNodeIdx >= searchPath.size()-1);
103
104 String key = generateCacheKey(searchPathAsString, nextNodeIdx, atEnd);
105 SearchPath sp = cachedSearchPaths.get(key);
106 if (null == sp) {
107 sp = new SearchPath(searchPathAsString, nextNodeIdx, atEnd);
108 SearchPath spFromCache = cachedSearchPaths.putIfAbsent(key, sp);
109 sp = (null == spFromCache) ? sp : spFromCache;
110 }
111 return sp;
112 }
113
114
115 public String lastNode() {
116 return searchPath.get(searchPath.size() - 1);
117 }
118
119 public static void main(String[] args) {
120 SearchPath sp = valueOf("node1.node2.node3.node4");
121 SearchPath newSp = sp.advanceToNextNode();
122 newSp = newSp.advanceToNextNode();
123 newSp = newSp.advanceToNextNode();
124 newSp = newSp.advanceToNextNode();
125 newSp = newSp.advanceToNextNode();
126 newSp = newSp.advanceToNextNode();
127 newSp = newSp.advanceToNextNode();
128 newSp = newSp.advanceToNextNode();
129 newSp = newSp.advanceToNextNode();
130 }
131
132 final String currentNode() {
133 return get(currentNodeIndex);
134 }
135
136 final int currentNodeIndex() {
137 return currentNodeIndex;
138 }
139
140 @Override
141 public int size() {
142 return searchPath.size();
143 }
144
145 @Override
146 public boolean isEmpty() {
147 return searchPath.isEmpty();
148 }
149
150 @Override
151 public boolean contains(Object o) {
152 return searchPath.contains(o);
153 }
154
155 @Override
156 public Iterator<String> iterator() {
157 return new ArrayList<>(searchPath).iterator();
158 }
159
160 @Override
161 public Object[] toArray() {
162 return searchPath.toArray();
163 }
164
165 @Override
166 public <T> T[] toArray(T[] a) {
167 return searchPath.toArray(a);
168 }
169
170 @Override
171 public boolean add(String s) {
172 throw new UnsupportedOperationException();
173 }
174
175 @Override
176 public boolean remove(Object o) {
177 throw new UnsupportedOperationException();
178 }
179
180 @Override
181 public boolean containsAll(Collection<?> c) {
182 return searchPath.containsAll(c);
183 }
184
185 @Override
186 public boolean addAll(Collection<? extends String> c) {
187 throw new UnsupportedOperationException();
188 }
189
190 @Override
191 public boolean addAll(int index, Collection<? extends String> c) {
192 throw new UnsupportedOperationException();
193 }
194
195 @Override
196 public boolean removeAll(Collection<?> c) {
197 throw new UnsupportedOperationException();
198 }
199
200 @Override
201 public boolean retainAll(Collection<?> c) {
202 throw new UnsupportedOperationException();
203 }
204
205 @Override
206 public void clear() {
207 throw new UnsupportedOperationException();
208 }
209
210 @Override
211 public String get(int index) {
212 return searchPath.get(index);
213 }
214
215 @Override
216 public String set(int index, String element) {
217 throw new UnsupportedOperationException();
218 }
219
220 @Override
221 public void add(int index, String element) {
222 throw new UnsupportedOperationException();
223 }
224
225 @Override
226 public String remove(int index) {
227 throw new UnsupportedOperationException();
228 }
229
230 @Override
231 public int indexOf(Object o) {
232 return searchPath.indexOf(o);
233 }
234
235 @Override
236 public int lastIndexOf(Object o) {
237 return searchPath.lastIndexOf(o);
238 }
239
240 @Override
241 public ListIterator<String> listIterator() {
242 return new ArrayList<>(searchPath).listIterator();
243 }
244
245 @Override
246 public ListIterator<String> listIterator(int index) {
247 return new ArrayList<>(searchPath).listIterator(index);
248 }
249
250 @Override
251 public List<String> subList(int fromIndex, int toIndex) {
252 return new ArrayList<>(searchPath).subList(fromIndex, toIndex);
253 }
254
255
256
257
258
259
260
261 @Override
262 public String toString() {
263 return searchPathAsString;
264 }
265
266 @Override
267 public boolean equals(Object o) {
268 return searchPath.equals(o);
269 }
270
271 @Override
272 public int hashCode() {
273 return searchPath.hashCode();
274 }
275
276 private static List<String> parseElementSearchPath(String pElemSearchPath) {
277 String[] nodes = pElemSearchPath.split("\\.");
278 return Arrays.stream(nodes).collect(Collectors.toCollection(() -> new ArrayList(nodes.length)));
279 }
280
281 boolean isAtEndOfSearchPath() {
282 return atEndOfSearchPath;
283 }
284 }