001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006    * See the AUTHORS.txt file in the distribution for a full listing of 
007    * individual contributors.
008     *
009     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     *
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.graph.property;
025    
026    import java.io.Serializable;
027    import java.util.Iterator;
028    import java.util.List;
029    import net.jcip.annotations.Immutable;
030    import org.jboss.dna.common.text.Jsr283Encoder;
031    import org.jboss.dna.common.text.NoOpEncoder;
032    import org.jboss.dna.common.text.TextDecoder;
033    import org.jboss.dna.common.text.TextEncoder;
034    import org.jboss.dna.common.text.UrlEncoder;
035    import org.jboss.dna.graph.property.basic.BasicName;
036    import org.jboss.dna.graph.property.basic.BasicPathSegment;
037    
038    /**
039     * An object representation of a node path within a repository.
040     * <p>
041     * A path consists of zero or more segments that can contain any characters, although the string representation may require some
042     * characters to be encoded. For example, if a path contains a segment with a forward slash, then this forward slash must be
043     * escaped when writing the whole path to a string (since a forward slash is used as the {@link #DELIMITER delimiter} between
044     * segments).
045     * </p>
046     * <p>
047     * Because of this encoding and decoding issue, there is no standard representation of a path as a string. Instead, this class
048     * uses {@link TextEncoder text encoders} to escape certain characters when writing to a string or unescaping the string
049     * representation. These encoders and used only with individual segments, and therefore are not used to encode the
050     * {@link #DELIMITER delimiter}. Three standard encoders are provided, although others can certainly be used:
051     * <ul>
052     * <li>{@link #JSR283_ENCODER Jsr283Encoder} - an encoder and decoder that is compliant with <a
053     * href="http://jcp.org/en/jsr/detail?id=283">JSR-283</a> by converting the reserved characters (namely '*', '/', ':', '[', ']'
054     * and '|') to their unicode equivalent.</td>
055     * </li>
056     * <li>{@link #URL_ENCODER UrlEncoder} - an encoder and decoder that is useful for converting text to be used within a URL, as
057     * defined by Section 2.3 of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>. This encoder does encode many characters
058     * (including '`', '@', '#', '$', '^', '&', '{', '[', '}', ']', '|', ':', ';', '\', '"', '<', ',', '>', '?', '/', and ' '), while
059     * others are not encoded (including '-', '_', '.', '!', '~', '*', '\', ''', '(', and ')'). Note that only the '*' character is
060     * the only character reserved by JSR-283 that is not encoded by the URL encoder.</li>
061     * <li>{@link #NO_OP_ENCODER NoOpEncoder} - an {@link TextEncoder encoder} implementation that does nothing.</li>
062     * </ul>
063     * </p>
064     * <p>
065     * This class simplifies working with paths and using a <code>Path</code> is often more efficient that processing and
066     * manipulating the equivalent <code>String</code>. This class can easily {@link #iterator() iterate} over the segments, return
067     * the {@link #size() number of segments}, {@link #compareTo(Path) compare} with other paths, {@link #resolve(Path) resolve}
068     * relative paths, return the {@link #getParent() ancestor (or parent)}, determine whether one path is an
069     * {@link #isAncestorOf(Path) ancestor} or {@link #isDecendantOf(Path) decendent} of another path, and
070     * {@link #getCommonAncestor(Path) finding a common ancestor}.
071     * </p>
072     * 
073     * @author Randall Hauch
074     * @author John Verhaeg
075     */
076    @Immutable
077    public interface Path extends Comparable<Path>, Iterable<Path.Segment>, Serializable, Readable {
078    
079        /**
080         * The text encoder that does nothing.
081         */
082        public static final TextEncoder NO_OP_ENCODER = new NoOpEncoder();
083    
084        /**
085         * The text encoder that encodes according to JSR-283.
086         */
087        public static final TextEncoder JSR283_ENCODER = new Jsr283Encoder();
088    
089        /**
090         * The text encoder that encodes text according to the rules of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
091         */
092        public static final TextEncoder URL_ENCODER = new UrlEncoder().setSlashEncoded(true);
093    
094        /**
095         * The text decoder that does nothing.
096         */
097        public static final TextDecoder NO_OP_DECODER = new NoOpEncoder();
098    
099        /**
100         * The text decoder that decodes according to JSR-283.
101         */
102        public static final TextDecoder JSR283_DECODER = new Jsr283Encoder();
103    
104        /**
105         * The text decoder that decodes text according to the rules of <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
106         */
107        public static final TextDecoder URL_DECODER = new UrlEncoder().setSlashEncoded(true);
108    
109        /**
110         * The default text encoder to be used when none is otherwise specified. This is currently the {@link #JSR283_ENCODER JSR-283
111         * encoder}.
112         */
113        public static final TextEncoder DEFAULT_ENCODER = JSR283_ENCODER;
114    
115        /**
116         * The default text decoder to be used when none is otherwise specified. This is currently the {@link #JSR283_ENCODER JSR-283
117         * encoder}.
118         */
119        public static final TextDecoder DEFAULT_DECODER = JSR283_DECODER;
120    
121        /**
122         * The delimiter character used to separate segments within a path.
123         */
124        public static final char DELIMITER = '/';
125    
126        /**
127         * String form of the delimiter used to separate segments within a path.
128         */
129        public static final String DELIMITER_STR = new String(new char[] {DELIMITER});
130    
131        /**
132         * String representation of the segment that references a parent.
133         */
134        public static final String PARENT = "..";
135    
136        /**
137         * String representation of the segment that references the same segment.
138         */
139        public static final String SELF = ".";
140    
141        /**
142         * The default index for a {@link Segment}.
143         */
144        public static final int DEFAULT_INDEX = 1;
145    
146        /**
147         * Representation of the segments that occur within a path.
148         * 
149         * @author Randall Hauch
150         */
151        @Immutable
152        public static interface Segment extends Cloneable, Comparable<Segment>, Serializable, Readable {
153    
154            /**
155             * Get the name component of this segment.
156             * 
157             * @return the segment's name
158             */
159            public Name getName();
160    
161            /**
162             * Get the index for this segment, which will be 1 by default.
163             * 
164             * @return the index
165             */
166            public int getIndex();
167    
168            /**
169             * Return whether this segment has an index.
170             * 
171             * @return true if this segment has an index, or false otherwise.
172             */
173            public boolean hasIndex();
174    
175            /**
176             * Return whether this segment is a self-reference.
177             * 
178             * @return true if the segment is a self-reference, or false otherwise.
179             */
180            public boolean isSelfReference();
181    
182            /**
183             * Return whether this segment is a reference to a parent.
184             * 
185             * @return true if the segment is a parent-reference, or false otherwise.
186             */
187            public boolean isParentReference();
188    
189            /**
190             * Get the raw string form of the segment using the {@link Path#NO_OP_ENCODER no-op encoder}. This is equivalent to
191             * calling <code>getString(Path.NO_OP_ENCODER)</code>.
192             * 
193             * @return the un-encoded string
194             * @see #getString(TextEncoder)
195             */
196            public String getUnencodedString();
197        }
198    
199        /**
200         * Singleton instance of the name referencing a self, provided as a convenience.
201         */
202        public static final Name SELF_NAME = new BasicName(null, SELF);
203    
204        /**
205         * Singleton instance of the name referencing a parent, provided as a convenience.
206         */
207        public static final Name PARENT_NAME = new BasicName(null, PARENT);
208    
209        /**
210         * Singleton instance of the path segment referencing a parent, provided as a convenience.
211         */
212        public static final Path.Segment SELF_SEGMENT = new BasicPathSegment(SELF_NAME);
213    
214        /**
215         * Singleton instance of the path segment referencing a parent, provided as a convenience.
216         */
217        public static final Path.Segment PARENT_SEGMENT = new BasicPathSegment(PARENT_NAME);
218    
219        /**
220         * Return the number of segments in this path.
221         * 
222         * @return the number of path segments
223         */
224        public int size();
225    
226        /**
227         * Return whether this path represents the root path.
228         * 
229         * @return true if this path is the root path, or false otherwise
230         */
231        public boolean isRoot();
232    
233        /**
234         * Determine whether this path represents the same as the supplied path. This is equivalent to calling <code>
235         * this.compareTo(other) == 0 </code>.
236         * 
237         * @param other the other path to compare with this path; may be null
238         * @return true if the paths are equivalent, or false otherwise
239         */
240        public boolean isSameAs( Path other );
241    
242        /**
243         * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isAncestorOf(Path) ancestor of} the
244         * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
245         * isAncestor(other)</code>, and is a convenience method that is identical to calling <code>other.isAtOrBelow(this)</code>.
246         * 
247         * @param other the other path to compare with this path; may be null
248         * @return true if the paths are equivalent or if this path is considered an ancestor of the other path, or false otherwise
249         */
250        public boolean isAtOrAbove( Path other );
251    
252        /**
253         * Determine whether this path is the {@link #isSameAs(Path) same as} to or a {@link #isDecendantOf(Path) decendant of} the
254         * supplied path. This method is equivalent to (but may be more efficient than) calling <code>isSame(other) ||
255         * isAncestor(other)</code>.
256         * 
257         * @param other the other path to compare with this path; may be null
258         * @return true if the paths are equivalent or if this path is considered a decendant of the other path, or false otherwise
259         */
260        public boolean isAtOrBelow( Path other );
261    
262        /**
263         * Determine whether this path is an ancestor of the supplied path. A path is considered an ancestor of another path if the
264         * the ancestor path appears in its entirety at the beginning of the decendant path, and where the decendant path contains at
265         * least one additional segment.
266         * 
267         * @param decendant the path that may be the decendant; may be null
268         * @return true if this path is an ancestor of the supplied path, or false otherwise
269         */
270        public boolean isAncestorOf( Path decendant );
271    
272        /**
273         * Determine whether this path is an decendant of the supplied path. A path is considered a decendant of another path if the
274         * the decendant path starts exactly with the entire ancestor path but contains at least one additional segment.
275         * 
276         * @param ancestor the path that may be the ancestor; may be null
277         * @return true if this path is an decendant of the supplied path, or false otherwise
278         */
279        public boolean isDecendantOf( Path ancestor );
280    
281        /**
282         * Return whether this path is an absolute path. A path is either relative or {@link #isAbsolute() absolute}. An absolute path
283         * starts with a "/".
284         * 
285         * @return true if the path is absolute, or false otherwise
286         */
287        public boolean isAbsolute();
288    
289        /**
290         * Return whether this path is normalized and contains no "." segments and as few ".." segments as possible. For example, the
291         * path "../a" is normalized, while "/a/b/c/../d" is not normalized.
292         * 
293         * @return true if this path is normalized, or false otherwise
294         */
295        public boolean isNormalized();
296    
297        /**
298         * Get a normalized path with as many ".." segments and all "." resolved. The relative path ".", however, will return itself
299         * as the normalized path, since it cannot be resolved any further.
300         * 
301         * @return the normalized path, or this object if this path is already normalized
302         * @throws InvalidPathException if the normalized form would result in a path with negative length (e.g., "/a/../../..")
303         */
304        public Path getNormalizedPath();
305    
306        /**
307         * Get the canonical form of this path. A canonical path has is {@link #isAbsolute() absolute} and {@link #isNormalized()}.
308         * 
309         * @return the canonical path, or this object if it is already in its canonical form
310         * @throws InvalidPathException if the path is not absolute and cannot be canonicalized
311         */
312        public Path getCanonicalPath();
313    
314        /**
315         * Get a relative path from the supplied path to this path.
316         * 
317         * @param startingPath the path specifying the starting point for the new relative path; may not be null
318         * @return the relative path
319         * @throws IllegalArgumentException if the supplied path is null
320         * @throws PathNotFoundException if both this path and the supplied path are not absolute
321         */
322        public Path relativeTo( Path startingPath );
323    
324        /**
325         * Get the absolute path by resolving the supplied relative (non-absolute) path against this absolute path.
326         * 
327         * @param relativePath the relative path that is to be resolved against this path
328         * @return the absolute and normalized path resolved from this path and the supplied absolute path
329         * @throws IllegalArgumentException if the supplied path is null
330         * @throws InvalidPathException if the this path is not absolute or if the supplied path is not relative.
331         */
332        public Path resolve( Path relativePath );
333    
334        /**
335         * Get the absolute path by resolving this relative (non-absolute) path against the supplied absolute path.
336         * 
337         * @param absolutePath the absolute path to which this relative path should be resolve
338         * @return the absolute path resolved from this path and the supplied absolute path
339         * @throws IllegalArgumentException if the supplied path is null
340         * @throws InvalidPathException if the supplied path is not absolute or if this path is not relative.
341         */
342        public Path resolveAgainst( Path absolutePath );
343    
344        /**
345         * Return the path to the parent, or this path if it is the {@link #isRoot() root}. This is an efficient operation that does
346         * not require copying any data.
347         * 
348         * @return the parent path, or this null if it is already the root
349         */
350        public Path getParent();
351    
352        /**
353         * Return the path to the ancestor of the supplied degree. An ancestor of degree <code>x</code> is the path that is <code>x
354         * </code> levels up along the path. For example, <code>degree = 0</code> returns this path, while <code>degree = 1</code>
355         * returns the parent of this path, <code>degree = 2</code> returns the grandparent of this path, and so on. Note that the
356         * result may be unexpected if this path is not {@link #isNormalized() normalized}, as a non-normalized path contains ".." and
357         * "." segments.
358         * 
359         * @param degree
360         * @return the ancestor of the supplied degree
361         * @throws IllegalArgumentException if the degree is negative
362         * @throws InvalidPathException if the degree is greater than the {@link #size() length} of this path
363         */
364        public Path getAncestor( int degree );
365    
366        /**
367         * Determine whether this path and the supplied path have the same immediate ancestor. In other words, this method determines
368         * whether the node represented by this path is a sibling of the node represented by the supplied path.
369         * 
370         * @param that the other path
371         * @return true if this path and the supplied path have the same immediate ancestor.
372         * @throws IllegalArgumentException if the supplied path is null
373         */
374        public boolean hasSameAncestor( Path that );
375    
376        /**
377         * Find the lowest common ancestor of this path and the supplied path.
378         * 
379         * @param that the other path
380         * @return the lowest common ancestor, which may be the root path if there is no other.
381         * @throws IllegalArgumentException if the supplied path is null
382         */
383        public Path getCommonAncestor( Path that );
384    
385        /**
386         * Get the last segment in this path.
387         * 
388         * @return the last segment, or null if the path is empty
389         */
390        public Segment getLastSegment();
391    
392        /**
393         * Get the segment at the supplied index.
394         * 
395         * @param index the index
396         * @return the segment
397         * @throws IndexOutOfBoundsException if the index is out of bounds
398         */
399        public Segment getSegment( int index );
400    
401        /**
402         * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive). This is equivalent to
403         * calling <code>path.subpath(beginIndex,path.size()-1)</code>.
404         * 
405         * @param beginIndex the beginning index, inclusive.
406         * @return the specified subpath
407         * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger than the length of this <code>
408         *            Path</code> object
409         */
410        public Path subpath( int beginIndex );
411    
412        /**
413         * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive) and the <code>endIndex
414         * </code> index (exclusive).
415         * 
416         * @param beginIndex the beginning index, inclusive.
417         * @param endIndex the ending index, exclusive.
418         * @return the specified subpath
419         * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or <code>endIndex</code> is larger than
420         *            the length of this <code>Path</code> object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
421         */
422        public Path subpath( int beginIndex,
423                             int endIndex );
424    
425        /**
426         * {@inheritDoc}
427         */
428        public Iterator<Segment> iterator();
429    
430        /**
431         * Return an iterator that walks the paths from the root path down to this path. This method always returns at least one path
432         * (the root returns an iterator containing itself).
433         * 
434         * @return the path iterator; never null
435         */
436        public Iterator<Path> pathsFromRoot();
437    
438        /**
439         * Obtain a copy of the segments in this path. None of the segments are encoded.
440         * 
441         * @return the array of segments as a copy
442         */
443        public Segment[] getSegmentsArray();
444    
445        /**
446         * Get an unmodifiable list of the path segments.
447         * 
448         * @return the unmodifiable list of path segments; never null
449         */
450        public List<Segment> getSegmentsList();
451    
452    }