// Copyright 2017 JanusGraph Authors
//
// Licensed 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.janusgraph.graphdb.vertices;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import org.janusgraph.core.JanusGraphEdge;
import org.janusgraph.core.JanusGraphVertexProperty;
import org.janusgraph.diskstorage.EntryList;
import org.janusgraph.diskstorage.keycolumnvalue.SliceQuery;
import org.janusgraph.graphdb.internal.ElementLifeCycle;
import org.janusgraph.graphdb.internal.InternalRelation;
import org.janusgraph.graphdb.query.vertex.VertexCentricQueryBuilder;
import org.janusgraph.graphdb.transaction.StandardJanusGraphTx;
import org.janusgraph.graphdb.util.ElementHelper;
import org.janusgraph.util.datastructures.Retriever;
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.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class PreloadedVertex extends CacheVertex {
public static final Retriever<SliceQuery, EntryList> EMPTY_RETRIEVER = new Retriever<SliceQuery, EntryList>() {
@Override
public EntryList get(SliceQuery input) {
return EntryList.EMPTY_LIST;
}
};
private PropertyMixing mixin = NO_MIXIN;
private AccessCheck accessCheck = DEFAULT_CHECK;
public PreloadedVertex(StandardJanusGraphTx tx, long id, byte lifecycle) {
super(tx, id, lifecycle);
assert lifecycle == ElementLifeCycle.Loaded : "Invalid lifecycle encountered: " + lifecycle;
}
public void setPropertyMixing(PropertyMixing mixin) {
Preconditions.checkNotNull(mixin);
Preconditions.checkArgument(this.mixin == NO_MIXIN, "A property mixing has already been set");
this.mixin = mixin;
}
public void setAccessCheck(final AccessCheck accessCheck) {
Preconditions.checkArgument(accessCheck!=null);
this.accessCheck=accessCheck;
}
@Override
public void addToQueryCache(final SliceQuery query, final EntryList entries) {
super.addToQueryCache(query, entries);
}
public EntryList getFromCache(final SliceQuery query) {
return queryCache.get(query);
}
@Override
public List<InternalRelation> getAddedRelations(Predicate<InternalRelation> query) {
return Collections.EMPTY_LIST;
}
@Override
public VertexCentricQueryBuilder query() {
if (super.getQueryCacheSize() > 0) return super.query().queryOnlyGivenVertex();
else throw GraphComputer.Exceptions.adjacentVertexEdgesAndVerticesCanNotBeReadOrUpdated();
}
@Override
public boolean hasLoadedRelations(SliceQuery query) {
return true;
}
@Override
public boolean hasRemovedRelations() {
return false;
}
@Override
public boolean hasAddedRelations() {
return false;
}
@Override
public EntryList loadRelations(SliceQuery query, Retriever<SliceQuery, EntryList> lookup) {
return super.loadRelations(query, accessCheck.retrieveSliceQuery());
}
@Override
public <V> JanusGraphVertexProperty<V> property(VertexProperty.Cardinality cardinality, String key, V value, Object... keyValues) {
accessCheck.accessSetProperty();
JanusGraphVertexProperty<V> p = mixin.property(cardinality, key, value);
ElementHelper.attachProperties(p, keyValues);
return p;
}
public <V> JanusGraphVertexProperty<V> property(final String key, final V value, final Object... keyValues) {
return property(VertexProperty.Cardinality.single, key, value, keyValues);
}
@Override
public <V> Iterator<VertexProperty<V>> properties(String... keys) {
accessCheck.accessProperties();
if (mixin == NO_MIXIN) return super.properties(keys);
if (keys != null && keys.length > 0) {
int count = 0;
for (int i = 0; i < keys.length; i++) if (mixin.supports(keys[i])) count++;
if (count == 0 || !mixin.properties(keys).hasNext()) return super.properties(keys);
else if (count == keys.length) return mixin.properties(keys);
}
return (Iterator) com.google.common.collect.Iterators.concat(super.properties(keys), mixin.properties(keys));
}
@Override
public JanusGraphEdge addEdge(String s, Vertex vertex, Object... keyValues) {
throw GraphComputer.Exceptions.adjacentVertexEdgesAndVerticesCanNotBeReadOrUpdated();
}
@Override
public Iterator<Edge> edges(final Direction direction, final String... edgeLabels) {
accessCheck.accessEdges();
return super.edges(direction,edgeLabels);
}
@Override
public void remove() {
}
@Override
public void removeRelation(InternalRelation e) {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public boolean addRelation(InternalRelation e) {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
public interface AccessCheck {
public void accessEdges();
public void accessProperties();
public void accessSetProperty();
public Retriever<SliceQuery, EntryList> retrieveSliceQuery();
}
public static final AccessCheck DEFAULT_CHECK = new AccessCheck() {
@Override
public final void accessEdges() {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public final void accessProperties() {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public void accessSetProperty() {
throw GraphComputer.Exceptions.adjacentVertexPropertiesCanNotBeReadOrUpdated();
}
@Override
public Retriever<SliceQuery, EntryList> retrieveSliceQuery() {
return EMPTY_RETRIEVER;
}
};
public static final AccessCheck CLOSEDSTAR_CHECK = new AccessCheck() {
@Override
public final void accessEdges() {
return; //Allowed
}
@Override
public final void accessProperties() {
return; //Allowed
}
@Override
public void accessSetProperty() {
return; //Allowed
}
@Override
public Retriever<SliceQuery, EntryList> retrieveSliceQuery() {
return EXCEPTION_RETRIEVER;
}
private final Retriever<SliceQuery,EntryList> EXCEPTION_RETRIEVER = new Retriever<SliceQuery, EntryList>() {
@Override
public EntryList get(SliceQuery input) {
throw new UnsupportedOperationException("Cannot access data that hasn't been preloaded.");
}
};
};
public static final AccessCheck OPENSTAR_CHECK = new AccessCheck() {
@Override
public final void accessEdges() {
return; //Allowed
}
@Override
public final void accessProperties() {
return; //Allowed
}
@Override
public void accessSetProperty() {
return; //Allowed
}
@Override
public Retriever<SliceQuery, EntryList> retrieveSliceQuery() {
return EMPTY_RETRIEVER;
}
};
public interface PropertyMixing {
public <V> Iterator<VertexProperty<V>> properties(String... keys);
public boolean supports(String key);
public <V> JanusGraphVertexProperty<V> property(VertexProperty.Cardinality cardinality, String key, V value);
}
private static PropertyMixing NO_MIXIN = new PropertyMixing() {
@Override
public <V> Iterator<VertexProperty<V>> properties(String... keys) {
return Collections.emptyIterator();
}
@Override
public boolean supports(String key) {
return false;
}
@Override
public <V> JanusGraphVertexProperty<V> property(VertexProperty.Cardinality cardinality, String key, V value) {
throw new UnsupportedOperationException("Provided key is not supported: " + key);
}
};
}