
com.tinkerpop.gremlin.structure.strategy.IdGraphStrategy Maven / Gradle / Ivy
package com.tinkerpop.gremlin.structure.strategy;
import com.tinkerpop.gremlin.structure.Edge;
import com.tinkerpop.gremlin.structure.Element;
import com.tinkerpop.gremlin.structure.Graph;
import com.tinkerpop.gremlin.structure.Property;
import com.tinkerpop.gremlin.structure.Vertex;
import com.tinkerpop.gremlin.structure.util.ElementHelper;
import com.tinkerpop.gremlin.util.function.STriFunction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
/**
* A {@link GraphStrategy} implementation which enables custom element IDs even for those graphs which don't
* otherwise support them.
*
* For those graphs which support vertex indices but not edge indices (or vice versa), the strategy can be configured
* to use custom IDs only for vertices or only for edges. ID generation is also configurable via ID {@link Supplier}
* functions.
*
* If the {@link IdGraphStrategy} is used in combination with a sequence of other strategies and when ID assignment
* is enabled for an element, calls to strategies following this one are not made. It is important to consider that
* aspect of its operation when doing strategy composition. Typically, the {@link IdGraphStrategy} should be
* executed last in a sequence.
*
* @author Joshua Shinavier (http://fortytwo.net)
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public class IdGraphStrategy implements GraphStrategy {
private final String idKey;
private final Supplier> edgeIdSupplier;
private final Supplier> vertexIdSupplier;
private final boolean supportsVertexId;
private final boolean supportsEdgeId;
/**
* Creates a new instance. Public instantiation should be handled through the {@link Builder}.
*/
private IdGraphStrategy(final String idKey, final Supplier> vertexIdSupplier,
final Supplier> edgeIdSupplier, final boolean supportsVertexId,
final boolean supportsEdgeId) {
this.idKey = idKey;
this.edgeIdSupplier = edgeIdSupplier;
this.vertexIdSupplier = vertexIdSupplier;
this.supportsEdgeId = supportsEdgeId;
this.supportsVertexId = supportsVertexId;
}
@Override
public UnaryOperator> getAddVertexStrategy(final Strategy.Context ctx) {
return (f) -> (keyValues) -> {
throwIfIdKeyIsSet(Vertex.class, ElementHelper.getKeys(keyValues));
return f.apply(this.injectId(supportsVertexId, keyValues, vertexIdSupplier).toArray());
};
}
@Override
public UnaryOperator> getAddEdgeStrategy(final Strategy.Context ctx) {
return (f) -> (label, v, keyValues) -> {
throwIfIdKeyIsSet(Edge.class, ElementHelper.getKeys(keyValues));
return f.apply(label, v, this.injectId(supportsEdgeId, keyValues, edgeIdSupplier).toArray());
};
}
@Override
public UnaryOperator> getGraphvStrategy(final Strategy.Context ctx) {
return supportsVertexId ? (f) -> (id) -> (Vertex) ctx.getBaseGraph().V().has(idKey, id).next() : UnaryOperator.identity();
}
@Override
public UnaryOperator> getGrapheStrategy(final Strategy.Context ctx) {
return supportsEdgeId ? (f) -> (id) -> (Edge) ctx.getBaseGraph().E().has(idKey, id).next() : UnaryOperator.identity();
}
@Override
public UnaryOperator> getElementId(final Strategy.Context extends StrategyWrappedElement> ctx) {
return supportsAnId(ctx.getCurrent().getClass()) ?
(f) -> () -> ctx.getCurrent().getBaseElement().property(idKey).value() : UnaryOperator.identity();
}
@Override
public UnaryOperator>> getElementProperty(final Strategy.Context extends StrategyWrappedElement> ctx) {
return (f) -> (k, v) -> {
throwIfIdKeyIsSet(ctx.getCurrent().getClass(), k);
return f.apply(k, v);
};
}
@Override
public UnaryOperator> getElementPropertiesSetter(Strategy.Context extends StrategyWrappedElement> ctx) {
return (f) -> (kvs) -> {
throwIfIdKeyIsSet(ctx.getCurrent().getClass(), ElementHelper.getKeys(kvs));
f.accept(kvs);
};
}
@Override
public String toString() {
return String.format("%s[%s]", IdGraphStrategy.class.getSimpleName(), idKey).toLowerCase();
}
private void throwIfIdKeyIsSet(final Class extends Element> element, final String k) {
if (supportsAnId(element) && this.idKey.equals(k))
throw new IllegalArgumentException(String.format("The key [%s] is protected by %s and cannot be set", idKey, IdGraphStrategy.class.getSimpleName()));
}
private void throwIfIdKeyIsSet(final Class extends Element> element, final Set keys) {
if (supportsAnId(element) && keys.contains(this.idKey))
throw new IllegalArgumentException(String.format("The key [%s] is protected by %s and cannot be set", idKey, IdGraphStrategy.class.getSimpleName()));
}
private boolean supportsAnId(final Class extends Element> element) {
return ((Vertex.class.isAssignableFrom(element) && supportsVertexId) || (Edge.class.isAssignableFrom(element) && supportsEdgeId));
}
/**
* Gets the property name of the key used to lookup graph elements. This is a "hidden" key created by
* {@link com.tinkerpop.gremlin.structure.Graph.Key#hide(String)}. Use this value to create an index in the underlying graph instance.
*/
public String getIdKey() {
return this.idKey;
}
public boolean isSupportsVertexId() {
return supportsVertexId;
}
public boolean isSupportsEdgeId() {
return supportsEdgeId;
}
private List