| /* |
| * 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.tinkergraph.process.computer; |
| |
| import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; |
| 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 org.apache.tinkerpop.gremlin.structure.util.ElementHelper; |
| import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; |
| import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; |
| import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerHelper; |
| import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; |
| import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertexProperty; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| /** |
| * @author Marko A. Rodriguez (http://markorodriguez.com) |
| */ |
| public final class TinkerGraphComputerView { |
| |
| private final TinkerGraph graph; |
| protected final Set<String> computeKeys; |
| private Map<Element, Map<String, List<VertexProperty<?>>>> computeProperties; |
| |
| public TinkerGraphComputerView(final TinkerGraph graph, final Set<String> computeKeys) { |
| this.graph = graph; |
| this.computeKeys = computeKeys; |
| this.computeProperties = new ConcurrentHashMap<>(); |
| } |
| |
| public <V> Property<V> addProperty(final TinkerVertex vertex, final String key, final V value) { |
| ElementHelper.validateProperty(key, value); |
| if (isComputeKey(key)) { |
| final TinkerVertexProperty<V> property = new TinkerVertexProperty<V>((TinkerVertex) vertex, key, value) { |
| @Override |
| public void remove() { |
| removeProperty(vertex, key, this); |
| } |
| }; |
| this.addValue(vertex, key, property); |
| return property; |
| } else { |
| throw GraphComputer.Exceptions.providedKeyIsNotAnElementComputeKey(key); |
| } |
| } |
| |
| public List<VertexProperty<?>> getProperty(final TinkerVertex vertex, final String key) { |
| return isComputeKey(key) ? this.getValue(vertex, key) : (List) TinkerHelper.getProperties(vertex).getOrDefault(key, Collections.emptyList()); |
| } |
| |
| public List<Property> getProperties(final TinkerVertex vertex) { |
| final Stream<Property> a = TinkerHelper.getProperties(vertex).values().stream().flatMap(list -> list.stream()); |
| final Stream<Property> b = this.computeProperties.containsKey(vertex) ? |
| this.computeProperties.get(vertex).values().stream().flatMap(list -> list.stream()) : |
| Stream.empty(); |
| return Stream.concat(a, b).collect(Collectors.toList()); |
| } |
| |
| public void removeProperty(final TinkerVertex vertex, final String key, final VertexProperty property) { |
| if (isComputeKey(key)) { |
| this.removeValue(vertex, key, property); |
| } else { |
| throw GraphComputer.Exceptions.providedKeyIsNotAnElementComputeKey(key); |
| } |
| } |
| |
| ////////////////////// |
| |
| public Graph processResultGraphPersist(final GraphComputer.ResultGraph resultGraph, final GraphComputer.Persist persist) { |
| if (GraphComputer.Persist.NOTHING == persist) { |
| if (GraphComputer.ResultGraph.ORIGINAL == resultGraph) |
| return this.graph; |
| else |
| return EmptyGraph.instance(); |
| } else if (GraphComputer.Persist.VERTEX_PROPERTIES == persist) { |
| if (GraphComputer.ResultGraph.ORIGINAL == resultGraph) { |
| this.addPropertiesToOriginalGraph(); |
| return this.graph; |
| } else { |
| final TinkerGraph newGraph = TinkerGraph.open(); |
| this.graph.vertices().forEachRemaining(vertex -> { |
| final Vertex newVertex = newGraph.addVertex(T.id, vertex.id(), T.label, vertex.label()); |
| vertex.properties().forEachRemaining(vertexProperty -> { |
| final VertexProperty<?> newVertexProperty = newVertex.property(VertexProperty.Cardinality.list, vertexProperty.key(), vertexProperty.value(), T.id, vertexProperty.id()); |
| vertexProperty.properties().forEachRemaining(property -> { |
| newVertexProperty.property(property.key(), property.value()); |
| }); |
| }); |
| }); |
| return newGraph; |
| } |
| } else { // Persist.EDGES |
| if (GraphComputer.ResultGraph.ORIGINAL == resultGraph) { |
| this.addPropertiesToOriginalGraph(); |
| return this.graph; |
| } else { |
| final TinkerGraph newGraph = TinkerGraph.open(); |
| this.graph.vertices().forEachRemaining(vertex -> { |
| final Vertex newVertex = newGraph.addVertex(T.id, vertex.id(), T.label, vertex.label()); |
| vertex.properties().forEachRemaining(vertexProperty -> { |
| final VertexProperty<?> newVertexProperty = newVertex.property(VertexProperty.Cardinality.list, vertexProperty.key(), vertexProperty.value(), T.id, vertexProperty.id()); |
| vertexProperty.properties().forEachRemaining(property -> { |
| newVertexProperty.property(property.key(), property.value()); |
| }); |
| }); |
| }); |
| this.graph.edges().forEachRemaining(edge -> { |
| final Vertex outVertex = newGraph.vertices(edge.outVertex().id()).next(); |
| final Vertex inVertex = newGraph.vertices(edge.inVertex().id()).next(); |
| final Edge newEdge = outVertex.addEdge(edge.label(), inVertex, T.id, edge.id()); |
| edge.properties().forEachRemaining(property -> newEdge.property(property.key(), property.value())); |
| }); |
| return newGraph; |
| } |
| } |
| } |
| |
| private void addPropertiesToOriginalGraph() { |
| TinkerHelper.dropGraphComputerView(this.graph); |
| this.computeProperties.forEach((element, properties) -> { |
| properties.forEach((key, vertexProperties) -> { |
| vertexProperties.forEach(vertexProperty -> { |
| final VertexProperty<?> newVertexProperty = ((Vertex) element).property(VertexProperty.Cardinality.list, vertexProperty.key(), vertexProperty.value(), T.id, vertexProperty.id()); |
| vertexProperty.properties().forEachRemaining(property -> { |
| newVertexProperty.property(property.key(), property.value()); |
| }); |
| }); |
| }); |
| }); |
| this.computeProperties.clear(); |
| } |
| |
| ////////////////////// |
| |
| private boolean isComputeKey(final String key) { |
| return this.computeKeys.contains(key); |
| } |
| |
| private void addValue(final Vertex vertex, final String key, final VertexProperty property) { |
| final Map<String, List<VertexProperty<?>>> elementProperties = this.computeProperties.computeIfAbsent(vertex, k -> new ConcurrentHashMap<>()); |
| elementProperties.compute(key, (k, v) -> { |
| if (null == v) v = Collections.synchronizedList(new ArrayList<>()); |
| v.add(property); |
| return v; |
| }); |
| } |
| |
| private void removeValue(final Vertex vertex, final String key, final VertexProperty property) { |
| this.computeProperties.computeIfPresent(vertex, (k, v) -> { |
| v.computeIfPresent(key, (k1, v1) -> { |
| v1.remove(property); |
| return v1; |
| }); |
| return v; |
| }); |
| } |
| |
| private List<VertexProperty<?>> getValue(final Vertex vertex, final String key) { |
| return this.computeProperties.getOrDefault(vertex, Collections.emptyMap()).getOrDefault(key, Collections.emptyList()); |
| } |
| } |