-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 79857b1
Showing
7 changed files
with
440 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.nofatclips.thumbtack; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import com.nofatclips.thumbtack.db.DataBase; | ||
import com.nofatclips.thumbtack.db.InvalidRollbackException; | ||
|
||
public class ThumbTackDB { | ||
|
||
private DataBase DB; | ||
|
||
public ThumbTackDB() { | ||
this.DB = new DataBase(); | ||
} | ||
|
||
public static void main(String[] args) { | ||
ThumbTackDB instance = new ThumbTackDB(); | ||
try { | ||
instance.repl(); | ||
} catch (IOException e) { | ||
// TODO Auto-generated catch block | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
public void repl() throws IOException { | ||
String s; | ||
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); | ||
boolean keepRunning = true; | ||
while (keepRunning) { | ||
s = stdin.readLine(); | ||
if (s == null || s.length() == 0) continue; | ||
String[] items = s.split(" "); | ||
String command = items[0]; | ||
List<String> arguments = Arrays.asList(items).subList(1, items.length); | ||
try { | ||
keepRunning = execute(command, arguments); | ||
} catch (IndexOutOfBoundsException e){ | ||
System.out.println("INVALID ARGUMENTS FOR " + command); | ||
} | ||
} | ||
|
||
} | ||
|
||
// TODO Refactor this using Command pattern | ||
private boolean execute(String command, List<String> arguments) throws IndexOutOfBoundsException { | ||
command = command.toUpperCase(); | ||
if (command.equals("SET")) { | ||
this.DB.set(arguments.get(0), arguments.get(1)); | ||
} else if (command.equals("GET")) { | ||
String s = this.DB.get(arguments.get(0)); | ||
System.out.println((s == null) ? "NULL" : s); | ||
} else if (command.equals("UNSET")) { | ||
this.DB.unset(arguments.get(0)); | ||
} else if (command.equals("NUMEQUALTO") || command.equals("NET")) { | ||
System.out.println(this.DB.numEqualTo(arguments.get(0)));; | ||
} else if (command.equals("BEGIN")) { | ||
this.DB.begin(); | ||
} else if (command.equals("ROLLBACK")) { | ||
try { | ||
this.DB.rollback(); | ||
} catch (InvalidRollbackException e) { | ||
System.out.println("INVALID ROLLBACK"); | ||
} | ||
} else if (command.equals("COMMIT")) { | ||
this.DB.commit(); | ||
} else if (command.equals("END")) { | ||
return false; | ||
} else { | ||
System.out.println("INVALID COMMAND"); | ||
} | ||
return true; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package com.nofatclips.thumbtack.db; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
/** | ||
* | ||
* The base layer for the DB. Implementation should be straightforward. | ||
* | ||
* It has an inverted (value to key) index for fast (<O(n)) implementation of numEqualTo() | ||
* | ||
* @author DeK | ||
* | ||
*/ | ||
|
||
public class BaseDataLayer implements DataLayer { | ||
|
||
private Map<String, String> layerStore; | ||
private BucketCounter layerIndex; | ||
|
||
public BaseDataLayer() { | ||
this.layerStore = new HashMap<String, String>(); | ||
this.layerIndex = new BucketCounter(); | ||
} | ||
|
||
@Override | ||
public void set(String name, String value) { | ||
index().removeOne(get(name)); // Remove old value from index if exists | ||
store().put(name, value); | ||
index().putOne(value); | ||
} | ||
|
||
@Override | ||
public String get(String name) { | ||
return store().get(name); | ||
} | ||
|
||
@Override | ||
public void unset(String name) { | ||
index().removeOne(get(name)); | ||
store().remove(name); | ||
} | ||
|
||
@Override | ||
public int numEqualTo(String value) { | ||
return index().numEqualTo(value); | ||
} | ||
|
||
public boolean containsValue(String value) { | ||
return index().containsKey(value); | ||
} | ||
|
||
public void mergeFrom(IncrementalDataLayer that) { | ||
this.store().putAll(that.store()); | ||
this.unsetAll(that.deletedKeys); | ||
this.index().mergeMap(that.index()); | ||
} | ||
|
||
protected void unsetAll(Set<String> deletedKeys) { | ||
for (String deletedKey: deletedKeys) { | ||
unset(deletedKey); | ||
} | ||
} | ||
|
||
public BucketCounter index() { | ||
return this.layerIndex; | ||
} | ||
|
||
public Map<String, String> store() { | ||
return this.layerStore; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.nofatclips.thumbtack.db; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
public class BucketCounter { | ||
|
||
private Map<String, Integer> index; | ||
|
||
public BucketCounter() { | ||
this.index = new HashMap<String, Integer>(); | ||
} | ||
|
||
public Integer get(String value) { | ||
return this.index.get(value); | ||
} | ||
|
||
public boolean containsKey(String value) { | ||
return this.index.containsKey(value); | ||
} | ||
|
||
public void putOne(String value) { | ||
if (!containsKey(value)) { | ||
initKey(value); | ||
} | ||
this.index.put(value, get(value)+1); | ||
} | ||
|
||
public void mergeMap(BucketCounter that) { | ||
for (Map.Entry<String, Integer> entry : that.entrySet()) { | ||
initKey(entry.getKey(), entry.getValue()); | ||
} | ||
} | ||
|
||
private Set<Map.Entry<String, Integer>> entrySet() { | ||
return this.index.entrySet(); | ||
} | ||
|
||
public void initKey(String value, Integer count) { | ||
this.index.put(value, count); | ||
} | ||
|
||
public void initKey(String value) { | ||
initKey(value, 0); | ||
} | ||
|
||
public void removeOne(String value) { | ||
if (containsKey(value)) { | ||
this.index.put(value, get(value)-1); | ||
} | ||
} | ||
|
||
public int numEqualTo(String value) { | ||
Integer bucketSize = this.index.get(value); | ||
if (bucketSize == null) return 0; | ||
return bucketSize; | ||
} | ||
|
||
public String toString() { | ||
String ret = "{"; | ||
String sep = ""; | ||
for (Map.Entry<String, Integer> entry : entrySet()) { | ||
ret += sep + entry.getKey() + ": '" + entry.getValue() + "'"; | ||
sep = ", "; | ||
} | ||
return ret + "}"; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package com.nofatclips.thumbtack.db; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.Deque; | ||
|
||
/** | ||
* | ||
* Classic composite pattern. It has a stack rather than a list of items and | ||
* the head is stored separately. It mostly delegates every operation | ||
* to the current item (the one on top of the stack, or the head if | ||
* the stack is empty). | ||
* | ||
* Each item provides incremental information to be added to the head | ||
* on commit. | ||
* | ||
* @author DeK | ||
* | ||
*/ | ||
|
||
public class DataBase implements DataLayer { | ||
|
||
private BaseDataLayer baseLayer; | ||
private Deque<IncrementalDataLayer> layerStack; | ||
|
||
public DataBase() { | ||
this.baseLayer = new BaseDataLayer(); | ||
this.layerStack = new ArrayDeque<IncrementalDataLayer>(); | ||
} | ||
|
||
@Override | ||
public void set(String name, String value) { | ||
currentLayer().set(name, value); | ||
} | ||
|
||
@Override | ||
public String get(String name) { | ||
// Iterator<DataLayer> inStackingOrder = this.layerStack.descendingIterator(); | ||
// while(inStackingOrder.hasNext()){ | ||
// String ret = inStackingOrder.next().get(name); | ||
// if (ret != null) return ret; | ||
// } | ||
return currentLayer().get(name); | ||
} | ||
|
||
@Override | ||
public void unset(String name) { | ||
currentLayer().unset(name); | ||
} | ||
|
||
@Override | ||
public int numEqualTo(String value) { | ||
return currentLayer().numEqualTo(value); | ||
} | ||
|
||
public void begin() { | ||
// Creates a new layer, uses the current one as its underlying layer, pushes onto stack | ||
push(new IncrementalDataLayer(currentLayer())); | ||
} | ||
|
||
public void rollback() throws InvalidRollbackException { | ||
if (transactionRunning()) { | ||
pop(); | ||
} else { | ||
throw new InvalidRollbackException(); | ||
} | ||
} | ||
|
||
protected boolean transactionRunning() { | ||
return !this.layerStack.isEmpty(); | ||
} | ||
|
||
public void commit() { | ||
while (transactionRunning()) { | ||
pop().mergeBack(); | ||
} | ||
} | ||
|
||
public BaseDataLayer currentLayer() { | ||
if (transactionRunning()) return this.layerStack.peekFirst(); | ||
return this.baseLayer; | ||
} | ||
|
||
public IncrementalDataLayer pop() { | ||
return this.layerStack.pop(); | ||
} | ||
|
||
public void push(IncrementalDataLayer newLayer) { | ||
this.layerStack.push(newLayer); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.nofatclips.thumbtack.db; | ||
|
||
public interface DataLayer { | ||
|
||
public void set(String name, String value); | ||
public String get(String name); | ||
public void unset(String name); | ||
public int numEqualTo(String value); | ||
|
||
} |
Oops, something went wrong.