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

org.apache.tinkerpop.gremlin.tinkergraph.structure.SpecializedTinkerVertex Maven / Gradle / Ivy

There is a newer version: 3.3.4.23
Show newest version
/*
 * 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.structure;

import gnu.trove.map.hash.THashMap;
import org.apache.tinkerpop.gremlin.structure.*;
import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.apache.tinkerpop.gremlin.util.iterator.MultiIterator;

import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public abstract class SpecializedTinkerVertex extends TinkerVertex {

    /** property keys for a specialized vertex  */
    protected abstract Set specificKeys();

    public abstract Set allowedOutEdgeLabels();
    public abstract Set allowedInEdgeLabels();

    protected Map> outEdgesByLabel;
    protected Map> inEdgesByLabel;

    /** `dirty` flag for serialization to avoid superfluous serialization */
    // TODO re-implement/verify this optimization: only re-serialize if element has been changed
    private boolean modifiedSinceLastSerialization = true;
    private Semaphore modificationSemaphore = new Semaphore(1);

    protected SpecializedTinkerVertex(long id, String label, TinkerGraph graph) {
        super(id, label, graph);
        if (graph != null && graph.referenceManager != null) {
            graph.referenceManager.applyBackpressureMaybe();
        }
    }

    @Override
    public Set keys() {
        return specificKeys();
    }

    @Override
    public  VertexProperty property(String key) {
        if (this.removed) return VertexProperty.empty();
        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();
        }
    }

    /* implement in concrete specialised instance to avoid using generic HashMaps */
    protected abstract  Iterator> specificProperties(String key);

    public abstract Map valueMap();

    @Override
    public  Iterator> properties(String... propertyKeys) {
        if (this.removed) return Collections.emptyIterator();
        if (propertyKeys.length == 0) { // return all properties
            return (Iterator) specificKeys().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  VertexProperty property(VertexProperty.Cardinality cardinality, String key, V value, Object... keyValues) {
        if (this.removed) throw elementAlreadyRemoved(Vertex.class, id);
        ElementHelper.legalPropertyKeyValueArray(keyValues);
        ElementHelper.validateProperty(key, value);
        acquireModificationLock();
        this.modifiedSinceLastSerialization = true;
        final VertexProperty vp = updateSpecificProperty(cardinality, key, value);
        TinkerHelper.autoUpdateIndex(this, key, value, null);
        releaseModificationLock();
        return vp;
    }

    protected abstract  VertexProperty updateSpecificProperty(
      VertexProperty.Cardinality cardinality, String key, V value);

    public void removeProperty(String key) {
        acquireModificationLock();
        modifiedSinceLastSerialization = true;
        removeSpecificProperty(key);
        releaseModificationLock();
    }

    protected abstract void removeSpecificProperty(String key);

    @Override
    public Edge addEdge(final String label, Vertex inVertex, final Object... keyValues) {
        if (graph.isClosed()) {
            throw new IllegalStateException("cannot add more elements, graph is closed");
        }
        if (null == inVertex) {
            throw Graph.Exceptions.argumentCanNotBeNull("inVertex");
        }
        VertexRef inVertexRef = null;
        if (inVertex instanceof VertexRef) {
            inVertexRef = (VertexRef) inVertex;
            inVertex = inVertexRef.get();
        }
        if (this.removed) {
            throw elementAlreadyRemoved(Vertex.class, this.id);
        }
        if (!allowedOutEdgeLabels().contains(label)) {
            throw new IllegalArgumentException(getClass().getName() + " doesn't allow outgoing edges with label=" + label);
        }
        if (!((SpecializedTinkerVertex) inVertex).allowedInEdgeLabels().contains(label)) {
            throw new IllegalArgumentException(inVertex.getClass().getName() + " doesn't allow incoming edges with label=" + label);
        }
        ElementHelper.legalPropertyKeyValueArray(keyValues);

        if (graph.specializedEdgeFactoryByLabel.containsKey(label)) {
            SpecializedElementFactory.ForEdge factory = graph.specializedEdgeFactoryByLabel.get(label);
            Long idValue = (Long) graph.edgeIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
            if (null != idValue) {
                if (graph.edges.containsKey(idValue))
                    throw Graph.Exceptions.edgeWithIdAlreadyExists(idValue);
            } else {
                idValue = (Long) graph.edgeIdManager.getNextId(graph);
            }
            graph.currentId.set(Long.max(idValue, graph.currentId.get()));

            // TODO hold link to vertexRef locally so we don't need the following lookup
            VertexRef outVertexRef = (VertexRef) graph.vertices.get(id);
            final SpecializedTinkerEdge underlying = factory.createEdge(idValue, graph, outVertexRef, inVertexRef);
            final Edge edge;
            if (graph.ondiskOverflowEnabled) {
                edge = factory.createEdgeRef(underlying);
            } else {
                edge = factory.createEdge(idValue, graph, outVertexRef, inVertexRef);
            }
            ElementHelper.attachProperties(edge, keyValues);
            graph.edges.put(edge.id(), edge);
            graph.getElementsByLabel(graph.edgesByLabel, label).add(edge);

//            acquireModificationLock();
            storeOutEdge(edge);
            ((SpecializedTinkerVertex) inVertex).storeInEdge(edge);
//            releaseModificationLock();
            modifiedSinceLastSerialization = true;
            return edge;
        } else { // edge label not registered for a specialized factory, treating as generic edge
            if (graph.usesSpecializedElements) {
                throw new IllegalArgumentException(
                    "this instance of TinkerGraph uses specialized elements, but doesn't have a factory for label " + label
                        + ". Mixing specialized and generic elements is not (yet) supported");
            }
            return super.addEdge(label, inVertex, keyValues);
        }
    }

    /** do not call directly (other than from deserializer and SpecializedTinkerVertex.addEdge) */
    public void storeOutEdge(final Edge edge) {
        storeEdge(edge, getOutEdgesByLabel());
    }
    
    /** do not call directly (other than from deserializer and SpecializedTinkerVertex.addEdge) */
    public void storeInEdge(final Edge edge) {
        storeEdge(edge, getInEdgesByLabel());
    }

    private void storeEdge(final Edge edge, final Map> edgesByLabel) {
        if (!edgesByLabel.containsKey(edge.label())) {
            // TODO ArrayLists aren't good for concurrent modification, use memory-light concurrency safe list
            edgesByLabel.put(edge.label(), new ArrayList<>());
        }
        edgesByLabel.get(edge.label()).add(edge);
    }

    @Override
    public Iterator edges(final Direction direction, final String... edgeLabels) {
        final MultiIterator multiIterator = new MultiIterator<>();

        if (edgeLabels.length == 0) { // follow all labels
            if (direction == Direction.OUT || direction == Direction.BOTH) {
                getOutEdgesByLabel().values().forEach(edges -> multiIterator.addIterator(edges.iterator()));
            }
            if (direction == Direction.IN || direction == Direction.BOTH) {
                getInEdgesByLabel().values().forEach(edges -> multiIterator.addIterator(edges.iterator()));
            }
        } else {
            for (String label : edgeLabels) {
                /* note: usage of `==` (pointer comparison) over `.equals` (String content comparison) is intentional for performance - use the statically defined strings */
                if (direction == Direction.OUT || direction == Direction.BOTH) {
                    multiIterator.addIterator(getOutEdgesByLabel(label).iterator());
                }
                if (direction == Direction.IN || direction == Direction.BOTH) {
                    multiIterator.addIterator(getInEdgesByLabel(label).iterator());
                }
            }
        }
        
        return multiIterator;
    }

    @Override
    public Iterator vertices(final Direction direction, final String... edgeLabels) {
        Iterator edges = edges(direction, edgeLabels);
        if (direction == Direction.IN) {
            return IteratorUtils.map(edges, Edge::outVertex);
        } else if (direction == Direction.OUT) {
            return IteratorUtils.map(edges, Edge::inVertex);
        } else if (direction == Direction.BOTH) {
            return IteratorUtils.concat(vertices(Direction.IN, edgeLabels), vertices(Direction.OUT, edgeLabels));
        } else {
            return Collections.emptyIterator();
        }
    }

    protected Map> getOutEdgesByLabel() {
        if (outEdgesByLabel == null) {
            this.outEdgesByLabel = new THashMap<>();
        }
        return outEdgesByLabel;
    }
    
    protected Map> getInEdgesByLabel() {
        if (inEdgesByLabel == null) {
            this.inEdgesByLabel = new THashMap<>();
        }
        return inEdgesByLabel;
    }

    protected List getOutEdgesByLabel(String label) {
        return getOutEdgesByLabel().getOrDefault(label, new ArrayList<>());
    }

    protected List getInEdgesByLabel(String label) {
        return getInEdgesByLabel().getOrDefault(label, new ArrayList<>());
    }

    protected  List specializedEdges(final Direction direction, final String label) {
        final Map> edgesByLabel;
        if (direction == Direction.OUT) {
            edgesByLabel = getOutEdgesByLabel();
        } else if (direction == Direction.IN) {
            edgesByLabel = getInEdgesByLabel();
        } else {
            throw new IllegalArgumentException("not implemented");
        }

        return edgesByLabel.get(label).stream().map(edge -> {
            if (edge instanceof EdgeRef) {
                edge = ((EdgeRef) edge).get();
            }
            return (E) edge;
        }).collect(Collectors.toList());
    }

    @Override
    public void remove() {
        super.remove();
        graph.getElementsByLabel(graph.verticesByLabel, label).remove(this);
        this.modifiedSinceLastSerialization = true;
    }

    public void setModifiedSinceLastSerialization(boolean modifiedSinceLastSerialization) {
        this.modifiedSinceLastSerialization = modifiedSinceLastSerialization;
    }

    public void acquireModificationLock() {
        try {
            modificationSemaphore.acquire();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void releaseModificationLock() {
        modificationSemaphore.release();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy