package org.vertexium.accumulo.util;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.hadoop.io.Text;
import org.vertexium.FetchHint;
import org.vertexium.Property;
import org.vertexium.Visibility;
import org.vertexium.accumulo.AccumuloGraph;
import org.vertexium.accumulo.AccumuloNameSubstitutionStrategy;
import org.vertexium.accumulo.LazyMutableProperty;
import org.vertexium.accumulo.LazyPropertyMetadata;
import org.vertexium.accumulo.iterator.model.*;
import org.vertexium.accumulo.iterator.util.DataOutputStreamUtils;
import org.vertexium.id.NameSubstitutionStrategy;
import javax.annotation.Nullable;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.*;
public class DataInputStreamUtils {
public static Text decodeText(DataInputStream in) throws IOException {
return org.vertexium.accumulo.iterator.util.DataInputStreamUtils.decodeText(in);
}
public static String decodeString(DataInputStream in) throws IOException {
return org.vertexium.accumulo.iterator.util.DataInputStreamUtils.decodeString(in);
}
public static List<Text> decodeTextList(DataInputStream in) throws IOException {
int count = in.readInt();
if (count == -1) {
return null;
}
List<Text> results = new ArrayList<>();
for (int i = 0; i < count; i++) {
results.add(decodeText(in));
}
return results;
}
public static ImmutableSet<String> decodeStringSet(DataInputStream in) throws IOException {
int count = in.readInt();
if (count == -1) {
return null;
}
ImmutableSet.Builder<String> results = ImmutableSet.builder();
for (int i = 0; i < count; i++) {
results.add(decodeString(in));
}
return results.build();
}
public static Iterable<Property> decodeProperties(
AccumuloGraph graph,
DataInputStream in,
EnumSet<FetchHint> fetchHints
) throws IOException {
List<Property> results = new ArrayList<>();
while (true) {
int propId = in.read();
if (propId == ElementData.PROP_END) {
break;
} else if (propId != ElementData.PROP_START) {
throw new IOException("Unexpected prop id: " + propId);
}
String propertyKey = graph.getNameSubstitutionStrategy().inflate(decodeString(in));
String propertyName = graph.getNameSubstitutionStrategy().inflate(decodeString(in));
Visibility propertyVisibility = new Visibility(decodeText(in).toString());
long propertyTimestamp = in.readLong();
int propertyValueLength = in.readInt();
byte[] propertyValue = new byte[propertyValueLength];
int read = in.read(propertyValue);
if (read != propertyValueLength) {
throw new IOException("Unexpected data length expected " + propertyValueLength + " found " + read);
}
List<Text> propertyHiddenVisibilitiesTextList = decodeTextList(in);
Set<Visibility> propertyHiddenVisibilities = null;
if (propertyHiddenVisibilitiesTextList != null) {
propertyHiddenVisibilities = Sets.newHashSet(Iterables.transform(propertyHiddenVisibilitiesTextList, new Function<Text, Visibility>() {
@Nullable
@Override
public Visibility apply(Text input) {
return new Visibility(input.toString());
}
}));
}
LazyPropertyMetadata metadata = decodePropertyMetadata(in, graph.getNameSubstitutionStrategy());
results.add(new LazyMutableProperty(
graph,
graph.getVertexiumSerializer(),
propertyKey,
propertyName,
propertyValue,
metadata,
propertyHiddenVisibilities,
propertyVisibility,
propertyTimestamp,
fetchHints
));
}
return results;
}
private static LazyPropertyMetadata decodePropertyMetadata(DataInputStream in, AccumuloNameSubstitutionStrategy nameSubstitutionStrategy) throws IOException {
LazyPropertyMetadata metadata = new LazyPropertyMetadata();
int entryCount = in.readInt();
for (int i = 0; i < entryCount; i++) {
String key = nameSubstitutionStrategy.deflate(decodeString(in));
Visibility visibility = new Visibility(decodeString(in));
int valueLength = in.readInt();
byte[] value = new byte[valueLength];
int read = in.read(value);
if (read != valueLength) {
throw new IOException("Unexpected data length expected " + valueLength + " found " + read);
}
metadata.add(key, visibility, value);
}
return metadata;
}
public static Edges decodeEdges(DataInputStream in, NameSubstitutionStrategy nameSubstitutionStrategy) throws IOException {
int edgeLabelMarker = in.readByte();
if (edgeLabelMarker == DataOutputStreamUtils.EDGE_LABEL_WITH_REFS_MARKER) {
return decodeEdgesWithRefs(in, nameSubstitutionStrategy);
} else if (edgeLabelMarker == DataOutputStreamUtils.EDGE_LABEL_ONLY_MARKER) {
return decodeEdgesLabelsOnly(in, nameSubstitutionStrategy);
} else {
throw new IOException("Unexpected edge label marker: " + edgeLabelMarker);
}
}
private static Edges decodeEdgesLabelsOnly(DataInputStream in, NameSubstitutionStrategy nameSubstitutionStrategy) throws IOException {
EdgesWithCount edges = new EdgesWithCount();
int count = in.readInt();
for (int i = 0; i < count; i++) {
String label = nameSubstitutionStrategy.inflate(decodeString(in));
int edgeByLabelCount = in.readInt();
edges.add(label, edgeByLabelCount);
}
return edges;
}
private static Edges decodeEdgesWithRefs(DataInputStream in, NameSubstitutionStrategy nameSubstitutionStrategy) throws IOException {
EdgesWithEdgeInfo edges = new EdgesWithEdgeInfo();
int count = in.readInt();
for (int i = 0; i < count; i++) {
String label = new String(decodeByteArray(in), DataOutputStreamUtils.CHARSET);
int edgeByLabelCount = in.readInt();
for (int edgeByLabelIndex = 0; edgeByLabelIndex < edgeByLabelCount; edgeByLabelIndex++) {
Text edgeId = decodeText(in);
long timestamp = in.readLong();
String vertexId = decodeString(in);
EdgeInfo edgeInfo = new EdgeInfo(nameSubstitutionStrategy.inflate(label), vertexId, timestamp);
edges.add(edgeId, edgeInfo);
}
}
return edges;
}
public static byte[] decodeByteArray(DataInputStream in) throws IOException {
return org.vertexium.accumulo.iterator.util.DataInputStreamUtils.decodeByteArray(in);
}
public static void decodeHeader(DataInputStream in, byte expectedTypeId) throws IOException {
byte[] header = new byte[ElementData.HEADER.length];
int read = in.read(header);
if (read != header.length) {
throw new IOException("Unexpected header length. Expected " + ElementData.HEADER.length + " found " + read);
}
if (!Arrays.equals(header, ElementData.HEADER)) {
throw new IOException("Unexpected header");
}
int typeId = in.read();
if (typeId != expectedTypeId) {
throw new IOException("Unexpected type id. Expected " + expectedTypeId + " found " + typeId);
}
}
}