Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.javersion.path.PropertyPath Maven / Gradle / Ivy
/*
* Copyright 2013 Samppa Saarela
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.javersion.path;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.Integer.parseInt;
import static org.apache.commons.lang3.StringEscapeUtils.escapeEcmaScript;
import static org.apache.commons.lang3.StringEscapeUtils.unescapeEcmaScript;
import java.util.Iterator;
import java.util.List;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.RuleNode;
import org.javersion.path.PropertyPath.SubPath;
import org.javersion.path.parser.PropertyPathBaseVisitor;
import org.javersion.path.parser.PropertyPathLexer;
import org.javersion.path.parser.PropertyPathParser;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
public abstract class PropertyPath implements Iterable {
public static abstract class NodeId {
private static final IndexId[] INDEXES;
static {
INDEXES = new IndexId[32];
for (int i=0; i < INDEXES.length; i++) {
INDEXES[i] = new IndexId(i);
}
}
public static IndexId valueOf(long index) {
Preconditions.checkArgument(index >= 0, "index should be >= 0");
if (index < INDEXES.length) {
return INDEXES[(int) index];
}
return new IndexId(index);
}
public static KeyId valueOf(String key) {
return new KeyId(key);
}
public static NodeId valueOf(Object object) {
if (object instanceof Number) {
return valueOf(((Number) object).longValue());
} else if (object instanceof String) {
return valueOf((String) object);
} else {
throw new IllegalArgumentException("Unsupported NodeId type: " + object);
}
}
private NodeId() {}
public boolean isIndex() {
return false;
}
public boolean isKey() {
return false;
}
public long getIndex() {
throw new UnsupportedOperationException();
}
public String getKey() {
throw new UnsupportedOperationException();
}
public Object getKeyOrIndex() {
if (isIndex()) {
return getIndex();
} else {
return getKey();
}
}
public abstract NodeId fallbackId();
}
public static final class IndexId extends NodeId {
public final long index;
private IndexId(long index) {
Preconditions.checkArgument(index >= 0, "index should be >= 0");
this.index = index;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof IndexId) {
return ((IndexId) obj).index == this.index;
} else {
return false;
}
}
@Override
public boolean isIndex() {
return true;
}
@Override
public long getIndex() {
return index;
}
@Override
public int hashCode() {
return Long.hashCode(index);
}
@Override
public String toString() {
return Long.toString(index);
}
@Override
public NodeId fallbackId() {
return AnyIndex.ID;
}
}
public static final class KeyId extends NodeId {
public final String key;
private KeyId(String key) {
Preconditions.checkNotNull(key);
this.key = key;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof KeyId) {
return ((KeyId) obj).key.equals(this.key);
} else {
return false;
}
}
@Override
public boolean isKey() {
return true;
}
@Override
public String getKey() {
return key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return key;
}
@Override
public NodeId fallbackId() {
return AnyKey.ID;
}
}
private static class SpecialNodeId extends NodeId {
private final String str;
private SpecialNodeId(String str) {
this.str = str;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof SpecialNodeId) {
return ((SpecialNodeId) obj).str.equals(this.str);
} else {
return false;
}
}
@Override
public int hashCode() {
return str.hashCode();
}
@Override
public String toString() {
return str;
}
@Override
public NodeId fallbackId() {
return ANY_NODE;
}
}
private static NodeId ANY_NODE = new SpecialNodeId("*") {
@Override
public NodeId fallbackId() {
return null;
}
};
private static final class SilentParseException extends RuntimeException {
public SilentParseException() {
super(null, null, false, false);
}
}
private static final ANTLRErrorListener BASIC_ERROR_LISTENER = new BaseErrorListener() {
@Override
public void syntaxError(Recognizer recognizer,
Object offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
throw new IllegalArgumentException("line " + line + ":" + charPositionInLine + " " + msg);
}
};
private static final ANTLRErrorListener SILENT_ERROR_LISTENER = new BaseErrorListener() {
@Override
public void syntaxError(Recognizer recognizer,
Object offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
throw new SilentParseException();
}
};
private static final String EMPTY_STRING = "";
public static final Root ROOT = Root.ROOT;
public static PropertyPath parse(String path) {
checkNotNull(path);
if (path.length() == 0) {
return ROOT;
}
return newParser(path, false).parsePath().accept(new PropertyPathBaseVisitor() {
private PropertyPath parent = ROOT;
@Override
public PropertyPath visitIndex(PropertyPathParser.IndexContext ctx) {
return parent = new Index(parent, parseInt(ctx.getText()));
}
@Override
public PropertyPath visitKey(PropertyPathParser.KeyContext ctx) {
String keyLiteral = ctx.getText();
return parent = new Key(parent, unescapeEcmaScript(keyLiteral.substring(1, keyLiteral.length() - 1)));
}
@Override
public PropertyPath visitProperty(PropertyPathParser.PropertyContext ctx) {
return parent = new Property(parent, ctx.getText());
}
@Override
public PropertyPath visitAnyIndex(PropertyPathParser.AnyIndexContext ctx) {
return parent = new AnyIndex(parent);
}
@Override
public PropertyPath visitAnyKey(PropertyPathParser.AnyKeyContext ctx) {
return parent = new AnyKey(parent);
}
@Override
public PropertyPath visitAny(PropertyPathParser.AnyContext ctx) {
return parent = new Any(parent);
}
@Override
protected PropertyPath defaultResult() {
return parent;
}
});
}
PropertyPath() {}
public Property property(String name) {
return newParser(name, false).parseProperty().accept(new PropertyPathBaseVisitor() {
@Override
public Property visitProperty(PropertyPathParser.PropertyContext ctx) {
return new Property(PropertyPath.this, ctx.getText());
}
@Override
protected boolean shouldVisitNextChild(RuleNode node, Property currentResult) {
return currentResult == null;
}
});
}
public final Index index(long index) {
checkArgument(index >= 0, "index should be >= 0");
return new Index(this, index);
}
public final Key key(String index) {
checkNotNull(index);
return new Key(this, index);
}
public final SubPath keyOrIndex(Object object) {
return keyOrIndex(NodeId.valueOf(object));
}
public final SubPath keyOrIndex(NodeId nodeId) {
Preconditions.checkNotNull(nodeId);
if (nodeId.isKey()) {
return new Key(this, nodeId);
} else if (nodeId.isIndex()) {
return new Index(this, nodeId);
} else {
throw new IllegalArgumentException("Expected KeyId or IndexId, got " + nodeId.getClass());
}
}
public final Any any() {
return new Any(this);
}
public final AnyIndex anyIndex() {
return new AnyIndex(this);
}
public final AnyKey anyKey() {
return new AnyKey(this);
}
public final SubPath propertyOrKey(String string) {
checkNotNull(string);
try {
return newParser(string, true).parseProperty().accept(new PropertyPathBaseVisitor() {
@Override
public SubPath visitProperty(PropertyPathParser.PropertyContext ctx) {
return new Property(PropertyPath.this, ctx.getText());
}
@Override
protected boolean shouldVisitNextChild(RuleNode node, SubPath currentResult) {
return currentResult == null;
}
});
} catch (SilentParseException e) {
return new Key(this, string);
}
}
public final PropertyPath path(PropertyPath path) {
PropertyPath result = this;
for (SubPath subPath : path) {
result = subPath.withParent(result);
}
return result;
}
public Iterator iterator() {
return asList().iterator();
}
public List asList() {
return fullPath != null ? fullPath : (fullPath = getFullPath());
}
public boolean isRoot() {
return false;
}
public boolean startsWith(PropertyPath other) {
if (other.isRoot()) {
return true;
} else if (this.isRoot()) {
return false;
} else {
List otherPath = other.asList();
List thisPath = asList();
int otherSize = otherPath.size();
return thisPath.size() >= otherSize && thisPath.get(otherSize - 1).equals(otherPath.get(otherSize - 1));
}
}
public PropertyPath toSchemaPath() {
PropertyPath schemaPath = ROOT;
for (SubPath path : this) {
schemaPath = path.toSchemaPath(schemaPath);
}
return schemaPath;
}
public abstract String toString();
public abstract boolean equals(Object obj);
public abstract int hashCode();
public abstract NodeId getNodeId();
abstract List getFullPath();
abstract PropertyPath withParent(PropertyPath newParent);
private volatile transient List fullPath;
public static final class Root extends PropertyPath {
public static final NodeId ID = new SpecialNodeId(EMPTY_STRING);
private static final Root ROOT = new Root();
private static final List FULL_PATH = ImmutableList.of();
private Root() {}
List getFullPath() {
return FULL_PATH;
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
@Override
public String toString() {
return EMPTY_STRING;
}
@Override
public int hashCode() {
return 1;
}
public boolean isRoot() {
return true;
}
@Override
public NodeId getNodeId() {
return ID;
}
@Override
PropertyPath withParent(PropertyPath newParent) {
return newParent;
}
}
public static abstract class SubPath extends PropertyPath {
public final NodeId nodeId;
public final PropertyPath parent;
private SubPath(PropertyPath parent, NodeId nodeId) {
this.parent = checkNotNull(parent, "parent");
this.nodeId = checkNotNull(nodeId, "nodeId");
}
List getFullPath() {
List parentPath = parent.getFullPath();
ImmutableList.Builder pathBuilder = ImmutableList.builder();
pathBuilder.addAll(parentPath);
pathBuilder.add(this);
return pathBuilder.build();
}
@Override
public final boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof SubPath) {
SubPath other = (SubPath) obj;
return this.nodeId.equals(other.nodeId) && parent.equals(other.parent);
} else {
return false;
}
}
@Override
public final int hashCode() {
return 31 * parent.hashCode() + nodeId.hashCode();
}
@Override
public final NodeId getNodeId() {
return nodeId;
}
public final String toString() {
StringBuilder sb = new StringBuilder();
sb.append(parent.toString());
appendNode(sb);
return sb.toString();
}
PropertyPath toSchemaPath(PropertyPath newParent) {
return withParent(newParent);
}
protected abstract void appendNode(StringBuilder sb);
}
public static final class Property extends SubPath {
private Property(PropertyPath parent, String name) {
super(parent, new KeyId(name));
}
private Property(PropertyPath parent, NodeId nodeId) {
super(parent, nodeId);
}
@Override
Property toSchemaPath(PropertyPath newParent) {
return withParent(newParent);
}
@Override
Property withParent(PropertyPath newParent) {
return newParent.equals(parent) ? this : new Property(newParent, nodeId);
}
@Override
protected void appendNode(StringBuilder sb) {
if (!parent.isRoot()) {
sb.append('.');
}
sb.append(nodeId);
}
}
public static final class Any extends SubPath {
public static final NodeId ID = ANY_NODE;
private Any(PropertyPath parent) {
super(parent, ID);
}
@Override
Any withParent(PropertyPath newParent) {
return newParent.equals(parent) ? this : new Any(newParent);
}
@Override
protected void appendNode(StringBuilder sb) {
sb.append(ID);
}
}
public static final class AnyIndex extends SubPath {
public static final NodeId ID = new SpecialNodeId("[]");
private AnyIndex(PropertyPath parent) {
super(parent, ID);
}
@Override
AnyIndex withParent(PropertyPath newParent) {
return newParent.equals(parent) ? this : new AnyIndex(newParent);
}
@Override
protected void appendNode(StringBuilder sb) {
sb.append(ID);
}
}
public static final class AnyKey extends SubPath {
public static final NodeId ID = new SpecialNodeId("{}");
private AnyKey(PropertyPath parent) {
super(parent, ID);
}
@Override
AnyKey withParent(PropertyPath newParent) {
return newParent.equals(parent) ? this : new AnyKey(newParent);
}
@Override
protected void appendNode(StringBuilder sb) {
sb.append(ID);
}
}
public static final class Index extends SubPath {
private Index(PropertyPath parent, long index) {
super(parent, new IndexId(index));
}
private Index(PropertyPath parent, NodeId id) {
super(parent, id);
}
@Override
AnyIndex toSchemaPath(PropertyPath newParent) {
return new AnyIndex(newParent);
}
@Override
Index withParent(PropertyPath newParent) {
return new Index(newParent, nodeId);
}
@Override
protected void appendNode(StringBuilder sb) {
sb.append('[').append(nodeId.getIndex()).append(']');
}
}
public static final class Key extends SubPath {
private Key(PropertyPath parent, String key) {
super(parent, new KeyId(key));
}
private Key(PropertyPath parent, NodeId id) {
super(parent, id);
}
@Override
AnyKey toSchemaPath(PropertyPath newParent) {
return new AnyKey(newParent);
}
@Override
SubPath withParent(PropertyPath newParent) {
return new Key(newParent, nodeId);
}
@Override
protected void appendNode(StringBuilder sb) {
sb.append("[\"").append(escapeEcmaScript(nodeId.getKey())).append("\"]").toString();
}
}
private static PropertyPathParser newParser(String input, boolean silent) {
PropertyPathLexer lexer = new PropertyPathLexer(new ANTLRInputStream(input));
lexer.removeErrorListeners();
lexer.addErrorListener(silent ? SILENT_ERROR_LISTENER: BASIC_ERROR_LISTENER);
PropertyPathParser parser = new PropertyPathParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(silent ? SILENT_ERROR_LISTENER: BASIC_ERROR_LISTENER);
return parser;
}
}