package be.bagofwords.db;
import be.bagofwords.db.combinator.Combinator;
import be.bagofwords.iterator.CloseableIterator;
import be.bagofwords.iterator.DataIterable;
import be.bagofwords.iterator.IterableUtils;
import be.bagofwords.iterator.SimpleIterator;
import be.bagofwords.text.BowString;
import be.bagofwords.ui.UI;
import be.bagofwords.util.HashUtils;
import be.bagofwords.util.KeyValue;
import be.bagofwords.util.StringUtils;
import java.util.Iterator;
import java.util.stream.Stream;
public abstract class DataInterface<T extends Object> implements DataIterable<KeyValue<T>> {
private final Combinator<T> combinator;
private final Class<T> objectClass;
private final String name;
private final Object closeLock = new Object();
private boolean wasClosed;
private boolean closeWasRequested;
private final boolean isTemporaryDataInterface;
protected DataInterface(String name, Class<T> objectClass, Combinator<T> combinator, boolean isTemporaryDataInterface) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Name can not be null or empty");
}
this.name = name;
this.objectClass = objectClass;
this.combinator = combinator;
this.isTemporaryDataInterface = isTemporaryDataInterface;
}
public abstract T read(long key);
public T read(String key) {
return read(HashUtils.hashCode(key));
}
public T read(BowString key) {
return read(HashUtils.hashCode(key));
}
public long readCount(long key) {
Long result = (Long) read(key);
if (result == null)
return 0;
else
return result;
}
public long readCount(BowString key) {
return readCount(HashUtils.hashCode(key));
}
public long readCount(String key) {
return readCount(HashUtils.hashCode(key));
}
public boolean mightContain(String key) {
return mightContain(HashUtils.hashCode(key));
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public boolean mightContain(long key) {
return read(key) != null;
}
public abstract CloseableIterator<KeyValue<T>> iterator();
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public CloseableIterator<Long> keyIterator() {
final CloseableIterator<KeyValue<T>> keyValueIterator = iterator();
return new CloseableIterator<Long>() {
@Override
public boolean hasNext() {
return keyValueIterator.hasNext();
}
@Override
public Long next() {
return keyValueIterator.next().getKey();
}
@Override
public void closeInt() {
keyValueIterator.close();
}
};
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public CloseableIterator<T> valueIterator() {
final CloseableIterator<KeyValue<T>> keyValueIterator = iterator();
return new CloseableIterator<T>() {
@Override
public boolean hasNext() {
return keyValueIterator.hasNext();
}
@Override
public T next() {
return keyValueIterator.next().getValue();
}
@Override
public void closeInt() {
keyValueIterator.close();
}
};
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public CloseableIterator<KeyValue<T>> iterator(final Iterator<Long> keyIterator) {
return IterableUtils.iterator(new SimpleIterator<KeyValue<T>>() {
@Override
public KeyValue<T> next() throws Exception {
while (keyIterator.hasNext()) {
Long next = keyIterator.next();
T value = read(next);
if (value != null) {
return new KeyValue<>(next, value);
}
}
return null;
}
});
}
public CloseableIterator<KeyValue<T>> cachedValueIterator() {
return new CloseableIterator<KeyValue<T>>() {
@Override
protected void closeInt() {
//ok
}
@Override
public boolean hasNext() {
return false;
}
@Override
public KeyValue<T> next() {
return null;
}
};
}
public abstract void optimizeForReading();
public abstract void dropAllData();
public abstract void flush();
public abstract long apprSize();
public Combinator<T> getCombinator() {
return combinator;
}
public abstract DataInterface getCoreDataInterface();
public Class<T> getObjectClass() {
return objectClass;
}
public void write(BowString key, T value) {
write(HashUtils.hashCode(key.getS()), value);
}
public void write(String key, T value) {
write(HashUtils.hashCode(key), value);
}
public abstract void write(long key, T value);
public void increaseCount(String key, Long value) {
write(key, (T) value);
}
public void increaseCount(long key, Long value) {
write(key, (T) value);
}
public void increaseCount(String key) {
increaseCount(key, 1l);
}
public void increaseCount(long key) {
increaseCount(key, 1l);
}
public String getName() {
return name;
}
public void remove(String key) {
remove(HashUtils.hashCode(key));
}
public void remove(long key) {
write(key, null);
}
public long dataCheckSum() {
CloseableIterator<KeyValue<T>> valueIterator = iterator();
final int numToSample = 10000;
long checksum = 0;
int numDone = 0;
while (valueIterator.hasNext() && numDone < numToSample) {
KeyValue<T> next = valueIterator.next();
if (next.getValue() == null) {
throw new RuntimeException("Iterating over values returned null for key " + next.getKey());
}
T value = next.getValue();
checksum += checksum * 31 + value.hashCode();
numDone++;
}
valueIterator.close();
return checksum;
}
/**
* You don't want to use this if you could use apprSize()
*/
public long exactSize() {
long result = 0;
CloseableIterator<Long> keyIt = keyIterator();
while (keyIt.hasNext()) {
keyIt.next();
result++;
}
keyIt.close();
return result;
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public void write(Iterator<KeyValue<T>> entries) {
while (entries.hasNext()) {
KeyValue<T> entry = entries.next();
write(entry.getKey(), entry.getValue());
}
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public Stream<KeyValue<T>> stream() {
return IterableUtils.stream(this, true);
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public Stream<T> streamValues() {
return IterableUtils.stream(valueIterator(), apprSize(), false);
}
/**
* This method can be overwritten in a subclass to improve efficiency
*/
public Stream<Long> streamKeys() {
return IterableUtils.stream(keyIterator(), apprSize(), true);
}
public final void close() {
requestClose();
ifNotClosed(() -> {
if (isTemporaryDataInterface) {
dropAllData();
}
flush();
doClose();
wasClosed = true;
}
);
}
protected void requestClose() {
closeWasRequested = true;
}
protected abstract void doClose();
public final boolean wasClosed() {
return wasClosed;
}
protected final boolean closeWasRequested() {
return closeWasRequested;
}
@Override
protected void finalize() throws Throwable {
ifNotClosed(() -> {
if (!isTemporaryDataInterface()) {
//the user did not close the data interface himself?
UI.write("Closing data interface " + getName() + " because it is about to be garbage collected.");
}
close();
});
super.finalize();
}
public void ifNotClosed(ActionIfNotClosed action) {
synchronized (closeLock) {
if (!wasClosed()) {
action.doAction();
}
}
}
public interface ActionIfNotClosed {
public void doAction();
}
public boolean isTemporaryDataInterface() {
return isTemporaryDataInterface;
}
}