package com.googlecode.objectify.impl;
import java.util.Objects;
/**
* Path represents the individual steps from the root object to the current property.
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
public class Path
{
/** */
private static final Path ROOT = new Path("", null);
public static Path root() {
return ROOT;
}
/** This path segment. */
private final String segment;
/** The previous step in the path, null only for the special {@link #ROOT} element. */
private final Path previous;
/** */
private Path(String name, Path path) {
segment = name;
previous = path;
}
/** Create the full x.y.z string */
public String toPathString() {
if (this == ROOT) {
return "";
} else {
StringBuilder builder = new StringBuilder();
toPathString(builder);
return builder.toString();
}
}
/** */
private void toPathString(StringBuilder builder) {
if (previous != ROOT) {
previous.toPathString(builder);
builder.append('.');
}
builder.append(segment);
}
/** */
public Path extend(String name) {
return new Path(name, this);
}
/** Get this segment of the path. For root this will be null. */
public String getSegment() {
return segment;
}
/** Get the previous path; for root this will be null */
public Path getPrevious() {
return previous;
}
/** */
public boolean isRoot() { return this == ROOT; }
/** */
@Override
public String toString() {
return toPathString();
}
/** Compares on complete path */
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass())
return false;
Path other = (Path)obj;
if (!this.segment.equals(other.segment))
return false;
else
return Objects.equals(this.previous, other.previous);
}
/** Generates hash code for complete path */
@Override
public int hashCode() {
int hash = segment.hashCode();
if (previous == null)
return hash;
else
return hash ^ previous.hashCode();
}
/** Convenient way to include path location in the exception message. Never returns. */
public Object throwIllegalState(String message) {
throw new IllegalStateException("At path '" + this + "': " + message);
}
/** Convenient way to include path location in the exception message. Never returns. */
public Object throwIllegalState(String message, Throwable cause) {
throw new IllegalStateException("At path '" + this + "': " + message, cause);
}
/**
* ROOT is 0, top level Entity properties are 1, embedded things are higher.
*/
public int depth() {
int depth = 0;
Path here = this;
while (here != ROOT) {
depth++;
here = here.previous;
}
return depth;
}
/** Anything with a depth greater than 1 is embedded */
boolean isEmbedded() {
return depth() > 1;
}
}