AbstractPath implements Path { @Override public Path relativeTo( Path startingPath ) { CheckArg.isNotNull(startingPath, "to"); if (!this.isAbsolute()) { String msg = GraphI18n.pathIsNotAbsolute.text(this); throw new InvalidPathException(msg); } if (startingPath.isRoot()) { return relativeToRoot(); } if (!startingPath.isAbsolute()) { String msg = GraphI18n.pathIsNotAbsolute.text(startingPath); throw new InvalidPathException(msg); } int lengthOfCommonAncestor = 0; Iterator<Segment> thisIter = this.getNormalizedPath().iterator(); Iterator<Segment> toIter = startingPath.getNormalizedPath().iterator(); while (thisIter.hasNext() && toIter.hasNext()) { Segment thisSeg = thisIter.next(); Segment toSeg = toIter.next(); if (thisSeg.equals(toSeg)) { ++lengthOfCommonAncestor; } else { break; } } int numberOfParentReferences = startingPath.size() - lengthOfCommonAncestor; List<Segment> relativeSegments = new ArrayList<Segment>(); for (int i = 0; i != numberOfParentReferences; ++i) { relativeSegments.add(Path.PARENT_SEGMENT); } for (int i = lengthOfCommonAncestor; i < this.size(); ++i) { relativeSegments.add(getSegment(i)); } if (relativeSegments.isEmpty()) { relativeSegments.add(Path.SELF_SEGMENT); } return new BasicPath(relativeSegments, false); }  @Override boolean isIdentifier(); @Override Path getCanonicalPath(); @Override Path getCommonAncestor( Path that ); @Override Path.Segment getLastSegment(); @Override boolean endsWith( Name nameOfLastSegment ); @Override boolean endsWith( Name nameOfLastSegment,
                             int snsIndex ); @Override Path getParent(); @Override Segment getSegment( int index ); @Override Segment[] getSegmentsArray(); @Override Path getNormalizedPath(); @Override String getString(); @Override String getString( TextEncoder encoder ); @Override String getString( NamespaceRegistry namespaceRegistry ); @Override String getString( NamespaceRegistry namespaceRegistry,
                             TextEncoder encoder ); @Override String getString( NamespaceRegistry namespaceRegistry,
                             TextEncoder encoder,
                             TextEncoder delimiterEncoder ); @Override boolean hasSameAncestor( Path that ); @Override boolean isAncestorOf( Path descendant ); @Override boolean isAtOrBelow( Path other ); @Override boolean isAtOrAbove( Path other ); @Override boolean isDescendantOf( Path ancestor ); @Override boolean isSameAs( Path other ); @Override Iterator<Segment> iterator(); @Override Iterator<Path> pathsFromRoot(); @Override Path relativeToRoot(); @Override Path relativeTo( Path startingPath ); @Override Path resolve( Path relativePath ); @Override Path resolveAgainst( Path absolutePath ); @Override Path subpath( int beginIndex ); @Override Path subpath( int beginIndex,
                         int endIndex ); @Override int hashCode(); @Override boolean equals( Object obj ); @Override int compareTo( Path that ); @Override String toString(); static final Path SELF_PATH; }
@Test( expected = InvalidPathException.class ) public void shouldNotFindRelativePathToAnotherRelativePath() { Path other = mock(Path.class); when(other.isAbsolute()).thenReturn(false); path.relativeTo(other); }