KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > Fqn


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 org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12
13 import java.io.Externalizable JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.ObjectInput JavaDoc;
16 import java.io.ObjectOutput JavaDoc;
17 import java.security.AccessController JavaDoc;
18 import java.security.PrivilegedAction JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Arrays JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25
26 /**
27  * Fully qualified name. A list of relatives names, which can be any Object,
28  * from root to a given node. This class is immutable.
29  * <p/>
30  * Note that<br>
31  * <p/>
32  * <code>Fqn f = new Fqn("/a/b/c");</code>
33  * <p/>
34  * is <b>not</b> the same as
35  * <p/>
36  * <code>Fqn f = Fqn.fromString("/a/b/c");</code>
37  * <p/>
38  * The former will result in a single Fqn, called "/a/b/c" which hangs directly under Fqn.ROOT.
39  * <p/>
40  * 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.
41  * <p/>
42  * Another way to look at it is that the "/" separarator is only parsed when it form sa part of a String passed in to Fqn.fromString() and not otherwise.
43  *
44  * @version $Revision: 1.46 $
45  */

46 public class Fqn implements Cloneable JavaDoc, Externalizable JavaDoc, Comparable JavaDoc<Fqn>
47 {
48
49    /**
50     * Separator between FQN elements.
51     */

52    public static final String JavaDoc SEPARATOR = "/";
53
54    private List JavaDoc elements;
55    private transient int hash_code = 0;
56
57    /**
58     * Controls the implementation of read/writeExternal.
59     * Package protected so CacheImpl can set it when ReplicationVersion is set.
60     */

61    static boolean REL_123_COMPATIBLE = false;
62
63    static
64    {
65       // Check if they set a system property telling us to use 1.2.3 compatibility mode
66
// Obscure use case for this: Server has multiple caches, only one of which has
67
// CacheImpl.setReplicationVersion() set to "1.2.3". If the 1.2.4+ caches start
68
// first, they will begin exchanging 1.2.4 style fqn's, and then when the 1.2.3
69
// cache starts the format will change when it changes REL_123_COMPATIBLE. This
70
// could cause chaos. The system property allows the 1.2.3 mode to be used by
71
// the 1.2.4 caches from the start.
72
try
73       {
74          AccessController.doPrivileged(
75                  new PrivilegedAction JavaDoc()
76                  {
77                     public Object JavaDoc run()
78                     {
79                        String JavaDoc compatible = System.getProperty("jboss.cache.fqn.123compatible");
80                        REL_123_COMPATIBLE = Boolean.valueOf(compatible);
81                        return null;
82                     }
83                  });
84       }
85       catch (SecurityException JavaDoc ignored)
86       {
87          // they just can't use system properties
88
}
89       catch (Throwable JavaDoc t)
90       {
91          LogFactory.getLog(Fqn.class).error("Caught throwable reading system property " +
92                  "jboss.cache.fqn.123compatible", t);
93       }
94    }
95
96    private static final long serialVersionUID = -5351930616956603651L;
97
98    /**
99     * Immutable root FQN.
100     */

101    public static final Fqn ROOT = new Fqn();
102    private static Log log = LogFactory.getLog(Fqn.class);
103
104    /**
105     * Constructs a root FQN.
106     */

107    public Fqn()
108    {
109       elements = Collections.EMPTY_LIST;
110    }
111
112    /**
113     * Constructs a single element Fqn. Note that if a String is passed in, separator characters {@link #SEPARATOR} are <b>not</b> parsed.
114     * If you wish these to be parsed, use {@link #fromString(String)}.
115     *
116     * @see #fromString(String)
117     */

118    public Fqn(Object JavaDoc name)
119    {
120       elements = Collections.singletonList(name);
121    }
122
123    /**
124     * Constructs a FQN from a list of names.
125     */

126    public Fqn(List JavaDoc names)
127    {
128       if (names != null)
129          elements = Arrays.asList(names.toArray());
130       else
131          elements = Collections.EMPTY_LIST;
132    }
133
134    /**
135     * Constructs a FQN from an array of names.
136     */

137    public Fqn(Object JavaDoc[] names)
138    {
139       if (names == null)
140          elements = Collections.EMPTY_LIST;
141       else
142       {
143          elements = Arrays.asList(names);
144       }
145    }
146
147    /**
148     * Constructs a FQN from a base and relative name.
149     */

150    public Fqn(Fqn base, Object JavaDoc relative_name)
151    {
152       elements = new ArrayList JavaDoc(base.elements.size() + 1);
153       elements.addAll(base.elements);
154       elements.add(relative_name);
155    }
156
157    /**
158     * Constructs a FQN from a base and relative FQN.
159     */

160    public Fqn(Fqn base, Fqn relative)
161    {
162       this(base, relative.elements);
163    }
164
165    /**
166     * Constructs a FQN from a base and a list of relative names.
167     */

168    public Fqn(Fqn base, List JavaDoc relative)
169    {
170       elements = new ArrayList JavaDoc(base.elements.size() + relative.size());
171       elements.addAll(base.elements);
172       elements.addAll(relative);
173    }
174
175    /**
176     * Constructs a FQN from a base and two relative names.
177     */

178    public Fqn(Fqn base, Object JavaDoc relative_name1, Object JavaDoc relative_name2)
179    {
180       elements = new ArrayList JavaDoc(base.elements.size() + 2);
181       elements.addAll(base.elements);
182       elements.add(relative_name1);
183       elements.add(relative_name2);
184    }
185
186    /**
187     * Constructs a FQN from a base and three relative names.
188     */

189    public Fqn(Fqn base, Object JavaDoc relative_name1, Object JavaDoc relative_name2, Object JavaDoc relative_name3)
190    {
191       elements = new ArrayList JavaDoc(base.elements.size() + 3);
192       elements.addAll(base.elements);
193       elements.add(relative_name1);
194       elements.add(relative_name2);
195       elements.add(relative_name3);
196    }
197
198    /**
199     * Construct from an unmodifiable list.
200     * For internal use.
201     */

202    private static Fqn createFqn(List JavaDoc list)
203    {
204       Fqn fqn = new Fqn();
205       fqn.elements = list;
206       return fqn;
207    }
208
209    /**
210     * Returns a new FQN from a string, where the elements are deliminated by
211     * one or more separator ({@link #SEPARATOR}) characters.<br><br>
212     * Example use:<br>
213     * <pre>
214     * Fqn.fromString("/a//b/c/");
215     * </pre><br>
216     * is equivalent to:<br>
217     * <pre>
218     * new Fqn(new Object[] { "a", "b", "c" });
219     * </pre><br>
220     * but not<br>
221     * <pre>
222     * new Fqn("/a/b/c");
223     * </pre>
224     *
225     * @see #Fqn(Object[])
226     * @see #Fqn(Object)
227     */

228    public static Fqn fromString(String JavaDoc fqn)
229    {
230       if (fqn == null)
231          return ROOT;
232       return createFqn(parse(fqn));
233    }
234
235    private static List JavaDoc parse(String JavaDoc fqn)
236    {
237       List JavaDoc list = new ArrayList JavaDoc();
238       StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(fqn, SEPARATOR);
239       while (tok.hasMoreTokens())
240       {
241          list.add(tok.nextToken());
242       }
243       return list;
244    }
245
246    /**
247     * Obtains a sub-Fqn from the given Fqn. Literally performs <code>elements.subList(0, index)</code>
248     * such that if
249     * <code>
250     * index == fqn.size()
251     * </code>
252     * then the return value is the fqn itself, and if
253     * <code>
254     * index == fqn.size() - 1
255     * </code>
256     * then the return value is the same as
257     * <code>
258     * fqn.getParent()
259     * </code>
260     */

261    public Fqn getFqnChild(int index)
262    {
263       return getFqnChild(0, index);
264    }
265
266    /**
267     * Obtains a sub-Fqn from the given Fqn. Literally performs <code>elements.subList(startIndex, endIndex)</code>
268     */

269    public Fqn getFqnChild(int startIndex, int endIndex)
270    {
271       return createFqn(elements.subList(startIndex, endIndex));
272    }
273
274    /**
275     * Returns the number of elements in the FQN.
276     * The root node contains zero.
277     */

278    public int size()
279    {
280       return elements.size();
281    }
282
283    /**
284     * Returns the Nth element in the FQN.
285     */

286    public Object JavaDoc get(int index)
287    {
288       return elements.get(index);
289    }
290
291    /**
292     * Returns the last element in the FQN.
293     *
294     * @see #getLastElementAsString
295     */

296    public Object JavaDoc getLastElement()
297    {
298       if (isRoot()) return SEPARATOR;
299       return elements.get(elements.size() - 1);
300    }
301
302    /**
303     * Returns true if the FQN contains this element.
304     */

305    public boolean hasElement(Object JavaDoc o)
306    {
307       return elements.lastIndexOf(o) != -1;
308    }
309
310    /**
311     * Clones the FQN.
312     */

313    public Fqn clone()
314    {
315       try
316       {
317          return (Fqn) super.clone();
318       }
319       catch (CloneNotSupportedException JavaDoc e)
320       {
321          log.error("Unable to clone Fqn " + this, e);
322          return null;
323       }
324    }
325
326    /**
327     * Returns true if obj is a FQN with the same elements.
328     */

329    public boolean equals(Object JavaDoc obj)
330    {
331       if (this == obj)
332          return true;
333       if (!(obj instanceof Fqn))
334          return false;
335       Fqn other = (Fqn) obj;
336       return elements.equals(other.elements);
337    }
338
339    /**
340     * Returns a hash code with FQN elements.
341     */

342    public int hashCode()
343    {
344       if (hash_code == 0)
345       {
346          hash_code = _hashCode();
347       }
348       return hash_code;
349    }
350
351    /**
352     * Returns this FQN as a string, prefixing the first element with a / and
353     * joining each subsequent element with a /.
354     * If this is the root FQN, returns {@link Fqn#SEPARATOR}.
355     * Example:
356     * <pre>
357     * new Fqn(new Object[] { "a", "b", "c" }).toString(); // "/a/b/c"
358     * Fqn.ROOT.toString(); // "/"
359     * </pre>
360     */

361    public String JavaDoc toString()
362    {
363       if (isRoot()) return SEPARATOR;
364       StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
365       for (Iterator JavaDoc it = elements.iterator(); it.hasNext();)
366       {
367          sb.append(SEPARATOR).append(it.next());
368       }
369       return sb.toString();
370    }
371
372    public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc
373    {
374
375       if (REL_123_COMPATIBLE)
376       {
377          out.writeObject(elements);
378       }
379       else
380       {
381          out.writeShort(elements.size());
382          Object JavaDoc element;
383          for (Iterator JavaDoc it = elements.iterator(); it.hasNext();)
384          {
385             element = it.next();
386             out.writeObject(element);
387          }
388       }
389    }
390
391    public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc
392    {
393
394       if (REL_123_COMPATIBLE)
395       {
396          elements = (List JavaDoc) in.readObject();
397       }
398       else
399       {
400          short length = in.readShort();
401          this.elements = new ArrayList JavaDoc(length);
402          for (int i = 0; i < length; i++)
403          {
404             elements.add(in.readObject());
405          }
406       }
407    }
408
409
410    /**
411     * Returns true if this fqn is child of parentFqn
412     *
413     * @param parentFqn
414     * @return true if the target is a child of parentFqn
415     */

416    public boolean isChildOf(Fqn parentFqn)
417    {
418       if (parentFqn.elements.size() == elements.size())
419          return false;
420       return isChildOrEquals(parentFqn);
421    }
422
423    /**
424     * Returns true if this fqn is equals or the child of parentFqn.
425     */

426    public boolean isChildOrEquals(Fqn parentFqn)
427    {
428       List JavaDoc parentList = parentFqn.elements;
429       if (parentList.size() > elements.size())
430          return false;
431       for (int i = parentList.size() - 1; i >= 0; i--)
432       {
433          if (!parentList.get(i).equals(elements.get(i)))
434             return false;
435       }
436       return true;
437    }
438
439    /**
440     * Calculates a hash code by summing the hash code of all elements.
441     */

442    private int _hashCode()
443    {
444       int hashCode = 0;
445       int count = 1;
446       Object JavaDoc o;
447       for (Iterator JavaDoc it = elements.iterator(); it.hasNext();)
448       {
449          o = it.next();
450          hashCode += (o == null) ? 0 : o.hashCode() * count++;
451       }
452       if (hashCode == 0) // fix degenerate case
453
hashCode = 0xFEED;
454       return hashCode;
455    }
456
457    /**
458     * Returns the parent of this FQN.
459     */

460    public Fqn getParent()
461    {
462       switch (elements.size())
463       {
464          case 0:
465          case 1:
466             return ROOT;
467          default:
468             return createFqn(elements.subList(0, elements.size() - 1));
469       }
470    }
471
472    /**
473     * Returns true if this is a root FQN.
474     */

475    public boolean isRoot()
476    {
477       return elements.isEmpty();
478    }
479
480    /**
481     * Returns a String representation of the last element that makes up this Fqn.
482     * If this is the root, returns {@link Fqn#SEPARATOR}.
483     */

484    public String JavaDoc getLastElementAsString()
485    {
486       if (isRoot()) return SEPARATOR;
487       else return String.valueOf(getLastElement());
488    }
489
490    /**
491     * Peeks into the elements that build up this Fqn. The list returned is
492     * read-only, to maintain the immutable nature of Fqn.
493     *
494     * @return an unmodifiable list
495     */

496    public List JavaDoc peekElements()
497    {
498       return Collections.unmodifiableList(elements);
499    }
500
501    /**
502     * Compares this Fqn to another using {@link FqnComparator}.
503     */

504    public int compareTo(Fqn fqn)
505    {
506       return FqnComparator.INSTANCE.compare(this, fqn);
507    }
508 }
Popular Tags