blob: f51663055068b592135184bf86bfea5c19f3c31f [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.star;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
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.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.Attachable;
import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
/**
* A {@code StarGraph} is a form of {@link Attachable} (though the {@link Graph} implementation does not implement
* that interface itself). It is a very limited {@link Graph} implementation that holds a single {@link Vertex}
* and its related properties and edges (and their properties). It is designed to be an efficient memory
* representation of this data structure, thus making it good for network and disk-based serialization.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public final class StarGraph implements Graph, Serializable {
private static final Configuration STAR_GRAPH_CONFIGURATION = new BaseConfiguration();
static {
STAR_GRAPH_CONFIGURATION.setProperty(Graph.GRAPH, StarGraph.class.getCanonicalName());
}
protected Long nextId = 0l;
protected StarVertex starVertex = null;
protected Map<Object, Map<String, Object>> edgeProperties = null;
protected Map<Object, Map<String, Object>> metaProperties = null;
private StarGraph() {
}
/**
* Gets the {@link Vertex} representative of the {@link StarGraph}.
*/
public StarVertex getStarVertex() {
return this.starVertex;
}
private Long nextId() {
return this.nextId++;
}
@Override
public Vertex addVertex(final Object... keyValues) {
if (null == this.starVertex) {
ElementHelper.legalPropertyKeyValueArray(keyValues);
this.starVertex = new StarVertex(ElementHelper.getIdValue(keyValues).orElse(this.nextId()), ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL));
ElementHelper.attachProperties(this.starVertex, VertexProperty.Cardinality.list, keyValues); // TODO: is this smart? I say no... cause vertex property ids are not preserved.
return this.starVertex;
} else
return new StarAdjacentVertex(ElementHelper.getIdValue(keyValues).orElse(this.nextId()));
}
@Override
public <C extends GraphComputer> C compute(final Class<C> graphComputerClass) throws IllegalArgumentException {
throw Graph.Exceptions.graphComputerNotSupported();
}
@Override
public GraphComputer compute() throws IllegalArgumentException {
throw Graph.Exceptions.graphComputerNotSupported();
}
@Override
public Iterator<Vertex> vertices(final Object... vertexIds) {
if (null == this.starVertex)
return Collections.emptyIterator();
else if (vertexIds.length > 0 && vertexIds[0] instanceof StarVertex)
return Stream.of(vertexIds).map(v -> (Vertex) v).iterator(); // todo: maybe do this better - not sure of star semantics here
else if (ElementHelper.idExists(this.starVertex.id(), vertexIds))
return IteratorUtils.of(this.starVertex);
else
return Collections.emptyIterator();
// TODO: is this the semantics we want? the only "real vertex" is star vertex.
/*return null == this.starVertex ?
Collections.emptyIterator() :
Stream.concat(
Stream.of(this.starVertex),
Stream.concat(
this.starVertex.outEdges.values()
.stream()
.flatMap(List::stream)
.map(Edge::inVertex),
this.starVertex.inEdges.values()
.stream()
.flatMap(List::stream)
.map(Edge::outVertex)))
.filter(vertex -> ElementHelper.idExists(vertex.id(), vertexIds))
.iterator();*/
}
@Override
public Iterator<Edge> edges(final Object... edgeIds) {
return null == this.starVertex ?
Collections.emptyIterator() :
Stream.concat(
null == this.starVertex.inEdges ? Stream.empty() : this.starVertex.inEdges.values().stream(),
null == this.starVertex.outEdges ? Stream.empty() : this.starVertex.outEdges.values().stream())
.flatMap(List::stream)
.filter(edge -> {
// todo: kinda fishy - need to better nail down how stuff should work here - none of these feel consistent right now.
if (edgeIds.length > 0 && edgeIds[0] instanceof Edge)
return ElementHelper.idExists(edge.id(), Stream.of(edgeIds).map(e -> ((Edge) e).id()).toArray());
else
return ElementHelper.idExists(edge.id(), edgeIds);
})
.iterator();
}
@Override
public Transaction tx() {
throw Graph.Exceptions.transactionsNotSupported();
}
@Override
public Variables variables() {
throw Graph.Exceptions.variablesNotSupported();
}
@Override
public Configuration configuration() {
return STAR_GRAPH_CONFIGURATION;
}
@Override
public Features features() {
return StarGraphFeatures.INSTANCE;
}
@Override
public void close() throws Exception {
}
@Override
public String toString() {
return StringFactory.graphString(this, "starOf:" + this.starVertex);
}
/**
* Creates an empty {@link StarGraph}.
*/
public static StarGraph open() {
return new StarGraph();
}
/**
* Creates a new {@link StarGraph} from a {@link Vertex}.
*/
public static StarGraph of(final Vertex vertex) {
if (vertex instanceof StarVertex) return (StarGraph) vertex.graph();
// else convert to a star graph
final StarGraph starGraph = new StarGraph();
final StarVertex starVertex = (StarVertex) starGraph.addVertex(T.id, vertex.id(), T.label, vertex.label());
final boolean supportsMetaProperties = vertex.graph().features().vertex().supportsMetaProperties();
vertex.properties().forEachRemaining(vp -> {
final VertexProperty<?> starVertexProperty = starVertex.property(VertexProperty.Cardinality.list, vp.key(), vp.value(), T.id, vp.id());
if (supportsMetaProperties)
vp.properties().forEachRemaining(p -> starVertexProperty.property(p.key(), p.value()));
});
vertex.edges(Direction.IN).forEachRemaining(edge -> {
final Edge starEdge = starVertex.addInEdge(edge.label(), starGraph.addVertex(T.id, edge.outVertex().id()), T.id, edge.id());
edge.properties().forEachRemaining(p -> starEdge.property(p.key(), p.value()));
});
vertex.edges(Direction.OUT).forEachRemaining(edge -> {
final Edge starEdge = starVertex.addOutEdge(edge.label(), starGraph.addVertex(T.id, edge.inVertex().id()), T.id, edge.id());
edge.properties().forEachRemaining(p -> starEdge.property(p.key(), p.value()));
});
return starGraph;
}
///////////////////////
//// STAR ELEMENT ////
//////////////////////
public abstract class StarElement<E extends Element> implements Element, Attachable<E> {
protected final Object id;
protected final String label;
protected StarElement(final Object id, final String label) {
this.id = id;
this.label = label.intern();
}
@Override
public Object id() {
return this.id;
}
@Override
public String label() {
return this.label;
}
@Override
public Graph graph() {
return StarGraph.this;
}
@Override
public boolean equals(final Object other) {
return ElementHelper.areEqual(this, other);
}
@Override
public int hashCode() {
return ElementHelper.hashCode(this);
}
@Override
public E get() {
return (E) this;
}
}
//////////////////////
//// STAR VERTEX ////
/////////////////////
public final class StarVertex extends StarElement<Vertex> implements Vertex {
protected Map<String, List<Edge>> outEdges = null;
protected Map<String, List<Edge>> inEdges = null;
protected Map<String, List<VertexProperty>> vertexProperties = null;
public StarVertex(final Object id, final String label) {
super(id, label);
}
public void dropEdges() {
if (null != this.outEdges) this.outEdges.clear();
if (null != this.inEdges) this.inEdges.clear();
this.outEdges = null;
this.inEdges = null;
}
public void dropVertexProperties(final String... propertyKeys) {
if (null != this.vertexProperties) {
for (final String key : propertyKeys) {
this.vertexProperties.remove(key);
}
}
}
@Override
public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues) {
final Edge edge = this.addOutEdge(label, inVertex, keyValues);
if (inVertex.equals(this)) {
if(null == this.inEdges)
this.inEdges = new HashMap<>();
List<Edge> inE = this.inEdges.get(label);
if (null == inE) {
inE = new ArrayList<>();
this.inEdges.put(label, inE);
}
inE.add(edge);
}
return edge;
}
@Override
public <V> VertexProperty<V> property(final String key, final V value, final Object... keyValues) {
ElementHelper.validateProperty(key, value);
ElementHelper.legalPropertyKeyValueArray(keyValues);
return this.property(VertexProperty.Cardinality.single, key, value, keyValues);
}
Edge addOutEdge(final String label, final Vertex inVertex, final Object... keyValues) {
ElementHelper.validateLabel(label);
ElementHelper.legalPropertyKeyValueArray(keyValues);
if (null == this.outEdges)
this.outEdges = new HashMap<>();
List<Edge> outE = this.outEdges.get(label);
if (null == outE) {
outE = new ArrayList<>();
this.outEdges.put(label, outE);
}
final StarEdge outEdge = new StarOutEdge(ElementHelper.getIdValue(keyValues).orElse(nextId()), label, inVertex.id());
ElementHelper.attachProperties(outEdge, keyValues);
outE.add(outEdge);
return outEdge;
}
Edge addInEdge(final String label, final Vertex outVertex, final Object... keyValues) {
ElementHelper.validateLabel(label);
ElementHelper.legalPropertyKeyValueArray(keyValues);
if (null == this.inEdges)
this.inEdges = new HashMap<>();
List<Edge> inE = this.inEdges.get(label);
if (null == inE) {
inE = new ArrayList<>();
this.inEdges.put(label, inE);
}
final StarEdge inEdge = new StarInEdge(ElementHelper.getIdValue(keyValues).orElse(nextId()), label, outVertex.id());
ElementHelper.attachProperties(inEdge, keyValues);
inE.add(inEdge);
return inEdge;
}
@Override
public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, V value, final Object... keyValues) {
ElementHelper.legalPropertyKeyValueArray(keyValues);
if (null == this.vertexProperties)
this.vertexProperties = new HashMap<>();
final List<VertexProperty> list = cardinality.equals(VertexProperty.Cardinality.single) ? new ArrayList<>(1) : this.vertexProperties.getOrDefault(key, new ArrayList<>());
final VertexProperty<V> vertexProperty = new StarVertexProperty<>(ElementHelper.getIdValue(keyValues).orElse(nextId()), key, value);
ElementHelper.attachProperties(vertexProperty, keyValues);
list.add(vertexProperty);
this.vertexProperties.put(key, list);
return vertexProperty;
}
@Override
public Iterator<Edge> edges(final Direction direction, final String... edgeLabels) {
if (direction.equals(Direction.OUT)) {
return null == this.outEdges ? Collections.emptyIterator() : edgeLabels.length == 0 ?
IteratorUtils.flatMap(this.outEdges.values().iterator(), List::iterator) :
this.outEdges.entrySet().stream()
.filter(entry -> ElementHelper.keyExists(entry.getKey(), edgeLabels))
.map(Map.Entry::getValue)
.flatMap(List::stream)
.iterator();
} else if (direction.equals(Direction.IN)) {
return null == this.inEdges ? Collections.emptyIterator() : edgeLabels.length == 0 ?
IteratorUtils.flatMap(this.inEdges.values().iterator(), List::iterator) :
this.inEdges.entrySet().stream()
.filter(entry -> ElementHelper.keyExists(entry.getKey(), edgeLabels))
.map(Map.Entry::getValue)
.flatMap(List::stream)
.iterator();
} else
return IteratorUtils.concat(this.edges(Direction.IN, edgeLabels), this.edges(Direction.OUT, edgeLabels));
}
@Override
public Iterator<Vertex> vertices(final Direction direction, final String... edgeLabels) {
if (direction.equals(Direction.OUT))
return IteratorUtils.map(this.edges(direction, edgeLabels), Edge::inVertex);
else if (direction.equals(Direction.IN))
return IteratorUtils.map(this.edges(direction, edgeLabels), Edge::outVertex);
else
return IteratorUtils.concat(this.vertices(Direction.IN, edgeLabels), this.vertices(Direction.OUT, edgeLabels));
}
@Override
public void remove() {
throw new IllegalStateException("The star vertex can not be removed from the StarGraph: " + this);
}
@Override
public String toString() {
return StringFactory.vertexString(this);
}
@Override
public <V> Iterator<VertexProperty<V>> properties(final String... propertyKeys) {
if (null == this.vertexProperties || this.vertexProperties.isEmpty())
return Collections.emptyIterator();
else if (propertyKeys.length == 0)
return (Iterator) this.vertexProperties.entrySet().stream()
.flatMap(entry -> entry.getValue().stream())
.iterator();
else if (propertyKeys.length == 1)
return (Iterator) this.vertexProperties.getOrDefault(propertyKeys[0], Collections.emptyList()).iterator();
else
return (Iterator) this.vertexProperties.entrySet().stream()
.filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys))
.flatMap(entry -> entry.getValue().stream())
.iterator();
}
}
///////////////////////////////
//// STAR VERTEX PROPERTY ////
//////////////////////////////
public final class StarVertexProperty<V> extends StarElement<VertexProperty<V>> implements VertexProperty<V> {
private final V value;
private StarVertexProperty(final Object id, final String key, final V value) {
super(id, key);
this.value = value;
}
@Override
public String key() {
return this.label();
}
@Override
public V value() throws NoSuchElementException {
return this.value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Vertex element() {
return StarGraph.this.starVertex;
}
@Override
public void remove() {
if (null != StarGraph.this.starVertex.vertexProperties)
StarGraph.this.starVertex.vertexProperties.get(this.label()).remove(this);
}
@Override
public <U> Iterator<Property<U>> properties(final String... propertyKeys) {
final Map<String, Object> properties = null == metaProperties ? null : metaProperties.get(this.id);
if (null == properties || properties.isEmpty())
return Collections.emptyIterator();
else if (propertyKeys.length == 0)
return (Iterator) properties.entrySet().stream()
.map(entry -> new StarProperty<>(entry.getKey(), entry.getValue(), this))
.iterator();
else if (propertyKeys.length == 1) {
final Object v = properties.get(propertyKeys[0]);
return null == v ?
Collections.emptyIterator() :
(Iterator) IteratorUtils.of(new StarProperty<>(propertyKeys[0], v, this));
} else {
return (Iterator) properties.entrySet().stream()
.filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys))
.map(entry -> new StarProperty<>(entry.getKey(), entry.getValue(), this))
.iterator();
}
}
@Override
public <U> Property<U> property(final String key, final U value) {
if (null == metaProperties)
metaProperties = new HashMap<>();
Map<String, Object> properties = metaProperties.get(this.id);
if (null == properties) {
properties = new HashMap<>();
metaProperties.put(this.id, properties);
}
properties.put(key, value);
return new StarProperty<>(key, value, this);
}
@Override
public String toString() {
return StringFactory.propertyString(this);
}
}
///////////////////////////////
//// STAR ADJACENT VERTEX ////
//////////////////////////////
public class StarAdjacentVertex implements Vertex {
private final Object id;
private StarAdjacentVertex(final Object id) {
this.id = id;
}
@Override
public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues) {
if (inVertex.equals(starVertex))
return starVertex.addInEdge(label, this, keyValues);
else
throw GraphComputer.Exceptions.adjacentVertexEdgesAndVerticesCanNotBeReadOrUpdated();
}
@Override
public <V> VertexProperty<V> property(final String key, final V value, final Object... keyValues) {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public Iterator<Edge> edges(final Direction direction, final String... edgeLabels) {
throw GraphComputer.Exceptions.adjacentVertexEdgesAndVerticesCanNotBeReadOrUpdated();
}
@Override
public Iterator<Vertex> vertices(final Direction direction, final String... edgeLabels) {
throw GraphComputer.Exceptions.adjacentVertexEdgesAndVerticesCanNotBeReadOrUpdated();
}
@Override
public Object id() {
return this.id;
}
@Override
public String label() {
throw GraphComputer.Exceptions.adjacentVertexLabelsCanNotBeRead();
}
@Override
public Graph graph() {
return StarGraph.this;
}
@Override
public void remove() {
throw Vertex.Exceptions.vertexRemovalNotSupported();
}
@Override
public <V> Iterator<VertexProperty<V>> properties(final String... propertyKeys) {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public boolean equals(final Object other) {
return ElementHelper.areEqual(this, other);
}
@Override
public int hashCode() {
return ElementHelper.hashCode(this);
}
@Override
public String toString() {
return StringFactory.vertexString(this);
}
}
////////////////////
//// STAR EDGE ////
///////////////////
public abstract class StarEdge extends StarElement<Edge> implements Edge {
protected final Object otherId;
private StarEdge(final Object id, final String label, final Object otherId) {
super(id, label);
this.otherId = otherId;
}
@Override
public <V> Property<V> property(final String key, final V value) {
ElementHelper.validateProperty(key, value);
if (null == edgeProperties)
edgeProperties = new HashMap<>();
Map<String, Object> properties = edgeProperties.get(this.id);
if (null == properties) {
properties = new HashMap<>();
edgeProperties.put(this.id, properties);
}
properties.put(key, value);
return new StarProperty<>(key, value, this);
}
@Override
public <V> Iterator<Property<V>> properties(final String... propertyKeys) {
Map<String, Object> properties = null == edgeProperties ? null : edgeProperties.get(this.id);
if (null == properties || properties.isEmpty())
return Collections.emptyIterator();
else if (propertyKeys.length == 0)
return (Iterator) properties.entrySet().stream()
.map(entry -> new StarProperty<>(entry.getKey(), entry.getValue(), this))
.iterator();
else if (propertyKeys.length == 1) {
final Object v = properties.get(propertyKeys[0]);
return null == v ?
Collections.emptyIterator() :
(Iterator) IteratorUtils.of(new StarProperty<>(propertyKeys[0], v, this));
} else {
return (Iterator) properties.entrySet().stream()
.filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys))
.map(entry -> new StarProperty<>(entry.getKey(), entry.getValue(), this))
.iterator();
}
}
@Override
public Iterator<Vertex> vertices(final Direction direction) {
if (direction.equals(Direction.OUT))
return IteratorUtils.of(this.outVertex());
else if (direction.equals(Direction.IN))
return IteratorUtils.of(this.inVertex());
else
return IteratorUtils.of(this.outVertex(), this.inVertex());
}
@Override
public void remove() {
throw Edge.Exceptions.edgeRemovalNotSupported();
}
@Override
public String toString() {
return StringFactory.edgeString(this);
}
}
public final class StarOutEdge extends StarEdge {
private StarOutEdge(final Object id, final String label, final Object otherId) {
super(id, label, otherId);
}
@Override
public Vertex outVertex() {
return starVertex;
}
@Override
public Vertex inVertex() {
return new StarAdjacentVertex(this.otherId);
}
}
public final class StarInEdge extends StarEdge {
private StarInEdge(final Object id, final String label, final Object otherId) {
super(id, label, otherId);
}
@Override
public Vertex outVertex() {
return new StarAdjacentVertex(this.otherId);
}
@Override
public Vertex inVertex() {
return starVertex;
}
}
////////////////////////
//// STAR PROPERTY ////
///////////////////////
public final class StarProperty<V> implements Property<V>, Attachable<Property<V>> {
private final String key;
private final V value;
private final Element element;
private StarProperty(final String key, final V value, final Element element) {
this.key = key.intern();
this.value = value;
this.element = element;
}
@Override
public String key() {
return this.key;
}
@Override
public V value() throws NoSuchElementException {
return this.value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Element element() {
return this.element;
}
@Override
public void remove() {
throw Property.Exceptions.propertyRemovalNotSupported();
}
@Override
public String toString() {
return StringFactory.propertyString(this);
}
@Override
public boolean equals(final Object object) {
return ElementHelper.areEqual(this, object);
}
@Override
public int hashCode() {
return ElementHelper.hashCode(this);
}
@Override
public Property<V> get() {
return this;
}
}
public static class StarGraphFeatures implements Features {
public static final StarGraphFeatures INSTANCE = new StarGraphFeatures();
private StarGraphFeatures() {
}
@Override
public GraphFeatures graph() {
return StarGraphGraphFeatures.INSTANCE;
}
@Override
public EdgeFeatures edge() {
return StarGraphEdgeFeatures.INSTANCE;
}
@Override
public VertexFeatures vertex() {
return StarGraphVertexFeatures.INSTANCE;
}
@Override
public String toString() {
return StringFactory.featureString(this);
}
}
static class StarGraphVertexFeatures implements Features.VertexFeatures {
public static final StarGraphVertexFeatures INSTANCE = new StarGraphVertexFeatures();
private StarGraphVertexFeatures() {
}
@Override
public Features.VertexPropertyFeatures properties() {
return StarGraphVertexPropertyFeatures.INSTANCE;
}
@Override
public boolean supportsCustomIds() {
return false;
}
@Override
public boolean willAllowId(final Object id) {
return true;
}
}
static class StarGraphEdgeFeatures implements Features.EdgeFeatures {
public static final StarGraphEdgeFeatures INSTANCE = new StarGraphEdgeFeatures();
private StarGraphEdgeFeatures() {
}
@Override
public boolean supportsCustomIds() {
return false;
}
@Override
public boolean willAllowId(final Object id) {
return true;
}
}
static class StarGraphGraphFeatures implements Features.GraphFeatures {
public static final StarGraphGraphFeatures INSTANCE = new StarGraphGraphFeatures();
private StarGraphGraphFeatures() {
}
@Override
public boolean supportsTransactions() {
return false;
}
@Override
public boolean supportsPersistence() {
return false;
}
@Override
public boolean supportsThreadedTransactions() {
return false;
}
}
static class StarGraphVertexPropertyFeatures implements Features.VertexPropertyFeatures {
public static final StarGraphVertexPropertyFeatures INSTANCE = new StarGraphVertexPropertyFeatures();
private StarGraphVertexPropertyFeatures() {
}
@Override
public boolean supportsCustomIds() {
return false;
}
@Override
public boolean willAllowId(final Object id) {
return true;
}
}
}