From afe94793c0e6d60869e9cf974f5be6fe807c4169 Mon Sep 17 00:00:00 2001 From: jaebradley Date: Fri, 11 Aug 2017 17:38:19 -0400 Subject: [PATCH 1/2] initial stack with rollback implementation --- .../TransactionRolledbackException.java | 7 ++ .../problems/impl/TransactionalStack.java | 115 ++++++++++++++++++ .../problems/impl/TransactionalStackTest.java | 66 ++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/main/java/problems/exceptions/TransactionRolledbackException.java create mode 100644 src/main/java/problems/impl/TransactionalStack.java create mode 100644 src/test/java/problems/impl/TransactionalStackTest.java diff --git a/src/main/java/problems/exceptions/TransactionRolledbackException.java b/src/main/java/problems/exceptions/TransactionRolledbackException.java new file mode 100644 index 0000000..3534916 --- /dev/null +++ b/src/main/java/problems/exceptions/TransactionRolledbackException.java @@ -0,0 +1,7 @@ +package problems.exceptions; + +public class TransactionRolledbackException extends RuntimeException { + public TransactionRolledbackException(final String message) { + super(message); + } +} diff --git a/src/main/java/problems/impl/TransactionalStack.java b/src/main/java/problems/impl/TransactionalStack.java new file mode 100644 index 0000000..7532f9f --- /dev/null +++ b/src/main/java/problems/impl/TransactionalStack.java @@ -0,0 +1,115 @@ +package problems.impl; + +import problems.exceptions.TransactionRolledbackException; + +import java.util.EmptyStackException; +import java.util.Stack; + +public class TransactionalStack { + private final Stack stack = new Stack<>(); + private final Stack> transactionEvents = new Stack<>(); + private Stack> rolledbackTransactionEvents = new Stack<>(); + + + private static class TransactionEvent { + private enum Type { + BEGIN, + PUSH, + POP + } + + private final Type type; + private final E value; + + public TransactionEvent(Type type, E value) { + this.type = type; + this.value = value; + } + } + + public void push(T value) { + stack.push(value); + + if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) { + transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.PUSH, value)); + } + } + + public T pop() { + T value; + + try { + value = stack.pop(); + } catch (EmptyStackException e) { + if (transactionEvents.empty()) { + throw e; + } + + rollback(); + + throw new TransactionRolledbackException("Cannot pop an empty stack. Rolled back changes."); + } + + if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) { + transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.POP, value)); + } + + return value; + } + + public int getSize() { + return stack.size(); + } + + public T peek() { + try { + return stack.peek(); + } catch (EmptyStackException e) { + if (transactionEvents.empty()) { + throw e; + } + + rollback(); + + throw new TransactionRolledbackException("Cannot peek an empty stack. Rolled back changes."); + } + } + + public void begin() { + transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.BEGIN, null)); + } + + public void commit() { + while (!transactionEvents.empty()) { + TransactionEvent transactionEvent = transactionEvents.pop(); + if (transactionEvent.type == TransactionEvent.Type.BEGIN) { + return; + } + } + } + + public void rollback() { + while (!transactionEvents.empty()) { + TransactionEvent transactionEvent = transactionEvents.pop(); + rolledbackTransactionEvents.push(transactionEvent); + + switch (transactionEvent.type) { + case POP: { + push(transactionEvent.value); + break; + } + + case PUSH: { + pop(); + break; + } + + default: { + return; + } + } + } + + rolledbackTransactionEvents = new Stack<>(); + } +} diff --git a/src/test/java/problems/impl/TransactionalStackTest.java b/src/test/java/problems/impl/TransactionalStackTest.java new file mode 100644 index 0000000..0c210e9 --- /dev/null +++ b/src/test/java/problems/impl/TransactionalStackTest.java @@ -0,0 +1,66 @@ +package problems.impl; + +import org.junit.Test; +import problems.exceptions.TransactionRolledbackException; + +import static org.junit.Assert.*; + +public class TransactionalStackTest { + private final String first = "first"; + private final String second = "second"; + + @Test + public void testInnerTransactionRollback() { + TransactionalStack stack = new TransactionalStack<>(); + stack.begin(); + stack.push(first); + stack.begin(); + stack.push(second); + stack.rollback(); + stack.commit(); + assertEquals(first, stack.peek()); + } + + @Test + public void testRollback() { + TransactionalStack stack = new TransactionalStack<>(); + stack.push(first); + stack.begin(); + stack.pop(); + stack.push(second); + + assertEquals(1, stack.getSize()); + assertEquals(second, stack.peek()); + + stack.rollback(); + + assertEquals(1, stack.getSize()); + assertEquals(first, stack.peek()); + } + + @Test + public void testRollbackWhenPoppingOnEmptyStack() { + TransactionalStack stack = new TransactionalStack<>(); + stack.push(first); + stack.push(second); + try { + stack.begin(); + stack.pop(); + stack.pop(); + stack.pop(); + } catch (TransactionRolledbackException e) { + assertEquals(2, stack.getSize()); + assertEquals(second, stack.peek()); + } + } + + @Test + public void testCommit() { + TransactionalStack stack = new TransactionalStack<>(); + stack.begin(); + stack.push(first); + stack.commit(); + assertEquals(1, stack.getSize()); + assertEquals(first, stack.peek()); + } +} \ No newline at end of file From b1adafa5a711d8e56937b664e6eb9e433c0fbd1b Mon Sep 17 00:00:00 2001 From: jaebradley Date: Sun, 13 Aug 2017 20:32:25 -0400 Subject: [PATCH 2/2] updates --- .../problems/impl/TransactionalStack.java | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/src/main/java/problems/impl/TransactionalStack.java b/src/main/java/problems/impl/TransactionalStack.java index 7532f9f..9751839 100644 --- a/src/main/java/problems/impl/TransactionalStack.java +++ b/src/main/java/problems/impl/TransactionalStack.java @@ -2,14 +2,11 @@ import problems.exceptions.TransactionRolledbackException; -import java.util.EmptyStackException; import java.util.Stack; public class TransactionalStack { private final Stack stack = new Stack<>(); private final Stack> transactionEvents = new Stack<>(); - private Stack> rolledbackTransactionEvents = new Stack<>(); - private static class TransactionEvent { private enum Type { @@ -30,29 +27,19 @@ public TransactionEvent(Type type, E value) { public void push(T value) { stack.push(value); - if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) { + if (!transactionEvents.isEmpty()) { transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.PUSH, value)); } } public T pop() { - T value; - - try { - value = stack.pop(); - } catch (EmptyStackException e) { - if (transactionEvents.empty()) { - throw e; - } - - rollback(); + validateNonEmptyStack(); - throw new TransactionRolledbackException("Cannot pop an empty stack. Rolled back changes."); - } + T value = stack.pop(); - if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) { + if (!transactionEvents.isEmpty()) { transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.POP, value)); - } + } return value; } @@ -62,17 +49,9 @@ public int getSize() { } public T peek() { - try { - return stack.peek(); - } catch (EmptyStackException e) { - if (transactionEvents.empty()) { - throw e; - } - - rollback(); + validateNonEmptyStack(); - throw new TransactionRolledbackException("Cannot peek an empty stack. Rolled back changes."); - } + return stack.peek(); } public void begin() { @@ -91,25 +70,35 @@ public void commit() { public void rollback() { while (!transactionEvents.empty()) { TransactionEvent transactionEvent = transactionEvents.pop(); - rolledbackTransactionEvents.push(transactionEvent); switch (transactionEvent.type) { case POP: { - push(transactionEvent.value); + stack.push(transactionEvent.value); break; } case PUSH: { - pop(); + validateNonEmptyStack(); + + stack.pop(); break; } - default: { + case BEGIN: { return; } + + default: { + throw new RuntimeException(String.format("Unknown event: {}", transactionEvent.type)); + } } } + } - rolledbackTransactionEvents = new Stack<>(); + private void validateNonEmptyStack() { + if (stack.empty()) { + rollback(); + throw new TransactionRolledbackException("Cannot execute action on an empty stack. Rolled back changes."); + } } }