Skip to content

Commit

Permalink
Merge pull request #13 from RVRX/cli
Browse files Browse the repository at this point in the history
Add Timer capabilities
  • Loading branch information
RVRX authored Jun 20, 2021
2 parents cbdae9e + bb22988 commit f95d808
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 3 deletions.
203 changes: 203 additions & 0 deletions src/CustomTimer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import java.io.*;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;

/**
* Singleton timer class.
* Warning: gets confusing with all the uses of timer as a word, vs the actual JDK Timer class.
*/
public class CustomTimer {

private static CustomTimer singleton = new CustomTimer();
private Timer currentTimer;
protected boolean isTimerRunning = false;

public Timer getCurrentTimer() {
return currentTimer;
}

private CustomTimer() {}

/* Static 'instance' method */
public static CustomTimer getInstance() {
return singleton;
}

//File paths
String timerLength = Main.inputPath + "TimerLength.txt";
String timerTXT = Main.outputPath + "Timer.txt";


/*--- Methods ---*/

/**
* Sets the value the timer will go to on initial start, or after a restart or stop
*
* @param seconds a positive integer
* @see #getInitialTimerLength()
*/
public void setInitialTimerLength(int seconds) throws IOException {
if (seconds <= 0) throw new IllegalArgumentException("Cannot set initial timer value to 0");
//open TimerLength file and set a new value
Writer fileWriter = new FileWriter(timerLength);
fileWriter.write(String.valueOf(seconds));
fileWriter.close();
}

/**
* Resets the timer back to beginning value and starts it again
* @see #start()
* @see #stop()
* @see #pause()
*/
public void restart() throws IOException, InvalidDataException {
//stop timer
if (currentTimer != null) {
currentTimer.cancel();
isTimerRunning = false;
}

//write initial timer length to Timer.txt
set(getInitialTimerLength());

//start
start();
}

/**
* Gets the total length of the timer. Not to be confused with the current length,
* this value is where the timer will go to when restarting or starting after a stop.
*
* @return timer length in seconds
* @throws IOException
* @throws NoSuchElementException TimerLength.txt contains an unexpected character.
* @see #setInitialTimerLength(int)
*/
public int getInitialTimerLength() throws IOException, NoSuchElementException, InvalidDataException {
//open TimerLength file and gets the current value
File timerSettings = new File(timerLength);
Scanner scanner = new Scanner(timerSettings);
int value = scanner.nextInt();
if (value <= 0) throw new InvalidDataException("TimerLength.txt cannot contain a non-postive value");
return value;
}

/**
* Start the timer (initial or after a pause)
* @throws IOException
* @throws NoSuchElementException
* @see #stop()
* @see #pause()
* @see #restart()
*/
public void start() throws IOException, NoSuchElementException, InvalidDataException {
if (!isTimerRunning) { //prevent starting when there is already a timer. no doubling up!
if (get() <= 0) { //if timer is currently at 0, restart
restart();
} else { //otherwise, resume from last position by starting CountdownTimer task
isTimerRunning = true;
currentTimer = new Timer();
TimerTask task = new CountdownTimer();
currentTimer.schedule(task,0, 1000);
}
} else System.err.println("Timer already running!");
}

/**
* Completely stops and cancels the timer. NOT EQUIVALENT TO A PAUSE.
* Starting after this state will restart the timer.
*
* @throws NullPointerException there is no current timer or {@link TimerTask} running
* @throws IOException if I/O error is encountered when writing to Timer.txt
* @see #start()
* @see #pause()
* @see #restart()
*/
public void stop() throws NullPointerException, IOException {
//stop the current TimerTask
if (currentTimer != null) {
currentTimer.cancel();
isTimerRunning = false;
}
//set Timer.txt to nothing [will cause start() to restart timer if called next]
set(0);
}

/**
* Pauses the current timer state. Starting from this after this state will
* continue the timer from where the timer left off.
* Does nothing if there is no active timer
*
* @see #start()
* @see #stop()
* @see #restart()
*/
public void pause() {
//cancel TimerTask to stop countdown. Keeps value, so start() can resume from it
if (currentTimer != null) {
currentTimer.cancel();
isTimerRunning = false;
}
}

/**
* Set the <b>current</b> value of the timer
*
* @param seconds a second value (non-negative)
* @throws IOException if any I/O error occurs when accessing Timer.txt
*/
public void set(int seconds) throws IOException {
if (seconds < 0) {
throw new IllegalArgumentException("Cannot set a timer for negative seconds!");
}
Writer fileWriter = new FileWriter(timerTXT);
fileWriter.write(String.valueOf(seconds));
fileWriter.close();
}

/**
* Gets the <b>current</b> value of the timer.
* @return time left in seconds.
* @throws FileNotFoundException timer.txt cannot be found
* @throws NoSuchElementException timer.txt contains an unexpected character
*/
public int get() throws FileNotFoundException, NoSuchElementException {
//open Timer.txt and scan first integer
File timerSettings = new File(timerTXT);
Scanner scanner = new Scanner(timerSettings);
return scanner.nextInt();
}
}

/**
* Begins countdown from current value in the <code>Timer.txt</code> file. {@link TimerTask} to be scheduled in {@link Timer#schedule(TimerTask, long, long)} for the countdown timer system.
* @implNote Be careful with scheduling this function too fast/often, it doesn't implement any
* file locks and is not atomic as of the current version.
* @implNote Does not use system clock (or at least efficiently. Timer will start to lag behind if more system resources are used or application is active with other tasks
*/
class CountdownTimer extends TimerTask {

public void run() {

/*todo, implement lock*/

CustomTimer timer = CustomTimer.getInstance();

try {
int currentTime = timer.get();
if (currentTime <= 0) {
System.out.println("Timer Finished!\n>");
CustomTimer.getInstance().isTimerRunning = false;
timer.getCurrentTimer().cancel();
} else {
timer.set(currentTime - 1);
}
} catch (FileNotFoundException exception) {
exception.printStackTrace();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
8 changes: 8 additions & 0 deletions src/InvalidDataException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* A file read in by the application contains invalid data, typically due to outside tampering.
*/
public class InvalidDataException extends Exception {
public InvalidDataException(String message) {
super(message);
}
}
67 changes: 64 additions & 3 deletions src/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.*;
import java.util.stream.Stream;

public class Main {
Expand Down Expand Up @@ -37,6 +35,13 @@ private static void CLIParse(String input) {
System.out.println("TOURNAMENT NUMBER");
System.out.println(" 'number set', update current tourney number");

System.out.println("TIMER");
System.out.println(" 'timer set', determine timer length");
System.out.println(" 'timer start', start the timer");
System.out.println(" 'timer pause', pause the timer");
System.out.println(" 'timer stop', ends the timer early");
System.out.println(" 'timer restart', restarts the timer");

System.out.println("TEAMS");
System.out.println(" 'team add', add a team");
System.out.println(" 'team add-noimg', add a team (no image)");
Expand Down Expand Up @@ -122,6 +127,58 @@ private static void CLIParse(String input) {
}
break;

case "timer set":
CustomTimer timer = CustomTimer.getInstance();
try {
System.out.println("How many seconds would you like to set the timer for");
int setTime = scanner.nextInt();
timer.pause(); //pause timer to prevent ticking
timer.setInitialTimerLength(setTime); //set new initial
timer.set(setTime); //set currently displayed timer to not confuse user
} catch (IOException exception) {
exception.printStackTrace();
} catch (InputMismatchException exception) {
exception.printStackTrace(); //thrown by scanner
} catch (NoSuchElementException exception) {
exception.printStackTrace();
} catch (IllegalArgumentException exception) {
System.out.println("Cannot set initial timer value to 0!");
}
break;

case "timer start":
try {
CustomTimer.getInstance().start();
} catch (IOException exception) {
exception.printStackTrace();
} catch (NoSuchElementException exception) {
exception.printStackTrace();
} catch (InvalidDataException e) {
e.printStackTrace();
}
break;

case "timer stop":
try {
CustomTimer.getInstance().stop();
System.out.println("Stopped!");
} catch (IOException exception) {
exception.printStackTrace();
}
break;

case "timer pause":
CustomTimer.getInstance().pause();
break;

case "timer restart": case "timer reset":
try {
CustomTimer.getInstance().restart();
} catch (IOException | InvalidDataException exception) {
exception.printStackTrace();
}
break;

default:
System.out.println("Unknown Input. Type 'help' for commands");
}
Expand Down Expand Up @@ -207,6 +264,10 @@ public static void verifyContent() throws IOException {
//check teams.txt
File teams = new File(inputPath + "teams.txt");
teams.createNewFile(); // if file already exists will do nothing

//check TimerLength.txt
File timerLength = new File(inputPath + "TimerLength.txt");
teams.createNewFile(); // if file already exists will do nothing
}

/**
Expand Down

0 comments on commit f95d808

Please sign in to comment.