Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package problems.exceptions;

public class TransactionRolledbackException extends RuntimeException {
public TransactionRolledbackException(final String message) {
super(message);
}
}
104 changes: 104 additions & 0 deletions src/main/java/problems/impl/TransactionalStack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package problems.impl;

import problems.exceptions.TransactionRolledbackException;

import java.util.Stack;

public class TransactionalStack<T> {
private final Stack<T> stack = new Stack<>();
private final Stack<TransactionEvent<T>> transactionEvents = new Stack<>();

private static class TransactionEvent<E> {
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()) {
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.PUSH, value));
}
}

public T pop() {
validateNonEmptyStack();

T value = stack.pop();

if (!transactionEvents.isEmpty()) {
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.POP, value));
}

return value;
}

public int getSize() {
return stack.size();
}

public T peek() {
validateNonEmptyStack();

return stack.peek();
}

public void begin() {
transactionEvents.add(new TransactionEvent<>(TransactionEvent.Type.BEGIN, null));
}

public void commit() {
while (!transactionEvents.empty()) {
TransactionEvent<T> transactionEvent = transactionEvents.pop();
if (transactionEvent.type == TransactionEvent.Type.BEGIN) {
return;
}
}
}

public void rollback() {
while (!transactionEvents.empty()) {
TransactionEvent<T> transactionEvent = transactionEvents.pop();

switch (transactionEvent.type) {
case POP: {
stack.push(transactionEvent.value);
break;
}

case PUSH: {
validateNonEmptyStack();

stack.pop();
break;
}

case BEGIN: {
return;
}

default: {
throw new RuntimeException(String.format("Unknown event: {}", transactionEvent.type));
}
}
}
}

private void validateNonEmptyStack() {
if (stack.empty()) {
rollback();
throw new TransactionRolledbackException("Cannot execute action on an empty stack. Rolled back changes.");
}
}
}
66 changes: 66 additions & 0 deletions src/test/java/problems/impl/TransactionalStackTest.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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<String> 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<String> stack = new TransactionalStack<>();
stack.begin();
stack.push(first);
stack.commit();
assertEquals(1, stack.getSize());
assertEquals(first, stack.peek());
}
}