| /* |
| * 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); |
| } |
| } |
| |
| } |