View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6   * See the AUTHORS.txt file in the distribution for a full listing of 
7   * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.property;
25  
26  import java.io.Serializable;
27  import java.util.Iterator;
28  import java.util.List;
29  import net.jcip.annotations.Immutable;
30  import org.modeshape.common.text.Jsr283Encoder;
31  import org.modeshape.common.text.NoOpEncoder;
32  import org.modeshape.common.text.TextDecoder;
33  import org.modeshape.common.text.TextEncoder;
34  import org.modeshape.common.text.UrlEncoder;
35  import org.modeshape.graph.property.basic.BasicName;
36  import org.modeshape.graph.property.basic.BasicPathSegment;
37  
38  /**
39   * An object representation of a node path within a repository.
40   * <p>
41   * A path consists of zero or more segments that can contain any characters, although the string representation may require some
42   * characters to be encoded. For example, if a path contains a segment with a forward slash, then this forward slash must be
43   * escaped when writing the whole path to a string (since a forward slash is used as the {@link #DELIMITER delimiter} between
44   * segments).
45   * </p>
46   * <p>
47   * Because of this encoding and decoding issue, there is no standard representation of a path as a string. Instead, this class
48   * uses {@link TextEncoder text encoders} to escape certain characters when writing to a string or unescaping the string
49   * representation. These encoders and used only with individual segments, and therefore are not used to encode the
50   * {@link #DELIMITER delimiter}. Three standard encoders are provided, although others can certainly be used:
51   * <ul>
52   * <li>{@link #JSR283_ENCODER Jsr283Encoder} - an encoder and decoder that is compliant with <a
53   * href="http://jcp.org/en/jsr/detail?id=283">JSR-283</a> by converting the reserved characters (namely '*', '/', ':', '[', ']'
54   * and '|') to their unicode equivalent.</td>
55   * </li>
56   * <li>{@link #URL_ENCODER UrlEncoder} - an encoder and decoder that is useful for converting text to be used within a URL, as
57   * defined by Section 2.3 of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>. This encoder does encode many characters
58   * (including '`', '@', '#', '$', '^', '&', '{', '[', '}', ']', '|', ':', ';', '\', '"', '<', ',', '>', '?', '/', and ' '), while
59   * others are not encoded (including '-', '_', '.', '!', '~', '*', '\', ''', '(', and ')'). Note that only the '*' character is
60   * the only character reserved by JSR-283 that is not encoded by the URL encoder.</li>
61   * <li>{@link #NO_OP_ENCODER NoOpEncoder} - an {@link TextEncoder encoder} implementation that does nothing.</li>
62   * </ul>
63   * </p>
64   * <p>
65   * This class simplifies working with paths and using a <code>Path</code> is often more efficient that processing and
66   * manipulating the equivalent <code>String</code>. This class can easily {@link #iterator() iterate} over the segments, return
67   * the {@link #size() number of segments}, {@link #compareTo(Path) compare} with other paths, {@link #resolve(Path) resolve}
68   * relative paths, return the {@link #getParent() ancestor (or parent)}, determine whether one path is an
69   * {@link #isAncestorOf(Path) ancestor} or {@link #isDecendantOf(Path) decendent} of another path, and
70   * {@link #getCommonAncestor(Path) finding a common ancestor}.
71   * </p>
72   */
73  @Immutable
74  public interface Path extends Comparable<Path>, Iterable<Path.Segment>, Serializable, Readable {
75  
76      /**
77       * The text encoder that does nothing.
78       */
79      public static final TextEncoder NO_OP_ENCODER = new NoOpEncoder();
80  
81      /**
82       * The text encoder that encodes according to JSR-283.
83       */
84      public static final TextEncoder JSR283_ENCODER = new Jsr283Encoder();
85  
86      /**
87       * The text encoder that encodes text according to the rules of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
88       */
89      public static final TextEncoder URL_ENCODER = new UrlEncoder().setSlashEncoded(true);
90  
91      /**
92       * The text decoder that does nothing.
93       */
94      public static final TextDecoder NO_OP_DECODER = new NoOpEncoder();
95  
96      /**
97       * The text decoder that decodes according to JSR-283.
98       */
99      public static final TextDecoder JSR283_DECODER = new Jsr283Encoder();
100 
101     /**
102      * The text decoder that decodes text according to the rules of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
103      */
104     public static final TextDecoder URL_DECODER = new UrlEncoder().setSlashEncoded(true);
105 
106     /**
107      * The default text encoder to be used when none is otherwise specified. This is currently the {@link #JSR283_ENCODER JSR-283
108      * encoder}.
109      */
110     public static final TextEncoder DEFAULT_ENCODER = JSR283_ENCODER;
111 
112     /**
113      * The default text decoder to be used when none is otherwise specified. This is currently the {@link #JSR283_ENCODER JSR-283
114      * encoder}.
115      */
116     public static final TextDecoder DEFAULT_DECODER = JSR283_DECODER;
117 
118     /**
119      * The delimiter character used to separate segments within a path.
120      */
121     public static final char DELIMITER = '/';
122 
123     /**
124      * The character used to begin an identifier segment within a path.
125      */
126     public static final char IDENTIFIER_LEADING_TERMINAL = '[';
127 
128     /**
129      * The character used to end an identifier segment within a path.
130      */
131     public static final char IDENTIFIER_TRAILING_TERMINAL = ']';
132 
133     /**
134      * String form of the delimiter used to separate segments within a path.
135      */
136     public static final String DELIMITER_STR = new String(new char[] {DELIMITER});
137 
138     /**
139      * String representation of the segment that references a parent.
140      */
141     public static final String PARENT = "..";
142 
143     /**
144      * String representation of the segment that references the same segment.
145      */
146     public static final String SELF = ".";
147 
148     /**
149      * The default index for a {@link Segment}.
150      */
151     public static final int DEFAULT_INDEX = 1;
152 
153     /**
154      * Representation of the segments that occur within a path.
155      * 
156      * @author Randall Hauch
157      */
158     @Immutable
159     public static interface Segment extends Cloneable, Comparable<Segment>, Serializable, Readable {
160 
161         /**
162          * Get the name component of this segment.
163          * 
164          * @return the segment's name
165          */
166         public Name getName();
167 
168         /**
169          * Get the index for this segment, which will be 1 by default.
170          * 
171          * @return the index
172          */
173         public int getIndex();
174 
175         /**
176          * Return whether this segment has an index.
177          * 
178          * @return true if this segment has an index, or false otherwise.
179          */
180         public boolean hasIndex();
181 
182         /**
183          * Return whether this segment is a self-reference.
184          * 
185          * @return true if the segment is a self-reference, or false otherwise.
186          */
187         public boolean isSelfReference();
188 
189         /**
190          * Return whether this segment is a reference to a parent.
191          * 
192          * @return true if the segment is a parent-reference, or false otherwise.
193          */
194         public boolean isParentReference();
195 
196         /**
197          * Return whether this segment is an identifier segment.
198          * 
199          * @return true if the segment is an identifier segment, or false otherwise.
200          */
201         public boolean isIdentifier();
202 
203         /**
204          * Get the raw string form of the segment using the {@link Path#NO_OP_ENCODER no-op encoder}. This is equivalent to
205          * calling <code>getString(Path.NO_OP_ENCODER)</code>.
206          * 
207          * @return the un-encoded string
208          * @see #getString(TextEncoder)
209          */
210         public String getUnencodedString();
211     }
212 
213     /**
214      * Singleton instance of the name referencing a self, provided as a convenience.
215      */
216     public static final Name SELF_NAME = new BasicName(null, SELF);
217 
218     /**
219      * Singleton instance of the name referencing a parent, provided as a convenience.
220      */
221     public static final Name PARENT_NAME = new BasicName(null, PARENT);
222 
223     /**
224      * Singleton instance of the path segment referencing a parent, provided as a convenience.
225      */
226     public static final Path.Segment SELF_SEGMENT = new BasicPathSegment(SELF_NAME);
227 
228     /**
229      * Singleton instance of the path segment referencing a parent, provided as a convenience.
230      */
231     public static final Path.Segment PARENT_SEGMENT = new BasicPathSegment(PARENT_NAME);
232 
233     /**
234      * Return the number of segments in this path.
235      * 
236      * @return the number of path segments
237      */
238     public int size();
239 
240     /**
241      * Return whether this path represents the root path.
242      * 
243      * @return true if this path is the root path, or false otherwise
244      */
245     public boolean isRoot();
246 
247     /**
248      * Returns whether this path represents an identifier path. An identifier path is absolute, and contains a single
249      * {@link Segment#isIdentifier() identifier Segment} that contains an identifier in the name.
250      * 
251      * @return true if this path is an identifier path, or false otherwise.
252      */
253     public boolean isIdentifier();
254 
255     /**
256      * Determine whether this path represents the same as the supplied path. This is equivalent to calling <code>
257      * this.compareTo(other) == 0 </code>
258      * .
259      * 
260      * @param other the other path to compare with this path; may be null
261      * @return true if the paths are equivalent, or false otherwise
262      */
263     public boolean isSameAs( Path other );
264 
265     /**
266      * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isAncestorOf(Path) ancestor of} the
267      * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
268      * isAncestor(other)</code>, and is a convenience method that is identical to calling <code>other.isAtOrBelow(this)</code>.
269      * 
270      * @param other the other path to compare with this path; may be null
271      * @return true if the paths are equivalent or if this path is considered an ancestor of the other path, or false otherwise
272      */
273     public boolean isAtOrAbove( Path other );
274 
275     /**
276      * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isDecendantOf(Path) decendant of} the
277      * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
278      * isAncestor(other)</code>.
279      * 
280      * @param other the other path to compare with this path; may be null
281      * @return true if the paths are equivalent or if this path is considered a decendant of the other path, or false otherwise
282      */
283     public boolean isAtOrBelow( Path other );
284 
285     /**
286      * Determine whether this path is an ancestor of the supplied path. A path is considered an ancestor of another path if the
287      * the ancestor path appears in its entirety at the beginning of the decendant path, and where the decendant path contains at
288      * least one additional segment.
289      * 
290      * @param decendant the path that may be the decendant; may be null
291      * @return true if this path is an ancestor of the supplied path, or false otherwise
292      */
293     public boolean isAncestorOf( Path decendant );
294 
295     /**
296      * Determine whether this path is an decendant of the supplied path. A path is considered a decendant of another path if the
297      * the decendant path starts exactly with the entire ancestor path but contains at least one additional segment.
298      * 
299      * @param ancestor the path that may be the ancestor; may be null
300      * @return true if this path is an decendant of the supplied path, or false otherwise
301      */
302     public boolean isDecendantOf( Path ancestor );
303 
304     /**
305      * Return whether this path is an absolute path. A path is either relative or {@link #isAbsolute() absolute}. An absolute path
306      * starts with a "/".
307      * 
308      * @return true if the path is absolute, or false otherwise
309      */
310     public boolean isAbsolute();
311 
312     /**
313      * Return whether this path is normalized and contains no unnecessary "." segments and as few ".." segments as possible. For
314      * example, the path "../a" is normalized, while "/a/b/c/../d" is not normalized.
315      * 
316      * @return true if this path is normalized, or false otherwise
317      */
318     public boolean isNormalized();
319 
320     /**
321      * Get a normalized path with as many ".." segments and all "." resolved. The relative path ".", however, will return itself
322      * as the normalized path, since it cannot be resolved any further.
323      * 
324      * @return the normalized path, or this object if this path is already normalized
325      * @throws InvalidPathException if the normalized form would result in a path with negative length (e.g., "/a/../../..")
326      */
327     public Path getNormalizedPath();
328 
329     /**
330      * Get the canonical form of this path. A canonical path has is {@link #isAbsolute() absolute} and {@link #isNormalized()}.
331      * 
332      * @return the canonical path, or this object if it is already in its canonical form
333      * @throws InvalidPathException if the path is not absolute and cannot be canonicalized
334      */
335     public Path getCanonicalPath();
336 
337     /**
338      * Obtain a path that is relative to the root node. This is equivalent to calling {@link #relativeTo(Path)} with the root
339      * path.
340      * 
341      * @return the relative path from the root node; never null
342      */
343     public Path relativeToRoot();
344 
345     /**
346      * Get a relative path from the supplied path to this path.
347      * 
348      * @param startingPath the path specifying the starting point for the new relative path; may not be null
349      * @return the relative path
350      * @throws IllegalArgumentException if the supplied path is null
351      * @throws PathNotFoundException if both this path and the supplied path are not absolute
352      */
353     public Path relativeTo( Path startingPath );
354 
355     /**
356      * Get the absolute path by resolving the supplied relative (non-absolute) path against this absolute path.
357      * 
358      * @param relativePath the relative path that is to be resolved against this path
359      * @return the absolute and normalized path resolved from this path and the supplied absolute path
360      * @throws IllegalArgumentException if the supplied path is null
361      * @throws InvalidPathException if the this path is not absolute or if the supplied path is not relative.
362      */
363     public Path resolve( Path relativePath );
364 
365     /**
366      * Get the absolute path by resolving this relative (non-absolute) path against the supplied absolute path.
367      * 
368      * @param absolutePath the absolute path to which this relative path should be resolve
369      * @return the absolute path resolved from this path and the supplied absolute path
370      * @throws IllegalArgumentException if the supplied path is null
371      * @throws InvalidPathException if the supplied path is not absolute or if this path is not relative.
372      */
373     public Path resolveAgainst( Path absolutePath );
374 
375     /**
376      * Return the path to the parent, or this path if it is the {@link #isRoot() root}. This is an efficient operation that does
377      * not require copying any data.
378      * 
379      * @return the parent path, or this null if it is already the root
380      */
381     public Path getParent();
382 
383     /**
384      * Return the path to the ancestor of the supplied degree. An ancestor of degree <code>x</code> is the path that is <code>x
385      * </code>
386      * levels up along the path. For example, <code>degree = 0</code> returns this path, while <code>degree = 1</code> returns the
387      * parent of this path, <code>degree = 2</code> returns the grandparent of this path, and so on. Note that the result may be
388      * unexpected if this path is not {@link #isNormalized() normalized}, as a non-normalized path contains ".." and "." segments.
389      * 
390      * @param degree
391      * @return the ancestor of the supplied degree
392      * @throws IllegalArgumentException if the degree is negative
393      * @throws InvalidPathException if the degree is greater than the {@link #size() length} of this path
394      */
395     public Path getAncestor( int degree );
396 
397     /**
398      * Determine whether this path and the supplied path have the same immediate ancestor. In other words, this method determines
399      * whether the node represented by this path is a sibling of the node represented by the supplied path.
400      * 
401      * @param that the other path
402      * @return true if this path and the supplied path have the same immediate ancestor.
403      * @throws IllegalArgumentException if the supplied path is null
404      */
405     public boolean hasSameAncestor( Path that );
406 
407     /**
408      * Find the lowest common ancestor of this path and the supplied path.
409      * 
410      * @param that the other path
411      * @return the lowest common ancestor, which may be the root path if there is no other.
412      * @throws IllegalArgumentException if the supplied path is null
413      */
414     public Path getCommonAncestor( Path that );
415 
416     /**
417      * Get the last segment in this path.
418      * 
419      * @return the last segment, or null if the path is empty
420      */
421     public Segment getLastSegment();
422 
423     /**
424      * Determine if the path's {@link #getLastSegment()} has the supplied name and no {@link Segment#getIndex() SNS index}.
425      * 
426      * @param nameOfLastSegment the name
427      * @return return true if the supplied name is the name in the path's last segment (and there is no SNS index), or false
428      *         otherwise
429      */
430     public boolean endsWith( Name nameOfLastSegment );
431 
432     /**
433      * Determine if the path's {@link #getLastSegment()} has the supplied name and {@link Segment#getIndex() SNS index}.
434      * 
435      * @param nameOfLastSegment the name
436      * @param snsIndex the SNS index
437      * @return return true if the path's last segment has the supplied name and SNS, or false otherwise
438      */
439     public boolean endsWith( Name nameOfLastSegment,
440                              int snsIndex );
441 
442     /**
443      * Get the segment at the supplied index.
444      * 
445      * @param index the index
446      * @return the segment
447      * @throws IndexOutOfBoundsException if the index is out of bounds
448      */
449     public Segment getSegment( int index );
450 
451     /**
452      * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive). This is equivalent to
453      * calling <code>path.subpath(beginIndex,path.size()-1)</code>.
454      * 
455      * @param beginIndex the beginning index, inclusive.
456      * @return the specified subpath
457      * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger than the length of this <code>
458      *            Path</code>
459      *            object
460      */
461     public Path subpath( int beginIndex );
462 
463     /**
464      * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive) and the <code>endIndex
465      * </code> index
466      * (exclusive).
467      * 
468      * @param beginIndex the beginning index, inclusive.
469      * @param endIndex the ending index, exclusive.
470      * @return the specified subpath
471      * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or <code>endIndex</code> is larger than
472      *            the length of this <code>Path</code> object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
473      */
474     public Path subpath( int beginIndex,
475                          int endIndex );
476 
477     /**
478      * {@inheritDoc}
479      */
480     public Iterator<Segment> iterator();
481 
482     /**
483      * Return an iterator that walks the paths from the root path down to this path. This method always returns at least one path
484      * (the root returns an iterator containing itself).
485      * 
486      * @return the path iterator; never null
487      */
488     public Iterator<Path> pathsFromRoot();
489 
490     /**
491      * Obtain a copy of the segments in this path. None of the segments are encoded.
492      * 
493      * @return the array of segments as a copy
494      */
495     public Segment[] getSegmentsArray();
496 
497     /**
498      * Get an unmodifiable list of the path segments.
499      * 
500      * @return the unmodifiable list of path segments; never null
501      */
502     public List<Segment> getSegmentsList();
503 
504 }