package org.sef4j.core.helpers.proptree.model;
import java.io.Serializable;
/**
* immutable Path
*
* implementation note: data storage using pointer to parentPath + last (string) element<br/>
* getParentPath() takes o(1) as a pure getter
* ... but <code>elementAt(i)<code> takes o(length-i) (not efficient... in particular for iterating)!
*/
public final class Path implements Comparable<Path>, Serializable {
/** */
private static final long serialVersionUID = 1L;
private final Path parentPath;
private final String last;
private final int length;
// ------------------------------------------------------------------------
public Path(Path parentPath, String last) {
this.parentPath = parentPath;
this.last = last;
this.length = 1 + ((parentPath != null)? parentPath.getLength() : 0);
}
public static Path of(String element) {
return new Path(null, element);
}
public static Path of(Path parentPath, String element) {
return new Path(parentPath, element);
}
public static Path of(String... elements) {
return of(elements, 0, elements.length);
}
public static Path of(String[] elements, int from, int to) {
if (from == to-1) return of(elements[from]);
return new Path(of(elements, from, to-1), elements[to-1]);
}
// ------------------------------------------------------------------------
public Path getParentPath() {
return parentPath;
}
public Path getNthParentPath(int n) {
Path p = this;
for(int i = 0; i < n; i++) {
p = p.getParentPath();
}
return p;
}
public String getLast() {
return last;
}
public int getLength() {
return length;
}
public String elementAt(int index) {
if (index < 0 || index >= length) throw new ArrayIndexOutOfBoundsException();
String res = (index == length-1)? last : parentPath.elementAt(index);
return res;
}
public String[] toArray() {
int len = getLength();
String[] res = new String[len];
int i = len-1;
for(Path p = this; p != null; p = p.parentPath) {
res[i--] = p.last;
}
return res;
}
public String[] toArray(int fromIndex, int toIndex) {
int resLen = toIndex - fromIndex;
String[] res = new String[resLen];
int i = resLen - 1;
for(Path p = getNthParentPath(length - toIndex); i >= 0; p = p.parentPath) {
res[i--] = p.last;
}
return res;
}
public boolean startsWith(Path other) {
int otherLength = other.getLength();
int remainParent = length - otherLength;
if (getLength() < otherLength) return false;
Path curr = getNthParentPath(remainParent);
Path currOther = other;
for(; curr != null; curr = curr.parentPath, currOther = currOther.parentPath) {
if (! curr.last.equals(currOther.last)) {
return false;
}
}
return true;
}
public boolean endsWith(Path other) {
int otherLength = other.getLength();
int length = getLength();
if (length < otherLength) return false;
Path curr = this;
Path currOther = other;
for(int i = 0; i < otherLength; i++, curr = curr.parentPath, currOther = currOther.parentPath) {
if (! curr.last.equals(currOther.last)) {
return false;
}
}
return true;
}
// ------------------------------------------------------------------------
@Override
public String toString() {
int textLen = 0;
for (Path p = this; p != null; p = p.parentPath) {
textLen += p.last.length();
if (p.parentPath != null) {
textLen += 1;
}
}
char[] text = new char[textLen];
int pos = textLen;
for (Path p = this; p != null; p = p.parentPath) {
pos -= p.last.length();
p.last.getChars(0, p.last.length(), text, pos);
if (p.parentPath != null) {
pos -= 1;
text[pos] = '/';
}
}
return new String(text);
}
@Override
public int compareTo(Path other) {
int res = 0;
int len = getLength();
int otherLen = other.getLength();
Path thisParent = this;
Path otherParent = other;
if (len < otherLen) {
otherParent = otherParent.getNthParentPath(otherLen-len);
} else if (len > otherLen) {
thisParent = getNthParentPath(len - otherLen);
}
res = recursiveCompare(thisParent, otherParent);
if (res == 0) {
res = Integer.compare(len, otherLen);
}
return res;
}
/*pp*/ static int recursiveCompare(Path left, Path right) {
int res = 0;
if (left.length > 1) {
res = recursiveCompare(left.parentPath, right.parentPath);
}
if (res == 0) {
res = left.last.compareTo(right.last);
}
return res;
}
@Override
public int hashCode() {
final int prime = 31;
int res = 1;
for (Path p = this; p != null; p = p.parentPath) {
res = prime * res + p.last.hashCode();
}
return res;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Path)) {
return false;
}
Path other = (Path) obj;
if (length != other.length) {
return false;
}
Path curr = this;
Path currOther = other;
for(; curr != null; curr = curr.parentPath, currOther = currOther.parentPath) {
if (! curr.last.equals(currOther.last)) {
return false;
}
}
return true;
}
}