// 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.olap.computer;
import com.google.common.base.Preconditions;
import org.apache.tinkerpop.gremlin.process.computer.MessageCombiner;
import org.apache.tinkerpop.gremlin.process.computer.MessageScope;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class VertexState<M> {
protected Object properties;
private Object previousMessages;
private Object currentMessages;
private VertexState() {
properties=null;
previousMessages=null;
currentMessages=null;
}
public VertexState(Map<String,Integer> keyMap) {
assert isValidIdMap(keyMap);
previousMessages = null;
currentMessages = null;
if (keyMap.isEmpty() || keyMap.size()==1) properties = null;
else properties = new Object[keyMap.size()];
}
public<V> void setProperty(String key, V value, Map<String,Integer> keyMap) {
assert !keyMap.isEmpty() && keyMap.containsKey(key);
if (keyMap.size()==1) properties = value;
else ((Object[])properties)[keyMap.get(key)]=value;
}
public<V> V getProperty(String key, Map<String,Integer> keyMap) {
assert !keyMap.isEmpty() && keyMap.containsKey(key);
if (keyMap.size()==1) return (V)properties;
else return (V)((Object[])properties)[keyMap.get(key)];
}
private void initializeCurrentMessages(Map<MessageScope,Integer> scopeMap) {
assert !scopeMap.isEmpty() && isValidIdMap(scopeMap);
if (currentMessages==null) {
if (scopeMap.size()>1) currentMessages = new Object[scopeMap.size()];
}
}
public synchronized void setMessage(M message, MessageScope scope, Map<MessageScope,Integer> scopeMap) {
assert message!=null && scope!=null;
Preconditions.checkArgument(scopeMap.containsKey(scope),"Provided scope was not declared in the VertexProgram: %s",scope);
initializeCurrentMessages(scopeMap);
if (scopeMap.size()==1) currentMessages = message;
else ((Object[])currentMessages)[scopeMap.get(scope)]=message;
}
public synchronized void addMessage(M message, MessageScope scope, Map<MessageScope,Integer> scopeMap,
MessageCombiner<M> combiner) {
assert message!=null && scope!=null && combiner!=null;
Preconditions.checkArgument(scopeMap.containsKey(scope),"Provided scope was not declared in the VertexProgram: %s",scope);
assert scopeMap.containsKey(scope);
initializeCurrentMessages(scopeMap);
if (scopeMap.size()==1) {
if (currentMessages==null) currentMessages = message;
else currentMessages = combiner.combine(message,(M)currentMessages);
} else {
int pos = scopeMap.get(scope);
Object[] msgs = (Object[])currentMessages;
if (msgs[pos]==null) msgs[pos]=message;
else msgs[pos] = combiner.combine(message,(M)msgs[pos]);
}
}
public M getMessage(MessageScope scope, Map<MessageScope,Integer> scopeMap) {
assert scope!=null && isValidIdMap(scopeMap) && scopeMap.containsKey(scope);
if (scopeMap.size()==1) return (M)previousMessages;
else return (M)((Object[])previousMessages)[scopeMap.get(scope)];
}
public synchronized void completeIteration() {
previousMessages = currentMessages;
currentMessages = null;
}
public static boolean isValidIdMap(Map<?,Integer> map) {
if (map==null) return false;
if (map.isEmpty()) return true;
int size = map.size();
Set<Integer> ids = new HashSet<>(size);
for (Integer id : map.values()) {
if (id>=size || id<0) return false;
if (!ids.add(id)) return false;
}
return true;
}
static final VertexState EMPTY_STATE = new EmptyState();
private static class EmptyState<M> extends VertexState<M> {
@Override
public<V> void setProperty(String key, V value, Map<String,Integer> keyMap) {
throw new UnsupportedOperationException();
}
@Override
public<V> V getProperty(String key, Map<String,Integer> keyMap) {
return null;
}
@Override
public M getMessage(MessageScope scope, Map<MessageScope,Integer> scopeMap) {
return null;
}
@Override
public synchronized void setMessage(M message, MessageScope scope, Map<MessageScope,Integer> scopeMap) {
throw new UnsupportedOperationException();
}
@Override
public synchronized void addMessage(M message, MessageScope scope, Map<MessageScope,Integer> scopeMap,
MessageCombiner<M> combiner) {
throw new UnsupportedOperationException();
}
@Override
public synchronized void completeIteration() {
throw new UnsupportedOperationException();
}
}
}