|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
Fqn.java | 94.4% | 95.5% | 100% | 96.1% |
|
1 | /* | |
2 | * JBoss, the OpenSource J2EE webOS | |
3 | * | |
4 | * Distributable under LGPL license. | |
5 | * See terms of license at gnu.org. | |
6 | */ | |
7 | package org.jboss.cache; | |
8 | ||
9 | ||
10 | import net.jcip.annotations.Immutable; | |
11 | import org.apache.commons.logging.Log; | |
12 | import org.apache.commons.logging.LogFactory; | |
13 | ||
14 | import java.io.Externalizable; | |
15 | import java.io.IOException; | |
16 | import java.io.ObjectInput; | |
17 | import java.io.ObjectOutput; | |
18 | import java.util.ArrayList; | |
19 | import java.util.Arrays; | |
20 | import java.util.Collections; | |
21 | import java.util.List; | |
22 | import java.util.StringTokenizer; | |
23 | ||
24 | /** | |
25 | * A Fully Qualified Name (Fqn) is a list of names (typically Strings but can be any Object), | |
26 | * which represent a path to a particular {@link Node} or sometimes a {@link Region} in a {@link Cache}. | |
27 | * <p/> | |
28 | * This name can be absolute (i.e., relative from the root node - {@link #ROOT}), or relative to any node in the cache. Reading the | |
29 | * documentation on each API call that makes use of {@link org.jboss.cache.Fqn}s will tell you whether the API expects a | |
30 | * relative or absolute Fqn. | |
31 | * <p/> | |
32 | * For instance, using this class to fetch a particular node might look like | |
33 | * this. (Here data on "Joe" is kept under the "Smith" surname node, under | |
34 | * the "people" tree.) | |
35 | * <pre> | |
36 | * Fqn<String> abc = Fqn.fromString("/people/Smith/Joe/"); | |
37 | * Node joesmith = Cache.getRoot().getChild(abc); | |
38 | * </pre> | |
39 | * Alternatively, the same Fqn could be constructed using an array: | |
40 | * <pre> | |
41 | * Fqn<String> abc = new Fqn<String>(new String[] { "people", "Smith", "Joe" }); | |
42 | * </pre> | |
43 | * This is a bit more efficient to construct. | |
44 | * <p/> | |
45 | * <p/> | |
46 | * Note that<br> | |
47 | * <p/> | |
48 | * <code>Fqn<String> f = new Fqn<String>("/a/b/c");</code> | |
49 | * <p/> | |
50 | * is <b>not</b> the same as | |
51 | * <p/> | |
52 | * <code>Fqn<String> f = Fqn.fromString("/a/b/c");</code> | |
53 | * <p/> | |
54 | * The former will result in a single Fqn, called "/a/b/c" which hangs directly under Fqn.ROOT. | |
55 | * <p/> | |
56 | * The latter will result in 3 Fqns, called "a", "b" and "c", where "c" is a child of "b", "b" is a child of "a", and "a" hangs off Fqn.ROOT. | |
57 | * <p/> | |
58 | * Another way to look at it is that the "/" separarator is only parsed when it forms | |
59 | * part of a String passed in to Fqn.fromString() and not otherwise. | |
60 | * | |
61 | * @version $Revision: 1.54 $ | |
62 | */ | |
63 | @Immutable | |
64 | public class Fqn<E> implements Cloneable, Externalizable, Comparable<Fqn> | |
65 | { | |
66 | ||
67 | /** | |
68 | * Separator between FQN elements. | |
69 | */ | |
70 | public static final String SEPARATOR = "/"; | |
71 | private static final long serialVersionUID = -5351930616956603651L; | |
72 | private List<E> elements; | |
73 | private transient int hash_code = 0; | |
74 | ||
75 | /** | |
76 | * Immutable root FQN. | |
77 | */ | |
78 | public static final Fqn ROOT = new Fqn(); | |
79 | private static Log log = LogFactory.getLog(Fqn.class); | |
80 | // a cached string representation of this Fqn, used by toString to it isn't calculated again every time. | |
81 | private String cachedStringRep; | |
82 | ||
83 | /** | |
84 | * Constructs a root Fqn | |
85 | */ | |
86 | 25816 | public Fqn() |
87 | { | |
88 | 25816 | elements = Collections.emptyList(); |
89 | } | |
90 | ||
91 | /** | |
92 | * Constructs a FQN from a list of names. | |
93 | * | |
94 | * @param names List of names | |
95 | */ | |
96 | 7827474 | public Fqn(List<E> names) |
97 | { | |
98 | // the list is unsafe - may be referenced externally | |
99 | 7828769 | this(names, false); |
100 | } | |
101 | ||
102 | /** | |
103 | * If safe is false, Collections.unmodifiableList() is used to wrap the list passed in. This is an optimisation so | |
104 | * Fqn.fromString(), probably the most frequently used factory method, doesn't end up needing to use the unmodifiableList() | |
105 | * since it creates the list internally. | |
106 | * | |
107 | * @param names List of names | |
108 | * @param safe whether this list is referenced externally (safe = false) or not (safe = true). | |
109 | */ | |
110 | 9066444 | protected Fqn(List<E> names, boolean safe) |
111 | { | |
112 | 9066445 | if (names != null) |
113 | { | |
114 | // if not safe make a defensive copy | |
115 | 9066445 | elements = safe ? names : new ArrayList<E>(names); |
116 | } | |
117 | else | |
118 | { | |
119 | 0 | elements = Collections.emptyList(); |
120 | } | |
121 | } | |
122 | ||
123 | ||
124 | /** | |
125 | * Constructs a Fqn from an array of names. | |
126 | * | |
127 | * @param names Names that comprose this Fqn | |
128 | */ | |
129 | 168414 | public Fqn(E... names) |
130 | { | |
131 | // safe - the list is created here. | |
132 | 168414 | this(Arrays.asList(names), true); |
133 | } | |
134 | ||
135 | /** | |
136 | * Constructs a Fqn from a base and relative Fqn. | |
137 | * | |
138 | * @param base parent Fqn | |
139 | * @param relative Sub-Fqn relative to the parent | |
140 | */ | |
141 | 57214 | public Fqn(Fqn<E> base, Fqn<E> relative) |
142 | { | |
143 | 57214 | this(base, relative.elements); |
144 | } | |
145 | ||
146 | /** | |
147 | * Constructs a Fqn from a base and a list of relative names. | |
148 | * | |
149 | * @param base parent Fqn | |
150 | * @param relative List of elements that identify the child Fqn, relative to the parent | |
151 | */ | |
152 | 57222 | public Fqn(Fqn<E> base, List<E> relative) |
153 | { | |
154 | 57222 | List<E> elements = new ArrayList<E>(base.elements.size() + relative.size()); |
155 | 57222 | elements.addAll(base.elements); |
156 | 57222 | elements.addAll(relative); |
157 | 57222 | this.elements = elements; |
158 | } | |
159 | ||
160 | /** | |
161 | * Constructs a Fqn from a base and two relative names. | |
162 | * | |
163 | * @param base parent Fqn | |
164 | * @param childNames elements that denote the path to the Fqn, under the parent | |
165 | */ | |
166 | 1389605 | public Fqn(Fqn<E> base, E... childNames) |
167 | { | |
168 | 1389605 | List<E> elements = new ArrayList<E>(base.elements.size() + childNames.length); |
169 | 1389605 | elements.addAll(base.elements); |
170 | 1389605 | elements.addAll(Arrays.asList(childNames)); |
171 | 1389605 | this.elements = elements; |
172 | } | |
173 | ||
174 | /** | |
175 | * Returns a new Fqn from a string, where the elements are deliminated by | |
176 | * one or more separator ({@link #SEPARATOR}) characters.<br><br> | |
177 | * Example use:<br> | |
178 | * <pre> | |
179 | * Fqn.fromString("/a/b/c/"); | |
180 | * </pre><br> | |
181 | * is equivalent to:<br> | |
182 | * <pre> | |
183 | * new Fqn("a", "b", "c"); | |
184 | * </pre><br> | |
185 | * but not<br> | |
186 | * <pre> | |
187 | * new Fqn("/a/b/c"); | |
188 | * </pre> | |
189 | * | |
190 | * @param stringRepresentation String representation of the Fqn | |
191 | * @return an Fqn<String> constructed from the string representation passed in | |
192 | * @see #Fqn(Object[]) | |
193 | */ | |
194 | 1069263 | public static Fqn<String> fromString(String stringRepresentation) |
195 | { | |
196 | 1069263 | if (stringRepresentation == null) |
197 | { | |
198 | 1 | return Fqn.ROOT; |
199 | } | |
200 | 1069262 | List<String> list = new ArrayList<String>(); |
201 | 1069262 | StringTokenizer tok = new StringTokenizer(stringRepresentation, SEPARATOR); |
202 | 2615110 | while (tok.hasMoreTokens()) list.add(tok.nextToken()); |
203 | 1069262 | return new Fqn<String>(list, true); |
204 | } | |
205 | ||
206 | /** | |
207 | * Obtains an ancestor of the current Fqn. Literally performs <code>elements.subList(0, generation)</code> | |
208 | * such that if | |
209 | * <code> | |
210 | * generation == Fqn.size() | |
211 | * </code> | |
212 | * then the return value is the Fqn itself (current generation), and if | |
213 | * <code> | |
214 | * generation == Fqn.size() - 1 | |
215 | * </code> | |
216 | * then the return value is the same as | |
217 | * <code> | |
218 | * Fqn.getParent() | |
219 | * </code> | |
220 | * i.e., just one generation behind the current generation. | |
221 | * <code> | |
222 | * generation == 0 | |
223 | * </code> | |
224 | * would return Fqn.ROOT. | |
225 | * | |
226 | * @param generation the generation of the ancestor to retrieve | |
227 | * @return an ancestor of the current Fqn | |
228 | */ | |
229 | 7224 | public Fqn<E> getAncestor(int generation) |
230 | { | |
231 | 420 | if (generation == 0) return Fqn.ROOT; |
232 | 6804 | return getSubFqn(0, generation); |
233 | } | |
234 | ||
235 | /** | |
236 | * Obtains a sub-Fqn from the given Fqn. Literally performs <code>elements.subList(startIndex, endIndex)</code> | |
237 | */ | |
238 | 6977 | public Fqn<E> getSubFqn(int startIndex, int endIndex) |
239 | { | |
240 | 6977 | return new Fqn<E>(elements.subList(startIndex, endIndex)); |
241 | } | |
242 | ||
243 | /** | |
244 | * @return the number of elements in the Fqn. The root node contains zero. | |
245 | */ | |
246 | 12417794 | public int size() |
247 | { | |
248 | 12417776 | return elements.size(); |
249 | } | |
250 | ||
251 | /** | |
252 | * @param n index of the element to return | |
253 | * @return Returns the nth element in the Fqn. | |
254 | */ | |
255 | 26968136 | public E get(int n) |
256 | { | |
257 | 26968132 | return elements.get(n); |
258 | } | |
259 | ||
260 | /** | |
261 | * @return the last element in the Fqn. | |
262 | * @see #getLastElementAsString | |
263 | */ | |
264 | 1901480 | public E getLastElement() |
265 | { | |
266 | 1428095 | if (isRoot()) return null; |
267 | 473385 | return elements.get(elements.size() - 1); |
268 | } | |
269 | ||
270 | /** | |
271 | * @param element element to find | |
272 | * @return true if the Fqn contains this element, false otherwise. | |
273 | */ | |
274 | 228792 | public boolean hasElement(E element) |
275 | { | |
276 | 228789 | return elements.lastIndexOf(element) != -1; |
277 | } | |
278 | ||
279 | /** | |
280 | * Clones the Fqn. | |
281 | */ | |
282 | 4 | public Fqn<E> clone() throws CloneNotSupportedException |
283 | { | |
284 | 4 | try |
285 | { | |
286 | 4 | return (Fqn<E>) super.clone(); |
287 | } | |
288 | catch (CloneNotSupportedException e) | |
289 | { | |
290 | 0 | log.error("Unable to clone Fqn " + this, e); |
291 | 0 | return null; |
292 | } | |
293 | } | |
294 | ||
295 | /** | |
296 | * Returns true if obj is a Fqn with the same elements. | |
297 | */ | |
298 | 25413298 | public boolean equals(Object obj) |
299 | { | |
300 | 25413298 | if (this == obj) |
301 | { | |
302 | 5077813 | return true; |
303 | } | |
304 | 20335485 | if (!(obj instanceof Fqn)) |
305 | { | |
306 | 436 | return false; |
307 | } | |
308 | 20335049 | Fqn other = (Fqn) obj; |
309 | 20335049 | return elements.equals(other.elements); |
310 | } | |
311 | ||
312 | /** | |
313 | * Returns a hash code with Fqn elements. | |
314 | */ | |
315 | 23642915 | public int hashCode() |
316 | { | |
317 | 23642915 | if (hash_code == 0) |
318 | { | |
319 | 8996738 | hash_code = _hashCode(); |
320 | } | |
321 | 23642905 | return hash_code; |
322 | } | |
323 | ||
324 | /** | |
325 | * Returns this Fqn as a string, prefixing the first element with a {@link Fqn#SEPARATOR} and | |
326 | * joining each subsequent element with a {@link Fqn#SEPARATOR}. | |
327 | * If this is the root Fqn, returns {@link Fqn#SEPARATOR}. | |
328 | * Example: | |
329 | * <pre> | |
330 | * new Fqn(new Object[] { "a", "b", "c" }).toString(); // "/a/b/c" | |
331 | * Fqn.ROOT.toString(); // "/" | |
332 | * </pre> | |
333 | */ | |
334 | 1484746 | public String toString() |
335 | { | |
336 | 1484748 | if (cachedStringRep == null) |
337 | { | |
338 | 842460 | if (isRoot()) |
339 | { | |
340 | 306 | cachedStringRep = SEPARATOR; |
341 | } | |
342 | else | |
343 | { | |
344 | 842154 | StringBuffer sb = new StringBuffer(); |
345 | 842154 | for (E element : elements) |
346 | { | |
347 | 4710803 | sb.append(SEPARATOR).append(element); |
348 | } | |
349 | 842154 | cachedStringRep = sb.toString(); |
350 | } | |
351 | } | |
352 | 1484748 | return cachedStringRep; |
353 | } | |
354 | ||
355 | 58200 | public void writeExternal(ObjectOutput out) throws IOException |
356 | { | |
357 | 58200 | out.writeShort(elements.size()); |
358 | 58200 | for (E element : elements) |
359 | { | |
360 | 207550 | out.writeObject(element); |
361 | } | |
362 | } | |
363 | ||
364 | 25445 | public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException |
365 | { | |
366 | 25445 | short length = in.readShort(); |
367 | 25445 | this.elements = new ArrayList<E>(length); |
368 | 25445 | for (int i = 0; i < length; i++) |
369 | { | |
370 | 70897 | elements.add((E) in.readObject()); |
371 | } | |
372 | } | |
373 | ||
374 | ||
375 | /** | |
376 | * Returns true if this Fqn is child of parentFqn. | |
377 | * Example usage: | |
378 | * <pre> | |
379 | * Fqn<String> f1 = Fqn.fromString("/a/b"); | |
380 | * Fqn<String> f2 = Fqn.fromString("/a/b/c"); | |
381 | * assertTrue(f1.isChildOf(f2)); | |
382 | * assertFalse(f1.isChildOf(f1)); | |
383 | * assertFalse(f2.isChildOf(f1)); | |
384 | * </pre> | |
385 | * | |
386 | * @param parentFqn candidate parent to test against | |
387 | * @return true if the target is a child of parentFqn | |
388 | */ | |
389 | 4455 | public boolean isChildOf(Fqn<E> parentFqn) |
390 | { | |
391 | 4455 | return parentFqn.elements.size() != elements.size() && isChildOrEquals(parentFqn); |
392 | } | |
393 | ||
394 | /** | |
395 | * Returns true if this Fqn is equals or the child of parentFqn. | |
396 | * Example usage: | |
397 | * <pre> | |
398 | * Fqn<String> f1 = Fqn.fromString("/a/b"); | |
399 | * Fqn<String> f2 = Fqn.fromString("/a/b/c"); | |
400 | * assertTrue(f1.isChildOrEquals(f2)); | |
401 | * assertTrue(f1.isChildOrEquals(f1)); | |
402 | * assertFalse(f2.isChildOrEquals(f1)); | |
403 | * </pre> | |
404 | * | |
405 | * @param parentFqn candidate parent to test against | |
406 | * @return true if this Fqn is equals or the child of parentFqn. | |
407 | */ | |
408 | 30395 | public boolean isChildOrEquals(Fqn<E> parentFqn) |
409 | { | |
410 | 30395 | List<E> parentList = parentFqn.elements; |
411 | 30395 | if (parentList.size() > elements.size()) |
412 | { | |
413 | 1964 | return false; |
414 | } | |
415 | 28431 | for (int i = parentList.size() - 1; i >= 0; i--) |
416 | { | |
417 | 34097 | if (!parentList.get(i).equals(elements.get(i))) |
418 | { | |
419 | 5054 | return false; |
420 | } | |
421 | } | |
422 | 23377 | return true; |
423 | } | |
424 | ||
425 | /** | |
426 | * Calculates a hash code by summing the hash code of all elements. | |
427 | * | |
428 | * @return a cached hashcode | |
429 | */ | |
430 | 8996738 | private int _hashCode() |
431 | { | |
432 | 8996738 | int hashCode = 0; |
433 | 8996738 | int count = 1; |
434 | 8996738 | Object o; |
435 | 8996738 | for (E element : elements) |
436 | { | |
437 | 35721465 | o = element; |
438 | 35721545 | hashCode += (o == null) ? 0 : o.hashCode() * count++; |
439 | } | |
440 | 8996738 | if (hashCode == 0)// fix degenerate case |
441 | { | |
442 | 542 | hashCode = 0xFEED; |
443 | } | |
444 | 8996738 | return hashCode; |
445 | } | |
446 | ||
447 | /** | |
448 | * Returns the parent of this Fqn. | |
449 | * The parent of the root node is {@link #ROOT}. | |
450 | * Examples: | |
451 | * <pre> | |
452 | * Fqn<String> f1 = Fqn.fromString("/a"); | |
453 | * Fqn<String> f2 = Fqn.fromString("/a/b"); | |
454 | * assertEquals(f1, f2.getParent()); | |
455 | * assertEquals(Fqn.ROOT, f1.getParent().getParent()); | |
456 | * assertEquals(Fqn.ROOT, Fqn.ROOT.getParent()); | |
457 | * </pre> | |
458 | * | |
459 | * @return the parent Fqn | |
460 | */ | |
461 | 8314895 | public Fqn<E> getParent() |
462 | { | |
463 | 8314895 | switch (elements.size()) |
464 | { | |
465 | 4495 | case 0: |
466 | 798707 | case 1: |
467 | 803202 | return ROOT; |
468 | 7511693 | default: |
469 | 7511693 | return new Fqn<E>(elements.subList(0, elements.size() - 1)); |
470 | } | |
471 | } | |
472 | ||
473 | /** | |
474 | * Returns true if this is a root Fqn. | |
475 | * | |
476 | * @return true if the Fqn is Fqn.ROOT. | |
477 | */ | |
478 | 11474061 | public boolean isRoot() |
479 | { | |
480 | 11474061 | return elements.isEmpty(); |
481 | } | |
482 | ||
483 | /** | |
484 | * If this is the root, returns {@link Fqn#SEPARATOR}. | |
485 | * | |
486 | * @return a String representation of the last element that makes up this Fqn. | |
487 | */ | |
488 | 47 | public String getLastElementAsString() |
489 | { | |
490 | 47 | if (isRoot()) |
491 | { | |
492 | 0 | return SEPARATOR; |
493 | } | |
494 | else | |
495 | { | |
496 | 47 | return String.valueOf(getLastElement()); |
497 | } | |
498 | } | |
499 | ||
500 | /** | |
501 | * Peeks into the elements that build up this Fqn. The list returned is | |
502 | * read-only, to maintain the immutable nature of Fqn. | |
503 | * | |
504 | * @return an unmodifiable list | |
505 | */ | |
506 | 3205 | public List<E> peekElements() |
507 | { | |
508 | 3205 | return elements; |
509 | } | |
510 | ||
511 | /** | |
512 | * Compares this Fqn to another using {@link FqnComparator}. | |
513 | */ | |
514 | 1363 | public int compareTo(Fqn Fqn) |
515 | { | |
516 | 1363 | return FqnComparator.INSTANCE.compare(this, Fqn); |
517 | } | |
518 | } |
|