All Downloads are FREE. Search and download functionalities are using the official Maven repository.

overflowdb.OdbNode Maven / Gradle / Ivy

There is a newer version: 1.115
Show newest version
package overflowdb;

import org.apache.commons.lang3.NotImplementedException;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import overflowdb.tp3.Converters;
import overflowdb.util.ArrayOffsetIterator;
import overflowdb.util.MultiIterator2;
import overflowdb.util.PackedIntArray;
import org.apache.tinkerpop.gremlin.structure.Edge;
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.EmptyProperty;
import overflowdb.util.PropertyHelper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.StreamSupport;

/**
 * Node that stores adjacent Nodes directly, rather than via edges.
 * Motivation: in many graph use cases, edges don't hold any properties and thus accounts for more memory and
 * traversal time than necessary
 */
public abstract class OdbNode implements Vertex, Node {
  public final NodeRef ref;

  /**
   * holds refs to all adjacent nodes (a.k.a. dummy edges) and the edge properties
   */
  private Object[] adjacentNodesWithProperties = new Object[0];

  /* store the start offset and length into the above `adjacentNodesWithProperties` array in an interleaved manner,
   * i.e. each outgoing edge type has two entries in this array. */
  private PackedIntArray edgeOffsets;

  /**
   * Flag that helps us save time when serializing, both when overflowing to disk and when storing
   * the graph on close.
   * `true`  when node is first created, or is modified (property or edges)
   * `false` when node is freshly serialized to disk or deserialized from disk
   */
  private boolean dirty;

  private static final String[] ALL_LABELS = new String[0];

  protected OdbNode(NodeRef ref) {
    this.ref = ref;

    ref.setNode(this);
    if (ref.graph != null) {
      ref.graph.referenceManager.applyBackpressureMaybe();
    }

    edgeOffsets = PackedIntArray.create(layoutInformation().numberOfDifferentAdjacentTypes() * 2);
  }

  public abstract NodeLayoutInformation layoutInformation();

  protected  Iterator> specificProperties(String key) {
    final Object value = specificProperty2(key);
    if (value != null) return IteratorUtils.of(new OdbNodeProperty(this, key, value));
    else return Collections.emptyIterator();
  }

  // TODO drop suffix `2` after tinkerpop interface is gone
  protected abstract Object specificProperty2(String key);

  public Object[] getAdjacentNodesWithProperties() {
    return adjacentNodesWithProperties;
  }

  public void setAdjacentNodesWithProperties(Object[] adjacentNodesWithProperties) {
    this.adjacentNodesWithProperties = adjacentNodesWithProperties;
  }

  public int[] getEdgeOffsets() {
    return edgeOffsets.toIntArray();
  }

  public PackedIntArray getEdgeOffsetsPackedArray() {
    return edgeOffsets;
  }


  public void setEdgeOffsets(int[] edgeOffsets) {
    this.edgeOffsets = PackedIntArray.of(edgeOffsets);
  }

  public abstract Map valueMap();

  @Override
  public Graph graph() {
    return ref.graph;
  }

  @Override
  public OdbGraph graph2() {
    return ref.graph;
  }

  @Override
  public Object id() {
    return ref.id;
  }

  public long id2() {
    return ref.id;
  }

  @Override
  public String label() {
    return ref.label();
  }

  @Override
  public Set keys() {
    return layoutInformation().propertyKeys();
  }

  @Override
  public  VertexProperty property(String key) {
    return specificProperty(key);
  }

  /* You can override this default implementation in concrete specialised instances for performance
   * if you like, since technically the Iterator isn't necessary.
   * This default implementation works fine though. */
  protected  VertexProperty specificProperty(String key) {
    Iterator> iter = specificProperties(key);
    if (iter.hasNext()) {
      return iter.next();
    } else {
      return VertexProperty.empty();
    }
  }

  @Override
  public  Iterator> properties(String... propertyKeys) {
    if (propertyKeys.length == 0) { // return all properties
      return (Iterator) layoutInformation().propertyKeys().stream().flatMap(key ->
          StreamSupport.stream(Spliterators.spliteratorUnknownSize(
              specificProperties(key), Spliterator.ORDERED), false)
      ).iterator();
    } else if (propertyKeys.length == 1) { // treating as special case for performance
      return specificProperties(propertyKeys[0]);
    } else {
      return (Iterator) Arrays.stream(propertyKeys).flatMap(key ->
          StreamSupport.stream(Spliterators.spliteratorUnknownSize(
              specificProperties(key), Spliterator.ORDERED), false)
      ).iterator();
    }
  }

  @Override
  public Map propertyMap() {
    final Set propertyKeys = layoutInformation().propertyKeys();
    final Map results = new HashMap<>(propertyKeys.size());

    for (String propertyKey : propertyKeys) {
      final Object value = property2(propertyKey);
      if (value != null) results.put(propertyKey, value);
    }

    return results;
  }

  @Override
  public 

P property2(String propertyKey) { return (P) specificProperty2(propertyKey); } @Override public VertexProperty property(VertexProperty.Cardinality cardinality, String key, V value, Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); ElementHelper.validateProperty(key, value); final VertexProperty vp = updateSpecificProperty(cardinality, key, value); ref.graph.indexManager.putIfIndexed(key, value, ref); /* marking as dirty *after* we updated - if node gets serialized before we finish, it'll be marked as dirty */ this.markAsDirty(); return vp; } @Override public

void setProperty(String key, P value) { this.property(VertexProperty.Cardinality.single, key, value); } protected abstract VertexProperty updateSpecificProperty( VertexProperty.Cardinality cardinality, String key, V value); protected abstract void removeSpecificProperty(String key); @Override public void remove() { final List edges = new ArrayList<>(); bothE().forEachRemaining(edges::add); for (Edge edge : edges) { if (!((OdbEdge) edge).isRemoved()) { edge.remove(); } } ref.graph.remove(this); /* marking as dirty *after* we updated - if node gets serialized before we finish, it'll be marked as dirty */ this.markAsDirty(); } public void markAsDirty() { this.dirty = true; } public void markAsClean() { this.dirty = false; } public Iterator> getEdgeProperties(Direction direction, OdbEdge edge, int blockOffset, String... keys) { List> result = new ArrayList<>(); if (keys.length != 0) { for (String key : keys) { result.add(getEdgeProperty(direction, edge, blockOffset, key)); } } else { for (String propertyKey : layoutInformation().edgePropertyKeys(edge.label())) { result.add(getEdgeProperty(direction, edge, blockOffset, propertyKey)); } } return result.iterator(); } public Map getEdgePropertyMap(Direction direction, OdbEdge edge, int blockOffset) { final Set edgePropertyKeys = layoutInformation().edgePropertyKeys(edge.label()); final Map results = new HashMap<>(edgePropertyKeys.size()); for (String propertyKey : edgePropertyKeys) { final Object value = getEdgeProperty2(direction, edge, blockOffset, propertyKey); if (value != null) results.put(propertyKey, value); } return results; } public Property getEdgeProperty(Direction direction, OdbEdge edge, int blockOffset, String key) { V value = getEdgeProperty2(direction, edge, blockOffset, key); if (value == null) { return EmptyProperty.instance(); } return new OdbProperty<>(key, value, edge); } // TODO drop suffix `2` after tinkerpop interface is gone public

P getEdgeProperty2(Direction direction, OdbEdge edge, int blockOffset, String key) { int propertyPosition = getEdgePropertyIndex(direction, edge.label(), key, blockOffset); if (propertyPosition == -1) { return null; } return (P) adjacentNodesWithProperties[propertyPosition]; } public void setEdgeProperty(Direction direction, String edgeLabel, String key, V value, int blockOffset) { int propertyPosition = getEdgePropertyIndex(direction, edgeLabel, key, blockOffset); if (propertyPosition == -1) { throw new RuntimeException("Edge " + edgeLabel + " does not support property `" + key + "`."); } adjacentNodesWithProperties[propertyPosition] = value; /* marking as dirty *after* we updated - if node gets serialized before we finish, it'll be marked as dirty */ this.markAsDirty(); } private int calcAdjacentNodeIndex(Direction direction, String edgeLabel, int blockOffset) { int offsetPos = getPositionInEdgeOffsets(direction, edgeLabel); if (offsetPos == -1) { return -1; } int start = startIndex(offsetPos); return start + blockOffset; } /** * Return -1 if there exists no edge property for the provided argument combination. */ private int getEdgePropertyIndex(Direction direction, String label, String key, int blockOffset) { int adjacentNodeIndex = calcAdjacentNodeIndex(direction, label, blockOffset); if (adjacentNodeIndex == -1) { return -1; } int propertyOffset = layoutInformation().getOffsetRelativeToAdjacentNodeRef(label, key); if (propertyOffset == -1) { return -1; } return adjacentNodeIndex + propertyOffset; } @Override public OdbEdge addEdge2(String label, Node inNode, Object... keyValues) { final NodeRef inNodeRef = (NodeRef) inNode; NodeRef thisNodeRef = ref; int outBlockOffset = storeAdjacentNode(Direction.OUT, label, inNodeRef, keyValues); int inBlockOffset = inNodeRef.get().storeAdjacentNode(Direction.IN, label, thisNodeRef, keyValues); OdbEdge dummyEdge = instantiateDummyEdge(label, thisNodeRef, inNodeRef); dummyEdge.setOutBlockOffset(outBlockOffset); dummyEdge.setInBlockOffset(inBlockOffset); return dummyEdge; } @Override public OdbEdge addEdge2(String label, Node inNode, Map keyValues) { return addEdge2(label, inNode, PropertyHelper.toKeyValueArray(keyValues)); } @Override public void addEdgeSilent(String label, Node inNode, Object... keyValues) { final NodeRef inNodeRef = (NodeRef) inNode; NodeRef thisNodeRef = ref; storeAdjacentNode(Direction.OUT, label, inNodeRef, keyValues); inNodeRef.get().storeAdjacentNode(Direction.IN, label, thisNodeRef, keyValues); } @Override public void addEdgeSilent(String label, Node inNode, Map keyValues) { addEdgeSilent(label, inNode, PropertyHelper.toKeyValueArray(keyValues)); } @Override public Edge addEdge(String label, Vertex inNode, Object... keyValues) { return addEdge2(label, (Node) inNode, keyValues); } @Override public Iterator edges(org.apache.tinkerpop.gremlin.structure.Direction tinkerDirection, String... edgeLabels) { Direction direction = Converters.fromTinker(tinkerDirection); final MultiIterator2 multiIterator = new MultiIterator2<>(); if (direction == Direction.IN || direction == Direction.BOTH) { for (String label : calcInLabels(edgeLabels)) { Iterator edgeIterator = createDummyEdgeIterator(Direction.IN, label); multiIterator.addIterator(edgeIterator); } } if (direction == Direction.OUT || direction == Direction.BOTH) { for (String label : calcOutLabels(edgeLabels)) { Iterator edgeIterator = createDummyEdgeIterator(Direction.OUT, label); multiIterator.addIterator(edgeIterator); } } return multiIterator; } @Override public Iterator vertices(org.apache.tinkerpop.gremlin.structure.Direction direction, String... edgeLabels) { return nodes(Converters.fromTinker(direction), edgeLabels); } /* lookup adjacent nodes via direction and labels */ public Iterator nodes(Direction direction, String... edgeLabels) { final MultiIterator2 multiIterator = new MultiIterator2<>(); if (direction == Direction.IN || direction == Direction.BOTH) { for (String label : calcInLabels(edgeLabels)) { multiIterator.addIterator(in(label)); } } if (direction == Direction.OUT || direction == Direction.BOTH) { for (String label : calcOutLabels(edgeLabels)) { multiIterator.addIterator(out(label)); } } return multiIterator; } /* adjacent OUT nodes (all labels) */ @Override public Iterator out() { return createAdjacentNodeIterator(Direction.OUT, ALL_LABELS); } /* adjacent OUT nodes for given labels */ @Override public Iterator out(String... edgeLabels) { return createAdjacentNodeIterator(Direction.OUT, edgeLabels); } /* adjacent IN nodes (all labels) */ @Override public Iterator in() { final MultiIterator2 multiIterator = new MultiIterator2<>(); for (String label : layoutInformation().allowedInEdgeLabels()) { multiIterator.addIterator(in(label)); } return multiIterator; } /* adjacent IN nodes for given labels */ @Override public Iterator in(String... edgeLabels) { return createAdjacentNodeIterator(Direction.IN, edgeLabels); } /* adjacent OUT/IN nodes (all labels) */ @Override public Iterator both() { final MultiIterator2 multiIterator = new MultiIterator2<>(); multiIterator.addIterator(out()); multiIterator.addIterator(in()); return multiIterator; } /* adjacent OUT/IN nodes for given labels */ @Override public Iterator both(String... edgeLabels) { final MultiIterator2 multiIterator = new MultiIterator2<>(); multiIterator.addIterator(out(edgeLabels)); multiIterator.addIterator(in(edgeLabels)); return multiIterator; } /* adjacent OUT edges (all labels) */ @Override public Iterator outE() { final MultiIterator2 multiIterator = new MultiIterator2<>(); for (String label : layoutInformation().allowedOutEdgeLabels()) { multiIterator.addIterator(outE(label)); } return multiIterator; } /* adjacent OUT edges for given labels */ @Override public Iterator outE(String... edgeLabels) { return createDummyEdgeIterator(Direction.OUT, edgeLabels); } /* adjacent IN edges (all labels) */ @Override public Iterator inE() { final MultiIterator2 multiIterator = new MultiIterator2<>(); for (String label : layoutInformation().allowedInEdgeLabels()) { multiIterator.addIterator(inE(label)); } return multiIterator; } /* adjacent IN edges for given labels */ @Override public Iterator inE(String... edgeLabels) { return createDummyEdgeIterator(Direction.IN, edgeLabels); } /* adjacent OUT/IN edges (all labels) */ @Override public Iterator bothE() { final MultiIterator2 multiIterator = new MultiIterator2<>(); multiIterator.addIterator(outE()); multiIterator.addIterator(inE()); return multiIterator; } /* adjacent OUT/IN edges for given labels */ @Override public Iterator bothE(String... edgeLabels) { final MultiIterator2 multiIterator = new MultiIterator2<>(); multiIterator.addIterator(outE(edgeLabels)); multiIterator.addIterator(inE(edgeLabels)); return multiIterator; } /** * If there are multiple edges between the same two nodes with the same label, we use the * `occurrence` to differentiate between those edges. Both nodes use the same occurrence * index for the same edge. * * @return the occurrence for a given edge, calculated by counting the number times the given * adjacent node occurred between the start of the edge-specific block and the blockOffset */ protected final int blockOffsetToOccurrence(Direction direction, String label, NodeRef otherNode, int blockOffset) { int offsetPos = getPositionInEdgeOffsets(direction, label); int start = startIndex(offsetPos); int strideSize = getStrideSize(label); int occurrenceCount = -1; for (int i = start; i <= start + blockOffset; i += strideSize) { final NodeRef adjacentNodeWithProperty = (NodeRef) adjacentNodesWithProperties[i]; if (adjacentNodeWithProperty != null && adjacentNodeWithProperty.id().equals(otherNode.id())) { occurrenceCount++; } } if (occurrenceCount == -1) throw new RuntimeException("unable to calculate occurrenceCount"); else return occurrenceCount; } /** * @param direction OUT or IN * @param label the edge label * @param occurrence if there are multiple edges between the same two nodes with the same label, * this is used to differentiate between those edges. * Both nodes use the same occurrence index in their `adjacentNodesWithProperties` array for the same edge. * @return the index into `adjacentNodesWithProperties` */ protected final int occurrenceToBlockOffset(Direction direction, String label, NodeRef adjacentNode, int occurrence) { int offsetPos = getPositionInEdgeOffsets(direction, label); int start = startIndex(offsetPos); int length = blockLength(offsetPos); int strideSize = getStrideSize(label); int currentOccurrence = 0; for (int i = start; i < start + length; i += strideSize) { final NodeRef adjacentNodeWithProperty = (NodeRef) adjacentNodesWithProperties[i]; if (adjacentNodeWithProperty != null && adjacentNodeWithProperty.id().equals(adjacentNode.id())) { if (currentOccurrence == occurrence) { int adjacentNodeIndex = i - start; return adjacentNodeIndex; } else { currentOccurrence++; } } } throw new RuntimeException("Unable to find occurrence " + occurrence + " of " + label + " edge to node " + adjacentNode.id()); } /** * Removes an 'edge', i.e. in reality it removes the information about the adjacent node from * `adjacentNodesWithProperties`. The corresponding elements will be set to `null`, i.e. we'll have holes. * Note: this decrements the `offset` of the following edges in the same block by one, but that's ok because the only * thing that matters is that the offset is identical for both connected nodes (assuming thread safety). * * @param blockOffset must have been initialized */ protected final synchronized void removeEdge(Direction direction, String label, int blockOffset) { int offsetPos = getPositionInEdgeOffsets(direction, label); int start = startIndex(offsetPos) + blockOffset; int strideSize = getStrideSize(label); for (int i = start; i < start + strideSize; i++) { adjacentNodesWithProperties[i] = null; } /* marking as dirty *after* we updated - if node gets serialized before we finish, it'll be marked as dirty */ this.markAsDirty(); } private Iterator createDummyEdgeIterator(Direction direction, String... labels) { if (labels.length == 1) { return createDummyEdgeIteratorForSingleLabel(direction, labels[0]); } else { final String[] labelsToFollow = labels.length == 0 ? allowedLabelsByDirection(direction) : labels; final MultiIterator2 multiIterator = new MultiIterator2<>(); for (String label : labelsToFollow) { multiIterator.addIterator(createDummyEdgeIteratorForSingleLabel(direction, label)); } return multiIterator; } } private Iterator createDummyEdgeIteratorForSingleLabel(Direction direction, String label) { int offsetPos = getPositionInEdgeOffsets(direction, label); if (offsetPos != -1) { int start = startIndex(offsetPos); int length = blockLength(offsetPos); int strideSize = getStrideSize(label); return new DummyEdgeIterator(adjacentNodesWithProperties, start, start + length, strideSize, direction, label, ref); } else { return Collections.emptyIterator(); } } private final Iterator createAdjacentNodeIterator(Direction direction, String... labels) { if (labels.length == 1) { return createAdjacentNodeIteratorByOffSet(getPositionInEdgeOffsets(direction, labels[0])); } else { final String[] labelsToFollow = labels.length == 0 ? allowedLabelsByDirection(direction) : labels; final MultiIterator2 multiIterator = new MultiIterator2<>(); for (String label : labelsToFollow) { multiIterator.addIterator(createAdjacentNodeIteratorByOffSet(getPositionInEdgeOffsets(direction, label))); } return multiIterator; } } /* Simplify hoisting of string lookups. * n.b. `final` so that the JIT compiler can inline it */ public final Iterator createAdjacentNodeIteratorByOffSet(int offsetPos){ if (offsetPos != -1) { int start = startIndex(offsetPos); int length = blockLength(offsetPos); int strideSize = layoutInformation().getEdgePropertyCountByOffsetPos(offsetPos) + 1; return new ArrayOffsetIterator<>(adjacentNodesWithProperties, start, start + length, strideSize); } else { return Collections.emptyIterator(); } } private final String[] allowedLabelsByDirection(Direction direction) { if (direction.equals(Direction.OUT)) return layoutInformation().allowedOutEdgeLabels(); else if (direction.equals(Direction.IN)) return layoutInformation().allowedInEdgeLabels(); else throw new NotImplementedException(direction.toString()); } private int storeAdjacentNode(Direction direction, String edgeLabel, NodeRef nodeRef, Object... edgeKeyValues) { int blockOffset = storeAdjacentNode(direction, edgeLabel, nodeRef); /* set edge properties */ for (int i = 0; i < edgeKeyValues.length; i = i + 2) { if (!edgeKeyValues[i].equals(T.id) && !edgeKeyValues[i].equals(T.label)) { String key = (String) edgeKeyValues[i]; Object value = edgeKeyValues[i + 1]; setEdgeProperty(direction, edgeLabel, key, value, blockOffset); } } /* marking as dirty *after* we updated - if node gets serialized before we finish, it'll be marked as dirty */ this.markAsDirty(); return blockOffset; } private final synchronized int storeAdjacentNode(Direction direction, String edgeLabel, NodeRef nodeRef) { int offsetPos = getPositionInEdgeOffsets(direction, edgeLabel); if (offsetPos == -1) { throw new RuntimeException("Edge of type " + edgeLabel + " with direction " + direction + " not supported by class " + getClass().getSimpleName()); } int start = startIndex(offsetPos); int length = blockLength(offsetPos); int strideSize = getStrideSize(edgeLabel); int insertAt = start + length; if (adjacentNodesWithProperties.length <= insertAt || adjacentNodesWithProperties[insertAt] != null) { // space already occupied - grow adjacentNodesWithProperties array, leaving some room for more elements adjacentNodesWithProperties = growAdjacentNodesWithProperties(offsetPos, strideSize, insertAt, length); } adjacentNodesWithProperties[insertAt] = nodeRef; // update edgeOffset length to include the newly inserted element edgeOffsets.set(2 * offsetPos + 1, length + strideSize); int blockOffset = length; return blockOffset; } private int startIndex(int offsetPosition) { return edgeOffsets.get(2 * offsetPosition); } /** * @return number of elements reserved in `adjacentNodesWithProperties` for a given edge label * includes space for the node ref and all properties */ private final int getStrideSize(String edgeLabel) { int sizeForNodeRef = 1; Set allowedPropertyKeys = layoutInformation().edgePropertyKeys(edgeLabel); return sizeForNodeRef + allowedPropertyKeys.size(); } /** * @return The position in edgeOffsets array. -1 if the edge label is not supported */ private final int getPositionInEdgeOffsets(Direction direction, String label) { final Integer positionOrNull; if (direction == Direction.OUT) { positionOrNull = layoutInformation().outEdgeToOffsetPosition(label); } else { positionOrNull = layoutInformation().inEdgeToOffsetPosition(label); } if (positionOrNull != null) { return positionOrNull; } else { return -1; } } /** * Returns the length of an edge type block in the adjacentNodesWithProperties array. * Length means number of index positions. */ private final int blockLength(int offsetPosition) { return edgeOffsets.get(2 * offsetPosition + 1); } private final String[] calcInLabels(String... edgeLabels) { if (edgeLabels.length != 0) { return edgeLabels; } else { return layoutInformation().allowedInEdgeLabels(); } } private final String[] calcOutLabels(String... edgeLabels) { if (edgeLabels.length != 0) { return edgeLabels; } else { return layoutInformation().allowedOutEdgeLabels(); } } /** * grow the adjacentNodesWithProperties array *

* preallocates more space than immediately necessary, so we don't need to grow the array every time * (tradeoff between performance and memory). * grows with the square root of the double of the current capacity. */ private final synchronized Object[] growAdjacentNodesWithProperties(int offsetPos, int strideSize, int insertAt, int currentLength) { // TODO optimize growth function - optimizing has potential to save a lot of memory, but the below slowed down processing massively // int currentCapacity = currentLength / strideSize; // double additionalCapacity = Math.sqrt(currentCapacity) + 1; // int additionalCapacityInt = (int) Math.ceil(additionalCapacity); // int additionalEntriesCount = additionalCapacityInt * strideSize; int growthEmptyFactor = 2; int additionalEntriesCount = (currentLength + strideSize) * growthEmptyFactor; int newSize = adjacentNodesWithProperties.length + additionalEntriesCount; Object[] newArray = new Object[newSize]; System.arraycopy(adjacentNodesWithProperties, 0, newArray, 0, insertAt); System.arraycopy(adjacentNodesWithProperties, insertAt, newArray, insertAt + additionalEntriesCount, adjacentNodesWithProperties.length - insertAt); // Increment all following start offsets by `additionalEntriesCount`. for (int i = offsetPos + 1; 2 * i < edgeOffsets.length(); i++) { edgeOffsets.set(2 * i, edgeOffsets.get(2 * i) + additionalEntriesCount); } return newArray; } /** * to follow the tinkerpop api, instantiate and return a dummy edge, which doesn't really exist in the graph */ protected final OdbEdge instantiateDummyEdge(String label, NodeRef outNode, NodeRef inNode) { final EdgeFactory edgeFactory = ref.graph.edgeFactoryByLabel.get(label); if (edgeFactory == null) throw new IllegalArgumentException("specializedEdgeFactory for label=" + label + " not found - please register on startup!"); return edgeFactory.createEdge(ref.graph, outNode, inNode); } /** * Trims the node to save storage: shrinks overallocations * */ public synchronized long trim(){ int newSize = 0; for(int offsetPos = 0; 2*offsetPos < edgeOffsets.length(); offsetPos++){ int length = blockLength(offsetPos); newSize += length; } Object[] newArray = new Object[newSize]; int off = 0; for(int offsetPos = 0; 2*offsetPos < edgeOffsets.length(); offsetPos++){ int start = startIndex(offsetPos); int length = blockLength(offsetPos); System.arraycopy(adjacentNodesWithProperties, start, newArray, off, length); edgeOffsets.set(2 * offsetPos, off); off += length; } int oldsize = adjacentNodesWithProperties.length; adjacentNodesWithProperties = newArray; return (long)newSize + ( ((long)oldsize) << 32); } public final boolean isDirty() { return dirty; } @Override public int hashCode() { return Objects.hash(id2()); } @Override public boolean equals(final Object obj) { return (obj instanceof OdbNode) && id2() == ((OdbNode) obj).id2(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy