package com.g414.haildb.tpl; import java.io.Closeable; import java.util.Iterator; import java.util.Map; import com.g414.haildb.Cursor; import com.g414.haildb.Cursor.CursorDirection; import com.g414.haildb.Cursor.LockMode; import com.g414.haildb.Cursor.SearchMode; import com.g414.haildb.TableDef; import com.g414.haildb.Transaction; import com.g414.haildb.Tuple; public class Functional { public interface Traversal<T> extends Closeable, Iterator<T> { public void traverseAll(); public void close(); } public enum TraversalMode { READ_ONLY, READ_WRITE; } public enum MutationType { NONE, INSERT_OR_UPDATE, DELETE; } public static class Target { private final TableDef tableDef; private final String indexDef; public Target(TableDef tableDef) { this(tableDef, null); } public Target(TableDef tableDef, String indexDef) { this.tableDef = tableDef; this.indexDef = indexDef; } public TableDef getTableDef() { return tableDef; } public String getIndexDef() { return indexDef; } } public static class Mutation { private final MutationType type; private final Map<String, Object> instance; public Mutation(MutationType type, Map<String, Object> instance) { this.type = type; this.instance = instance; } public MutationType getType() { return type; } public Map<String, Object> getInstance() { return instance; } } public interface Mapping<T> { public T map(Map<String, Object> row); } public interface Reduction<T> { public T reduce(Map<String, Object> row, T initial); } public interface Filter extends Mapping<Boolean> { } public static class TraversalSpec { private final Target target; private final CursorDirection cursorDirection; private final SearchMode searchMode; private final Map<String, Object> firstKey; private final Filter primaryFilter; private final Filter filter; public TraversalSpec(Target target, Map<String, Object> firstKey, Filter primaryFilter, Filter filter) { this(target, CursorDirection.ASC, SearchMode.GE, firstKey, primaryFilter, filter); } public TraversalSpec(Target target, CursorDirection cursorDirection, SearchMode searchMode, Map<String, Object> firstKey, Filter primaryFilter, Filter filter) { this.target = target; this.cursorDirection = cursorDirection; this.searchMode = searchMode; this.firstKey = firstKey; this.primaryFilter = primaryFilter; this.filter = filter; } public Target getTarget() { return target; } public CursorDirection getCursorDirection() { return cursorDirection; } public SearchMode getSearchMode() { return searchMode; } public Map<String, Object> getFirstKey() { return firstKey; } public Filter getPrimaryFilter() { return primaryFilter; } public Filter getFilter() { return filter; } } public static <T> void foreach(final Transaction txn, final TraversalSpec traversalSpec, final Mapping<T> r) { map(txn, traversalSpec, r).traverseAll(); } public static <T> Traversal<T> map(final Transaction txn, final TraversalSpec traversalSpec, final Mapping<T> mapping) { return new TraversalImpl<T>(txn, TraversalMode.READ_ONLY, traversalSpec, mapping); } public static <T> T reduce(final Transaction txn, final TraversalSpec traversalSpec, final Reduction<T> reduction, final T initial) { MapReduction<T> mr = new MapReduction<T>(initial, reduction); Traversal<T> iter = map(txn, traversalSpec, mr); try { while (iter.hasNext()) { iter.next(); } return mr.getAccum(); } finally { iter.close(); } } public static Traversal<Mutation> apply(final Transaction txn, final DatabaseTemplate dbt, final TraversalSpec traversalSpec, final Mapping<Mutation> mutation) { final Mapping<Mutation> mapping = new Mapping<Functional.Mutation>() { public Mutation map(Map<String, Object> row) { Mutation m = mutation.map(row); Target target = traversalSpec.getTarget(); switch (m.getType()) { case NONE: break; case INSERT_OR_UPDATE: dbt.insertOrUpdate(txn, target.getTableDef(), m.getInstance()); break; case DELETE: dbt.delete(txn, target.getTableDef(), m.getInstance()); break; default: throw new IllegalArgumentException(); } return m; } }; return new TraversalImpl<Mutation>(txn, TraversalMode.READ_WRITE, traversalSpec, mapping); } private static class MapReduction<T> implements Mapping<T> { T accum; Reduction<T> r; public MapReduction(T initial, Reduction<T> r) { accum = initial; this.r = r; } public T map(Map<String, Object> row) { accum = r.reduce(row, accum); return accum; } public T getAccum() { return accum; } } private static class TraversalImpl<T> implements Traversal<T> { private final boolean isSecondary; private final boolean isReadOnly; private final TableDef tableDef; private final Filter primaryFilter; private final Filter filter; private final Mapping<T> mapping; private final boolean isAscending; private Cursor c0; private Cursor c1; private Map<String, Object> nextItem; public TraversalImpl(Transaction txn, TraversalMode traversalMode, TraversalSpec traversalSpec, Mapping<T> mapping) { Target target = traversalSpec.getTarget(); this.primaryFilter = traversalSpec.getPrimaryFilter(); this.filter = traversalSpec.getFilter(); this.mapping = mapping; this.isReadOnly = traversalMode.equals(TraversalMode.READ_ONLY); this.isSecondary = target.getIndexDef() != null; this.tableDef = target.getTableDef(); this.isAscending = traversalSpec.getCursorDirection().equals( CursorDirection.ASC); this.c0 = txn.openTable(tableDef); if (this.isSecondary) { this.c1 = c0.openIndex(target.getIndexDef()); this.c1.setClusterAccess(); } else { this.c1 = this.c0; } if (!this.isReadOnly) { try { this.c1.setLockMode(LockMode.INTENTION_EXCLUSIVE); this.c1.lock(LockMode.LOCK_EXCLUSIVE); } catch (Exception e) { this.close(); throw new RuntimeException(e); } } Map<String, Object> firstKey = traversalSpec.getFirstKey(); if (firstKey != null) { Tuple tuple = isSecondary ? c1 .createSecondaryIndexSearchTuple(firstKey) : c1 .createClusteredIndexSearchTuple(firstKey); c1.find(tuple, traversalSpec.getSearchMode()); } else { if (this.isAscending) { c1.first(); } else { c1.last(); } } nextItem = advance(); } private Map<String, Object> advance() { Map<String, Object> toReturn = null; while (c1 != null && c1.isPositioned() && c1.hasNext()) { Tuple read = c1.createClusteredIndexReadTuple(); try { c1.readRow(read); Map<String, Object> row = read.valueMap(); if (!primaryFilter.map(row)) { close(); break; } if (filter == null || filter.map(row)) { toReturn = row; break; } } catch (Exception e) { close(); throw new RuntimeException(e); } finally { read.delete(); if (c1 != null) { if (this.isAscending) { c1.next(); } else { c1.prev(); } } } } if (toReturn == null) { close(); } return toReturn; } public boolean hasNext() { return nextItem != null; } public T next() { if (nextItem == null) { throw new IllegalStateException("next() called on empty iter"); } Map<String, Object> orig = nextItem; nextItem = advance(); try { return mapping.map(orig); } catch (Exception e) { close(); throw new RuntimeException(e); } } public void close() { if (c1 != null) { c1.close(); c1 = null; } if (isSecondary && c0 != null) { c0.close(); c0 = null; } } public void traverseAll() { try { while (hasNext()) { next(); } } finally { close(); } } public void remove() { throw new UnsupportedOperationException(); } } }