blob: fa999aa84a200f67c28930f8d9a6dc0797699f96 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.tinkerpop.gremlin.structure.util;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import java.util.Iterator;
import java.util.Optional;
import java.util.function.Function;
/**
* An interface that provides methods for detached properties and elements to be re-attached to the {@link Graph}.
* There are two general ways in which they can be attached: {@link Method#get} or {@link Method#create}.
* A {@link Method#get} will find the property/element at the host location and return it.
* A {@link Method#create} will create the property/element at the host location and return it.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public interface Attachable<V> {
/**
* Get the raw object trying to be attached.
*
* @return the raw object to attach
*/
public V get();
/**
* Provide a way to attach an {@link Attachable} implementation to a host. Note that the context of the host
* is not defined by way of the attachment method itself that is supplied as an argument. It is up to the
* implementer to supply that context.
*
* @param method a {@link Function} that takes an {@link Attachable} and returns the "re-attached" object
* @return the return value of the {@code method}
* @throws IllegalStateException if the {@link Attachable} is not a "graph" object (i.e. host or
* attachable don't work together)
*/
public default V attach(final Function<Attachable<V>, V> method) throws IllegalStateException {
return method.apply(this);
}
/**
* A collection of general methods of attachment. Note that more efficient methods of attachment might exist
* if the user knows the source data being attached and the features of the graph that the data is being
* attached to.
*/
public static class Method {
public static <V> Function<Attachable<V>, V> get(final Host hostVertexOrGraph) {
return (Attachable<V> attachable) -> {
final Object base = attachable.get();
if (base instanceof Vertex) {
final Optional<Vertex> optional = hostVertexOrGraph instanceof Graph ?
Method.getVertex((Attachable<Vertex>) attachable, (Graph) hostVertexOrGraph) :
Method.getVertex((Attachable<Vertex>) attachable, (Vertex) hostVertexOrGraph);
return (V) optional.orElseThrow(() -> hostVertexOrGraph instanceof Graph ?
Attachable.Exceptions.canNotGetAttachableFromHostGraph(attachable, (Graph) hostVertexOrGraph) :
Attachable.Exceptions.canNotGetAttachableFromHostVertex(attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof Edge) {
final Optional<Edge> optional = hostVertexOrGraph instanceof Graph ?
Method.getEdge((Attachable<Edge>) attachable, (Graph) hostVertexOrGraph) :
Method.getEdge((Attachable<Edge>) attachable, (Vertex) hostVertexOrGraph);
return (V) optional.orElseThrow(() -> hostVertexOrGraph instanceof Graph ?
Attachable.Exceptions.canNotGetAttachableFromHostGraph(attachable, (Graph) hostVertexOrGraph) :
Attachable.Exceptions.canNotGetAttachableFromHostVertex(attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof VertexProperty) {
final Optional<VertexProperty> optional = hostVertexOrGraph instanceof Graph ?
Method.getVertexProperty((Attachable<VertexProperty>) attachable, (Graph) hostVertexOrGraph) :
Method.getVertexProperty((Attachable<VertexProperty>) attachable, (Vertex) hostVertexOrGraph);
return (V) optional.orElseThrow(() -> hostVertexOrGraph instanceof Graph ?
Attachable.Exceptions.canNotGetAttachableFromHostGraph(attachable, (Graph) hostVertexOrGraph) :
Attachable.Exceptions.canNotGetAttachableFromHostVertex(attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof Property) {
final Optional<Property> optional = hostVertexOrGraph instanceof Graph ?
Method.getProperty((Attachable<Property>) attachable, (Graph) hostVertexOrGraph) :
Method.getProperty((Attachable<Property>) attachable, (Vertex) hostVertexOrGraph);
return (V) optional.orElseThrow(() -> hostVertexOrGraph instanceof Graph ?
Attachable.Exceptions.canNotGetAttachableFromHostGraph(attachable, (Graph) hostVertexOrGraph) :
Attachable.Exceptions.canNotGetAttachableFromHostVertex(attachable, (Vertex) hostVertexOrGraph));
} else
throw Attachable.Exceptions.providedAttachableMustContainAGraphObject(attachable);
};
}
public static <V> Function<Attachable<V>, V> getOrCreate(final Host hostVertexOrGraph) {
return (Attachable<V> attachable) -> {
final Object base = attachable.get();
if (base instanceof Vertex) {
return (V) (hostVertexOrGraph instanceof Graph ?
Method.getVertex((Attachable<Vertex>) attachable, (Graph) hostVertexOrGraph) :
Method.getVertex((Attachable<Vertex>) attachable, (Vertex) hostVertexOrGraph))
.orElseGet(() -> hostVertexOrGraph instanceof Graph ?
Method.createVertex((Attachable<Vertex>) attachable, (Graph) hostVertexOrGraph) :
Method.createVertex((Attachable<Vertex>) attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof Edge) {
return (V) (hostVertexOrGraph instanceof Graph ?
Method.getEdge((Attachable<Edge>) attachable, (Graph) hostVertexOrGraph) :
Method.getEdge((Attachable<Edge>) attachable, (Vertex) hostVertexOrGraph))
.orElseGet(() -> hostVertexOrGraph instanceof Graph ?
Method.createEdge((Attachable<Edge>) attachable, (Graph) hostVertexOrGraph) :
Method.createEdge((Attachable<Edge>) attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof VertexProperty) {
return (V) (hostVertexOrGraph instanceof Graph ?
Method.getVertexProperty((Attachable<VertexProperty>) attachable, (Graph) hostVertexOrGraph) :
Method.getVertexProperty((Attachable<VertexProperty>) attachable, (Vertex) hostVertexOrGraph))
.orElseGet(() -> hostVertexOrGraph instanceof Graph ?
Method.createVertexProperty((Attachable<VertexProperty>) attachable, (Graph) hostVertexOrGraph) :
Method.createVertexProperty((Attachable<VertexProperty>) attachable, (Vertex) hostVertexOrGraph));
} else if (base instanceof Property) {
return (V) (hostVertexOrGraph instanceof Graph ?
Method.getProperty((Attachable<Property>) attachable, (Graph) hostVertexOrGraph) :
Method.getProperty((Attachable<Property>) attachable, (Vertex) hostVertexOrGraph))
.orElseGet(() -> hostVertexOrGraph instanceof Graph ?
Method.createProperty((Attachable<Property>) attachable, (Graph) hostVertexOrGraph) :
Method.createProperty((Attachable<Property>) attachable, (Vertex) hostVertexOrGraph));
} else
throw Attachable.Exceptions.providedAttachableMustContainAGraphObject(attachable);
};
}
public static <V> Function<Attachable<V>, V> create(final Host hostVertexOrGraph) {
return (Attachable<V> attachable) -> {
final Object base = attachable.get();
if (base instanceof Vertex) {
return hostVertexOrGraph instanceof Graph ?
(V) Method.createVertex((Attachable<Vertex>) attachable, (Graph) hostVertexOrGraph) :
(V) Method.createVertex((Attachable<Vertex>) attachable, (Vertex) hostVertexOrGraph);
} else if (base instanceof Edge) {
return hostVertexOrGraph instanceof Graph ?
(V) Method.createEdge((Attachable<Edge>) attachable, (Graph) hostVertexOrGraph) :
(V) Method.createEdge((Attachable<Edge>) attachable, (Vertex) hostVertexOrGraph);
} else if (base instanceof VertexProperty) {
return hostVertexOrGraph instanceof Graph ?
(V) Method.createVertexProperty((Attachable<VertexProperty>) attachable, (Graph) hostVertexOrGraph) :
(V) Method.createVertexProperty((Attachable<VertexProperty>) attachable, (Vertex) hostVertexOrGraph);
} else if (base instanceof Property) {
return hostVertexOrGraph instanceof Graph ?
(V) Method.createProperty((Attachable<Property>) attachable, (Graph) hostVertexOrGraph) :
(V) Method.createProperty((Attachable<Property>) attachable, (Vertex) hostVertexOrGraph);
} else
throw Attachable.Exceptions.providedAttachableMustContainAGraphObject(attachable);
};
}
///////////////////
///// GET HELPER METHODS
public static Optional<Vertex> getVertex(final Attachable<Vertex> attachableVertex, final Graph hostGraph) {
final Iterator<Vertex> vertexIterator = hostGraph.vertices(attachableVertex.get().id());
return vertexIterator.hasNext() ? Optional.of(vertexIterator.next()) : Optional.empty();
}
public static Optional<Vertex> getVertex(final Attachable<Vertex> attachableVertex, final Vertex hostVertex) {
return ElementHelper.areEqual(attachableVertex.get(), hostVertex) ? Optional.of(hostVertex) : Optional.empty();
}
public static Optional<Edge> getEdge(final Attachable<Edge> attachableEdge, final Graph hostGraph) {
final Iterator<Edge> edgeIterator = hostGraph.edges(attachableEdge.get().id());
return edgeIterator.hasNext() ? Optional.of(edgeIterator.next()) : Optional.empty();
}
public static Optional<Edge> getEdge(final Attachable<Edge> attachableEdge, final Vertex hostVertex) {
final Edge baseEdge = attachableEdge.get();
final Iterator<Edge> edgeIterator = hostVertex.edges(Direction.OUT, attachableEdge.get().label());
while (edgeIterator.hasNext()) {
final Edge edge = edgeIterator.next();
if (ElementHelper.areEqual(edge, baseEdge))
return Optional.of(edge);
}
return Optional.empty();
}
public static Optional<VertexProperty> getVertexProperty(final Attachable<VertexProperty> attachableVertexProperty, final Graph hostGraph) {
final VertexProperty baseVertexProperty = attachableVertexProperty.get();
final Iterator<Vertex> vertexIterator = hostGraph.vertices(baseVertexProperty.element().id());
if (vertexIterator.hasNext()) {
final Iterator<VertexProperty<Object>> vertexPropertyIterator = vertexIterator.next().properties(baseVertexProperty.key());
while (vertexPropertyIterator.hasNext()) {
final VertexProperty vertexProperty = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vertexProperty, baseVertexProperty))
return Optional.of(vertexProperty);
}
}
return Optional.empty();
}
public static Optional<VertexProperty> getVertexProperty(final Attachable<VertexProperty> attachableVertexProperty, final Vertex hostVertex) {
final VertexProperty baseVertexProperty = attachableVertexProperty.get();
final Iterator<VertexProperty<Object>> vertexPropertyIterator = hostVertex.properties(baseVertexProperty.key());
while (vertexPropertyIterator.hasNext()) {
final VertexProperty vertexProperty = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vertexProperty, baseVertexProperty))
return Optional.of(vertexProperty);
}
return Optional.empty();
}
public static Optional<Property> getProperty(final Attachable<Property> attachableProperty, final Graph hostGraph) {
final Property baseProperty = attachableProperty.get();
final Element propertyElement = attachableProperty.get().element();
if (propertyElement instanceof Vertex) {
return (Optional) Method.getVertexProperty((Attachable) attachableProperty, hostGraph);
} else if (propertyElement instanceof Edge) {
final Iterator<Edge> edgeIterator = hostGraph.edges(propertyElement.id());
while (edgeIterator.hasNext()) {
final Property property = edgeIterator.next().property(baseProperty.key());
if (property.isPresent() && property.value().equals(baseProperty.value()))
return Optional.of(property);
}
return Optional.empty();
} else { // vertex property
final Iterator<Vertex> vertexIterator = hostGraph.vertices(((VertexProperty) propertyElement).element().id());
if (vertexIterator.hasNext()) {
final Iterator<VertexProperty<Object>> vertexPropertyIterator = vertexIterator.next().properties();
while (vertexPropertyIterator.hasNext()) {
final VertexProperty vertexProperty = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vertexProperty, baseProperty.element())) {
final Property property = vertexProperty.property(baseProperty.key());
if (property.isPresent() && property.value().equals(baseProperty.value()))
return Optional.of(property);
else
return Optional.empty();
}
}
}
return Optional.empty();
}
}
public static Optional<Property> getProperty(final Attachable<Property> attachableProperty, final Vertex hostVertex) {
final Property baseProperty = attachableProperty.get();
final Element propertyElement = attachableProperty.get().element();
if (propertyElement instanceof Vertex) {
return (Optional) Method.getVertexProperty((Attachable) attachableProperty, hostVertex);
} else if (propertyElement instanceof Edge) {
final Iterator<Edge> edgeIterator = hostVertex.edges(Direction.OUT);
while (edgeIterator.hasNext()) {
final Edge edge = edgeIterator.next();
if (ElementHelper.areEqual(edge, propertyElement)) {
final Property property = edge.property(baseProperty.key());
if (ElementHelper.areEqual(baseProperty, property))
return Optional.of(property);
}
}
return Optional.empty();
} else { // vertex property
final Iterator<VertexProperty<Object>> vertexPropertyIterator = hostVertex.properties();
while (vertexPropertyIterator.hasNext()) {
final VertexProperty vertexProperty = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vertexProperty, baseProperty.element())) {
final Property property = vertexProperty.property(baseProperty.key());
if (property.isPresent() && property.value().equals(baseProperty.value()))
return Optional.of(property);
else
return Optional.empty();
}
}
return Optional.empty();
}
}
///// CREATE HELPER METHODS
public static Vertex createVertex(final Attachable<Vertex> attachableVertex, final Graph hostGraph) {
final Vertex baseVertex = attachableVertex.get();
final Vertex vertex = hostGraph.features().vertex().willAllowId(baseVertex.id()) ?
hostGraph.addVertex(T.id, baseVertex.id(), T.label, baseVertex.label()) :
hostGraph.addVertex(T.label, baseVertex.label());
baseVertex.properties().forEachRemaining(vp -> {
final VertexProperty vertexProperty = hostGraph.features().vertex().properties().willAllowId(vp.id()) ?
vertex.property(hostGraph.features().vertex().getCardinality(vp.key()), vp.key(), vp.value(), T.id, vp.id()) :
vertex.property(hostGraph.features().vertex().getCardinality(vp.key()), vp.key(), vp.value());
vp.properties().forEachRemaining(p -> vertexProperty.property(p.key(), p.value()));
});
return vertex;
}
public static Vertex createVertex(final Attachable<Vertex> attachableVertex, final Vertex hostVertex) {
throw new IllegalStateException("It is not possible to create a vertex at a host vertex");
}
public static Edge createEdge(final Attachable<Edge> attachableEdge, final Graph hostGraph) {
final Edge baseEdge = attachableEdge.get();
Iterator<Vertex> vertices = hostGraph.vertices(baseEdge.outVertex().id());
final Vertex outV = vertices.hasNext() ? vertices.next() : hostGraph.features().vertex().willAllowId(baseEdge.outVertex().id()) ? hostGraph.addVertex(T.id, baseEdge.outVertex().id()) : hostGraph.addVertex();
vertices = hostGraph.vertices(baseEdge.inVertex().id());
final Vertex inV = vertices.hasNext() ? vertices.next() : hostGraph.features().vertex().willAllowId(baseEdge.inVertex().id()) ? hostGraph.addVertex(T.id, baseEdge.inVertex().id()) : hostGraph.addVertex();
if (ElementHelper.areEqual(outV, inV)) {
final Iterator<Edge> itty = outV.edges(Direction.OUT, baseEdge.label());
while (itty.hasNext()) {
final Edge e = itty.next();
if (ElementHelper.areEqual(baseEdge, e))
return e;
}
}
final Edge e = hostGraph.features().edge().willAllowId(baseEdge.id()) ? outV.addEdge(baseEdge.label(), inV, T.id, baseEdge.id()) : outV.addEdge(baseEdge.label(), inV);
baseEdge.properties().forEachRemaining(p -> e.property(p.key(), p.value()));
return e;
}
public static Edge createEdge(final Attachable<Edge> attachableEdge, final Vertex hostVertex) {
return Method.createEdge(attachableEdge, hostVertex.graph()); // TODO (make local to vertex)
}
public static VertexProperty createVertexProperty(final Attachable<VertexProperty> attachableVertexProperty, final Graph hostGraph) {
final VertexProperty<Object> baseVertexProperty = attachableVertexProperty.get();
final Iterator<Vertex> vertexIterator = hostGraph.vertices(baseVertexProperty.element().id());
if (vertexIterator.hasNext()) {
final VertexProperty vertexProperty = hostGraph.features().vertex().properties().willAllowId(baseVertexProperty.id()) ?
vertexIterator.next().property(hostGraph.features().vertex().getCardinality(baseVertexProperty.key()), baseVertexProperty.key(), baseVertexProperty.value(), T.id, baseVertexProperty.id()) :
vertexIterator.next().property(hostGraph.features().vertex().getCardinality(baseVertexProperty.key()), baseVertexProperty.key(), baseVertexProperty.value());
baseVertexProperty.properties().forEachRemaining(p -> vertexProperty.property(p.key(), p.value()));
return vertexProperty;
}
throw new IllegalStateException("Could not find vertex to create the attachable vertex property on");
}
public static VertexProperty createVertexProperty(final Attachable<VertexProperty> attachableVertexProperty, final Vertex hostVertex) {
final VertexProperty<Object> baseVertexProperty = attachableVertexProperty.get();
final VertexProperty vertexProperty = hostVertex.graph().features().vertex().properties().willAllowId(baseVertexProperty.id()) ?
hostVertex.property(hostVertex.graph().features().vertex().getCardinality(baseVertexProperty.key()), baseVertexProperty.key(), baseVertexProperty.value(), T.id, baseVertexProperty.id()) :
hostVertex.property(hostVertex.graph().features().vertex().getCardinality(baseVertexProperty.key()), baseVertexProperty.key(), baseVertexProperty.value());
baseVertexProperty.properties().forEachRemaining(p -> vertexProperty.property(p.key(), p.value()));
return vertexProperty;
}
public static Property createProperty(final Attachable<Property> attachableProperty, final Graph hostGraph) {
final Property baseProperty = attachableProperty.get();
final Element baseElement = baseProperty.element();
if (baseElement instanceof Vertex) {
return Method.createVertexProperty((Attachable) attachableProperty, hostGraph);
} else if (baseElement instanceof Edge) {
final Iterator<Edge> edgeIterator = hostGraph.edges(baseElement.id());
if (edgeIterator.hasNext())
return edgeIterator.next().property(baseProperty.key(), baseProperty.value());
throw new IllegalStateException("Could not find edge to create the attachable property on");
} else { // vertex property
final Iterator<Vertex> vertexIterator = hostGraph.vertices(((VertexProperty) baseElement).element().id());
if (vertexIterator.hasNext()) {
final Vertex vertex = vertexIterator.next();
final Iterator<VertexProperty<Object>> vertexPropertyIterator = vertex.properties(((VertexProperty) baseElement).key());
while (vertexPropertyIterator.hasNext()) {
final VertexProperty<Object> vp = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vp, baseElement))
return vp.property(baseProperty.key(), baseProperty.value());
}
}
throw new IllegalStateException("Could not find vertex property to create the attachable property on");
}
}
public static Property createProperty(final Attachable<Property> attachableProperty, final Vertex hostVertex) {
final Property baseProperty = attachableProperty.get();
final Element baseElement = baseProperty.element();
if (baseElement instanceof Vertex) {
return Method.createVertexProperty((Attachable) attachableProperty, hostVertex);
} else if (baseElement instanceof Edge) {
final Iterator<Edge> edgeIterator = hostVertex.edges(Direction.OUT);
if (edgeIterator.hasNext())
return edgeIterator.next().property(baseProperty.key(), baseProperty.value());
throw new IllegalStateException("Could not find edge to create the property on");
} else { // vertex property
final Iterator<VertexProperty<Object>> vertexPropertyIterator = hostVertex.properties(((VertexProperty) baseElement).key());
while (vertexPropertyIterator.hasNext()) {
final VertexProperty<Object> vp = vertexPropertyIterator.next();
if (ElementHelper.areEqual(vp, baseElement))
return vp.property(baseProperty.key(), baseProperty.value());
}
throw new IllegalStateException("Could not find vertex property to create the attachable property on");
}
}
}
public static class Exceptions {
private Exceptions() {
}
public static IllegalStateException canNotGetAttachableFromHostVertex(final Attachable<?> attachable, final Vertex hostVertex) {
return new IllegalStateException("Can not get the attachable from the host vertex: " + attachable + "-/->" + hostVertex);
}
public static IllegalStateException canNotGetAttachableFromHostGraph(final Attachable<?> attachable, final Graph hostGraph) {
return new IllegalStateException("Can not get the attachable from the host vertex: " + attachable + "-/->" + hostGraph);
}
public static IllegalArgumentException providedAttachableMustContainAGraphObject(final Attachable<?> attachable) {
return new IllegalArgumentException("The provided attachable must contain a graph object: " + attachable);
}
}
}