package SimpleDB;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
/*
* A DB contains a sequence of uncommitted Transaction Blocks.
*/
public class Database {
private LinkedList<TransactionBlock> blocks;
// Initialize db with an initial transaction block to hold transaction history
public Database(){
blocks = new LinkedList<TransactionBlock>();
blocks.add(new TransactionBlock());
}
// Set the variable name to the value.
blocks.getLast().set(name, value);
}
// Get the value of the variable name, or NULL if that variable is not set.
return blocks.getLast().get(name);
}
// Return the number of variables that are currently set to value. If no variables equal that value, print 0.
return blocks.getLast().numEqualTo(value);
}
// Close all open transaction blocks, permanently applying the changes made in them. Return false if no transaction is in progress.
@SuppressWarnings("unchecked")
public boolean commit() {
if (blocks.size() <= 1) return false;
HashMap
<String, Integer
> name_value
= new HashMap
<String, Integer
>(); HashMap
<Integer, Integer
> value_counter
= new HashMap
<Integer, Integer
>();
ListIterator<TransactionBlock> iterator = blocks.listIterator();
while (iterator.hasNext()) {
TransactionBlock block = iterator.next();
name_value.
putAll((Map
<? extends String,
? extends Integer
>) block.
getNameValue()); }
for (Entry
<String, Integer
> entry
: name_value.
entrySet()) { if(value_counter.get(value) == null){
value_counter.
put(value,
new Integer(1)); }
else{
value_counter.
put(value,
new Integer(value_counter.
get(value
) + 1)); }
name_value.put(entry.getKey(),entry.getValue());
}
blocks = new LinkedList<TransactionBlock>();
blocks.add(new TransactionBlock(name_value, value_counter));
return true;
}
// Undo all of the commands issued in the most recent transaction block, and discard the block. Return false if no previous block to roll back to.
public boolean rollBack(){
if (blocks.size() <= 1) return false;
blocks.removeLast();
return true;
}
// Open a new transaction block.Transaction blocks can be nested; a BEGIN can be issued inside of an existing block.
public void begin(){
TransactionBlock block = new TransactionBlock();
block.setPrev(blocks.getLast());
blocks.add(block);
}
public static void main
(String[] args
) { Database db = new Database();
Scanner scanner
= new Scanner
(System.
in); scanner.useDelimiter("\\s+"); // space delimited
String cmdLine
; // cmdLine is typically 'cmd' followed by 'key' followed by 'value' while (scanner.hasNextLine()) {
cmdLine = scanner.nextLine();
String[] tokens
= cmdLine.
split("\\s+"); try {
switch (cmd) {
case "GET":
name = tokens[1];
System.
out.
println(db.
get(name
) != null ? db.
get(name
):"NULL"); break;
case "SET":
name = tokens[1];
value
= Integer.
parseInt(tokens
[2]); db.set(name, value);
break;
case "UNSET":
name = tokens[1];
db.set(name, null);
break;
case "NUMEQUALTO":
value
= Integer.
parseInt(tokens
[1]); System.
out.
println(db.
numEqualTo(value
)); break;
case "BEGIN":
db.begin();
break;
case "ROLLBACK":
if (!db.
rollBack()) System.
out.
println("NO TRANSACTION"); break;
case "COMMIT":
if (!db.
commit()) System.
out.
println("NO TRANSACTION"); break;
case "END":
return;
case "":
break;
default:
System.
out.
println("Invalid command: " + cmd
); }
System.
out.
println("Invalid number format: " + cmdLine
); System.
out.
println("Possibly missing operand: " + cmdLine
); }
}
scanner.close();
}
}
/**
* A transaction block conceptually includes all past uncommitted transactions accessible through traversing to the previous transaction block.
*/
class TransactionBlock {
private TransactionBlock prev; // point to the immediate past TransactionBlock
// Delta only. Not the full transaction history.
private HashMap
<String, Integer
> name_value
= new HashMap
<String, Integer
>(); private HashMap
<Integer, Integer
> value_counter
= new HashMap
<Integer, Integer
>();
public TransactionBlock(){}
public void setPrev(TransactionBlock block) {
prev = block;
}
public TransactionBlock
(HashMap
<String, Integer
>nameValue, HashMap
<Integer, Integer
>valueCounter
){ name_value = nameValue;
value_counter = valueCounter;
}
public HashMap
<String, Integer
> getNameValue
(){ return name_value;
}
// Set the variable name to the value and maintain delta counter.
// maintain delta value_counter, decrease counter of old 'name' value
if (prevValue != null){
Integer prevValueCounter
= numEqualTo
(prevValue
); value_counter.put(prevValue, --prevValueCounter);
}
// maintain delta value_counter, increase counter of new 'name' value
Integer currentValueCounter
= numEqualTo
(currentValue
); if (currentValue != null) {
if (currentValueCounter != null) {
value_counter.put(currentValue, ++currentValueCounter);
} else {
value_counter.
put(currentValue,
new Integer(1)); }
}
name_value.put(name, currentValue);
}
// Get the value for the given name
TransactionBlock block = this;
Integer value
= block.
name_value.
get(name
); while(!block.name_value.containsKey(name) && block.prev != null){
block = block.prev;
value = block.name_value.get(name);
}
return value;
}
// Print out the number of variables that are currently set to value. If no variables equal that value, print 0.
if (value == null) return 0;
TransactionBlock block = this;
Integer counter
= block.
value_counter.
get(value
); while(counter == null && block.prev != null){
block = block.prev;
counter = block.value_counter.get(value);
}
if (counter == null)
return 0;
else{
return counter;
}
}
}