/**
* Nap 0.2
* Copyright 2007 Zach Scrivena
* 2007-05-03
* zachscrivena@gmail.com
*
* Pauses for a specified duration, and displays a countdown.
*
* TERMS & CONDITIONS:
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
import java.io.InputStreamReader;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Nap pauses for a specified duration, and displays a countdown.
*/
public class Nap
{
/**
* PROGRAM PARAMETERS
*/
/* Program title */
private static final String programTitle =
"Nap 0.2 Copyright 2007 Zach Scrivena 2007-05-03";
/* Width of console display; used for erasing displayed output */
private static final int consoleWidth = 80;
/* Refresh interval in milliseconds */
private static final int refreshIntervalMs = 150;
/* Verbosity level; default is 2 */
private static int verbosity = 2;
/* Allow user to abort countdown */
private static boolean checkUserAbort = false;
/* Pause before exiting program */
private static boolean pauseBeforeExit = false;
/* User abort keys (ENTER) */
private static final String userAbortKeys = "\n\r";
/* Nap duration as specified on the command-line */
private static Hmss duration = null;
/**
* Main entry point for program.
*/
public static void main(final String args[]) throws Exception
{
/* process command-line arguments */
processArguments(args);
/* Nap duration in milliseconds */
final long durationMs = hmssToMs(Nap.duration);
/* backspaces to the first column of the console output */
StringBuffer backspaces = new StringBuffer();
for (int i = 0; i < consoleWidth; i++) backspaces.append('\b');
/* print header */
if (Nap.verbosity > 0)
{
StringBuffer header = new StringBuffer();
header.append("\n");
if (Nap.checkUserAbort)
header.append("Press ENTER to abort countdown...\n\n");
if (Nap.verbosity == 1)
{
header.append("Nap for " + Nap.duration + "...");
}
else if (Nap.verbosity == 4)
{
header.append(" Nap duration" +
"\n HH:mm:ss.SSS" +
"\n " + Nap.duration +
"\n" +
"\n Elapsed Countdown" +
"\n HH:mm:ss.SSS HH:mm:ss.SSS" +
"\n");
}
System.out.print(header);
System.out.flush();
}
/* display countdown and time elapsed, in real-time */
Hmss elapsedHmss, countdownHmss;
long elapsedurationMs, countdownMs;
/* start time, in milliseconds since the epoch */
final long startMs = System.currentTimeMillis();
/* buffered reader to get user input, if necessary */
InputStreamReader userInput = null;
if (Nap.checkUserAbort)
userInput = new InputStreamReader(System.in);
/* indicates if user has aborted the countdown */
boolean abortNow = false;
while (true)
{
/* refresh time counters */
elapsedurationMs = System.currentTimeMillis() - startMs;
elapsedHmss = msToHmss(elapsedurationMs);
countdownMs = durationMs - elapsedurationMs;
countdownHmss = msToHmss(countdownMs);
/* time is up! */
if (elapsedurationMs > durationMs)
break;
/* update display */
if (Nap.verbosity == 2)
{
System.out.print(backspaces + "Nap for " + countdownHmss + "... ");
System.out.flush();
}
else if (Nap.verbosity == 3)
{
System.out.print(backspaces + "Nap for " + countdownHmss + " (" + elapsedHmss + " elapsed)... ");
System.out.flush();
}
else if (Nap.verbosity == 4)
{
System.out.print(backspaces + " " + elapsedHmss + " " + countdownHmss + " ");
System.out.flush();
}
/* check if user has hit ENTER to abort countdown */
if (Nap.checkUserAbort)
{
try
{
while (userInput.ready())
{
if (Nap.userAbortKeys.contains("" + ((char) userInput.read())))
{
abortNow = true;
break;
}
}
}
catch (Exception e)
{
/* do nothing */
}
}
/* check if user has aborted the countdown */
if (abortNow)
break;
/* pause the thread for a while (to prevent excessive CPU usage) */
Thread.sleep(refreshIntervalMs);
}
if (!abortNow)
{
/* print the final time */
if (Nap.verbosity == 1)
{
System.out.print("\n");
}
else if (Nap.verbosity == 2)
{
System.out.print(backspaces + "Nap for 00:00:00.000... \n");
}
else if (Nap.verbosity == 3)
{
System.out.print(backspaces + "Nap for 00:00:00.000 (" + Nap.duration + " elapsed)... \n");
}
else if (Nap.verbosity == 4)
{
System.out.print(backspaces + " " + Nap.duration + " 00:00:00.000 \n" );
}
}
if (Nap.pauseBeforeExit)
{
if (verbosity > 0)
{
System.out.print("\nPress ENTER to continue...");
System.out.flush();
}
(new InputStreamReader(System.in)).read();
}
/* successful termination */
if (verbosity > 0)
System.out.print("\n");
System.exit(0);
}
/**
* Convenience method for printing to standard output.
*
* @params o object to be printed.
*/
/*
private static void p(
final Object o)
{
System.out.print(o + "");
System.out.flush();
}
*/
/**
* Convert the specified time duration in milliseconds to a Hmss object.
*
* @param ms duration in milliseconds to be converted
* @return Hmss representation
*/
private static Hmss msToHmss(
long ms)
{
/* return value */
Hmss h = new Hmss();
h.HH = ms / (1000 * 60 * 60);
ms %= (1000 * 60 * 60);
h.mm = ms / (1000 * 60);
ms %= (1000 * 60);
h.ss = ms / (1000);
ms %= (1000);
h.SSS = ms;
return h;
}
/**
* Convert the duration represented by the specified Hmss object
* to milliseconds.
*
* @param h Hmss object to be converted.
* @return millisecond representation of the specified Hmss object
*/
private static long hmssToMs(
final Hmss h)
{
/* return value (number of milliseconds) */
long ms = 0;
ms += (h.SSS);
ms += (1000 * h.ss);
ms += (1000 * 60 * h.mm);
ms += (1000 * 60 * 60 * h.HH);
return ms;
}
/**
* Parses a specified string into a Hmss object.
*
* @param s string to be parsed
* @return Hmss object corresponding to the specified string; null on failure
*/
private static Hmss parseStringToHmss(
final String s)
{
/* return value */
final Hmss h = new Hmss();
h.HH = 0;
h.mm = 0;
h.ss = 0;
h.SSS = 0;
/* Parse using the format HH:mm:ss.SSS */
final Matcher m = Pattern.compile(
"(?:(?:([0-9]*)\\:)?([0-9]*)\\:)?([0-9]*)(?:\\.([0-9]*))?").matcher(s);
if (m.matches())
{
String HH = m.group(1);
String mm = m.group(2);
String ss = m.group(3);
String SSS = m.group(4);
/* Parse substrings */
if ((HH != null) && (HH.length() > 0))
h.HH = Long.parseLong(HH);
if ((mm != null) && (mm.length() > 0))
h.mm = Long.parseLong(mm);
if ((ss != null) && (ss.length() > 0))
h.ss = Long.parseLong(ss);
if ((SSS != null) && (SSS.length() > 0))
h.SSS = (long) (1000.0 * Double.parseDouble("0." + SSS));
/* Handle overflows */
h.ss += (h.SSS / 1000);
h.SSS %= 1000;
h.mm += (h.ss / 60);
h.ss %= 60;
h.HH += (h.mm / 60);
h.mm %= 60;
return h;
}
/* unable to parse specified string */
return null;
}
/**
* Process command-line arguments.
*
* @param args Array of command-line argument strings
*/
private static void processArguments(
final String args[])
{
/* error message */
String err = null;
CheckArguments:
do
{
/* run one iteration */
if (args.length == 0)
{
/* print usage help */
printUsage();
System.exit(0);
break CheckArguments;
}
else if (args.length < 1)
{
err = "Insufficient arguments.";
break CheckArguments;
}
/* Nap duration */
String duration = args[args.length - 1].trim();
Nap.duration = parseStringToHmss(duration);
if (Nap.duration == null)
{
err = "Unable to parse the specified duration \"" + duration + "\".";
break CheckArguments;
}
/* process switches */
for (int i = 0; i < args.length - 1; i++)
{
final String sw = args[i].trim();
if ("-0".equals(sw) || "-s".equals(sw))
{
Nap.verbosity = 0;
}
else if ("-1".equals(sw))
{
Nap.verbosity = 1;
}
else if ("-2".equals(sw))
{
Nap.verbosity = 2;
}
else if ("-3".equals(sw))
{
Nap.verbosity = 3;
}
else if ("-4".equals(sw) || "-v".equals(sw))
{
Nap.verbosity = 4;
}
else if ("-u".equals(sw))
{
Nap.checkUserAbort = true;
}
else if ("-p".equals(sw))
{
Nap.pauseBeforeExit = true;
}
else
{
err ="Invalid switch " + sw + ".";
break CheckArguments;
}
}
}
while (false);
/* Invalid command-line arguments encountered */
if (err != null)
{
System.err.print("\n" + Nap.programTitle + "\n\nERROR: " + err +
"\nTo display help, run Nap without any command-line arguments.\n");
System.exit(1);
}
}
/**
* Class representing the HH, mm, ss, and SSS corresponding to a time duration.
*/
static class Hmss
{
long HH; // hours
long mm; // minutes
long ss; // seconds
long SSS; // fractions of a second, i.e. "0.SSS second"
/**
* Return string representation of "HH:mm:ss.SSS".
*
* @return string representation in the form HH:mm:ss.SSS
*/
public String toString()
{
return String.format("%02d:%02d:%02d.%03d", HH, mm, ss, SSS);
}
}
/**
* Prints out usage syntax, notes, and comments.
*/
private static void printUsage()
{
/* RULER 00000000011111111112222222222333333333344444444445555555555666666666677777777778 */
/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
System.out.print(
"\n" + Nap.programTitle + "\n" +
"\nNap pauses for a specified duration, and displays a countdown." +
"\n" +
"\nUSAGE: java -jar Nap.jar <switches> [duration]" +
"\n" +
"\n <Switches>:" +
"\n -[0|1|2|3|4] verbosity level: -0 is least verbose; -4 is most verbose" +
"\n (default verbosity level is 2)" +
"\n -s silent mode; equivalent to -0" +
"\n -v verbose mode; equivalent to -4" +
"\n -u allow user to abort countdown" +
"\n -p pause before exiting" +
"\n" +
"\n [duration]:" +
"\n The nap duration, in the form HH:mm:ss.SSS" +
"\n" +
"\nNOTES:" +
"\n" +
"\n 1. Nap is very flexible when parsing the specified duration. For example, two" +
"\n minutes can be given as \"00:02:00.000\" or \"2:00\" or \"2:\" or even \"120\"." +
"\n" +
"\nEXAMPLES:" +
"\n" +
"\n 1. Nap for 24 hours, at verbosity level 3:" +
"\n java -jar Nap.jar -3 24:00:00" +
"\n 2. Nap for 2.5 seconds, in silent mode:" +
"\n java -jar Nap.jar -s 2.5" +
"\n 3. Nap for 1 minute, at verbosity level 4, and allow user to abort countdown:" +
"\n java -jar Nap.jar -4 -u 1:00" +
"\n 4. Nap for 10 seconds, and pause before exiting:" +
"\n java -jar Nap.jar -p 10" +
"\n\n");
}
}
/**
* BUILD NOTES:
*
* Compile:
* javac -Xlint Nap.java
*
* Package into JAR executable:
* jar cvfm Nap.jar manifest.txt Nap*.class
*/