package org.openntf.domino.graph2.impl; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javolution.util.FastMap; import javolution.util.FastSet; import org.openntf.domino.Database; import org.openntf.domino.Document; import org.openntf.domino.View; import org.openntf.domino.big.NoteList; import org.openntf.domino.graph2.DEdgeList; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.VertexQuery; import com.tinkerpop.blueprints.util.DefaultVertexQuery; import com.tinkerpop.blueprints.util.MultiIterable; import com.tinkerpop.blueprints.util.VerticesFromEdgesIterable; public class DVertex extends DElement implements org.openntf.domino.graph2.DVertex { private static final long serialVersionUID = 1L; private static final Logger log_ = Logger.getLogger(DVertex.class.getName()); public static final String IN_PREFIX = "_OPEN_IN_"; public static final String OUT_PREFIX = "_OPEN_OUT_"; protected FastSet<String> inDirtyKeySet_; protected FastSet<String> outDirtyKeySet_; // private FastMap<String, FastSet<String>> inEdgesMap_; protected FastMap<String, NoteList> inEdgesMapCompressed_; // private FastMap<String, FastSet<String>> outEdgesMap_; protected FastMap<String, NoteList> outEdgesMapCompressed_; protected transient FastMap<String, DEdgeList> inEdgeCache_; protected transient FastMap<String, DEdgeList> outEdgeCache_; public DVertex(final org.openntf.domino.graph2.DGraph parent) { super(parent); } DVertex(final org.openntf.domino.graph2.DGraph parent, final Map<String, Object> delegate) { super(parent); setDelegate(delegate); } @Override public Iterable<Edge> getEdges(final Direction direction, final String... labels) { if (direction == Direction.IN) { if (labels == null || labels.length == 0) { return getInEdgeObjects().unmodifiable(); } else { return getInEdgeObjects(labels); } } else if (direction == Direction.OUT) { if (labels == null || labels.length == 0) { return getOutEdgeObjects().unmodifiable(); } else { return getOutEdgeObjects(labels); } } else { DEdgeList result = null; if (labels == null || labels.length == 0) { result = getInEdgeObjects(); if (result == null) { result = getOutEdgeObjects(); } else { result.addAll(getOutEdgeObjects()); } } else { result = getInEdgeObjects(labels); if (result == null) { result = getOutEdgeObjects(labels); } else { result.addAll(getOutEdgeObjects(labels)); } } // if (result != null) { // System.out.println("Found " + result.size() + " edges going " + direction.name()); // } return result; } } @Override public Iterable<Vertex> getVertices(final Direction direction, final String... labels) { if (direction == Direction.BOTH) { List<Iterable<Vertex>> list = new ArrayList<Iterable<Vertex>>(); list.add(new VerticesFromEdgesIterable(this, Direction.IN, labels)); list.add(new VerticesFromEdgesIterable(this, Direction.OUT, labels)); return new MultiIterable<Vertex>(list); } else { return new VerticesFromEdgesIterable(this, direction, labels); } } @Override public VertexQuery query() { return new DefaultVertexQuery(this); } @Override public Edge addEdge(final String label, final Vertex inVertex) { return getParent().addEdge(null, this, inVertex, label); } @Override public void addInEdge(final Edge edge) { // boolean adding = false; String label = edge.getLabel(); // NoteList ins = getInEdgesSet(label); // Object eid = edge.getId(); // if (eid instanceof NoteCoordinate) { // // NOP // } else if (eid instanceof CharSequence) { // eid = new org.openntf.domino.big.impl.NoteCoordinate((CharSequence) eid); // } else { // log_.log(Level.WARNING, "Edge ids of type " + eid.getClass().getName() + " not yet supported"); // } // if (!ins.contains(eid)) { // adding = true; // // ins.add((NoteCoordinate) eid); // } else { // // System.out.println("TEMP DEBUG: Not adding an in edge on " + label + " because it's already in the list"); // } // if (adding) { getParent().startTransaction(this); getInDirtyKeySet().add(label); List<Edge> inLabelObjs = getInEdgeCache(label); inLabelObjs.add(edge); // } } @Override public void addOutEdge(final Edge edge) { // boolean adding = false; String label = edge.getLabel(); // NoteList outs = getOutEdgesSet(label); // Object eid = edge.getId(); // if (eid instanceof NoteCoordinate) { // // NOP // } else if (eid instanceof CharSequence) { // eid = new org.openntf.domino.big.impl.NoteCoordinate((CharSequence) eid); // } else { // log_.log(Level.WARNING, "Edge ids of type " + eid.getClass().getName() + " not yet supported"); // } // if (!outs.contains(eid)) { // adding = true; // // outs.add((NoteCoordinate) eid); // } else { // // System.out.println("TEMP DEBUG: Not adding an out edge on " + label + " because it's already in the list"); // } // if (adding) { getParent().startTransaction(this); getOutDirtyKeySet().add(label); List<Edge> outLabelObjs = getOutEdgeCache(label); outLabelObjs.add(edge); // } } @Override public String validateEdges() { // TODO Implement this or remove comment return null; } @Override public Set<String> getInEdgeLabels() { FastSet<String> result = new FastSet<String>(); for (String key : getPropertyKeys()) { if (key.startsWith(IN_PREFIX)) { result.add(key.substring(IN_PREFIX.length())); } } return result.unmodifiable(); } @Override public Set<String> getOutEdgeLabels() { FastSet<String> result = new FastSet<String>(); for (String key : getPropertyKeys()) { if (key.startsWith(OUT_PREFIX)) { result.add(key.substring(OUT_PREFIX.length())); } } return result.unmodifiable(); } @Override public Iterable<Edge> getEdges(final String... labels) { DEdgeList result = new org.openntf.domino.graph2.impl.DEdgeList(this).atomic(); result.addAll(getInEdgeObjects(labels)); result.addAll(getOutEdgeObjects(labels)); return result.unmodifiable(); } @Override public void remove() { getParent().removeVertex(this); } @Override public Edge findInEdge(final Vertex otherVertex, final String label, final boolean isUnique) { DEdgeList edgeList = getInEdgeCache(label); edgeList.setUnique(isUnique); Edge result = edgeList.findEdge(otherVertex); return result; } @Override public Edge findOutEdge(final Vertex otherVertex, final String label, final boolean isUnique) { // System.out.println("DEBUG: Attempting to find OUT edge"); DEdgeList edgeList = getOutEdgeCache(label); edgeList.setUnique(isUnique); return edgeList.findEdge(otherVertex); } @Override public Edge findEdge(final Vertex otherVertex, final String label) { // System.out.println("DEBUG: FIND method"); Edge result = findInEdge(otherVertex, label, false); if (result == null) { result = findOutEdge(otherVertex, label, false); } return result; } protected void removeEdge(final Edge edge) { getParent().startTransaction(this); String label = edge.getLabel(); boolean inChanged = false; try { NoteList ins = getInEdgesSet(label); if (ins != null) { inChanged = ins.remove(edge.getId()); } if (inChanged) { List<Edge> inObjs = getInEdgeCache(label); inObjs.remove(edge); getInDirtyKeySet().add(label); } } catch (Throwable t) { System.err.println("Exception occured trying to remove an edge from vertex " + getId() + ": " + t.getClass().getSimpleName()); t.printStackTrace(); } boolean outChanged = false; try { NoteList outs = getOutEdgesSet(label); if (outs != null) { outChanged = outs.remove(edge.getId()); } if (outChanged) { List<Edge> outObjs = getOutEdgeCache(label); outObjs.remove(edge); getOutDirtyKeySet().add(label); } } catch (Throwable t) { System.err.println("Exception occured trying to remove an edge from vertex " + getId() + ": " + t.getClass().getSimpleName()); t.printStackTrace(); } } protected Map<String, DEdgeList> getInEdgeCache() { if (inEdgeCache_ == null) { inEdgeCache_ = new FastMap<String, DEdgeList>().atomic(); } return inEdgeCache_; } protected DEdgeList getInEdgeCache(final String label) { Map<String, DEdgeList> inCache = getInEdgeCache(); DEdgeList result = null; result = inCache.get(label); if (result == null) { NoteList list = getInEdgesSet(label); // if (list.size() > 0) { // Factory.println("Got a " + label + " list with " + list.size() + " entries for Vertex " + getId()); // } // result = getParent().getEdgesFromIds(this, list); result = new DFastEdgeList(this, getParent(), list, label); // result.setLabel(label); inCache.put(label, result); } return result.atomic(); } protected DEdgeList getOutEdgeCache(final String label) { Map<String, DEdgeList> outCache = getOutEdgeCache(); DEdgeList result = null; result = outCache.get(label); if (result == null) { NoteList list = getOutEdgesSet(label); result = new DFastEdgeList(this, getParent(), list, label); outCache.put(label, result); } return result.atomic(); } protected FastMap<String, DEdgeList> getOutEdgeCache() { if (outEdgeCache_ == null) { outEdgeCache_ = new FastMap<String, DEdgeList>().atomic(); } return outEdgeCache_; } @Override public int getInEdgeCount(final String label) { NoteList edgeIds = getInEdgesMap().get(label); if (edgeIds == null) { return getProperty("_COUNT" + DVertex.IN_PREFIX + label, Integer.class, false); } else { return edgeIds.size(); } } protected NoteList getInEdgesSet(final String label) { NoteList edgeIds = getInEdgesMap().get(label); if (edgeIds == null) { edgeIds = new org.openntf.domino.big.impl.NoteList(true); String key = DVertex.IN_PREFIX + label; Map<String, Object> delegate = getDelegate(); if (delegate.containsKey(key)) { if (delegate instanceof Document) { byte[] bytes = ((Document) delegate).readBinary(key); // Factory.println("Loading a NoteList from an item for label " + label + " for vertex " + getId()); edgeIds.loadByteArray(bytes); } else { Object o = getProperty(key, java.util.Collection.class); if (o instanceof NoteList) { edgeIds = ((NoteList) o); } else if (o instanceof java.util.Collection) { for (Object raw : (Collection<?>) o) { if (raw instanceof String) { edgeIds.add(new org.openntf.domino.big.impl.NoteCoordinate( ""/*TODO NTF This should be some default replid*/, (String) raw)); } else { //TODO NTF } } } else { log_.log(Level.SEVERE, "ALERT! InEdges returned something other than a Collection " + o.getClass().getName() + ". We are clearing the values and rebuilding the edges."); } } } else { // Factory.println("Created a new in NoteList for " + label + " in vertex " + getId()); // Throwable t = new Throwable(); // t.printStackTrace(); } Map<String, NoteList> map = getInEdgesMap(); map.put(label, edgeIds); } else { // Factory.println("Found a cached in NoteList for " + label + " in vertex " + getId()); } return edgeIds; } @Override public int getOutEdgeCount(final String label) { NoteList edgeIds = getOutEdgesMap().get(label); if (edgeIds == null) { try { return getProperty("_COUNT" + DVertex.OUT_PREFIX + label, Integer.class, false); } catch (Throwable t) { throw new RuntimeException("Exception getting edge count for label " + label, t); } } else { return edgeIds.size(); } } protected NoteList getOutEdgesSet(final String label) { NoteList edgeIds = getOutEdgesMap().get(label); if (edgeIds == null) { edgeIds = new org.openntf.domino.big.impl.NoteList(true); String key = DVertex.OUT_PREFIX + label; Map<String, Object> delegate = getDelegate(); if (delegate.containsKey(key)) { if (delegate instanceof Document) { byte[] bytes = ((Document) delegate).readBinary(key); // System.out.println("TEMP DEBUG: read Document-based NoteList binary for " + label + " and found " + bytes.length // + " bytes"); edgeIds.loadByteArray(bytes); } else { Object o = getProperty(key, java.util.Collection.class); if (o != null) { if (o instanceof NoteList) { edgeIds = ((NoteList) o); } else if (o instanceof java.util.Collection) { for (Object raw : (Collection<?>) o) { if (raw instanceof String) { edgeIds.add(new org.openntf.domino.big.impl.NoteCoordinate( ""/*TODO NTF This should be some default replid*/, (String) raw)); } else { //TODO NTF } } } else { log_.log(Level.SEVERE, "ALERT! OutEdges returned something other than a Collection " + o.getClass().getName() + ". We are clearing the values and rebuilding the edges."); } } } } else { // System.out.println("TEMP DEBUG Returning new out edge set for " + label); } Map<String, NoteList> map = getOutEdgesMap(); map.put(label, edgeIds); } return edgeIds; } protected FastSet<String> getInDirtyKeySet() { if (inDirtyKeySet_ == null) { inDirtyKeySet_ = new FastSet<String>().atomic(); } return inDirtyKeySet_; } protected Map<String, NoteList> getInEdgesMap() { if (inEdgesMapCompressed_ == null) { inEdgesMapCompressed_ = new FastMap<String, NoteList>().atomic(); } return inEdgesMapCompressed_; } protected FastSet<String> getOutDirtyKeySet() { if (outDirtyKeySet_ == null) { outDirtyKeySet_ = new FastSet<String>().atomic(); } return outDirtyKeySet_; } protected Map<String, NoteList> getOutEdgesMap() { if (outEdgesMapCompressed_ == null) { outEdgesMapCompressed_ = new FastMap<String, NoteList>().atomic(); } return outEdgesMapCompressed_; } @Override public void applyChanges() { writeEdges(); super.applyChanges(); } protected boolean writeEdges() { boolean result = false; Map<String, NoteList> inMap = getInEdgesMap(); FastSet<String> inDirtySet = getInDirtyKeySet(); if (!inDirtySet.isEmpty()) { for (String key : inDirtySet) { NoteList edgeIds = inMap.get(key); if (edgeIds != null) { // if ("foundin".equalsIgnoreCase(key)) { // System.out.println("Writing a FoundIn IN list with " + edgeIds.size() + " elements."); // } setProperty(DVertex.IN_PREFIX + key, edgeIds); setProperty("_COUNT" + DVertex.IN_PREFIX + key, edgeIds.size()); result = true; } } inDirtySet.clear(); } Map<String, NoteList> outMap = getOutEdgesMap(); FastSet<String> outDirtySet = getOutDirtyKeySet(); if (!outDirtySet.isEmpty()) { for (String key : outDirtySet) { NoteList edgeIds = outMap.get(key); if (edgeIds != null) { // if ("foundin".equalsIgnoreCase(key)) { // System.out.println("Writing a FoundIn OUT list with " + edgeIds.size() + " elements."); // } setProperty(DVertex.OUT_PREFIX + key, edgeIds); setProperty("_COUNT" + DVertex.OUT_PREFIX + key, edgeIds.size()); result = true; } } outDirtySet.clear(); } return result; } protected DEdgeList getInEdgeObjects(final String... labels) { Map<String, DEdgeList> inCache = getInEdgeCache(); DEdgeList result = null; if (labels == null || labels.length == 0) { Set<String> labelSet = getInEdgeLabels(); for (String label : labelSet) { if (result == null) { result = getInEdgeObjects(label); } else { result.addAll(getInEdgeObjects(label)); } } } else if (labels.length == 1) { String label = labels[0]; // System.out.println("TEMP DEBUG Getting in edge objects for label " + label); result = inCache.get(label); if (result == null) { NoteList edgeIds = getInEdgesSet(label); // DEdgeList edges = getParent().getEdgesFromIds(this, edgeIds); DEdgeList edges = new DFastEdgeList(this, getParent(), edgeIds, label); // edges.setLabel(label); if (edges != null) { result = edges.atomic(); } inCache.put(label, result); } } else { result = new org.openntf.domino.graph2.impl.DEdgeList(this); for (String label : labels) { result.addAll(getInEdgeObjects(label)); } } return result == null ? null : result.unmodifiable(); } protected DEdgeList getOutEdgeObjects(final String... labels) { FastMap<String, DEdgeList> outCache = getOutEdgeCache(); DEdgeList result = null; if (labels == null || labels.length == 0) { Set<String> labelSet = getOutEdgeLabels(); for (String label : labelSet) { if (result == null) { result = getOutEdgeObjects(label); } else { result.addAll(getOutEdgeObjects(label)); } } } else if (labels.length == 1) { String label = labels[0]; if (label == null) { return getOutEdgeObjects().unmodifiable(); } result = outCache.get(label); if ((result == null || result.isEmpty()) && label.equalsIgnoreCase("contents")) { // System.out.println("TEMP DEBUG getting 'contents' label for a vertex with delegate " + getDelegateType().getName()); if (getDelegateType().equals(View.class)) { // System.out.println("TEMP DEBUG getting contents from ViewVertex"); DEdgeList edges = new DEdgeEntryList(this, (org.openntf.domino.graph2.impl.DElementStore) getStore()); edges.setLabel(label); result = edges.unmodifiable(); } else if (this instanceof DCategoryVertex) { DEdgeList edges = new DEdgeEntryList(this, (org.openntf.domino.graph2.impl.DElementStore) getStore()); edges.setLabel(label); result = edges.unmodifiable(); } } if (result == null) { // System.out.println("TEMP DEBUG Getting out edge objects for label " + label); NoteList edgeIds = getOutEdgesSet(label); DEdgeList edges = new DFastEdgeList(this, getParent(), edgeIds, label); // getParent().getEdgesFromIds(this, edgeIds); // edges.setLabel(label); if (edges != null) { result = edges.atomic(); } } if (result != null) { outCache.put(label, result); } } else { result = new org.openntf.domino.graph2.impl.DEdgeList(this); for (String label : labels) { result.addAll(getOutEdgeObjects(label)); } } return result == null ? null : result.unmodifiable(); } @Override public View getView() { if (getDelegateType().equals(org.openntf.domino.View.class)) { Database db = ((Document) getDelegate()).getParentDatabase(); return db.getView((Document) getDelegate()); } return null; } }