/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.storage.datastream;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readCommit;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readFeature;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readFeatureType;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readHeader;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readTag;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.readTree;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.requireHeader;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeCommit;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeFeature;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeFeatureType;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeHeader;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeTag;
import static org.locationtech.geogig.storage.datastream.FormatCommonV2.writeTree;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.EnumMap;
import java.util.Map;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevCommit;
import org.locationtech.geogig.api.RevFeature;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.RevTag;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.storage.ObjectReader;
import org.locationtech.geogig.storage.ObjectSerializingFactory;
import org.locationtech.geogig.storage.ObjectWriter;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
/**
* Serialization factory for serial version 2
*/
public class DataStreamSerializationFactoryV2 implements ObjectSerializingFactory {
public static final DataStreamSerializationFactoryV2 INSTANCE = new DataStreamSerializationFactoryV2();
private final static ObjectReader<RevObject> OBJECT_READER = new ObjectReaderV2();
private static final EnumMap<TYPE, Serializer<? extends RevObject>> serializers = Maps
.newEnumMap(TYPE.class);
static {
serializers.put(TYPE.COMMIT, new CommitSerializer());
serializers.put(TYPE.FEATURE, new FeatureSerializer());
serializers.put(TYPE.FEATURETYPE, new FeatureTypeSerializer());
serializers.put(TYPE.TAG, new TagSerializer());
serializers.put(TYPE.TREE, new TreeSerializer());
}
@SuppressWarnings("unchecked")
private static <T extends RevObject> Serializer<T> serializer(TYPE type) {
Serializer<? extends RevObject> serializer = serializers.get(type);
if (serializer == null) {
throw new UnsupportedOperationException("No serializer for " + type);
}
return (Serializer<T>) serializer;
}
@Override
public ObjectReader<RevCommit> createCommitReader() {
return serializer(TYPE.COMMIT);
}
@Override
public ObjectReader<RevTree> createRevTreeReader() {
return serializer(TYPE.TREE);
}
@Override
public ObjectReader<RevFeature> createFeatureReader() {
return serializer(TYPE.FEATURE);
}
@Override
public ObjectReader<RevFeature> createFeatureReader(Map<String, Serializable> hints) {
return serializer(TYPE.FEATURE);
}
@Override
public ObjectReader<RevFeatureType> createFeatureTypeReader() {
return serializer(TYPE.FEATURETYPE);
}
@Override
public <T extends RevObject> ObjectWriter<T> createObjectWriter(TYPE type) {
return serializer(type);
}
@Override
public <T extends RevObject> ObjectReader<T> createObjectReader(TYPE type) {
return serializer(type);
}
@Override
public ObjectReader<RevObject> createObjectReader() {
return OBJECT_READER;
}
/**
* Provides an interface for reading and writing objects.
*/
private static abstract class Serializer<T extends RevObject> implements ObjectReader<T>,
ObjectWriter<T> {
private final TYPE header;
Serializer(TYPE type) {
this.header = type;
}
@Override
public T read(ObjectId id, InputStream rawData) throws IllegalArgumentException {
DataInput in = new DataInputStream(rawData);
try {
requireHeader(in, header);
return readBody(id, in);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
protected abstract T readBody(ObjectId id, DataInput in) throws IOException;
/**
* Writers must call
* {@link FormatCommonV2#writeHeader(java.io.DataOutput, org.locationtech.geogig.api.RevObject.TYPE)},
* readers must not, in order for {@link ObjectReaderV2} to be able of parsing the header
* and call the appropriate read method.
*/
@Override
public void write(T object, OutputStream out) throws IOException {
DataOutput data = new DataOutputStream(out);
writeHeader(data, object.getType());
writeBody(object, data);
}
public abstract void writeBody(T object, DataOutput data) throws IOException;
}
private static final class CommitSerializer extends Serializer<RevCommit> {
CommitSerializer() {
super(TYPE.COMMIT);
}
@Override
public RevCommit readBody(ObjectId id, DataInput in) throws IOException {
return readCommit(id, in);
}
@Override
public void writeBody(RevCommit commit, DataOutput data) throws IOException {
writeCommit(commit, data);
}
}
private static final class FeatureSerializer extends Serializer<RevFeature> {
FeatureSerializer() {
super(TYPE.FEATURE);
}
@Override
public RevFeature readBody(ObjectId id, DataInput in) throws IOException {
return readFeature(id, in);
}
@Override
public void writeBody(RevFeature feature, DataOutput data) throws IOException {
writeFeature(feature, data);
}
}
private static final class FeatureTypeSerializer extends Serializer<RevFeatureType> {
FeatureTypeSerializer() {
super(TYPE.FEATURETYPE);
}
@Override
public RevFeatureType readBody(ObjectId id, DataInput in) throws IOException {
return readFeatureType(id, in);
}
@Override
public void writeBody(RevFeatureType object, DataOutput data) throws IOException {
writeFeatureType(object, data);
}
}
private static final class TagSerializer extends Serializer<RevTag> {
TagSerializer() {
super(TYPE.TAG);
}
@Override
public RevTag readBody(ObjectId id, DataInput in) throws IOException {
return readTag(id, in);
}
@Override
public void writeBody(RevTag tag, DataOutput data) throws IOException {
writeTag(tag, data);
}
}
private static final class TreeSerializer extends Serializer<RevTree> {
TreeSerializer() {
super(TYPE.TREE);
}
@Override
public RevTree readBody(ObjectId id, DataInput in) throws IOException {
return readTree(id, in);
}
@Override
public void writeBody(RevTree tree, DataOutput data) throws IOException {
writeTree(tree, data);
}
}
private static final class ObjectReaderV2 implements ObjectReader<RevObject> {
@Override
public RevObject read(ObjectId id, InputStream rawData) throws IllegalArgumentException {
DataInput in = new DataInputStream(rawData);
try {
return readData(id, in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private RevObject readData(ObjectId id, DataInput in) throws IOException {
final TYPE type = readHeader(in);
Serializer<RevObject> serializer = DataStreamSerializationFactoryV2.serializer(type);
RevObject object = serializer.readBody(id, in);
return object;
}
}
}