• Source
    1. package SimpleDB;
    2.  
    3. import java.util.HashMap;
    4. import java.util.LinkedList;
    5. import java.util.ListIterator;
    6. import java.util.Map;
    7. import java.util.Map.Entry;
    8. import java.util.Scanner;
    9.  
    10. /*
    11.  * A DB contains a sequence of uncommitted Transaction Blocks.
    12.  */
    13. public class Database {
    14. private LinkedList<TransactionBlock> blocks;
    15.  
    16. // Initialize db with an initial transaction block to hold transaction history
    17. public Database(){
    18. blocks = new LinkedList<TransactionBlock>();
    19. blocks.add(new TransactionBlock());
    20. }
    21.  
    22. // Set the variable name to the value.
    23. public void set(String name, Integer value){
    24. blocks.getLast().set(name, value);
    25. }
    26.  
    27. // Get the value of the variable name, or NULL if that variable is not set.
    28. public Integer get(String name){
    29. return blocks.getLast().get(name);
    30. }
    31.  
    32. // Return the number of variables that are currently set to value. If no variables equal that value, print 0.
    33. public Integer numEqualTo(Integer value){
    34. return blocks.getLast().numEqualTo(value);
    35. }
    36.  
    37. // Close all open transaction blocks, permanently applying the changes made in them. Return false if no transaction is in progress.
    38. @SuppressWarnings("unchecked")
    39. public boolean commit() {
    40. if (blocks.size() <= 1) return false;
    41.  
    42. HashMap<String, Integer> name_value = new HashMap<String, Integer>();
    43. HashMap<Integer, Integer> value_counter = new HashMap<Integer, Integer>();
    44.  
    45. ListIterator<TransactionBlock> iterator = blocks.listIterator();
    46. while (iterator.hasNext()) {
    47. TransactionBlock block = iterator.next();
    48. name_value.putAll((Map<? extends String, ? extends Integer>) block.getNameValue());
    49. }
    50.  
    51. for (Entry<String, Integer> entry : name_value.entrySet()) {
    52. Integer value = entry.getValue();
    53. if(value_counter.get(value) == null){
    54. value_counter.put(value, new Integer(1));
    55. }
    56. else{
    57. value_counter.put(value, new Integer(value_counter.get(value) + 1));
    58. }
    59. name_value.put(entry.getKey(),entry.getValue());
    60. }
    61.  
    62. blocks = new LinkedList<TransactionBlock>();
    63. blocks.add(new TransactionBlock(name_value, value_counter));
    64.  
    65. return true;
    66. }
    67.  
    68. // 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.
    69. public boolean rollBack(){
    70. if (blocks.size() <= 1) return false;
    71. blocks.removeLast();
    72. return true;
    73. }
    74.  
    75. // Open a new transaction block.Transaction blocks can be nested; a BEGIN can be issued inside of an existing block.
    76. public void begin(){
    77. TransactionBlock block = new TransactionBlock();
    78. block.setPrev(blocks.getLast());
    79. blocks.add(block);
    80. }
    81.  
    82. public static void main(String[] args) {
    83. Database db = new Database();
    84. Scanner scanner = new Scanner(System.in);
    85. scanner.useDelimiter("\\s+"); // space delimited
    86. String cmdLine; // cmdLine is typically 'cmd' followed by 'key' followed by 'value'
    87. while (scanner.hasNextLine()) {
    88. cmdLine = scanner.nextLine();
    89. String[] tokens = cmdLine.split("\\s+");
    90. String cmd = tokens[0];
    91. String name;
    92. Integer value;
    93. try {
    94. switch (cmd) {
    95. case "GET":
    96. name = tokens[1];
    97. System.out.println(db.get(name) != null ? db.get(name):"NULL");
    98. break;
    99. case "SET":
    100. name = tokens[1];
    101. value = Integer.parseInt(tokens[2]);
    102. db.set(name, value);
    103. break;
    104. case "UNSET":
    105. name = tokens[1];
    106. db.set(name, null);
    107. break;
    108. case "NUMEQUALTO":
    109. value = Integer.parseInt(tokens[1]);
    110. System.out.println(db.numEqualTo(value));
    111. break;
    112. case "BEGIN":
    113. db.begin();
    114. break;
    115. case "ROLLBACK":
    116. if (!db.rollBack()) System.out.println("NO TRANSACTION");
    117. break;
    118. case "COMMIT":
    119. if (!db.commit()) System.out.println("NO TRANSACTION");
    120. break;
    121. case "END":
    122. return;
    123. case "":
    124. break;
    125. default:
    126. System.out.println("Invalid command: " + cmd );
    127. }
    128. } catch (NumberFormatException e) { // SET n a
    129. System.out.println("Invalid number format: " + cmdLine );
    130. } catch (ArrayIndexOutOfBoundsException e) {// GET
    131. System.out.println("Possibly missing operand: " + cmdLine );
    132. }
    133. }
    134. scanner.close();
    135. }
    136. }
    137.  
    138. /**
    139.  * A transaction block conceptually includes all past uncommitted transactions accessible through traversing to the previous transaction block.
    140.  */
    141. class TransactionBlock {
    142. private TransactionBlock prev; // point to the immediate past TransactionBlock
    143.  
    144. // Delta only. Not the full transaction history.
    145. private HashMap<String, Integer> name_value = new HashMap<String, Integer>();
    146. private HashMap<Integer, Integer> value_counter = new HashMap<Integer, Integer>();
    147.  
    148. public TransactionBlock(){}
    149.  
    150. public void setPrev(TransactionBlock block) {
    151. prev = block;
    152. }
    153.  
    154. public TransactionBlock(HashMap<String, Integer>nameValue, HashMap<Integer, Integer>valueCounter){
    155. name_value = nameValue;
    156. value_counter = valueCounter;
    157. }
    158.  
    159. public HashMap<String, Integer> getNameValue(){
    160. return name_value;
    161. }
    162.  
    163. // Set the variable name to the value and maintain delta counter.
    164. public void set(String name, Integer currentValue){
    165.  
    166. // maintain delta value_counter, decrease counter of old 'name' value
    167. Integer prevValue = get(name);
    168. if (prevValue != null){
    169. Integer prevValueCounter = numEqualTo(prevValue);
    170. value_counter.put(prevValue, --prevValueCounter);
    171. }
    172.  
    173. // maintain delta value_counter, increase counter of new 'name' value
    174. Integer currentValueCounter = numEqualTo(currentValue);
    175. if (currentValue != null) {
    176. if (currentValueCounter != null) {
    177. value_counter.put(currentValue, ++currentValueCounter);
    178. } else {
    179. value_counter.put(currentValue, new Integer(1));
    180. }
    181. }
    182.  
    183. name_value.put(name, currentValue);
    184. }
    185.  
    186. // Get the value for the given name
    187. public Integer get(String name) {
    188. TransactionBlock block = this;
    189. Integer value = block.name_value.get(name);
    190. while(!block.name_value.containsKey(name) && block.prev != null){
    191. block = block.prev;
    192. value = block.name_value.get(name);
    193. }
    194. return value;
    195. }
    196.  
    197. // Print out the number of variables that are currently set to value. If no variables equal that value, print 0.
    198. public Integer numEqualTo(Integer value){
    199. if (value == null) return 0;
    200.  
    201. TransactionBlock block = this;
    202. Integer counter = block.value_counter.get(value);
    203. while(counter == null && block.prev != null){
    204. block = block.prev;
    205. counter = block.value_counter.get(value);
    206. }
    207.  
    208. if (counter == null)
    209. return 0;
    210. else{
    211. return counter;
    212. }
    213. }
    214. }
    215.