Java tutorial
/** * Copyright: (c) Syncleus, Inc. * * You may redistribute and modify this source code under the terms and * conditions of the Open Source Community License - Type C version 1.0 * or any later version as published by Syncleus, Inc. at www.syncleus.com. * There should be a copy of the license included with this file. If a copy * of the license is not included you are granted no right to distribute or * otherwise use this file except through a legal and valid license. You * should also contact Syncleus, Inc. at the information below if you cannot * find a license: * * Syncleus, Inc. * 2604 South 12th Street * Philadelphia, PA 19148 */ package com.syncleus.spangraph; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.primitives.Bytes; import com.google.common.primitives.Longs; import com.tinkerpop.blueprints.*; import com.tinkerpop.blueprints.util.*; import org.infinispan.commons.util.InfinispanCollections; import org.infinispan.commons.util.WeakValueHashMap; import java.io.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Blueprints Graph interface with adjacency implemented by some Map implementation. * Iterables in Elements, Vertices, and Edges are implemented by guava Iterator/Iterable lazy wrappers, avoiding collection allocation */ abstract public class MapGraph<X extends Serializable> implements Graph { final private static Map<String, MapGraph> global = new WeakValueHashMap<>(); /** blank string used if null labels are allowed and none is specified */ public static final String DEFAULT_EDGE_LABEL = ""; /** name of the graph combined with the peerID */ public final String id; /** name of the graph as shared on the network */ public final String globalID; protected Map<X, Vertex> vertices; protected Map<X, Edge> edges; //protected Map<String, TinkerIndex> indices = new HashMap<String, TinkerIndex>(); //protected TinkerKeyIndex<InfiniVertex> vertexKeyIndex = new TinkerKeyIndex<InfiniVertex>(InfiniVertex.class, this); //protected TinkerKeyIndex<InfiniEdge> edgeKeyIndex = new TinkerKeyIndex<InfiniEdge>(InfiniEdge.class, this); private static final Features FEATURES = new Features(); static { FEATURES.supportsDuplicateEdges = true; FEATURES.supportsSelfLoops = true; FEATURES.supportsSerializableObjectProperty = true; FEATURES.supportsBooleanProperty = true; FEATURES.supportsDoubleProperty = true; FEATURES.supportsFloatProperty = true; FEATURES.supportsIntegerProperty = true; FEATURES.supportsPrimitiveArrayProperty = true; FEATURES.supportsUniformListProperty = true; FEATURES.supportsMixedListProperty = true; FEATURES.supportsLongProperty = true; FEATURES.supportsMapProperty = true; FEATURES.supportsStringProperty = true; FEATURES.ignoresSuppliedIds = false; FEATURES.isPersistent = false; FEATURES.isWrapper = false; FEATURES.supportsIndices = false; FEATURES.supportsKeyIndices = false; FEATURES.supportsVertexKeyIndex = false; FEATURES.supportsEdgeKeyIndex = false; FEATURES.supportsVertexIndex = false; FEATURES.supportsEdgeIndex = false; FEATURES.supportsTransactions = false; FEATURES.supportsVertexIteration = true; FEATURES.supportsEdgeIteration = true; FEATURES.supportsEdgeRetrieval = true; FEATURES.supportsVertexProperties = true; FEATURES.supportsEdgeProperties = true; FEATURES.supportsThreadedTransactions = false; FEATURES.supportsThreadIsolatedTransactions = false; } private boolean requireEdgeLabels = false; public int vertexCount() { return vertexCollection().size(); } public int edgeCount() { return edgeCollection().size(); } protected MapGraph() { super(); this.id = this.globalID = null; } protected MapGraph(String id) { this(id, id); } protected MapGraph(String globalID, String peerID) { super(); this.globalID = globalID; this.id = globalID + ':' + peerID; if (global.put(id, this) != null) throw new RuntimeException("graph " + id + " already exists"); } /** call this at the end of implementing class constructors */ protected void init() { this.vertices = newVertexMap(); this.edges = newEdgeMap(); } protected abstract Map<X, Edge> newEdgeMap(); protected abstract Map<X, Vertex> newVertexMap(); public String newID() { String r; do { long low = UUID.randomUUID().getLeastSignificantBits(); long high = UUID.randomUUID().getMostSignificantBits(); r = new String( Base64.getEncoder().encode(Bytes.concat(Longs.toByteArray(low), Longs.toByteArray(high)))); } while (this.vertices.containsKey(r)); return r; } public void setRequireEdgeLabels(boolean requireEdgeLabels) { this.requireEdgeLabels = requireEdgeLabels; } public MVertex<X> addVertex(Object id) { if (null == id) { id = newID(); } else { if (this.vertices.containsKey(id)) { throw ExceptionFactory.vertexWithIdAlreadyExists(id); } } MVertex<X> vertex = new MVertex<X>((X) id, this, newVertexEdgeMap(), newVertexEdgeMap()); this.vertices.put(vertex.id, vertex); return vertex; } public MVertex<X> getVertex(final Object id) { if (null == id) throw ExceptionFactory.vertexIdCanNotBeNull(); return (MVertex<X>) this.vertices.get(id); } public MEdge<X> getEdge(final Object id) { if (null == id) throw ExceptionFactory.edgeIdCanNotBeNull(); return (MEdge<X>) this.edges.get(id); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof MapGraph)) return false; MapGraph m = (MapGraph) obj; if ((vertices.size() != m.vertices.size()) || (edges.size() != m.edges.size())) return false; return m.vertexSet().equals(vertexSet()) && m.edgeSet().equals(edgeSet()); } public Set<MVertex<X>> differentVertices(MapGraph<X> y) { Set<MVertex<X>> xv = vertexSet(); Set<MVertex<X>> yv = y.vertexSet(); return Sets.difference(xv, yv); } public Set<MEdge<X>> differentEdges(MapGraph<X> y) { Set<MEdge<X>> xe = edgeSet(); Set<MEdge<X>> ye = y.edgeSet(); return Sets.difference(xe, ye); } //TODO deepEquals which will compare the properties of each vertex and edge public Collection<Vertex> vertexCollection() { return vertices.values(); } public Collection<Edge> edgeCollection() { return edges.values(); } public Set<MVertex<X>> vertexSet() { return new HashSet(vertices.values()); } public TreeSet<MVertex<X>> vertexSetSorted() { return new TreeSet(vertices.values()); } public Set<MEdge<X>> edgeSet() { return new HashSet(edges.values()); } public TreeSet<MEdge<X>> edgeSetSorted() { return new TreeSet(edges.values()); } public Iterable<Vertex> getVertices() { //Unmodifiable? return this.vertices.values(); } /* * Return an iterable to all the vertices in the graph that have a particular key/value property. * If this is not possible for the implementation, then an UnsupportedOperationException can be thrown. * The graph implementation should use indexing structures to make this efficient else a full vertex-filter scan is required. */ @Override public Iterable<Vertex> getVertices(String s, Object o) { //TODO write a subclass with a property->vertex reverse index return Iterables.filter(getVertices(), v -> ((MVertex) v).hasProperty(s, o)); } public Iterable<Edge> getEdges() { ////Unmodifiable? return this.edges.values(); } @Override public Iterable<Edge> getEdges(String s, Object o) { //TODO write a subclass with a property->edge reverse index return Iterables.filter(getEdges(), e -> ((MEdge) e).hasProperty(s, o)); } public void removeVertex(final Vertex vertex) { if (null == this.vertices.remove(vertex.getId())) throw ExceptionFactory.vertexWithIdDoesNotExist(vertex.getId()); for (Edge edge : vertex.getEdges(Direction.BOTH)) { this.removeEdge(edge); } this.vertices.remove(vertex.getId()); } public Edge addEdge(final Object edgeID, Object out, Object in) { final Vertex o = getVertex(out); if (o == null) throw new RuntimeException("Unknown source vertex " + out); final Vertex i = getVertex(in); if (i == null) throw new RuntimeException("Unknown target vertex " + in); return addEdge(edgeID, o, i, null); } public Edge addEdge(Object id, final Vertex outVertex, final Vertex inVertex, String label) { if (requireEdgeLabels && label == null) throw ExceptionFactory.edgeLabelCanNotBeNull(); if (null != id) { if (this.edges.containsKey(id)) { throw ExceptionFactory.edgeWithIdAlreadyExist(id); } } else { String nid = newID(); id = nid; } if (label == null) label = DEFAULT_EDGE_LABEL; MEdge<X> edge = new MEdge<X>((X) id, (MVertex) outVertex, (MVertex) inVertex, label, this); this.edges.put(edge.id, edge); ((MVertex) outVertex).add(edge, true); ((MVertex) inVertex).add(edge, false); return edge; } public void removeEdge(final Edge edge) { if (null == this.edges.remove(edge.getId())) { return; } MVertex<X> outVertex = ((MEdge) edge).outVertex; MVertex<X> inVertex = ((MEdge) edge).inVertex; if (null != outVertex && null != outVertex.outEdges) { if (!outVertex.remove(edge, true)) throw new RuntimeException("edge " + edge + " unable not removed from outVertex + " + outVertex); } if (null != inVertex && null != inVertex.inEdges) { if (!inVertex.remove(edge, false)) throw new RuntimeException("edge " + edge + " unable not removed from inVertex + " + outVertex); } } public GraphQuery query() { return new DefaultGraphQuery(this); } public String toString() { return StringFactory.graphString(this, "vertices:" + this.vertices.size() + " edges:" + this.edges.size()); } public void clear() { this.vertices.clear(); this.edges.clear(); } public void shutdown() { } // private String getNextId() { // String idString; // while (true) { // idString = this.currentId.toString(); // this.currentId++; // if (null == this.vertices.get(idString) || null == this.edges.get(idString) || this.currentId == Long.MAX_VALUE) // break; // } // return idString; // } public Features getFeatures() { return FEATURES; } // protected class TinkerKeyIndex<T extends TinkerElement> extends TinkerIndex<T> implements Serializable { // // private final Set<String> indexedKeys = new HashSet<String>(); // private TinkerGraph graph; // // public TinkerKeyIndex(final Class<T> indexClass, final TinkerGraph graph) { // super(null, indexClass); // this.graph = graph; // } // // public void autoUpdate(final String key, final Object newValue, final Object oldValue, final T element) { // if (this.indexedKeys.contains(key)) { // if (oldValue != null) // this.remove(key, oldValue, element); // this.put(key, newValue, element); // } // } // // public void autoRemove(final String key, final Object oldValue, final T element) { // if (this.indexedKeys.contains(key)) { // this.remove(key, oldValue, element); // } // } // // public void createKeyIndex(final String key) { // if (this.indexedKeys.contains(key)) // return; // // this.indexedKeys.add(key); // // if (InfiniVertex.class.equals(this.indexClass)) { // KeyIndexableGraphHelper.reIndexElements(graph, graph.getVertices(), new HashSet<String>(Arrays.asList(key))); // } else { // KeyIndexableGraphHelper.reIndexElements(graph, graph.getEdges(), new HashSet<String>(Arrays.asList(key))); // } // } // // public void dropKeyIndex(final String key) { // if (!this.indexedKeys.contains(key)) // return; // // this.indexedKeys.remove(key); // this.index.remove(key); // // } // // public Set<String> getIndexedKeys() { // if (null != this.indexedKeys) // return new HashSet<String>(this.indexedKeys); // else // return Collections.emptySet(); // } // } abstract protected static class MElement<X extends Serializable> implements Element, Serializable { public Map<String, Serializable> properties = null; public X id; public String graphID; transient String globalID; transient MapGraph<X> graph; public MElement() { } protected MElement(final X id, final MapGraph<X> graph) { setGraph(graph.id); this.graph = graph; this.id = id; } public boolean hasProperty(String k, Object value) { if (properties == null) return false; Serializable x = properties.get(k); if (x == null) return false; return x.equals(value); } public Map<String, Serializable> prop(final boolean createIfMissing) { if (properties == null) { if (createIfMissing) { //TODO make this an abstract method of MapGraph //properties = new LinkedHashMap(2); properties = new ConcurrentHashMap(2); } else return InfinispanCollections.emptyMap(); } return properties; } public MapGraph<X> graph() { if (graph == null) { graph = MapGraph.the(graphID); if (graph == null) throw new RuntimeException(this + " refers to unknown graph " + graphID); } return graph; } protected void setGraph(String newGraphID) { if (this.graphID == null || !this.graphID.equals(newGraphID)) { graphID = newGraphID; globalID = null; graph = null; //invalidates it, so use graph() to access this field } } public Set<String> getPropertyKeys() { return prop(false).keySet(); } public <T> T getProperty(final String key) { return (T) prop(false).get(key); } public void setProperty(final String key, final Object value) { Serializable v = (Serializable) value; ElementHelper.validateProperty(this, key, value); Object oldValue = prop(true).put(key, v); afterAdd(key, v); } abstract protected void afterAdd(final String key, final Serializable value); public <T> T removeProperty(final String key) { Serializable oldValue = prop(false).remove(key); if (oldValue != null) { beforeRemove(key, oldValue); return (T) oldValue; } return null; } public boolean isGlobal() { return graphID != null; } public String global() { if (!isGlobal()) return null; return globalID = graphID.substring(0, graphID.indexOf(':')); } abstract protected void beforeRemove(final String key, final Serializable oldValue); final static int PRIME = 31; final static int PRIME2 = 92821; public int hashCode() { if (graphID == null) return id.hashCode(); return hashL(this.id.hashCode(), global().hashCode()); } /** also includes properties in the hash */ public int hashCodeDeep() { return PRIME * hashCode() + properties.hashCode(); } public final static int hashL(int a, int b) { return PRIME2 * (31 + a) + b; } public X getId() { return this.id; } public boolean equals(final Object object) { if (this == object) return true; if (object == null) return false; if (object.getClass() != getClass()) return false; MElement o = (MElement) object; if (o.getId().equals(getId())) { if (isGlobal()) return o.global().equals(global()); return true; } return false; } /** also includes properties in the equality test */ public boolean deepEquals(final Object object) { if (object.getClass() != getClass()) return false; MElement o = (MElement) object; return o.getId().equals(getId()) && o.global().equals(global()) && properties.equals(o.properties); } } public static <X extends Serializable> MapGraph<X> the(String graphID) { MapGraph<X> g = global.get(graphID); return g; } abstract protected Map<String, Set<Edge>> newVertexEdgeMap(); abstract protected Set<Edge> newEdgeSet(int size); public static class MVertex<X extends Serializable> extends MElement<X> implements Vertex, Serializable { public final Map<String, Set<Edge>> outEdges; public final Map<String, Set<Edge>> inEdges; protected MVertex(final X id, final MapGraph<X> graph, Map<String, Set<Edge>> iEdges, Map<String, Set<Edge>> oEdges) { super(id, graph); this.outEdges = oEdges; this.inEdges = iEdges; } public Iterable<Edge> getEdges(final Direction direction, final String... labels) { if (direction.equals(Direction.OUT)) { return this.getOutEdges(labels); } else if (direction.equals(Direction.IN)) return this.getInEdges(labels); else { return Iterables.concat(this.getInEdges(labels), this.getOutEdges(labels)); } } public Iterable<Vertex> getVertices(final Direction direction, final String... labels) { return new VerticesFromEdgesIterable(this, direction, labels); } public Iterable<Edge> getEdges(final Map<String, Set<Edge>> e, final String... labels) { if (labels.length == 0) { return Iterables.concat(e.values()); } else if (labels.length == 1) { final Set<Edge> edges = e.get(labels[0]); if (null == edges) { return Collections.emptyList(); } else { return edges; } } else { final Set<String> labelSet = Sets.newHashSet(labels); return Iterables.concat(Iterables.transform(e.entrySet(), x -> { if (labelSet.contains(x.getKey())) return x.getValue(); return Collections.emptyList(); })); } } private Iterable<Edge> getInEdges(final String... labels) { return getEdges(inEdges, labels); } private Iterable<Edge> getOutEdges(final String... labels) { return getEdges(outEdges, labels); } public VertexQuery query() { return new DefaultVertexQuery(this); } public String toString() { if (isGlobal()) return new StringBuilder().append("v[").append(global()).append(':').append(getId()).append("]") .toString(); else return new StringBuilder().append("v[").append(getId()).append("]").toString(); } public Edge addEdge(final String label, final Vertex vertex) { return graph().addEdge(null, this, vertex, label); } private Set<Edge> newEdgeSet(int size) { return graph().newEdgeSet(size); } @Override protected void afterAdd(String key, Serializable value) { //this.graph.vertexKeyIndex.autoUpdate(key, value, oldValue, (InfiniVertex) this); graph().update(this); } @Override protected void beforeRemove(String key, Serializable oldValue) { //this.graph.vertexKeyIndex.autoRemove(key, oldValue, (InfiniVertex) this); } @Override public void remove() { graph().removeVertex(this); } public boolean remove(Edge edge, boolean incoming) { Map<String, Set<Edge>> target = incoming ? outEdges : inEdges; Set<Edge> ss = target.get(edge.getLabel()); if (ss == null) return false; if (ss.remove(edge)) { graph().update(this); return true; } return false; } public boolean add(MEdge<X> edge, boolean incoming) { Map<String, Set<Edge>> target = incoming ? outEdges : inEdges; String label = edge.getLabel(); Set<Edge> edges = target.get(label); if (null == edges) { target.put(label, edges = newEdgeSet(1)); } if (edges.add(edge)) { graph().update(this); return true; } return false; } } /** called if an vertex changes its properties */ protected void update(MVertex<X> xmVertex) { } /** called if an edge changes its properties */ protected void update(MEdge<X> xmVertex) { } public static class MEdge<X extends Serializable> extends MElement<X> implements Edge, Externalizable { protected String label; protected MVertex<X> inVertex; protected MVertex<X> outVertex; public MEdge() { } protected MEdge(final X id, final MVertex<X> outVertex, final MVertex<X> inVertex, final String label, final MapGraph<X> graph) { super(id, graph); if (label == null) throw new RuntimeException("edge label can not be null"); this.label = label; this.outVertex = outVertex; this.inVertex = inVertex; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(id); out.writeUTF(graphID); out.writeUTF(label == null ? "" : label); out.writeObject(outVertex.getId()); out.writeObject(inVertex.getId()); out.writeObject(prop(false)); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { id = (X) in.readObject(); setGraph(in.readUTF()); label = in.readUTF(); outVertex = graph().getVertex(in.readObject()); inVertex = graph().getVertex(in.readObject()); properties = (Map<String, Serializable>) in.readObject(); } public String getLabel() { return this.label; } public Vertex getVertex(final Direction direction) throws IllegalArgumentException { if (direction.equals(Direction.IN)) return this.inVertex; else if (direction.equals(Direction.OUT)) return this.outVertex; else throw ExceptionFactory.bothIsNotSupported(); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("e["); if (isGlobal()) sb.append(global()).append(':'); sb.append(getId()).append("][").append(getVertex(Direction.OUT).getId()).append("-"); String label = getLabel(); if (label != null) sb.append(getLabel()); return sb.append("->").append(getVertex(Direction.IN).getId()).append(']').toString(); } @Override public boolean hasProperty(String k, Object value) { if (k.equals(StringFactory.LABEL)) return label.equals(value); return super.hasProperty(k, value); } @Override protected void afterAdd(String key, Serializable value) { //this.graph.edgeKeyIndex.autoUpdate(key, value, oldValue, (InfiniEdge) this);*/ graph().update(this); } @Override protected void beforeRemove(String key, Serializable oldValue) { //this.graph.edgeKeyIndex.autoRemove(key, oldValue, (InfiniEdge) this);*/ graph().update(this); } @Override public void remove() { graph().removeEdge(this); } // @Override // public boolean equals(Object object) { // return label.equals(((MEdge)object).label); // } } }