/* 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.bdbje;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.repository.Hints;
import org.locationtech.geogig.storage.ConfigDatabase;
import org.locationtech.geogig.storage.GraphDatabase;
import org.locationtech.geogig.storage.SynchronizedGraphDatabase;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
/**
* Implementation of {@link GraphDatabase} backed by a BerkeleyDB Java Edition database.
* <p>
* Implementation note: Since this is the only kind of mutable state we maintain, this
* implementation extends {@link SynchronizedGraphDatabase} to avoid concurrent threads stepping
* over each other's feet and overriding graph relations. An alternate solution would be to
* serialize writes and have free threaded reads.
* </p>
*/
public class JEGraphDatabase_v0_2 extends JEGraphDatabase {
private static final TupleBinding<NodeData> BINDING_V2 = new GraphNodeBinding();
@Inject
public JEGraphDatabase_v0_2(final ConfigDatabase config, final EnvironmentBuilder envProvider,
final Hints hints) {
super(config, envProvider, BINDING_V2, "0.2", hints);
}
private static class GraphNodeBinding extends TupleBinding<NodeData> {
private static final ObjectIdBinding OID = new ObjectIdBinding();
private static final OidListBinding OIDLIST = new OidListBinding();
private static final PropertiesBinding PROPS = new PropertiesBinding();
@Override
public NodeData entryToObject(TupleInput input) {
ObjectId id = OID.entryToObject(input);
ObjectId mappedTo = OID.entryToObject(input);
List<ObjectId> outgoing = OIDLIST.entryToObject(input);
List<ObjectId> incoming = OIDLIST.entryToObject(input);
Map<String, String> properties = PROPS.entryToObject(input);
NodeData nodeData = new NodeData(id, mappedTo, outgoing, incoming, properties);
return nodeData;
}
@Override
public void objectToEntry(NodeData node, TupleOutput output) {
OID.objectToEntry(node.id, output);
OID.objectToEntry(node.mappedTo, output);
OIDLIST.objectToEntry(node.outgoing, output);
OIDLIST.objectToEntry(node.incoming, output);
PROPS.objectToEntry(node.properties, output);
}
private static class ObjectIdBinding extends TupleBinding<ObjectId> {
@Nullable
@Override
public ObjectId entryToObject(TupleInput input) {
int size = input.read();
if (size == 0) {
return ObjectId.NULL;
}
Preconditions.checkState(ObjectId.NUM_BYTES == size);
byte[] hash = new byte[size];
Preconditions.checkState(size == input.read(hash));
return ObjectId.createNoClone(hash);
}
@Override
public void objectToEntry(@Nullable ObjectId object, TupleOutput output) {
if (null == object || object.isNull()) {
output.write(0);
} else {
output.write(ObjectId.NUM_BYTES);
output.write(object.getRawValue());
}
}
}
private static class OidListBinding extends TupleBinding<List<ObjectId>> {
private static final ObjectIdBinding OID = new ObjectIdBinding();
@Override
public List<ObjectId> entryToObject(TupleInput input) {
int len = input.readInt();
List<ObjectId> list = new ArrayList<ObjectId>((int) (1.5 * len));
for (int i = 0; i < len; i++) {
list.add(OID.entryToObject(input));
}
return list;
}
@Override
public void objectToEntry(List<ObjectId> list, TupleOutput output) {
int len = list.size();
output.writeInt(len);
for (int i = 0; i < len; i++) {
OID.objectToEntry(list.get(i), output);
}
}
}
private static class PropertiesBinding extends TupleBinding<Map<String, String>> {
@Override
public Map<String, String> entryToObject(TupleInput input) {
int len = input.readInt();
Map<String, String> props = new HashMap<String, String>();
for (int i = 0; i < len; i++) {
String k = input.readString();
String v = input.readString();
props.put(k, v);
}
return props;
}
@Override
public void objectToEntry(Map<String, String> props, TupleOutput output) {
output.writeInt(props.size());
for (Map.Entry<String, String> e : props.entrySet()) {
output.writeString(e.getKey());
output.writeString(e.getValue());
}
}
}
}
}