/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.spi; import java.io.Serializable; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; /** * The <code>Path</code> interface defines the SPI level representation of * a JCR path. It consists of an ordered list of {@link Path.Element} objects * and is immutable. * <p> * A {@link Path.Element} is either {@link Path.Element#denotesName() named} * or one of the following special elements: * <ul> * <li>the {@link Element#denotesCurrent() current} element (Notation: "."),</li> * <li>the {@link Element#denotesParent() parent} element (Notation: ".."),</li> * <li>the {@link Element#denotesRoot() root} element (Notation: {}), which can * only occur as the first element in a path, or</li> * <li>an {@link Element#denotesIdentifier() identifier} element, which can * only occur as the first element in a path.</li> * </ul> * <p> * A <code>Path</code> is defined to have the following characteristics: * <p> * <strong>Equality:</strong><br> * Two paths are equal if they consist of the same elements. * <p> * <strong>Length:</strong><br> * The {@link Path#getLength() length} of a path is the number of its elements. * <p> * <strong>Depth:</strong><br> * The {@link Path#getDepth() depth} of a path is * <ul> * <li>0 for the root path,</li> * <li>0 for a path consisting of an identifier element only,</li> * <li>0 for the path consisting of the current element only,</li> * <li>-1 for the path consisting of the parent element only,</li> * <li>1 for the path consisting of a single named element,</li> * <li>depth(P) + depth(Q) for the path P/Q.</li> * </ul> * <p> * The depth of a normalized absolute path equals its length minus 1. * <p> * <strong>Absolute vs. Relative</strong><br> * A path can be absolute or relative:<br> * A path {@link #isAbsolute() is absolute} if its first element is the root * or an identifier element. A path is relative if it is not absolute. * <ul> * <li>An absolute path is valid if its depth is greater or equals 0. A relative * path is always valid.</li> * <li>Two absolute paths are equivalent if "they refer to the same item in the * hierarchy".</li> * <li>Two relative paths P and Q are equivalent if for every absolute path R such * that R/P and R/Q are valid, R/P and R/Q are equivalent.</li> * </ul> * <p> * <strong>Normalization:</strong><br> * A path P {@link Path#isNormalized() is normalized} if P has minimal length * amongst the set of all paths Q which are equivalent to P.<br> * This means that '.' and '..' elements are resolved as much as possible. * An absolute path it is normalized if it is not identifier-based and * contains no current or parent elements. The normalization of a path * is unique.<br> * <p> * <strong>Equivalence:</strong><br> * Path P is {@link Path#isEquivalentTo(Path) equivalent} to path Q (in the above sense) * if the normalization of P is equal to the normalization of Q. This is * an equivalence relation (i.e. reflexive, transitive, * and symmetric). * <p> * <strong>Canonical Paths:</strong><br> * A path {@link Path#isCanonical() is canonical} if its absolute and normalized. * <p> * <strong>Hierarchical Relationship:</strong><br> * The ancestor relationship is a strict partial order (i.e. irreflexive, transitive, * and asymmetric). Path P is a direct ancestor of path Q if P is equivalent to Q/.. * <br> * Path P is an {@link Path#isAncestorOf(Path) ancestor} of path Q if * <ul> * <li>P is a direct ancestor of Q or</li> * <li>P is a direct ancestor of some path S which is an ancestor of Q.</li> * </ul> * <p> * Path P is an {@link Path#isDescendantOf(Path) descendant} of path Q if * <ul> * <li>Path P is a descendant of path Q if Q is an ancestor of P.</li> * </ul> */ public interface Path extends Serializable { /** * Constant representing an undefined index value */ public static final int INDEX_UNDEFINED = 0; /** * Constant representing the default (initial) index value. */ public static final int INDEX_DEFAULT = 1; /** * Constant defining the depth of the root path */ public static final int ROOT_DEPTH = 0; /** * Delimiter used in order to concatenate the Path.Element objects * upon {@link Path#getString()}. */ public static final char DELIMITER = '\t'; /** * Returns the name of the last path element, or <code>null</code> * for an identifier. The names of the special root, current and parent * elements are "", "." and ".." in the default namespace. * * @return name of the last path element, or <code>null</code> */ Name getName(); /** * Returns the index of the last path element, or {@link #INDEX_UNDEFINED} * if the index is not defined or not applicable. The index of an * identifier or the special root, current or parent element is always * undefined. * * @return index of the last path element, or {@link #INDEX_UNDEFINED} */ int getIndex(); /** * Returns the normalized index of the last path element. The normalized * index of an element with an undefined index is {@link #INDEX_DEFAULT}. * * @return normalized index of the last path element */ int getNormalizedIndex(); /** * Returns the identifier of a single identifier element. Returns null * for non-identifier paths or identifier paths with other relative path * elements. * * @return identifier, or <code>null</code> */ String getIdentifier(); /** * Tests whether this is the root path, i.e. "/". * * @return <code>true</code> if this is the root path, * <code>false</code> otherwise. */ boolean denotesRoot(); /** * Test if this path consists of a single identifier element. * * @return <code>true</code> if this path is an identifier */ boolean denotesIdentifier(); /** * Checks if the last path element is the parent element (".."). * * @return <code>true</code> if the last path element is the parent element, * <code>false</code> otherwise */ boolean denotesParent(); /** * Checks if the last path element is the current element ("."). * * @return <code>true</code> if the last path element is the current element, * <code>false</code> otherwise */ boolean denotesCurrent(); /** * Checks if the last path element is a named and optionally indexed * element. * * @return <code>true</code> if the last path element is a named element, * <code>false</code> otherwise */ boolean denotesName(); /** * Test if this path represents an unresolved identifier-based path. * * @return <code>true</code> if this path represents an unresolved * identifier-based path. */ boolean isIdentifierBased(); /** * Tests whether this path is absolute, i.e. whether it starts with "/" or * is an identifier based path. * * @return true if this path is absolute; false otherwise. */ public boolean isAbsolute(); /** * Tests whether this path is canonical, i.e. whether it is absolute and * does not contain redundant elements such as "." and "..". * * @return true if this path is canonical; false otherwise. * @see #isAbsolute() */ public boolean isCanonical(); /** * Tests whether this path is normalized, i.e. whether it does not * contain redundant elements such as "." and "..". * <p> * Note that a normalized path can still contain ".." elements if they are * not redundant, e.g. "../../a/b/c" would be a normalized relative path, * whereas "../a/../../a/b/c" wouldn't (although they're semantically * equivalent). * * @return true if this path is normalized; false otherwise. * @see #getNormalizedPath() */ public boolean isNormalized(); /** * Returns the normalized path representation of this path. * <p> * If the path cannot be normalized (e.g. if an absolute path is normalized * that would result in a 'negative' path) a RepositoryException is thrown. * * @return a normalized path representation of this path. * @throws RepositoryException if the path cannot be normalized. * @see #isNormalized() */ public Path getNormalizedPath() throws RepositoryException; /** * Returns the canonical path representation of this path. * <p> * If the path is relative or cannot be normalized a RepositoryException * is thrown. * * @return a canonical path representation of this path. * @throws RepositoryException if this path can not be canonicalized * (e.g. if it is relative). */ public Path getCanonicalPath() throws RepositoryException; /** * Resolves the given path element against this path. If the given * element is absolute (i.e. the root or an identifier element), then * a path containing just that element is returned. Otherwise the * returned path consists of this path followed by the given element. * * @param element path element * @return resolved path */ Path resolve(Element element); /** * Resolves the given path against this path. If the given path is * absolute, then it is returned as-is. Otherwise the path is resolved * relative to this path and the resulting resolved path is returned. * * @param relative the path to be resolved * @return resolved path */ Path resolve(Path relative); /** * Computes the relative path from <code>this</code> absolute path to * <code>other</code>. * * @param other an absolute path. * @return the relative path from <code>this</code> path to <code>other</code> * path. * @throws RepositoryException if either <code>this</code> or * <code>other</code> path is not absolute. */ public Path computeRelativePath(Path other) throws RepositoryException; /** * Normalizes this path and returns the ancestor path of the specified * relative degree. * <p> * An ancestor of relative degree <i>x</i> is the path that is <i>x</i> * levels up along the path. * <ul> * <li><i>degree</i> = 0 returns this path. * <li><i>degree</i> = 1 returns the parent of this path. * <li><i>degree</i> = 2 returns the grandparent of this path. * <li>And so on to <i>degree</i> = <i>n</i>, where <i>n</i> is the depth * of this path, which returns the root path. * </ul> * <p> * If this path is relative the implementation may not be able to determine * if the ancestor at <code>degree</code> exists. Such an implementation * should properly build the ancestor (i.e. parent of .. is ../..) and * leave if it the caller to throw <code>PathNotFoundException</code>. * * @param degree the relative degree of the requested ancestor. * @return the normalized ancestor path of the specified degree. * @throws IllegalArgumentException if <code>degree</code> is negative. * @throws PathNotFoundException if the implementation is able to determine * that there is no ancestor of the specified degree. In case of this * being an absolute path, this would be the case if <code>degree</code> is * greater that the {@link #getDepth() depth} of this path. * @throws RepositoryException If the implementation is not able to determine * the ancestor of the specified degree for some other reason. */ public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException, RepositoryException; /** * Returns the number of ancestors of this path. This is the equivalent * of <code>{@link #getDepth()}</code> in case of a absolute path. * For relative path the number of ancestors cannot be determined and * -1 should be returned. * * @return the number of ancestors of this path or -1 if the number of * ancestors cannot be determined. * @see #getDepth() * @see #getLength() * @see #isCanonical() */ public int getAncestorCount(); /** * Returns the length of this path, i.e. the number of its elements. * Note that the root element "/" counts as a separate element, e.g. * the length of "/a/b/c" is 4 whereas the length of "a/b/c" is 3. * <p> * Also note that the special elements "." and ".." are not treated * specially, e.g. both "/a/./.." and "/a/b/c" have a length of 4 * but this value does not necessarily reflect the true hierarchy level as * returned by <code>{@link #getDepth()}</code>. * * @return the length of this path * @see #getDepth() * @see #getAncestorCount() */ public int getLength(); /** * Returns the depth of this path. The depth reflects the absolute or * relative hierarchy level this path is representing, depending on whether * this path is an absolute or a relative path. The depth also takes '.' * and '..' elements into account. The depth of the root path, an * identifier and the current path must be 0. * <p> * Note that the returned value might be negative if this path is not * canonical, e.g. the depth of "../../a" is -1. * * @return the depth this path * @see #getLength() * @see #getAncestorCount() */ public int getDepth(); /** * Determines if the the <code>other</code> path would be equal to <code>this</code> * path if both of them are normalized. * * @param other Another path. * @return true if the given other path is equivalent to this path. * @throws IllegalArgumentException if the given path is <code>null</code> * or if not both paths are either absolute or relative. * @throws RepositoryException if any of the path cannot be normalized. */ public boolean isEquivalentTo(Path other) throws IllegalArgumentException, RepositoryException; /** * Determines if <i>this</i> path is an ancestor of the specified path, * based on their (absolute or relative) hierarchy level as returned by * <code>{@link #getDepth()}</code>. In case of undefined ancestor/descendant * relationship that might occur with relative paths, the return value * should be <code>false</code>. * * @return <code>true</code> if <code>other</code> is a descendant; * otherwise <code>false</code>. * @throws IllegalArgumentException if the given path is <code>null</code> * or if not both paths are either absolute or relative. * @throws RepositoryException if any of the path cannot be normalized. * @see #getDepth() */ public boolean isAncestorOf(Path other) throws IllegalArgumentException, RepositoryException; /** * Determines if <i>this</i> path is a descendant of the specified path, * based on their (absolute or relative) hierarchy level as returned by * <code>{@link #getDepth()}</code>. In case of undefined ancestor/descendant * relationship that might occur with relative paths, the return value * should be <code>false</code>. * * @return <code>true</code> if <code>other</code> is an ancestor; * otherwise <code>false</code>. * @throws IllegalArgumentException if the given path is <code>null</code> * or if not both paths are either absolute or relative. * @throws RepositoryException if any of the path cannot be normalized. * @see #isAncestorOf(Path) */ public boolean isDescendantOf(Path other) throws IllegalArgumentException, RepositoryException; /** * Returns a new <code>Path</code> consisting of those Path.Element objects * between the given <code>from</code>, inclusive, and the given <code>to</code>, * exclusive. An <code>IllegalArgumentException</code> is thrown if <code>from</code> * is greater or equal than <code>to</code> or if any of both params is * out of the possible range. * * @param from index of the element to start with and low endpoint * (inclusive) within the list of elements to use for the sub-path. * @param to index of the element outside of the range i.e. high endpoint * (exclusive) within the list of elements to use for the sub-path. * @return a new <code>Path</code> consisting of those Path.Element objects * between the given <code>from</code>, inclusive, and the given * <code>to</code>, exclusive. * @throws IllegalArgumentException if <code>from</code> * is greater or equal than <code>to</code> or if any of both params is * out of the possible range. */ public Path subPath(int from, int to) throws IllegalArgumentException; /** * Returns the elements of this path. * * @return the elements of this path. */ public Element[] getElements(); /** * Returns the name element (i.e. the last element) of this path. * * @return the name element of this path */ public Element getNameElement(); /** * Returns a path that consists of only the last element of this path. * * @see #getFirstElements() * @return last element of this path */ Path getLastElement(); /** * Returns a path that consists of all but the last element of this path. * Returns <code>null</code> if this path contains just a single element. * * @see #getLastElement() * @return first elements of this path, or <code>null</code> */ Path getFirstElements(); /** * Returns the String representation of this Path as it is used * by {@link PathFactory#create(String)}. * <p> * The String representation must consist of the String representation of * its elements separated by {@link Path#DELIMITER}. * * @see Path.Element#getString() * @return Returns the String representation of this Path. */ public String getString(); //----------------------------------------------------< inner interface >--- /** * Object representation of a single JCR path element. An <code>Element</code> * object contains the <code>Name</code> and optional index of a single * JCR path element. * <p> * Once created, a <code>Element</code> object must be immutable. * <p> * The String presentation of an <code>Element</code> must be in the format * "<code>{namespaceURI}localPart</code>" or * "<code>{namespaceURI}localPart[index]</code>" case of an index greater * than {@link Path#INDEX_DEFAULT}. * <p> * Note, that the implementation must implement the equals method such, that * two <code>Element</code> objects having equals <code>Name</code>s and the * same normalized index must be equal. */ public interface Element extends Serializable { /** * Returns the name of this path element. * * @return The name of this path element. */ public Name getName(); /** * Returns the index of the element as it has been assigned upon creation. * * @return index of the element as it has been assigned upon creation. */ public int getIndex(); /** * Returns the normalized index of this path element, i.e. the index * is always equals or greater that {@link Path#INDEX_DEFAULT}. * * @return the normalized index. */ public int getNormalizedIndex(); /** * Returns the identifier of an identifier element, or * <code>null</code> for other kinds of elements. * * @return identifier, or <code>null</code> */ String getIdentifier(); /** * Returns <code>true</code> if this element denotes the <i>root</i> element, * otherwise returns <code>false</code>. * * @return <code>true</code> if this element denotes the <i>root</i> * element; otherwise <code>false</code> */ public boolean denotesRoot(); /** * Returns <code>true</code> if this element denotes the <i>parent</i> * ('..') element, otherwise returns <code>false</code>. * * @return <code>true</code> if this element denotes the <i>parent</i> * element; otherwise <code>false</code> */ public boolean denotesParent(); /** * Returns <code>true</code> if this element denotes the <i>current</i> * ('.') element, otherwise returns <code>false</code>. * * @return <code>true</code> if this element denotes the <i>current</i> * element; otherwise <code>false</code> */ public boolean denotesCurrent(); /** * Returns <code>true</code> if this element represents a regular name * (i.e. neither root, '.' nor '..'), otherwise returns <code>false</code>. * * @return <code>true</code> if this element represents a regular name; * otherwise <code>false</code> */ public boolean denotesName(); /** * Returns <code>true</code> if this element represents an identifier element. * * @return <code>true</code> if this element represents an identifier element. * @since JCR 2.0 */ public boolean denotesIdentifier(); /** * Return the String presentation of a {@link Path.Element}. It must be * in the format "<code>{namespaceURI}localPart</code>" or * "<code>{namespaceURI}localPart[index]</code>" in case of an index * greater than {@link Path#INDEX_DEFAULT}. * * @return String representation of a {@link Path.Element}. */ public String getString(); } }