Skip to content

Commit afe9479

Browse files
committed
initial stack with rollback implementation
1 parent 299e8d5 commit afe9479

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package problems.exceptions;
2+
3+
public class TransactionRolledbackException extends RuntimeException {
4+
public TransactionRolledbackException(final String message) {
5+
super(message);
6+
}
7+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package problems.impl;
2+
3+
import problems.exceptions.TransactionRolledbackException;
4+
5+
import java.util.EmptyStackException;
6+
import java.util.Stack;
7+
8+
public class TransactionalStack<T> {
9+
private final Stack<T> stack = new Stack<>();
10+
private final Stack<TransactionEvent<T>> transactionEvents = new Stack<>();
11+
private Stack<TransactionEvent<T>> rolledbackTransactionEvents = new Stack<>();
12+
13+
14+
private static class TransactionEvent<E> {
15+
private enum Type {
16+
BEGIN,
17+
PUSH,
18+
POP
19+
}
20+
21+
private final Type type;
22+
private final E value;
23+
24+
public TransactionEvent(Type type, E value) {
25+
this.type = type;
26+
this.value = value;
27+
}
28+
}
29+
30+
public void push(T value) {
31+
stack.push(value);
32+
33+
if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) {
34+
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.PUSH, value));
35+
}
36+
}
37+
38+
public T pop() {
39+
T value;
40+
41+
try {
42+
value = stack.pop();
43+
} catch (EmptyStackException e) {
44+
if (transactionEvents.empty()) {
45+
throw e;
46+
}
47+
48+
rollback();
49+
50+
throw new TransactionRolledbackException("Cannot pop an empty stack. Rolled back changes.");
51+
}
52+
53+
if (!transactionEvents.isEmpty() && rolledbackTransactionEvents.empty()) {
54+
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.POP, value));
55+
}
56+
57+
return value;
58+
}
59+
60+
public int getSize() {
61+
return stack.size();
62+
}
63+
64+
public T peek() {
65+
try {
66+
return stack.peek();
67+
} catch (EmptyStackException e) {
68+
if (transactionEvents.empty()) {
69+
throw e;
70+
}
71+
72+
rollback();
73+
74+
throw new TransactionRolledbackException("Cannot peek an empty stack. Rolled back changes.");
75+
}
76+
}
77+
78+
public void begin() {
79+
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.BEGIN, null));
80+
}
81+
82+
public void commit() {
83+
while (!transactionEvents.empty()) {
84+
TransactionEvent<T> transactionEvent = transactionEvents.pop();
85+
if (transactionEvent.type == TransactionEvent.Type.BEGIN) {
86+
return;
87+
}
88+
}
89+
}
90+
91+
public void rollback() {
92+
while (!transactionEvents.empty()) {
93+
TransactionEvent<T> transactionEvent = transactionEvents.pop();
94+
rolledbackTransactionEvents.push(transactionEvent);
95+
96+
switch (transactionEvent.type) {
97+
case POP: {
98+
push(transactionEvent.value);
99+
break;
100+
}
101+
102+
case PUSH: {
103+
pop();
104+
break;
105+
}
106+
107+
default: {
108+
return;
109+
}
110+
}
111+
}
112+
113+
rolledbackTransactionEvents = new Stack<>();
114+
}
115+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package problems.impl;
2+
3+
import org.junit.Test;
4+
import problems.exceptions.TransactionRolledbackException;
5+
6+
import static org.junit.Assert.*;
7+
8+
public class TransactionalStackTest {
9+
private final String first = "first";
10+
private final String second = "second";
11+
12+
@Test
13+
public void testInnerTransactionRollback() {
14+
TransactionalStack<String> stack = new TransactionalStack<>();
15+
stack.begin();
16+
stack.push(first);
17+
stack.begin();
18+
stack.push(second);
19+
stack.rollback();
20+
stack.commit();
21+
assertEquals(first, stack.peek());
22+
}
23+
24+
@Test
25+
public void testRollback() {
26+
TransactionalStack<String> stack = new TransactionalStack<>();
27+
stack.push(first);
28+
stack.begin();
29+
stack.pop();
30+
stack.push(second);
31+
32+
assertEquals(1, stack.getSize());
33+
assertEquals(second, stack.peek());
34+
35+
stack.rollback();
36+
37+
assertEquals(1, stack.getSize());
38+
assertEquals(first, stack.peek());
39+
}
40+
41+
@Test
42+
public void testRollbackWhenPoppingOnEmptyStack() {
43+
TransactionalStack<String> stack = new TransactionalStack<>();
44+
stack.push(first);
45+
stack.push(second);
46+
try {
47+
stack.begin();
48+
stack.pop();
49+
stack.pop();
50+
stack.pop();
51+
} catch (TransactionRolledbackException e) {
52+
assertEquals(2, stack.getSize());
53+
assertEquals(second, stack.peek());
54+
}
55+
}
56+
57+
@Test
58+
public void testCommit() {
59+
TransactionalStack<String> stack = new TransactionalStack<>();
60+
stack.begin();
61+
stack.push(first);
62+
stack.commit();
63+
assertEquals(1, stack.getSize());
64+
assertEquals(first, stack.peek());
65+
}
66+
}

0 commit comments

Comments
 (0)