Many fixes, some are still only in temporary phase, according to what Arbel told me to do so far.

vbdev2
Hai Brenner 2016-05-23 14:43:01 +03:00
parent c04ed42dca
commit 94f3920e6d
32 changed files with 571 additions and 444 deletions

View File

@ -0,0 +1,84 @@
package meerkat.voting.controller;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Voting.*;
/**
* Created by hai on 18/05/16.
*/
final public class SystemMessages {
private SystemMessages() {
// This is a static class. No instantiation of this is needed.
}
public static UIElement getWaitForCommitMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
.build();
}
public static UIElement getWaitForAuditMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
.build();
}
public static UIElement getWaitForCastMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
.build();
}
public static UIElement getFatalForceRestartMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Fatal error: Internal controller queue received unrecognized command. Force restarting the voting process."))
.build();
}
public static UIElement getRestartVotingButton() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Restart voting"))
.build();
}
public static UIElement getUnrecognizedFinalizeResponseMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Could not understand response for Cast or Audit. Force restarting."))
.build();
}
public static UIElement getUnsuccessfulChannelChoiceMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Choice of channel was unsuccessful. Force restarting."))
.build();
}
public static UIElement getOutputDeviceFailureMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Ballot output device failure. Force restarting."))
.build();
}
public static UIElement getUnsuccessfulVotingMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Voting was unsuccessful. Force restarting."))
.build();
}
public static UIElement getSomethingWrongMessage() {
return UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Something was terribly wrong. Force restarting."))
.build();
}
}

View File

@ -8,12 +8,13 @@ import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager; import meerkat.voting.storage.StorageManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* An interface for the controller component of the voting booth * An interface for the controller component of the voting booth
*/ */
public interface VotingBoothController { public interface VotingBoothController extends Runnable{
/** /**
* initialize by setting all the different components of the Voting Booth to be recognized by this controller * initialize by setting all the different components of the Voting Booth to be recognized by this controller
@ -31,8 +32,7 @@ public interface VotingBoothController {
* set the voting questions * set the voting questions
* @param questions * @param questions
*/ */
public void setBallotChannelChoiceQuestions(BallotQuestion[] questions); public void setBallotChannelChoiceQuestions(List<BallotQuestion> questions);
public void setBallotChannelChoiceQuestions(ArrayList<BallotQuestion> questions);
/** /**
* Set the channel question-selector (the component which matches the ballot questions to each user) * Set the channel question-selector (the component which matches the ballot questions to each user)
@ -44,18 +44,12 @@ public interface VotingBoothController {
* set the voting race questions * set the voting race questions
* @param questions * @param questions
*/ */
public void setBallotRaceQuestions(BallotQuestion[] questions); public void setBallotRaceQuestions(List<BallotQuestion> questions);
public void setBallotRaceQuestions(ArrayList<BallotQuestion> questions);
/**
* a synchronous function. Starts running the controller component, and basically run the whole system for voting
*/
public void run ();
/** /**
* an asynchronous call from Admin Console (If there is such one implemented) to shut down the system * an asynchronous call from Admin Console (If there is such one implemented) to shut down the system
*/ */
public void shutDown(); public void callShutDown();
} }

View File

@ -12,20 +12,20 @@ import meerkat.voting.ui.VotingBoothUI;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.List;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
/** /**
* Created by hai on 28/03/16. * Created by hai on 28/03/16.
*/ */
public class VotingBoothImpl implements VotingBoothController, Runnable { public class VotingBoothImpl implements VotingBoothController {
private BallotOutputDevice outputDevice; private BallotOutputDevice outputDevice;
private VotingBoothEncryptor encryptor; private VotingBoothEncryptor encryptor;
private VotingBoothUI ui; private VotingBoothUI ui;
private StorageManager storageManager; private StorageManager storageManager;
private BallotQuestion[] questionsForChoosingChannel; private List<BallotQuestion> questionsForChoosingChannel;
private BallotQuestion[] questions; private List<BallotQuestion> questions;
private QuestionSelector questionSelector; private QuestionSelector questionSelector;
private LinkedBlockingQueue<ControllerCommand> queue; private LinkedBlockingQueue<ControllerCommand> queue;
@ -33,38 +33,18 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
private Logger logger; private Logger logger;
private ControllerState state; private ControllerState state;
private boolean shutDownHasBeenCalled; private volatile boolean shutDownHasBeenCalled;
protected final int MAX_REQUEST_IDENTIFIER = 100000; protected final int MAX_REQUEST_IDENTIFIER = 100000;
private static int requestCounter = 0; private static int requestCounter = 0;
private UIElement waitForCommitMessage;
private UIElement waitForAuditMessage;
private UIElement waitForCastMessage;
public VotingBoothImpl () { public VotingBoothImpl () {
logger = LoggerFactory.getLogger(VotingBoothImpl.class); logger = LoggerFactory.getLogger(VotingBoothImpl.class);
logger.info("A VotingBoothImpl is constructed"); logger.info("A VotingBoothImpl is constructed");
shutDownHasBeenCalled = false; shutDownHasBeenCalled = false;
queue = new LinkedBlockingQueue<>(); queue = new LinkedBlockingQueue<>();
state = new ControllerState();
waitForCommitMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
.build();
waitForAuditMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
.build();
waitForCastMessage = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
.build();
state = new VotingBoothImpl.ControllerState();
} }
@Override @Override
@ -80,42 +60,22 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
} }
@Override @Override
public void setBallotChannelChoiceQuestions(BallotQuestion[] questions) { public void setBallotChannelChoiceQuestions(List<BallotQuestion> questions) {
logger.info("setting questions"); logger.info("setting questions");
this.questionsForChoosingChannel = questions; this.questionsForChoosingChannel = questions;
} }
@Override
public void setBallotChannelChoiceQuestions(ArrayList<BallotQuestion> questions) {
BallotQuestion[] bqArr = new BallotQuestion[questions.size()];
for (int i = 0; i < bqArr.length; ++i) {
bqArr[i] = questions.get(i);
}
setBallotChannelChoiceQuestions(bqArr);
}
@Override @Override
public void setChannelQuestionSelector(QuestionSelector selector) { public void setChannelQuestionSelector(QuestionSelector selector) {
this.questionSelector = selector; this.questionSelector = selector;
} }
@Override @Override
public void setBallotRaceQuestions(BallotQuestion[] questions) { public void setBallotRaceQuestions(List<BallotQuestion> questions) {
logger.info("setting questions"); logger.info("setting questions");
this.questions = questions; this.questions = questions;
} }
@Override
public void setBallotRaceQuestions(ArrayList<BallotQuestion> questions) {
// TODO: why does the toArray method not work??
//BallotQuestion[] bqArr = (BallotQuestion[])(questions.toArray());
BallotQuestion[] bqArr = new BallotQuestion[questions.size()];
for (int i = 0; i < bqArr.length; ++i) {
bqArr[i] = questions.get(i);
}
setBallotRaceQuestions(bqArr);
}
@Override @Override
public void run() { public void run() {
logger.info("run command has been called"); logger.info("run command has been called");
@ -124,17 +84,6 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
} }
@Override
public void shutDown() {
logger.info("shutDown command has been called");
synchronized (this) {
shutDownHasBeenCalled = true;
}
}
private void runVotingFlow () { private void runVotingFlow () {
logger.info("entered the voting flow"); logger.info("entered the voting flow");
@ -151,64 +100,70 @@ public class VotingBoothImpl implements VotingBoothController, Runnable {
} }
} }
private void doShutDown () { @Override
logger.info("running shutDown"); public void callShutDown() {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
queue.add(new ShutDownCommand());
} }
private void handleSingleTask (ControllerCommand task) { private void handleSingleTask (ControllerCommand task) {
if (task instanceof RestartVotingCommand) { if (task.getBallotSerialNumber() != state.currentBallotSerialNumber && !(task instanceof RestartVotingCommand)) {
doRestartVoting (); // probably an old command relating to some old ballot serial number. Simply log it and ignore it.
return;
}
if (task.getBallotSerialNumber() != state.currentBallotSerialNumber) {
String errorMessage = "handleSingleTask: received a task too old. " + String errorMessage = "handleSingleTask: received a task too old. " +
task.getBallotSerialNumber() + " " + state.currentBallotSerialNumber; task.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
logger.warn (errorMessage); logger.debug(errorMessage);
return; return;
} }
if (task instanceof CastCommand) { if (task instanceof RestartVotingCommand) {
doCast(); doRestartVoting ();
return; }
else if (task instanceof CastCommand) {
doFinalize(false);
} }
else if (task instanceof AuditCommand) { else if (task instanceof AuditCommand) {
doAudit(); doFinalize(true);
return;
} }
else if (task instanceof ChannelChoiceCommand) { else if (task instanceof ChannelChoiceCommand) {
doChooseChannel(); doChooseChannel();
return;
} }
else if (task instanceof ChannelDeterminedCommand) { else if (task instanceof ChannelDeterminedCommand) {
doSetChannelAndAskQuestions ((ChannelDeterminedCommand)task); doSetChannelAndAskQuestions ((ChannelDeterminedCommand)task);
return;
} }
else if (task instanceof EncryptAndCommitBallotCommand) { else if (task instanceof EncryptAndCommitBallotCommand) {
doCommit ((EncryptAndCommitBallotCommand)task); doCommit ((EncryptAndCommitBallotCommand)task);
return; }
else if (task instanceof ReportErrorCommand) {
doReportErrorAndForceRestart((ReportErrorCommand)task);
}
else if (task instanceof ShutDownCommand) {
// wasShutDownCalled is now true. Do nothing.
// The program will not access the command queue anymore, and will exit normally
if (!wasShutDownCalled()) {
logger.warn("Received a ShutDownCommand command. At this point shutDownHasBeenCalled flag should have been True, but it's not...");
shutDownHasBeenCalled = true;
}
} }
else { else {
String errorMessage = "handleSingleTask: unknown type of ControllerCommand received: " + logger.error("handleSingleTask: unknown type of ControllerCommand received: " + task.getClass().getName());
task.getClass().getName(); doReportErrorAndForceRestart(SystemMessages.getSomethingWrongMessage());
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
} }
} }
/* private boolean wasShutDownCalled () {
Map<string, funcImpl> taskHandlers = new Map<>() {
CaskTask.getClass().getName() : doCast(),
ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
}
*/
private synchronized boolean wasShutDownCalled () {
return shutDownHasBeenCalled; return shutDownHasBeenCalled;
} }
private void doShutDown () {
logger.info("running callShutDown");
state.clearPlaintext();
state.clearCiphertext();
state.stateIdentifier = VBState.SHUT_DOWN;
//TODO: add commands to actually shut down the machine
}
private void doRestartVoting () { private void doRestartVoting () {
queue.clear(); queue.clear();
state.clearPlaintext(); state.clearPlaintext();
@ -216,7 +171,22 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
state.stateIdentifier = VBState.NEW_VOTER; state.stateIdentifier = VBState.NEW_VOTER;
state.currentBallotSerialNumber += 1; state.currentBallotSerialNumber += 1;
ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber , this.queue)); ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
private void doReportErrorAndForceRestart(ReportErrorCommand task) {
doReportErrorAndForceRestart(task.getErrorMessage());
}
private void doReportErrorAndForceRestart(UIElement errorMessage) {
queue.clear();
state.clearPlaintext();
state.clearCiphertext();
state.stateIdentifier = VBState.FATAL_ERROR_FORCE_NEW_VOTER;
state.currentBallotSerialNumber += 1;
ui.showErrorMessageWithButtons(errorMessage,
new UIElement[]{SystemMessages.getRestartVotingButton()},
new ErrorMessageRestartCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
} }
private void doChooseChannel () { private void doChooseChannel () {
@ -227,7 +197,7 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
new ChannelChoiceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue)); new ChannelChoiceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
} }
else { else {
logger.warn("doChooseChannel: current state is " + state.stateIdentifier); logger.debug("doChooseChannel: current state is " + state.stateIdentifier);
// ignore this request // ignore this request
} }
} }
@ -236,15 +206,14 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
if (state.stateIdentifier == VBState.CHOOSE_CHANNEL) { if (state.stateIdentifier == VBState.CHOOSE_CHANNEL) {
logger.debug("doing set channel and ask questions"); logger.debug("doing set channel and ask questions");
state.stateIdentifier = VBState.ANSWER_QUESTIONS; state.stateIdentifier = VBState.ANSWER_QUESTIONS;
BallotAnswer[] channelChoiceAnswers = task.channelChoiceAnswers; List<BallotAnswer> channelChoiceAnswers = task.channelChoiceAnswers;
state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers); state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers);
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(channelChoiceAnswers); state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(channelChoiceAnswers);
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
ui.askVoterQuestions(state.channelSpecificQuestions, ui.askVoterQuestions(state.channelSpecificQuestions,
new VotingCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue)); new VotingCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
} }
else { else {
logger.warn("doSetChannelAndAskQuestions: current state is " + state.stateIdentifier); logger.debug("doSetChannelAndAskQuestions: current state is " + state.stateIdentifier);
// ignore this request // ignore this request
} }
} }
@ -254,10 +223,8 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
if (state.stateIdentifier == VBState.ANSWER_QUESTIONS) { if (state.stateIdentifier == VBState.ANSWER_QUESTIONS) {
logger.debug("doing commit"); logger.debug("doing commit");
state.stateIdentifier = VBState.COMMITTING_TO_BALLOT; state.stateIdentifier = VBState.COMMITTING_TO_BALLOT;
VotingBoothEncryptor.EncryptionAndSecrets encryptionAndSecrets = encryptor.encrypt(state.plaintextBallot); setBallotData (task);
state.encryptedBallot = encryptionAndSecrets.getEncryptedBallot(); ui.notifyVoterToWaitForFinish(SystemMessages.getWaitForCommitMessage(),
state.secrets = encryptionAndSecrets.getSecrets();
ui.notifyVoterToWaitForFinish(waitForCommitMessage,
new WaitForFinishCallback(generateRequestIdentifier(), new WaitForFinishCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber, state.currentBallotSerialNumber,
this.queue)); this.queue));
@ -266,38 +233,38 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue)); new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
} }
else { else {
logger.warn("doCommit: current state is " + state.stateIdentifier); logger.debug("doCommit: current state is " + state.stateIdentifier);
// ignore this request // ignore this request
} }
} }
private void setBallotData (EncryptAndCommitBallotCommand task) {
private void doAudit () { state.plaintextBallot = PlaintextBallot.newBuilder()
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) { .setSerialNumber(task.getBallotSerialNumber())
logger.debug("doing audit"); .addAllAnswers(task.getVotingAnswers())
state.stateIdentifier = VBState.FINALIZING; .build();
outputDevice.audit(state.secrets, VotingBoothEncryptor.EncryptionAndSecrets encryptionAndSecrets = encryptor.encrypt(state.plaintextBallot);
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue)); state.encryptedBallot = encryptionAndSecrets.getEncryptedBallot();
ui.notifyVoterToWaitForFinish(waitForAuditMessage, state.secrets = encryptionAndSecrets.getSecrets();
new WaitForFinishCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
logger.warn("doAudit: current state is " + state.stateIdentifier);
// ignore this request
}
} }
private void doCast () { private void doFinalize (boolean auditRequested) {
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) { if (state.stateIdentifier == VBState.CAST_OR_AUDIT) {
logger.debug("casting ballot"); logger.debug("finalizing");
state.stateIdentifier = VBState.FINALIZING; state.stateIdentifier = VBState.FINALIZING;
outputDevice.castBallot( ui.notifyVoterToWaitForFinish(SystemMessages.getWaitForAuditMessage(),
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
ui.notifyVoterToWaitForFinish(waitForCastMessage,
new WaitForFinishCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue)); new WaitForFinishCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
if (auditRequested) {
outputDevice.audit(state.secrets,
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
outputDevice.castBallot(
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
} }
else { else {
logger.warn("doCast: current state is " + state.stateIdentifier); logger.debug("doFinalize: current state is " + state.stateIdentifier);
// ignore this request // ignore this request
} }
} }
@ -312,14 +279,16 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
ANSWER_QUESTIONS, ANSWER_QUESTIONS,
COMMITTING_TO_BALLOT, COMMITTING_TO_BALLOT,
CAST_OR_AUDIT, CAST_OR_AUDIT,
FINALIZING FINALIZING,
FATAL_ERROR_FORCE_NEW_VOTER,
SHUT_DOWN
} }
private class ControllerState { private class ControllerState {
public VotingBoothImpl.VBState stateIdentifier; public VBState stateIdentifier;
public int channelIdentifier; public int channelIdentifier;
public BallotQuestion[] channelSpecificQuestions; public List<BallotQuestion> channelSpecificQuestions;
public PlaintextBallot plaintextBallot; public PlaintextBallot plaintextBallot;
public EncryptedBallot encryptedBallot; public EncryptedBallot encryptedBallot;
public BallotSecrets secrets; public BallotSecrets secrets;
@ -338,12 +307,10 @@ ChannelChoiceCommand.getClass().getName() : doChooseChannel(),
public void clearPlaintext () { public void clearPlaintext () {
//TODO: Do we need safe erasure?
plaintextBallot = null; plaintextBallot = null;
} }
public void clearCiphertext () { public void clearCiphertext () {
//TODO: Do we need safe erasure?
encryptedBallot = null; encryptedBallot = null;
secrets = null; secrets = null;
} }

View File

@ -1,6 +1,6 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.*; import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand; import meerkat.voting.controller.commands.RestartVotingCommand;
@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class CastOrAuditCallback extends ControllerCallback { public class CastOrAuditCallback extends ControllerCallback<FinalizeBallotChoice> {
protected final static Logger logger = LoggerFactory.getLogger(CastOrAuditCallback.class); protected final static Logger logger = LoggerFactory.getLogger(CastOrAuditCallback.class);
public CastOrAuditCallback(int requestId, public CastOrAuditCallback(int requestId,
@ -20,18 +20,18 @@ public class CastOrAuditCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object result) { public void onSuccess(FinalizeBallotChoice result) {
assert (result instanceof FinalizeBallotChoice);
if (result == FinalizeBallotChoice.CAST) { if (result == FinalizeBallotChoice.CAST) {
controllerQueue.add(new CastCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new CastCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
else if (result == FinalizeBallotChoice.AUDIT) { else if (result == FinalizeBallotChoice.AUDIT) {
controllerQueue.add(new AuditCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new AuditCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
else { else {
logger.error("CastOrAuditCallback got an unrecognized response: " + result); logger.error("CastOrAuditCallback got an unrecognized response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnrecognizedFinalizeResponseMessage()));
} }
} }
@ -39,7 +39,9 @@ public class CastOrAuditCallback extends ControllerCallback {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("CastOrAuditCallback got a failure: " + t); logger.error("CastOrAuditCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnrecognizedFinalizeResponseMessage()));
} }
} }

View File

@ -1,18 +1,18 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.commands.ChannelDeterminedCommand; import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
/** /**
* Created by hai on 11/04/16. * Created by hai on 11/04/16.
*/ */
public class ChannelChoiceCallback extends ControllerCallback { public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(ChannelChoiceCallback.class); protected final static Logger logger = LoggerFactory.getLogger(ChannelChoiceCallback.class);
public ChannelChoiceCallback(int requestId, public ChannelChoiceCallback(int requestId,
@ -22,22 +22,22 @@ public class ChannelChoiceCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object result) { public void onSuccess(List<BallotAnswer> result) {
assert (result instanceof Voting.BallotAnswer[]); if (result.size() != 0) {
if (((Voting.BallotAnswer[])result).length != 0) {
logger.debug("callback for channel choice returned success"); logger.debug("callback for channel choice returned success");
controllerQueue.add( enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), (Voting.BallotAnswer[]) result));
} }
else { else {
logger.debug("ChannelChoiceCallback got a cancellation response: " + result); logger.debug("ChannelChoiceCallback got a cancellation response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("channel choice initiated a failure: " + t); logger.error("channel choice initiated a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulChannelChoiceMessage()));
} }
} }

View File

@ -1,14 +1,18 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
/**
* Created by hai on 18/05/16.
*/
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.ControllerCommand;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public abstract class ControllerCallback implements FutureCallback { public abstract class ControllerCallback<T> implements FutureCallback<T> {
private final int requestIdentifier; private final int requestIdentifier;
private final long ballotSerialNumber; private final long ballotSerialNumber;
protected LinkedBlockingQueue<ControllerCommand> controllerQueue; private LinkedBlockingQueue<ControllerCommand> controllerQueue;
protected ControllerCallback (int requestId, protected ControllerCallback (int requestId,
long ballotSerialNumber, long ballotSerialNumber,
@ -21,7 +25,13 @@ public abstract class ControllerCallback implements FutureCallback {
protected int getRequestIdentifier () { protected int getRequestIdentifier () {
return requestIdentifier; return requestIdentifier;
} }
protected long getBallotSerialNumber () { protected long getBallotSerialNumber () {
return ballotSerialNumber; return ballotSerialNumber;
} }
protected void enqueueCommand (ControllerCommand command) {
controllerQueue.add(command);
}
} }

View File

@ -0,0 +1,29 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
public class ErrorMessageRestartCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(ErrorMessageRestartCallback.class);
public ErrorMessageRestartCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Integer result) {
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("Error message execution initiated a failure: " + t);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -1,32 +0,0 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.ControllerCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by hai on 21/04/16.
*/
public class HaltCallback extends ControllerCallback{
protected final static Logger logger = LoggerFactory.getLogger(HaltCallback.class);
public HaltCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Object result) {
throw new UnsupportedOperationException("HaltCallback: onSuccess: There cannot be a successful return for this task. Returned value is " + result);
}
@Override
public void onFailure(Throwable t) {
logger.error("Halting initiated a failure: " + t);
}
}

View File

@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class NewVoterCallback extends ControllerCallback { public class NewVoterCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(NewVoterCallback.class); protected final static Logger logger = LoggerFactory.getLogger(NewVoterCallback.class);
public NewVoterCallback(int requestId, public NewVoterCallback(int requestId,
@ -18,15 +18,14 @@ public class NewVoterCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object v) { public void onSuccess(Void v) {
logger.debug("callback for new voting returned success"); logger.debug("callback for new voting returned success");
assert v==null; enqueueCommand(new ChannelChoiceCommand(getRequestIdentifier(), getBallotSerialNumber()));
controllerQueue.add(new ChannelChoiceCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("New voting session got a failure: " + t); logger.error("New voting session got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
} }

View File

@ -1,13 +1,15 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand; import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class OutputDeviceCallback extends ControllerCallback { public class OutputDeviceCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceCallback.class); protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceCallback.class);
public OutputDeviceCallback(int requestId, public OutputDeviceCallback(int requestId,
@ -17,13 +19,14 @@ public class OutputDeviceCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object v) { public void onSuccess(Void v) {
assert v instanceof Void;
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("WaitForFinishCallback got a failure: " + t); logger.error("WaitForFinishCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getOutputDeviceFailureMessage()));
} }
} }

View File

@ -1,15 +1,18 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.EncryptAndCommitBallotCommand; import meerkat.voting.controller.commands.EncryptAndCommitBallotCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand; import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class VotingCallback extends ControllerCallback { public class VotingCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(VotingCallback.class); protected final static Logger logger = LoggerFactory.getLogger(VotingCallback.class);
public VotingCallback(int requestId, public VotingCallback(int requestId,
@ -19,22 +22,22 @@ public class VotingCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object result) { public void onSuccess(List<BallotAnswer> result) {
assert result instanceof Voting.BallotAnswer[]; if (result.size() != 0) {
if (((Voting.BallotAnswer[])result).length != 0) {
logger.debug("callback for voting returned success"); logger.debug("callback for voting returned success");
controllerQueue.add( enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), (Voting.BallotAnswer[]) result));
} }
else { else {
logger.debug("VotingCallback got a cancellation response: " + result); logger.debug("VotingCallback got a cancellation response: " + result);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("voting initiated a failure: " + t); logger.error("voting initiated a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulVotingMessage()));
} }
} }

View File

@ -1,13 +1,15 @@
package meerkat.voting.controller.callbacks; package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.SystemMessages;
import meerkat.voting.controller.commands.ControllerCommand; import meerkat.voting.controller.commands.ControllerCommand;
import meerkat.voting.controller.commands.ReportErrorCommand;
import meerkat.voting.controller.commands.RestartVotingCommand; import meerkat.voting.controller.commands.RestartVotingCommand;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class WaitForFinishCallback extends ControllerCallback { public class WaitForFinishCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(WaitForFinishCallback.class); protected final static Logger logger = LoggerFactory.getLogger(WaitForFinishCallback.class);
public WaitForFinishCallback(int requestId, public WaitForFinishCallback(int requestId,
@ -17,13 +19,14 @@ public class WaitForFinishCallback extends ControllerCallback {
} }
@Override @Override
public void onSuccess(Object v) { public void onSuccess(Void v) {
assert v instanceof Void;
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logger.error("WaitForFinishCallback got a failure: " + t); logger.error("WaitForFinishCallback got a failure: " + t);
controllerQueue.add(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber())); enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getSomethingWrongMessage()));
} }
} }

View File

@ -1,14 +1,15 @@
package meerkat.voting.controller.commands; package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting; import meerkat.protobuf.Voting.*;
import java.util.List;
/** /**
* Created by hai on 11/04/16. * Created by hai on 11/04/16.
*/ */
public class ChannelDeterminedCommand extends ControllerCommand { public class ChannelDeterminedCommand extends ControllerCommand {
public Voting.BallotAnswer[] channelChoiceAnswers; public List<BallotAnswer> channelChoiceAnswers;
public ChannelDeterminedCommand(int requestIdentifier, long ballotSerialNumber, Voting.BallotAnswer[] answers) { public ChannelDeterminedCommand(int requestIdentifier, long ballotSerialNumber, List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber); super(requestIdentifier, ballotSerialNumber);
channelChoiceAnswers = answers; channelChoiceAnswers = answers;
} }

View File

@ -1,12 +1,18 @@
package meerkat.voting.controller.commands; package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting; import meerkat.protobuf.Voting.*;
import java.util.List;
public class EncryptAndCommitBallotCommand extends ControllerCommand { public class EncryptAndCommitBallotCommand extends ControllerCommand {
public Voting.BallotAnswer[] votingAnswers; private final List<BallotAnswer> votingAnswers;
public EncryptAndCommitBallotCommand(int requestIdentifier, long ballotSerialNumber, Voting.BallotAnswer[] answers) { public EncryptAndCommitBallotCommand(int requestIdentifier, long ballotSerialNumber, List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber); super(requestIdentifier, ballotSerialNumber);
votingAnswers = answers; votingAnswers = answers;
} }
public List<BallotAnswer> getVotingAnswers() {
return votingAnswers;
}
} }

View File

@ -0,0 +1,16 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.*;
public class ReportErrorCommand extends ControllerCommand {
private final UIElement errorMessage;
public ReportErrorCommand(int requestIdentifier, long ballotSerialNumber, UIElement errorMessage) {
super(requestIdentifier, ballotSerialNumber);
this.errorMessage = errorMessage;
}
public UIElement getErrorMessage() {
return errorMessage;
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
*/
public class ShutDownCommand extends ControllerCommand {
public ShutDownCommand() {
super(0, 0);
}
}

View File

@ -1,20 +1,18 @@
package meerkat.voting.controller.selector; package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import java.util.List;
/** /**
* Created by hai on 02/05/16. * Created by hai on 02/05/16.
*/ */
public abstract class QuestionSelector { public interface QuestionSelector {
protected Object selectionData; public void setAllBallotQuestions (List<BallotQuestion> allBallotQuestions);
protected BallotQuestion[] allBallotQuestions;
public QuestionSelector(Object selectionData, BallotQuestion[] allBallotQuestions) { public void setSelectionData(SelectionData data);
this.selectionData = selectionData;
this.allBallotQuestions = allBallotQuestions;
}
public abstract int getChannelIdentifier (BallotAnswer[] channelChoiceAnswers); public int getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
public abstract BallotQuestion[] selectQuestionsForVoter (BallotAnswer[] channelChoiceAnswers);
public List<BallotQuestion> selectQuestionsForVoter (List<BallotAnswer> channelChoiceAnswers);
} }

View File

@ -0,0 +1,7 @@
package meerkat.voting.controller.selector;
/**
* Created by hai on 11/05/16.
*/
public class SelectionData {
}

View File

@ -1,39 +1,60 @@
package meerkat.voting.controller.selector; package meerkat.voting.controller.selector;
import java.util.HashMap;
import java.lang.Math;
/** /**
* Created by hai on 02/05/16. * This class is the SelectionData used to initialize a SimpleListCategoriesSelector.
* It holds a table of question categories.
* Every answer in the channel choice phase dictates a category of questions to include in the ballot.
* This class is simply only the data class of these categories.
* Specifically the questionSelectionsByAnswer member is this categories table:
* questionSelectionsByAnswer[questionNumber][answerNumber] is an array of question indices to include in ballot
* whenever the voter answered answerNumber on question questionNumber in the channel choice phase.
* Also the sharedDefaults member is a category of question indices to include for EVERY voter,
* regardless of his channel choice answers
*/ */
public class SimpleCategoriesData { public class SimpleCategoriesData extends SelectionData{
private int[] sharedDefaults; private int[] sharedDefaults; // a category of questions to include for every voter
private HashMap<SingleChosenAnswer, int[]> questionsSelections; // the categories
// first index is the question number, the second is the answer number. the value is an array of question indices in this category
private int[][][] questionSelectionsByAnswer;
private int maxQuestionNumber; private int maxQuestionNumber;
public SimpleCategoriesData() { public SimpleCategoriesData() {
sharedDefaults = new int[0]; sharedDefaults = new int[0];
questionsSelections = new HashMap(); questionSelectionsByAnswer = new int[0][][];
maxQuestionNumber = -1; maxQuestionNumber = -1;
} }
public void setSharedDefaults(int[] sharedDefaultsIndices) { public void setSharedDefaults(int[] sharedDefaultsIndices) {
this.sharedDefaults = sharedDefaultsIndices; this.sharedDefaults = sharedDefaultsIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxInt(sharedDefaultsIndices)); maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(sharedDefaultsIndices));
} }
public int[] getSharedDefaults() { public int[] getSharedDefaults() {
return sharedDefaults; return sharedDefaults;
} }
public void setItem(SingleChosenAnswer key, int[] questionIndices) { public void setItem(int questionNumber, int answerNumber, int[] questionIndices) {
questionsSelections.put(key, questionIndices); if (questionNumber >= questionSelectionsByAnswer.length) {
maxQuestionNumber = Math.max(maxQuestionNumber, maxInt(questionIndices)); int[][][] tmp = new int[questionNumber+1][][];
System.arraycopy(questionSelectionsByAnswer, 0, tmp, 0, questionSelectionsByAnswer.length);
tmp[questionNumber] = new int[0][];
questionSelectionsByAnswer = tmp;
}
if (answerNumber >= questionSelectionsByAnswer[questionNumber].length) {
int[][] tmp = new int[answerNumber+1][];
System.arraycopy(questionSelectionsByAnswer[questionNumber], 0,
tmp, 0, questionSelectionsByAnswer[questionNumber].length);
questionSelectionsByAnswer[questionNumber] = tmp;
}
questionSelectionsByAnswer[questionNumber][answerNumber] = questionIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(questionIndices));
} }
public int[] getItem(SingleChosenAnswer key) { public int[] getItem(int questionNumber, int answerNumber) {
return questionsSelections.get(key); return questionSelectionsByAnswer[questionNumber][answerNumber];
} }
public int getMaxQuestionNumber() { public int getMaxQuestionNumber() {
@ -41,7 +62,7 @@ public class SimpleCategoriesData {
} }
private static int maxInt(int[] arr) { private static int maxIntInArray(int[] arr) {
int m = -1; int m = -1;
for (int i: arr) { for (int i: arr) {
m = Math.max(i , m); m = Math.max(i , m);
@ -53,8 +74,11 @@ public class SimpleCategoriesData {
String s = "SimpleCategoriesData:\n"; String s = "SimpleCategoriesData:\n";
s += "shared : " + arrayToString(sharedDefaults) + "\n"; s += "shared : " + arrayToString(sharedDefaults) + "\n";
s += "map : \n"; s += "map : \n";
for (SingleChosenAnswer key : questionsSelections.keySet()) { for (int questionNumber = 0; questionNumber < questionSelectionsByAnswer.length; ++questionNumber) {
s += key + " : " + arrayToString(questionsSelections.get(key)) + "\n"; for (int answerNumber = 0; answerNumber < questionSelectionsByAnswer[questionNumber].length; ++answerNumber) {
s += "(" + questionNumber + ", " + answerNumber + ") -> " +
arrayToString(questionSelectionsByAnswer[questionNumber][answerNumber]) + "\n";
}
} }
return s; return s;
} }

View File

@ -1,75 +1,88 @@
package meerkat.voting.controller.selector; package meerkat.voting.controller.selector;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException; import meerkat.protobuf.Voting.BallotAnswer;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.BallotQuestion;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* Created by hai on 02/05/16. * A simple implementation of a QuestionSelector.
* This implementation simply regards every single answer in the channel choice phase as an identifier of a category
* Every category is an array of ballot questions.
* Data of categories is initialized and stored by a SimpleCategoriesData object.
* After receiving the answers from a channel choice phase, this class simply gathers all the categories
* chosen and compiles the list of ballot questions to include in the ballot for this voter (a question
* is included if its index appears in any chosen category, or in the default category shared by all voters)
*/ */
public class SimpleListCategoriesSelector extends QuestionSelector { public class SimpleListCategoriesSelector implements QuestionSelector {
protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class); protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class);
private BallotQuestion[] allBallotQuestions;
private SimpleCategoriesData selectionData;
private boolean[] isSelected; private boolean[] isSelected;
public SimpleListCategoriesSelector(Object selectionData, BallotQuestion[] allBallotQuestions) {
super(selectionData, allBallotQuestions);
initializeAttributes (allBallotQuestions);
}
public SimpleListCategoriesSelector(Object selectionData, ArrayList<BallotQuestion> allBallotQuestions) { public SimpleListCategoriesSelector() {
super(selectionData, null); this.allBallotQuestions = null;
BallotQuestion[] questionsArr = new BallotQuestion[allBallotQuestions.size()]; this.selectionData = null;
for (int i = 0; i < questionsArr.length; ++i) {
questionsArr[i] = allBallotQuestions.get(i);
}
initializeAttributes (questionsArr);
} }
private void initializeAttributes (BallotQuestion[] allBallotQuestions) {
assert selectionData instanceof SimpleCategoriesData;
this.allBallotQuestions = allBallotQuestions;
SimpleCategoriesData data = (SimpleCategoriesData) selectionData;
int maxQuestionNumber = data.getMaxQuestionNumber();
int length = allBallotQuestions.length;
if (maxQuestionNumber >= length) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + length + " questions totally";
logger.error(errorMessage);
throw new ValueException(errorMessage);
}
isSelected = new boolean[allBallotQuestions.length];
}
@Override @Override
public int getChannelIdentifier(BallotAnswer[] channelChoiceAnswers) { public void setAllBallotQuestions(List<BallotQuestion> allBallotQuestions) {
this.allBallotQuestions = (BallotQuestion[])allBallotQuestions.toArray();
isSelected = new boolean[this.allBallotQuestions.length];
assertDataValid();
}
@Override
public void setSelectionData(SelectionData data) {
if (! (data instanceof SimpleCategoriesData)) {
String errorMessage = "Initialization of SimpleListCategoriesSelector with wrong object type";
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
}
this.selectionData = (SimpleCategoriesData)data;
assertDataValid();
}
private void assertDataValid () {
if (selectionData != null && allBallotQuestions != null) {
int questionsLength = allBallotQuestions.length;
int maxQuestionNumber = selectionData.getMaxQuestionNumber();
if (maxQuestionNumber >= questionsLength) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + questionsLength + " questions totally";
logger.error(errorMessage);
throw new IndexOutOfBoundsException(errorMessage);
}
}
}
@Override
public int getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
return 0; return 0;
} }
@Override @Override
public BallotQuestion[] selectQuestionsForVoter(BallotAnswer[] channelChoiceAnswers) { public List<BallotQuestion> selectQuestionsForVoter(List<BallotAnswer> channelChoiceAnswers) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData;
java.util.Arrays.fill(isSelected, false); java.util.Arrays.fill(isSelected, false);
markSelected(data.getSharedDefaults()); markSelected(selectionData.getSharedDefaults());
markSelectionsOfVoterChannelChoice (channelChoiceAnswers); markSelectionsOfVoterChannelChoice ((BallotAnswer[])channelChoiceAnswers.toArray());
int[] questionIndices = extractSelectedIndices(); int[] questionIndices = extractSelectedIndices();
BallotQuestion[] selectedQuestions = new BallotQuestion[questionIndices.length]; List<BallotQuestion> selectedQuestions = new ArrayList<>();
for (int i = 0; i < questionIndices.length; ++i) { for (int questionIndex: questionIndices) {
selectedQuestions[i] = allBallotQuestions[questionIndices[i]]; selectedQuestions.add(allBallotQuestions[questionIndex]);
} }
return selectedQuestions; return selectedQuestions;
} }
private void markSelectionsOfVoterChannelChoice(BallotAnswer[] channelChoiceAnswers) { private void markSelectionsOfVoterChannelChoice(BallotAnswer[] channelChoiceAnswers) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData;
for (int q = 0; q < channelChoiceAnswers.length; ++q) { for (int q = 0; q < channelChoiceAnswers.length; ++q) {
BallotAnswer ballotAnswer = channelChoiceAnswers[q]; BallotAnswer ballotAnswer = channelChoiceAnswers[q];
assertAnswerLengthIsOne(ballotAnswer, q); assertAnswerLengthIsOne(ballotAnswer, q);
SingleChosenAnswer key = new SingleChosenAnswer(q, ballotAnswer.getAnswer(0)); assertKeyInSelectionData(q, (int)ballotAnswer.getAnswer(0));
assertKeyInSelectionData(key); markSelected(selectionData.getItem(q, (int)ballotAnswer.getAnswer(0)));
markSelected(data.getItem(key));
} }
} }
@ -81,17 +94,16 @@ public class SimpleListCategoriesSelector extends QuestionSelector {
errorMessage += " " + i; errorMessage += " " + i;
} }
logger.error(errorMessage); logger.error(errorMessage);
throw new ValueException(errorMessage); throw new IllegalArgumentException(errorMessage);
} }
} }
private void assertKeyInSelectionData(SingleChosenAnswer key) { private void assertKeyInSelectionData(int questionNumber, int answerNumber) {
SimpleCategoriesData data = (SimpleCategoriesData)selectionData; int[] questionListToAdd = selectionData.getItem(questionNumber, answerNumber);
int[] questionListToAdd = data.getItem(key);
if (null == questionListToAdd) { if (null == questionListToAdd) {
String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + key.answerNumber + " for question number " + key.questionNumber; String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + answerNumber + " for question number " + questionNumber;
logger.error(errorMessage); logger.error(errorMessage);
throw new ValueException(errorMessage); throw new IllegalArgumentException(errorMessage);
} }
} }

View File

@ -1,31 +0,0 @@
package meerkat.voting.controller.selector;
/**
* Created by hai on 02/05/16.
*/
public class SingleChosenAnswer {
public int questionNumber;
public long answerNumber;
public SingleChosenAnswer(int questionNumber, long answerNumber) {
this.questionNumber = questionNumber;
this.answerNumber = answerNumber;
}
public String toString() {
return "SingleChosenAnswer(" + questionNumber + ", " + answerNumber + ")";
}
public boolean equals (Object other) {
if (!(other instanceof SingleChosenAnswer)) {
return false;
}
SingleChosenAnswer o = (SingleChosenAnswer)other;
return (o.questionNumber == this.questionNumber) && (o.answerNumber == this.answerNumber);
}
@Override
public int hashCode () {
return questionNumber*1000 + (int)answerNumber;
}
}

View File

@ -7,9 +7,7 @@ import meerkat.protobuf.Voting.*;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Date; import java.util.*;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import meerkat.voting.controller.callbacks.*; import meerkat.voting.controller.callbacks.*;
@ -45,54 +43,47 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
logger.info("entered the voting flow"); logger.info("entered the voting flow");
while (true) { while (true) {
try { try {
UICommand task = queue.take(); UICommand command = queue.take();
handleSingleTask (task); handleSingleCommand(command);
} }
catch (InterruptedException e) { catch (InterruptedException e) {
logger.warn ("Interrupted while reading from task queue " + e); logger.warn ("Interrupted while reading from command queue " + e);
} }
} }
} }
private void handleSingleTask (UICommand task) { private void handleSingleCommand(UICommand command) {
if (!(task instanceof TickCommand)) { if (!(command instanceof TickCommand)) {
if (startWaitingTime != null) { if (startWaitingTime != null) {
stopWaiting(); stopWaiting();
} }
} }
if (task instanceof StartSessionUICommand) { if (command instanceof StartSessionUICommand) {
doShowWelcomeScreen((StartSessionUICommand)task); doShowWelcomeScreen((StartSessionUICommand)command);
return;
} }
else if (task instanceof ChannelChoiceUICommand) { else if (command instanceof ChannelChoiceUICommand) {
doAskChannelChoiceQuestions((ChannelChoiceUICommand)task); doAskChannelChoiceQuestions((ChannelChoiceUICommand)command);
return;
} }
else if (task instanceof RaceVotingUICommand) { else if (command instanceof RaceVotingUICommand) {
doAskVotingQuestions((RaceVotingUICommand)task); doAskVotingQuestions((RaceVotingUICommand)command);
return;
} }
else if (task instanceof CastOrAuditUICommand) { else if (command instanceof CastOrAuditUICommand) {
doCastOrAudit ((CastOrAuditUICommand)task); doCastOrAudit ((CastOrAuditUICommand)command);
return;
} }
else if (task instanceof HaltCommand) { else if (command instanceof FatalErrorUICommand) {
doHalt((HaltCommand)task); doFatalError((FatalErrorUICommand)command);
return;
} }
else if (task instanceof WaitForFinishCommand) { else if (command instanceof WaitForFinishUICommand) {
doWaitForFinish((WaitForFinishCommand)task); doWaitForFinish((WaitForFinishUICommand)command);
return;
} }
else if (task instanceof TickCommand) { else if (command instanceof TickCommand) {
doTick (); doTick ();
return;
} }
else { else {
String errorMessage = "handleSingleTask: unknown type of UICommand received: " + String errorMessage = "handleSingleCommand: unknown type of UICommand received: " +
task.getClass().getName(); command.getClass().getName();
logger.error(errorMessage); logger.error(errorMessage);
throw new RuntimeException(errorMessage); throw new RuntimeException(errorMessage);
} }
@ -105,13 +96,13 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
queue.add(new StartSessionUICommand((ControllerCallback)callback)); queue.add(new StartSessionUICommand((ControllerCallback)callback));
} }
private void doShowWelcomeScreen(StartSessionUICommand task) { private void doShowWelcomeScreen(StartSessionUICommand command) {
logger.debug("UI entered doShowWelcomeScreen"); logger.debug("UI entered doShowWelcomeScreen");
System.out.println("Welcome, new voter!"); System.out.println("Welcome, new voter!");
waitForEnter(null); waitForEnter(null);
ControllerCallback callback = task.getCallback(); ControllerCallback callback = command.getCallback();
assert (callback instanceof NewVoterCallback); assert (callback instanceof NewVoterCallback);
((NewVoterCallback)callback).onSuccess(null); callback.onSuccess(null);
} }
private void stopWaiting () { private void stopWaiting () {
@ -138,45 +129,45 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
} }
@Override @Override
public void chooseChannel(BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback) { public void chooseChannel(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
logger.debug("UI interface call to chooseChannel"); logger.debug("UI interface call to chooseChannel");
ChannelChoiceUICommand task = new ChannelChoiceUICommand(questions, (ControllerCallback)callback); ChannelChoiceUICommand command = new ChannelChoiceUICommand(questions, (ControllerCallback)callback);
queue.add(task); queue.add(command);
} }
private void doAskChannelChoiceQuestions (ChannelChoiceUICommand task) { private void doAskChannelChoiceQuestions (ChannelChoiceUICommand command) {
logger.debug("UI: doAskChannelChoiceQuestions"); logger.debug("UI: doAskChannelChoiceQuestions");
System.out.println("Showing questions for choosing channel:\n"); System.out.println("Showing questions for choosing channel:\n");
try { try {
BallotAnswer[] answers = askVoterForAnswers(task.getQuestions()); List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
task.getCallback().onSuccess(answers); command.getCallback().onSuccess(answers);
} }
catch (IOException e) { catch (IOException e) {
String errorMessage = "Channel choice failed due to IOException: " + e; String errorMessage = "Channel choice failed due to IOException: " + e;
logger.error (errorMessage); logger.error (errorMessage);
System.err.println(errorMessage); System.err.println(errorMessage);
task.getCallback().onFailure(e); command.getCallback().onFailure(e);
} }
} }
@Override @Override
public void askVoterQuestions(BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback) { public void askVoterQuestions(List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback) {
logger.debug("UI interface call to chooseChannel"); logger.debug("UI interface call to chooseChannel");
queue.add(new RaceVotingUICommand(questions, (ControllerCallback)callback)); queue.add(new RaceVotingUICommand(questions, (ControllerCallback)callback));
} }
private void doAskVotingQuestions (RaceVotingUICommand task) { private void doAskVotingQuestions (RaceVotingUICommand command) {
logger.debug("UI: doAskVotingQuestions"); logger.debug("UI: doAskVotingQuestions");
System.out.println("Showing questions for race voting:\n"); System.out.println("Showing questions for race voting:\n");
try { try {
BallotAnswer[] answers = askVoterForAnswers(task.getQuestions()); List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
task.getCallback().onSuccess(answers); command.getCallback().onSuccess(answers);
} }
catch (IOException e) { catch (IOException e) {
String errorMessage = "Asking voting questions failed due to IOException: " + e; String errorMessage = "Asking voting questions failed due to IOException: " + e;
logger.error (errorMessage); logger.error (errorMessage);
System.err.println(errorMessage); System.err.println(errorMessage);
task.getCallback().onFailure(e); command.getCallback().onFailure(e);
} }
} }
@ -186,7 +177,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
queue.add(new CastOrAuditUICommand((ControllerCallback)callback)); queue.add(new CastOrAuditUICommand((ControllerCallback)callback));
} }
private void doCastOrAudit(CastOrAuditUICommand task) { private void doCastOrAudit(CastOrAuditUICommand command) {
logger.debug("UI entered doCastOrAudit"); logger.debug("UI entered doCastOrAudit");
System.out.println ("Finalizing your vote. Do you wish to (C)ast or (A)udit?"); System.out.println ("Finalizing your vote. Do you wish to (C)ast or (A)udit?");
@ -203,30 +194,30 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
else { else {
throw new IllegalArgumentException("UI could not understand the answer for cast/audit question '" + s + "'"); throw new IllegalArgumentException("UI could not understand the answer for cast/audit question '" + s + "'");
} }
ControllerCallback callback = task.getCallback(); ControllerCallback callback = command.getCallback();
assert (callback instanceof CastOrAuditCallback); assert (callback instanceof CastOrAuditCallback);
((CastOrAuditCallback)callback).onSuccess(fChoice); ((CastOrAuditCallback)callback).onSuccess(fChoice);
} }
catch (IllegalArgumentException|IOException e) { catch (IllegalArgumentException|IOException e) {
String errorMessage = "doCastOrAudit: some error with reading input from console. details: " + e; String errorMessage = "doCastOrAudit: some error with reading input from console. details: " + e;
logger.error(errorMessage); logger.error(errorMessage);
task.getCallback().onFailure(e); command.getCallback().onFailure(e);
} }
} }
@Override @Override
public void notifyVoterToWaitForFinish(UIElement message, FutureCallback<Boolean> callback) { public void notifyVoterToWaitForFinish(UIElement message, FutureCallback<Void> callback) {
logger.debug("UI interface call to notifyVoterToWaitForFinish"); logger.debug("UI interface call to notifyVoterToWaitForFinish");
queue.add(new WaitForFinishCommand(message, (ControllerCallback)callback)); queue.add(new WaitForFinishUICommand(message, (ControllerCallback)callback));
} }
public void doWaitForFinish (WaitForFinishCommand task) { public void doWaitForFinish (WaitForFinishUICommand command) {
logger.debug("UI entered doWaitForFinish"); logger.debug("UI entered doWaitForFinish");
startWaitingTime = new Date(); startWaitingTime = new Date();
UIElement message = task.getMessage(); UIElement message = command.getMessage();
String messageString; String messageString;
if (message.getType() != UIElementDataType.TEXT) { if (message.getType() != UIElementDataType.TEXT) {
messageString = "Default message: encountered an error. System halting"; messageString = "Default message: encountered an error. System halting";
@ -240,39 +231,57 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
@Override @Override
public void showErrorMessageAndHalt(UIElement errorMessage, FutureCallback<Boolean> callback) { public void showErrorMessageAndHalt(UIElement errorMessage, FutureCallback<Boolean> callback) {
logger.debug("UI interface call to showErrorMessageAndHalt"); logger.debug("UI interface call to showErrorMessageAndHalt");
queue.add(new HaltCommand(errorMessage, (ControllerCallback)callback)); throw new UnsupportedOperationException("Not implemented becuase currently not sure if we ever use it.");
} }
private void doHalt (HaltCommand task) {
logger.debug("UI entered doHalt");
UIElement errorMessage = task.getErrorMessage(); @Override
public void showErrorMessageWithButtons(UIElement errorMessage, UIElement[] buttonLabels, FutureCallback<Integer> callback) {
logger.debug("UI interface call to showErrorMessageWithButtons");
queue.clear();
queue.add(new FatalErrorUICommand(errorMessage, buttonLabels, (ControllerCallback)callback));
}
private void doFatalError (FatalErrorUICommand command) {
logger.debug("UI entered doFatalError");
UIElement errorMessage = command.getErrorMessage();
String errorMessageString; String errorMessageString;
if (errorMessage.getType() != UIElementDataType.TEXT) { if (errorMessage.getType() != UIElementDataType.TEXT) {
errorMessageString = "Default message: encountered an error. System halting"; errorMessageString = "Default message: encountered an error. System halting";
} else { } else {
errorMessageString = bytesToString(errorMessage.getData()); errorMessageString = bytesToString(errorMessage.getData());
} }
System.out.println(errorMessageString);
while (true) { UIElement[] buttonLabels = command.getButtonLabels();
try { String[] buttonLabelStrings = new String[buttonLabels.length];
wait(); for (int i = 0; i < buttonLabels.length; ++i) {
} catch (InterruptedException e) { if (buttonLabels[i].getType() != UIElementDataType.TEXT) {
logger.error("UI halt has been interrupted. Details: " + e); buttonLabelStrings[i] = "<NON_TEXT>";
ControllerCallback callback = task.getCallback(); } else {
assert callback instanceof HaltCallback; buttonLabelStrings[i] = bytesToString(errorMessage.getData());
((HaltCallback) callback).onFailure(e);
} }
} }
System.out.println(errorMessageString);
for (int i = 0; i < buttonLabelStrings.length; ++i) {
System.out.println("" + i + " - " + buttonLabelStrings[i]);
}
try {
String s = readInputLine();
Integer chosenButton = new Integer(s);
command.getCallback().onSuccess(chosenButton);
}
catch (IOException e) {
String err = "doFatalError: some error with reading input from console. details: " + e;
logger.error(err);
command.getCallback().onFailure(e);
}
} }
@Override
public void showErrorMessageWithButtons(UIElement errorMessage, UIElement[] buttonLabels, FutureCallback<Integer> callback) {
throw new UnsupportedOperationException();
}
private void doTick () { private void doTick () {
if (startWaitingTime != null) { if (startWaitingTime != null) {
System.out.print ("."); // still waiting System.out.print ("."); // still waiting
@ -295,9 +304,9 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
} }
private void assertQuestionsAreValid (BallotQuestion[] questions) { private void assertQuestionsAreValid (List<BallotQuestion> questions) {
for (int index = 0; index < questions.length; ++index) { for (int index = 0; index < questions.size(); ++index) {
BallotQuestion question = questions[index]; BallotQuestion question = questions.get(index);
if (question.getIsMandatory()) { if (question.getIsMandatory()) {
String errorMessage = "askVoterQuestions: question number " + index + " is marked as mandatory"; String errorMessage = "askVoterQuestions: question number " + index + " is marked as mandatory";
logger.error(errorMessage); logger.error(errorMessage);
@ -311,14 +320,14 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
} }
} }
private BallotAnswer[] askVoterForAnswers(BallotQuestion[] questions) throws IOException { private List<BallotAnswer> askVoterForAnswers(List<BallotQuestion> questions) throws IOException {
assertQuestionsAreValid (questions); assertQuestionsAreValid (questions);
BallotAnswer answers[] = new BallotAnswer[questions.length]; List<BallotAnswer> answers = new ArrayList<>();
int index = 0; int index = 0;
while (index < questions.length) { while (index < questions.size()) {
BallotQuestion question = questions[index]; BallotQuestion question = questions.get(index);
System.out.println("Question number " + index); System.out.println("Question number " + index);
showQuestionOnScreen(question); showQuestionOnScreen(question);
@ -330,13 +339,14 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
} }
else if (s.equals("back") || s.equals("b")) { else if (s.equals("back") || s.equals("b")) {
--index; --index;
answers.remove(index);
} }
else if (s.equals("skip") || s.equals("s")) { else if (s.equals("skip") || s.equals("s")) {
answers[index] = translateStringAnswerToProtoBufMessageAnswer(""); answers.add(translateStringAnswerToProtoBufMessageAnswer(""));
++index; ++index;
} }
else { else {
answers[index] = translateStringAnswerToProtoBufMessageAnswer(s); answers.add(translateStringAnswerToProtoBufMessageAnswer(s));
++index; ++index;
} }
} }

View File

@ -3,6 +3,8 @@ package meerkat.voting.ui;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import java.util.List;
/** /**
* An interface for the user interface component of the voting booth * An interface for the user interface component of the voting booth
@ -25,14 +27,14 @@ public interface VotingBoothUI {
* @param questions questions to determine the right voting channel for this voter * @param questions questions to determine the right voting channel for this voter
* @param callback that's where we store the answers to decide channel upon for the current voter * @param callback that's where we store the answers to decide channel upon for the current voter
*/ */
public void chooseChannel (BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback); public void chooseChannel (List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback);
/** /**
* Presents the set of questions to the voter. Collect all his responses. * Presents the set of questions to the voter. Collect all his responses.
* @param questions all ballot questions to present to the voter * @param questions all ballot questions to present to the voter
* @param callback the responses to the questions collected by the UI, to send back to the controller. Responses are null if voter chose to cancel session * @param callback the responses to the questions collected by the UI, to send back to the controller. Responses are null if voter chose to cancel session
*/ */
public void askVoterQuestions (BallotQuestion[] questions, FutureCallback<BallotAnswer[]> callback); public void askVoterQuestions (List<BallotQuestion> questions, FutureCallback<List<BallotAnswer>> callback);
/** /**
* Get a response from the voter on how to finalize the ballot. * Get a response from the voter on how to finalize the ballot.
@ -51,7 +53,7 @@ public interface VotingBoothUI {
* @param message a message to show the user on the UI device while waiting * @param message a message to show the user on the UI device while waiting
* @param callback a success return value of the wait (cancelling returns false) * @param callback a success return value of the wait (cancelling returns false)
*/ */
public void notifyVoterToWaitForFinish (UIElement message, FutureCallback<Boolean> callback); public void notifyVoterToWaitForFinish (UIElement message, FutureCallback<Void> callback);
/** /**
* show a fatal error message in the UI. Halts system. Waits for administrator interrupt or reset * show a fatal error message in the UI. Halts system. Waits for administrator interrupt or reset

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
/** /**
* Created by hai on 21/04/16. * Created by hai on 21/04/16.

View File

@ -1,22 +1,24 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
import java.util.List;
/** /**
* Created by hai on 18/04/16. * Created by hai on 18/04/16.
*/ */
public class ChannelChoiceUICommand extends UICommand { public class ChannelChoiceUICommand extends UICommand {
private final BallotQuestion[] questions; private final List<BallotQuestion> questions;
public ChannelChoiceUICommand(BallotQuestion[] questions, ControllerCallback callback) public ChannelChoiceUICommand(List<BallotQuestion> questions, ControllerCallback callback)
{ {
super(callback); super(callback);
this.questions = questions; this.questions = questions;
} }
public BallotQuestion[] getQuestions () { public List<BallotQuestion> getQuestions () {
return this.questions; return this.questions;
} }
} }

View File

@ -0,0 +1,28 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.callbacks.*;
/**
* Created by hai on 18/04/16.
*/
public class FatalErrorUICommand extends UICommand {
private final UIElement errorMessage;
private final UIElement[] buttonLabels;
public FatalErrorUICommand(UIElement errorMessage, UIElement[] buttonLabels, ControllerCallback callback)
{
super(callback);
this.errorMessage = errorMessage;
this.buttonLabels = buttonLabels;
}
public UIElement getErrorMessage() {
return this.errorMessage;
}
public UIElement[] getButtonLabels() {
return this.getButtonLabels();
}
}

View File

@ -1,22 +0,0 @@
package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* Created by hai on 18/04/16.
*/
public class HaltCommand extends UICommand {
private final UIElement errorMessage;
public HaltCommand(UIElement errorMessage, ControllerCallback callback)
{
super(callback);
this.errorMessage = errorMessage;
}
public UIElement getErrorMessage () {
return this.errorMessage;
}
}

View File

@ -1,22 +1,24 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
import java.util.List;
/** /**
* Created by hai on 18/04/16. * Created by hai on 18/04/16.
*/ */
public class RaceVotingUICommand extends UICommand { public class RaceVotingUICommand extends UICommand {
private final BallotQuestion[] questions; private final List<BallotQuestion> questions;
public RaceVotingUICommand(BallotQuestion[] questions, ControllerCallback callback) public RaceVotingUICommand(List<BallotQuestion> questions, ControllerCallback callback)
{ {
super(callback); super(callback);
this.questions = questions; this.questions = questions;
} }
public BallotQuestion[] getQuestions () { public List<BallotQuestion> getQuestions () {
return this.questions; return this.questions;
} }
} }

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
/** /**
* Created by hai on 18/04/16. * Created by hai on 18/04/16.

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
/** /**
* Created by hai on 18/04/16. * Created by hai on 18/04/16.

View File

@ -1,6 +1,6 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
//TODO: make this class generic //TODO: make this class generic
public abstract class UICommand { public abstract class UICommand {

View File

@ -1,17 +1,17 @@
package meerkat.voting.ui.uicommands; package meerkat.voting.ui.uicommands;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.controller.callbacks.*;
/** /**
* Created by hai on 18/04/16. * Created by hai on 18/04/16.
*/ */
public class WaitForFinishCommand extends UICommand { public class WaitForFinishUICommand extends UICommand {
private final UIElement message; private final UIElement message;
public WaitForFinishCommand(UIElement message, ControllerCallback callback) public WaitForFinishUICommand(UIElement message, ControllerCallback callback)
{ {
super(callback); super(callback);
this.message = message; this.message = message;