package ch.x42.terye; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; import javax.jcr.PathNotFoundException; public class Path { public static final String DELIMITER = "/"; public static final String ROOT = "/"; public static final String CURRENT = "."; public static final String PARENT = ".."; private String path; private LinkedList<String> segments; private boolean isAbsolute = false; private Boolean isNormalized; public Path(String path) { segments = new LinkedList<String>(); if (path == null || path.isEmpty()) { throw new IllegalArgumentException("Path not specified or empty"); } if (path.startsWith(ROOT)) { path = path.substring(1, path.length()); segments.add(ROOT); isAbsolute = true; } // remove trailing delimiter, if any if (path.endsWith(DELIMITER)) { path = path.substring(0, path.length() - 1); } // split into segments if (!path.isEmpty()) { segments.addAll(Arrays.asList(path.split(DELIMITER))); } } private Path(LinkedList<String> segments, boolean isAbsolute, Boolean isNormalized) { this.segments = segments; this.isAbsolute = isAbsolute; this.isNormalized = isNormalized; } public Path concat(String path) { return concat(new Path(path)); } public Path concat(Path path) { if (!path.isRelative()) { throw new IllegalArgumentException(path.toString() + "is not a relative path"); } LinkedList<String> segments = new LinkedList<String>(this.segments); segments.addAll(path.segments); return new Path(segments, isAbsolute(), null); } public boolean isNormalized() { if (isNormalized == null) { // all ".."s must be at the beginning (possible in relative paths) // ".."s and "."s are not allowed after that boolean parentAllowed = !isAbsolute(); for (String s : segments) { if (parentAllowed) { if (s.equals(PARENT)) { continue; } else { parentAllowed = false; } } if (s.equals(PARENT) || s.equals(CURRENT)) { isNormalized = false; return isNormalized; } } isNormalized = true; } return isNormalized; } public Path getCanonical() throws PathNotFoundException { if (!isAbsolute) { throw new UnsupportedOperationException( "Cannot make a relative path canonical"); } LinkedList<String> segments = new LinkedList<String>(this.segments); ListIterator<String> i = segments.listIterator(); int parents = 0; while (i.hasNext()) { String s = i.next(); // remove CURRENT segment if (s.equals(CURRENT)) { i.remove(); } else if (s.equals(PARENT)) { parents++; i.previous(); if (i.hasPrevious()) { String s2 = i.previous(); // remove PARENT segment if preceded by an identifier if (!s2.equals(CURRENT) && !s2.equals(PARENT) && !s2.equals(ROOT)) { i.remove(); i.next(); i.remove(); parents--; } else { // else go on i.next(); i.next(); } } else { // else go on i.next(); } } } if (parents > 0) { // there are remaining PARENT segments // -> path cannot be normalized throw new PathNotFoundException(toString() + " is an invalid path"); } return new Path(segments, true, true); } public String getLastSegment() { String seg = segments.getLast(); if (seg.equals(ROOT)) { seg = ""; } return seg; } public Path getParent() { LinkedList<String> segments = new LinkedList<String>(this.segments); segments.removeLast(); if (segments.isEmpty()) { return null; } return new Path(segments, isAbsolute(), null); } public boolean isAbsolute() { return isAbsolute; } public boolean isRelative() { return !isAbsolute(); } public boolean isCanonical() { return isAbsolute() && isNormalized(); } @Override public String toString() { if (path == null) { path = ""; Iterator<String> iterator = segments.iterator(); while (iterator.hasNext()) { String seg = iterator.next(); path += seg; if (!seg.equals(ROOT) && iterator.hasNext()) { path += DELIMITER; } } } return path; } }