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      * String form of the delimiter used to separate segments within a path.
125      */
126     public static final String DELIMITER_STR = new String(new char[] {DELIMITER});
127 
128     /**
129      * String representation of the segment that references a parent.
130      */
131     public static final String PARENT = "..";
132 
133     /**
134      * String representation of the segment that references the same segment.
135      */
136     public static final String SELF = ".";
137 
138     /**
139      * The default index for a {@link Segment}.
140      */
141     public static final int DEFAULT_INDEX = 1;
142 
143     /**
144      * Representation of the segments that occur within a path.
145      * 
146      * @author Randall Hauch
147      */
148     @Immutable
149     public static interface Segment extends Cloneable, Comparable<Segment>, Serializable, Readable {
150 
151         /**
152          * Get the name component of this segment.
153          * 
154          * @return the segment's name
155          */
156         public Name getName();
157 
158         /**
159          * Get the index for this segment, which will be 1 by default.
160          * 
161          * @return the index
162          */
163         public int getIndex();
164 
165         /**
166          * Return whether this segment has an index.
167          * 
168          * @return true if this segment has an index, or false otherwise.
169          */
170         public boolean hasIndex();
171 
172         /**
173          * Return whether this segment is a self-reference.
174          * 
175          * @return true if the segment is a self-reference, or false otherwise.
176          */
177         public boolean isSelfReference();
178 
179         /**
180          * Return whether this segment is a reference to a parent.
181          * 
182          * @return true if the segment is a parent-reference, or false otherwise.
183          */
184         public boolean isParentReference();
185 
186         /**
187          * Get the raw string form of the segment using the {@link Path#NO_OP_ENCODER no-op encoder}. This is equivalent to
188          * calling <code>getString(Path.NO_OP_ENCODER)</code>.
189          * 
190          * @return the un-encoded string
191          * @see #getString(TextEncoder)
192          */
193         public String getUnencodedString();
194     }
195 
196     /**
197      * Singleton instance of the name referencing a self, provided as a convenience.
198      */
199     public static final Name SELF_NAME = new BasicName(null, SELF);
200 
201     /**
202      * Singleton instance of the name referencing a parent, provided as a convenience.
203      */
204     public static final Name PARENT_NAME = new BasicName(null, PARENT);
205 
206     /**
207      * Singleton instance of the path segment referencing a parent, provided as a convenience.
208      */
209     public static final Path.Segment SELF_SEGMENT = new BasicPathSegment(SELF_NAME);
210 
211     /**
212      * Singleton instance of the path segment referencing a parent, provided as a convenience.
213      */
214     public static final Path.Segment PARENT_SEGMENT = new BasicPathSegment(PARENT_NAME);
215 
216     /**
217      * Return the number of segments in this path.
218      * 
219      * @return the number of path segments
220      */
221     public int size();
222 
223     /**
224      * Return whether this path represents the root path.
225      * 
226      * @return true if this path is the root path, or false otherwise
227      */
228     public boolean isRoot();
229 
230     /**
231      * Determine whether this path represents the same as the supplied path. This is equivalent to calling <code>
232      * this.compareTo(other) == 0 </code>.
233      * 
234      * @param other the other path to compare with this path; may be null
235      * @return true if the paths are equivalent, or false otherwise
236      */
237     public boolean isSameAs( Path other );
238 
239     /**
240      * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isAncestorOf(Path) ancestor of} the
241      * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
242      * isAncestor(other)</code>, and is a convenience method that is identical to calling <code>other.isAtOrBelow(this)</code>.
243      * 
244      * @param other the other path to compare with this path; may be null
245      * @return true if the paths are equivalent or if this path is considered an ancestor of the other path, or false otherwise
246      */
247     public boolean isAtOrAbove( Path other );
248 
249     /**
250      * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isDecendantOf(Path) decendant of} the
251      * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
252      * isAncestor(other)</code>.
253      * 
254      * @param other the other path to compare with this path; may be null
255      * @return true if the paths are equivalent or if this path is considered a decendant of the other path, or false otherwise
256      */
257     public boolean isAtOrBelow( Path other );
258 
259     /**
260      * Determine whether this path is an ancestor of the supplied path. A path is considered an ancestor of another path if the
261      * the ancestor path appears in its entirety at the beginning of the decendant path, and where the decendant path contains at
262      * least one additional segment.
263      * 
264      * @param decendant the path that may be the decendant; may be null
265      * @return true if this path is an ancestor of the supplied path, or false otherwise
266      */
267     public boolean isAncestorOf( Path decendant );
268 
269     /**
270      * Determine whether this path is an decendant of the supplied path. A path is considered a decendant of another path if the
271      * the decendant path starts exactly with the entire ancestor path but contains at least one additional segment.
272      * 
273      * @param ancestor the path that may be the ancestor; may be null
274      * @return true if this path is an decendant of the supplied path, or false otherwise
275      */
276     public boolean isDecendantOf( Path ancestor );
277 
278     /**
279      * Return whether this path is an absolute path. A path is either relative or {@link #isAbsolute() absolute}. An absolute path
280      * starts with a "/".
281      * 
282      * @return true if the path is absolute, or false otherwise
283      */
284     public boolean isAbsolute();
285 
286     /**
287      * Return whether this path is normalized and contains no unnecessary "." segments and as few ".." segments as possible. For
288      * example, the path "../a" is normalized, while "/a/b/c/../d" is not normalized.
289      * 
290      * @return true if this path is normalized, or false otherwise
291      */
292     public boolean isNormalized();
293 
294     /**
295      * Get a normalized path with as many ".." segments and all "." resolved. The relative path ".", however, will return itself
296      * as the normalized path, since it cannot be resolved any further.
297      * 
298      * @return the normalized path, or this object if this path is already normalized
299      * @throws InvalidPathException if the normalized form would result in a path with negative length (e.g., "/a/../../..")
300      */
301     public Path getNormalizedPath();
302 
303     /**
304      * Get the canonical form of this path. A canonical path has is {@link #isAbsolute() absolute} and {@link #isNormalized()}.
305      * 
306      * @return the canonical path, or this object if it is already in its canonical form
307      * @throws InvalidPathException if the path is not absolute and cannot be canonicalized
308      */
309     public Path getCanonicalPath();
310 
311     /**
312      * Obtain a path that is relative to the root node. This is equivalent to calling {@link #relativeTo(Path)} with the root
313      * path.
314      * 
315      * @return the relative path from the root node; never null
316      */
317     public Path relativeToRoot();
318 
319     /**
320      * Get a relative path from the supplied path to this path.
321      * 
322      * @param startingPath the path specifying the starting point for the new relative path; may not be null
323      * @return the relative path
324      * @throws IllegalArgumentException if the supplied path is null
325      * @throws PathNotFoundException if both this path and the supplied path are not absolute
326      */
327     public Path relativeTo( Path startingPath );
328 
329     /**
330      * Get the absolute path by resolving the supplied relative (non-absolute) path against this absolute path.
331      * 
332      * @param relativePath the relative path that is to be resolved against this path
333      * @return the absolute and normalized path resolved from this path and the supplied absolute path
334      * @throws IllegalArgumentException if the supplied path is null
335      * @throws InvalidPathException if the this path is not absolute or if the supplied path is not relative.
336      */
337     public Path resolve( Path relativePath );
338 
339     /**
340      * Get the absolute path by resolving this relative (non-absolute) path against the supplied absolute path.
341      * 
342      * @param absolutePath the absolute path to which this relative path should be resolve
343      * @return the absolute path resolved from this path and the supplied absolute path
344      * @throws IllegalArgumentException if the supplied path is null
345      * @throws InvalidPathException if the supplied path is not absolute or if this path is not relative.
346      */
347     public Path resolveAgainst( Path absolutePath );
348 
349     /**
350      * Return the path to the parent, or this path if it is the {@link #isRoot() root}. This is an efficient operation that does
351      * not require copying any data.
352      * 
353      * @return the parent path, or this null if it is already the root
354      */
355     public Path getParent();
356 
357     /**
358      * Return the path to the ancestor of the supplied degree. An ancestor of degree <code>x</code> is the path that is <code>x
359      * </code> levels up along the path. For example, <code>degree = 0</code> returns this path, while <code>degree = 1</code>
360      * returns the parent of this path, <code>degree = 2</code> returns the grandparent of this path, and so on. Note that the
361      * result may be unexpected if this path is not {@link #isNormalized() normalized}, as a non-normalized path contains ".." and
362      * "." segments.
363      * 
364      * @param degree
365      * @return the ancestor of the supplied degree
366      * @throws IllegalArgumentException if the degree is negative
367      * @throws InvalidPathException if the degree is greater than the {@link #size() length} of this path
368      */
369     public Path getAncestor( int degree );
370 
371     /**
372      * Determine whether this path and the supplied path have the same immediate ancestor. In other words, this method determines
373      * whether the node represented by this path is a sibling of the node represented by the supplied path.
374      * 
375      * @param that the other path
376      * @return true if this path and the supplied path have the same immediate ancestor.
377      * @throws IllegalArgumentException if the supplied path is null
378      */
379     public boolean hasSameAncestor( Path that );
380 
381     /**
382      * Find the lowest common ancestor of this path and the supplied path.
383      * 
384      * @param that the other path
385      * @return the lowest common ancestor, which may be the root path if there is no other.
386      * @throws IllegalArgumentException if the supplied path is null
387      */
388     public Path getCommonAncestor( Path that );
389 
390     /**
391      * Get the last segment in this path.
392      * 
393      * @return the last segment, or null if the path is empty
394      */
395     public Segment getLastSegment();
396 
397     /**
398      * Determine if the path's {@link #getLastSegment()} has the supplied name and no {@link Segment#getIndex() SNS index}.
399      * 
400      * @param nameOfLastSegment the name
401      * @return return true if the supplied name is the name in the path's last segment (and there is no SNS index), or false
402      *         otherwise
403      */
404     public boolean endsWith( Name nameOfLastSegment );
405 
406     /**
407      * Determine if the path's {@link #getLastSegment()} has the supplied name and {@link Segment#getIndex() SNS index}.
408      * 
409      * @param nameOfLastSegment the name
410      * @param snsIndex the SNS index
411      * @return return true if the path's last segment has the supplied name and SNS, or false otherwise
412      */
413     public boolean endsWith( Name nameOfLastSegment,
414                              int snsIndex );
415 
416     /**
417      * Get the segment at the supplied index.
418      * 
419      * @param index the index
420      * @return the segment
421      * @throws IndexOutOfBoundsException if the index is out of bounds
422      */
423     public Segment getSegment( int index );
424 
425     /**
426      * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive). This is equivalent to
427      * calling <code>path.subpath(beginIndex,path.size()-1)</code>.
428      * 
429      * @param beginIndex the beginning index, inclusive.
430      * @return the specified subpath
431      * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger than the length of this <code>
432      *            Path</code> object
433      */
434     public Path subpath( int beginIndex );
435 
436     /**
437      * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive) and the <code>endIndex
438      * </code> index (exclusive).
439      * 
440      * @param beginIndex the beginning index, inclusive.
441      * @param endIndex the ending index, exclusive.
442      * @return the specified subpath
443      * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or <code>endIndex</code> is larger than
444      *            the length of this <code>Path</code> object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
445      */
446     public Path subpath( int beginIndex,
447                          int endIndex );
448 
449     /**
450      * {@inheritDoc}
451      */
452     public Iterator<Segment> iterator();
453 
454     /**
455      * Return an iterator that walks the paths from the root path down to this path. This method always returns at least one path
456      * (the root returns an iterator containing itself).
457      * 
458      * @return the path iterator; never null
459      */
460     public Iterator<Path> pathsFromRoot();
461 
462     /**
463      * Obtain a copy of the segments in this path. None of the segments are encoded.
464      * 
465      * @return the array of segments as a copy
466      */
467     public Segment[] getSegmentsArray();
468 
469     /**
470      * Get an unmodifiable list of the path segments.
471      * 
472      * @return the unmodifiable list of path segments; never null
473      */
474     public List<Segment> getSegmentsList();
475 
476 }