net.minidev.json.actions.path.TreePath Maven / Gradle / Ivy
Show all versions of json-smart-action Show documentation
package net.minidev.json.actions.path;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
import java.util.ListIterator;
/**
* {@link TreePath} represents an n-gram formatted path
* corresponding to a branch in a tree of {@link Map}s
* and {@link List}s
*
* See package-info for more details
*
* @author [email protected]
*/
public class TreePath {
protected enum Step {
NONE, NEXT, PREV
}
protected final String path;
protected List keys;
protected ListIterator keysItr;
protected String currKey;
protected Step lastStep;
protected StringBuilder origin;
protected StringBuilder remainder;
protected PathDelimiter delim;
public TreePath(String path, PathDelimiter delim) {
this.delim = delim;
checkPath(path);
this.path = path;
this.keys = Arrays.asList(path.split(delim.regex()));
reset();
}
public void reset() {
keysItr = keys.listIterator();
currKey = "";
lastStep = Step.NONE;
origin = new StringBuilder("");
remainder = new StringBuilder(path);
}
public boolean hasNext() {
return keysItr.hasNext();
}
public int nextIndex() {
return keysItr.nextIndex();
}
public String next() {
currKey = keysItr.next();
/** when changing direction the {@link ListIterator} does not really
* move backward so an extra step is performed */
if (!lastStep.equals(Step.PREV)) {
originIncrement();
remainderDecrement();
}
lastStep = Step.NEXT;
return currKey;
}
public boolean hasPrev() {
return keysItr.hasPrevious();
}
public int prevIndex() {
return keysItr.previousIndex();
}
public String prev() {
String temp = currKey;
currKey = keysItr.previous();
/** when changing direction the {@link ListIterator} does not really
* move backward so an extra step is performed */
if (!lastStep.equals(Step.NEXT)) {
remainderIncrement(temp);
originDecrement();
}
lastStep = Step.PREV;
return currKey;
}
private void remainderDecrement() {
if (length() == 1)
remainder = new StringBuilder("");
else if (remainder.indexOf(delim.str()) < 0)
remainder = new StringBuilder("");
else
remainder.delete(0, remainder.indexOf(delim.str()) + 1);
}
private void originDecrement() {
if (length() == 1)
origin = new StringBuilder("");
else if (origin.indexOf(delim.str()) < 0)
origin = new StringBuilder("");
else
origin.delete(origin.lastIndexOf(delim.str()), origin.length());
}
private void originIncrement() {
if (origin.length() != 0) {
origin.append(delim.chr());
}
origin.append(currKey);
}
private void remainderIncrement(String prev) {
if (remainder.length() == 0)
remainder = new StringBuilder(prev);
else
remainder = new StringBuilder(prev).append(delim.chr()).append(remainder);
}
/**
* @return An n-gram path from the first key to the current key (inclusive)
*/
public String path() {
return path;
}
/**
* @return An n-gram path from the first key to the current key (inclusive)
*/
public String origin() {
return origin.toString();
}
/**
* @return An n-gram path from the current key to the last key (inclusive)
*/
public String remainder() {
return remainder.toString();
}
/**
* @return first element in the JSONPath
*/
public String first() {
return keys.get(0);
}
/**
* @return last element in the JSONPath
*/
public String last() {
return keys.get(keys.size() - 1);
}
/**
* @return current element pointed to by the path iterator
*/
public String curr() {
return currKey;
}
public int length() {
return keys.size();
}
public String subPath(int firstIndex, int lastIndex) {
if (lastIndex < firstIndex) {
throw new IllegalArgumentException("bad call to subPath");
}
StringBuilder sb = new StringBuilder(path.length());
for (int i = firstIndex; i <= lastIndex; i++) {
sb.append(keys.get(i));
if (i < lastIndex) {
sb.append(delim.chr());
}
}
sb.trimToSize();
return sb.toString();
}
private void checkPath(String path) {
if (path == null || path.equals(""))
throw new IllegalArgumentException("path cannot be null or empty");
if (path.startsWith(delim.str()) || path.endsWith(delim.str()) || path.contains(delim.str() + delim.str()))
throw new IllegalArgumentException(String.format("path cannot start or end with %s or contain '%s%s'", delim.str(), delim.str(), delim.str()));
}
@Override
public TreePath clone() throws CloneNotSupportedException {
TreePath cloned = new TreePath(this.path, this.delim);
while (cloned.nextIndex() != this.nextIndex()) {
cloned.next();
}
if (cloned.prevIndex() != this.prevIndex()) {
cloned.prev();
}
cloned.lastStep = this.lastStep;
cloned.currKey = new String(this.currKey);
cloned.origin = new StringBuilder(this.origin);
cloned.remainder = new StringBuilder(this.remainder);
return cloned;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TreePath treePath = (TreePath) o;
return path().equals(treePath.path()) && hasNext() == treePath.hasNext() && hasPrev() == treePath.hasPrev() && curr().equals(treePath.curr())
&& origin().equals(treePath.origin()) && remainder().equals(treePath.remainder()) && lastStep == treePath.lastStep
&& delim.equals(treePath.delim);
}
@Override
public int hashCode() {
int result = path.hashCode();
result = 31 * result + keys.hashCode();
result = 31 * result + keysItr.hashCode();
result = 31 * result + currKey.hashCode();
result = 31 * result + lastStep.hashCode();
result = 31 * result + origin.hashCode();
result = 31 * result + remainder.hashCode();
result = 31 * result + delim.hashCode();
return result;
}
}