I'm working on the mooc.fi assignments and I'm really stuck at the clock assignment. I don't want a solution, but advice on how to reach my solution as I'm still learning and really need to figure out how to work through this. If you're unfamiliar with mooc, the current section is on how to work with objects within objects.
It consists of three classes, the main creates the clock and the boundedcounter is what makes the clock tick. I get it to print onto the screen, but when a custom starting input is entered it prints that first, then resets the value back to zero. Can someone point me in the right direction please? Sorry for the basic question, still trying to learn this java language!
Main
public class Main {
public static void main(String[] args) {
Clock clock = new Clock(23, 59, 50);
int i = 0;
while( i < 20) {
System.out.println( clock );
clock.tick();
i++;
}
}
}
Clock
public class Clock {
private BoundedCounter hours;
private BoundedCounter minutes;
private BoundedCounter seconds;
public Clock(int hoursAtBeginning, int minutesAtBeginning, int secondsAtBeginning) {
// the counters that represent hours, minutes and seconds are created and set to have the correct initial values
this.hours = new BoundedCounter(hoursAtBeginning);
this.hours.setValue(hoursAtBeginning);
this.minutes = new BoundedCounter(minutesAtBeginning);
this.minutes.setValue(minutesAtBeginning);
this.seconds = new BoundedCounter(secondsAtBeginning);
this.seconds.setValue(secondsAtBeginning);
}
public void tick() { //increases the time
this.seconds.next();
if (this.seconds.getValue() == 0) {
this.minutes.next();
if (this.minutes.getValue() == 0) {
this.hours.next();
}
}
}
public String toString() {
// returns the string representation
return hours + ":" + minutes + ":" + seconds;
}
}
BoundedCounter
public class BoundedCounter {
private int value;
private int upperLimit;
public BoundedCounter(int upperLimit) {
// write code here
this.value = 0;
this.upperLimit = upperLimit;
}
public void next() {
// write code here
if (value < upperLimit) {
value++;
} else {
value = 0;
}
}
public int getValue() {
return value;
}
public void setValue(int value) {
if (value >= 0 && value <= upperLimit) {
this.value = value;
}
}
#Override
public String toString() {
if (value < 10) {
return "0" + value;
}
return "" + value;
}
}
You're passing the current time to BoundedCounter.
this.hours = new BoundedCounter(hoursAtBeginning);
this.minutes = new BoundedCounter(minutesAtBeginning);
this.seconds = new BoundedCounter(secondsAtBeginning);
But BoundedCounter is expecting the upper bound, not the current time.
public BoundedCounter(int upperLimit) {
Since whatever time you pass it is considered the upper limit, the next tick rolls over to 0:0:0.
BoundedCounter.next() sets value to 0 if value is not less than upperLimit. Since the Clock constructor always initializes all BoundedCounter value values to be equal to upperLimit, the first call to Clock.tick() will cause all BoundedCounter objects to set their value values to 0.
Besides the corrections:
this.hours = new BounedCounter(int upperLimit);
this.minutes = new BounedCounter(int upperLimit);
this.seconds = new BounedCounter(int upperLimit);
the method public void tick() has to be composed as shown below, otherwise when the value of the minutes is 00 while the value of the seconds is 01, 02, ...59, the value of the hours will be increased with every second, and the result will be 01:00:01, 2:00:02, 3:00:03, etc.
public void tick() {
this.seconds.next();
if (this.seconds.getValue() == 0) {
this.minutes.next();
}
if (this.minutes.getValue() == 0 && this.seconds.getValue() == 0) {
this.hours.next();
}
}
So I did search and read abut every factorial listing on this site but I cannot seem to figure out what is wrong with my code. Iv tried multiple different return methods but they all keep failing. Any ideas?
public class RecursivelyPrintFactorial {
public static void printFactorial(int factCounter, int factValue) {
int nextCounter = 0;
int nextValue = 0;
if (factCounter == 0) // Base case: 0! = 1
System.out.println("1");
}
else if (factCounter == 1) // Base case: print 1 and result
System.out.println(factCounter + " = " + factValue);
}
else { // Recursive case
System.out.print(factCounter + " * ");
nextCounter = factCounter - 1;
nextValue = nextCounter * factValue;
}
return factValue * printFactorial(factValue - factCounter);
}
}
public static void main (String [] args) {
int userVal = 0;
userVal = 5;
System.out.print(userVal + "! = ");
printFactorial(userVal, userVal);
}
}
I have a feeling I have the equation incorrect in my return but iv tried every combination I can think of. Its driving me insane. Every one reports an error. Any ideas?
return factValue * printFactorial(factValue - factCounter);
I assume that you should be using the "next" values instead of these.
Edit: Also note that the function takes two parameters and is void. Returning factValue times void doesn't make sense.
The program simulates a customer service operation in places, e.g., call center, bank, store, airport, with customers being served by tellers. The customers arrive at random time and wait in a line until a teller is available to serve them. The waiting line is implemented with queue data structure. However im getting two minor errors 1.) my enqueue method is not applicable for the argument and 2.)cannot cast from int to customers. Here is the code. The error is in the bolded lines towards the end
import java.util.Random;
class MyQueue<E> {
private int maxSize;
private int[] queArray;
private int front;
private int rear;
public MyQueue(int s) // constructor
{
maxSize = s+1; // array is 1 cell larger
queArray = new int[maxSize]; // than requested
front = 0;
rear = -1;
}
public void enqueue(int j) // put item at rear of queue
{
if(rear == maxSize-1)
rear = -1;
queArray[++rear] = j;
}
public int dequeue() // take item from front of queue
{
int temp = queArray[front++];
if(front == maxSize)
front = 0;
return temp;
}
public int peek() // peek at front of queue
{
return queArray[front];
}
public boolean isEmpty() // true if queue is empty
{
return ( rear+1==front || (front+maxSize-1==rear) );
}
public boolean isFull() // true if queue is full
{
return ( rear+2==front || (front+maxSize-2==rear) );
}
public int size() // (assumes queue not empty)
{
if(rear >= front) // contiguous sequence
return rear-front+1;
else // broken sequence
return (maxSize-front) + (rear+1);
}
}
class Customer { int arrive; // Time point that the customer arrived. int processTime; // Time duration that the customer will need to be served.
/**
* Default constructor
*/
public Customer() {
arrive = 0;
processTime = 0;
}
/**
* Set the arrival time point of the customer.
*
* #param Time
* point
*/
public Customer(int arrivalTime) {
arrive = arrivalTime;
// We set the processing time as a random integer between 1 and 3.
processTime = (int) (Math.random() * 3 + 1);
}
/**
* #return the arrival time point of the customer.
*/
public int getArrivalTime() {
return arrive;
}
/**
* #return the processing time of the customer.
*/
public int getProcTime() {
return processTime;
}
}
public class Simulator { /** * The main method of the class. * * #param args * Command line arguments. */ public static void main(String[] args) {
if (args.length != 3) {
System.out.println("usage: " + Simulator.class.getSimpleName()
+ " qCapacity simHours customPerHour");
System.out.println("Example: " + Simulator.class.getSimpleName()
+ " 10 1 30");
System.exit(-1);
}
// maximum size of queue
int qCapacity = Integer.parseInt(args[0]);
// number of simulation hours
int simHours = Integer.parseInt(args[1]);
// average number of customers per hour
int custPerHour = Integer.parseInt(args[2]);
// Run simulation
simulation(qCapacity, simHours, custPerHour);
}
private static void simulation(int qCapacity, int simHours, int custPerHour) {
// Constant
final int MIN_PER_HR = 60;
// A queue that will hold and manage objects of type Customer.
MyQueue<Customer> line = new MyQueue<Customer>(qCapacity);
// For how many cycles should the simulation run. We assume that each
// cycle takes one minute.
int cycleLimit = MIN_PER_HR * simHours;
// The average number of customers can arrive per minute
float custPerMin = ((float) custPerHour) / MIN_PER_HR;
// The number of customers that were turned away because the line
// (queue)
// was full at the time they arrived.
int turnAways = 0;
// Number of customers that arrived.
int customers = 0;
// Number of customers that were served.
int served = 0;
// Total number of customers that entered the line (queue).
int sumLine = 0;
// Waiting time until the next customer is served.
int waitTime = 0;
// Total time that all the customers waited in the line.
int lineWait = 0;
// Simulation
for (int cycle = 0; cycle < cycleLimit; cycle++) {
float j = custPerMin;
while (j > 0) {
if (newCustomer(j)) {
if (line.isFull()) {
turnAways++;
} else {
customers++;
Customer customer = new Customer(cycle);
**line.enqueue(customer);**
}
}
j = j - 1;
}
if (waitTime <= 0 && !line.isEmpty())
{
**Customer customer = (Customer) line.dequeue();**
waitTime = customer.getProcTime();
lineWait += cycle - customer.getArrivalTime();
served++;
}
if (waitTime > 0) {
waitTime--;
}
sumLine += line.size();
}
// Print the simulation results.
if (customers > 0) {
System.out.println("\nCustomers accepted: " + customers);
System.out.println(" Customers served: " + served);
System.out.println(" Customers waiting: " + line.size());
System.out.println(" Turnaways: " + turnAways);
System.out.println("Average queue size: " + (float) sumLine
/ cycleLimit);
System.out.println(" Average wait time: " + (float) lineWait
/ served + " minutes");
} else {
System.out.println("No customers!");
}
}
private static boolean newCustomer(float j) {
if(j > 1)
return true;
else
return (j > Math.random() );
}
It looks like your problem is with these two methods:
public void enqueue(int j) // put item at rear of queue
{
if(rear == maxSize-1)
rear = -1;
queArray[++rear] = j;
}
public int dequeue() // take item from front of queue
{
int temp = queArray[front++];
if(front == maxSize)
front = 0;
return temp;
}
If you had intended on the Queue to hold anything but integers, then you'll need to change the argument type / return type to reflect that.
**line.enqueue(customer);**
// 1.) my enqueue method is not applicable for the argument
Your enqueue method takes an int argmuent, yet you're trying to pass a Customer type to it. Maybe you want something like this: line.enqueue(customer.getSomething());. I can't really tell from your code.
**Customer customer = (Customer) line.dequeue();**
// 2.)cannot cast from int to customers
(Customer) line.dequeue();. Here you're casting Customer to int-line.dequeue()
Your dequque method return am int so basically you're saying that a Customer equals and int, which is impossible unless Customer isint, which it isn't
You want this:
Customer customer = new Customer(line.dequeue)
// Customer constructor takes an int value
I used the following code in my class with the intention of doing one round of recursion (specifically creating an object within an object of the same type). Well, that one round of recursion is now like 200 rounds of recursion... So that messes a lot of stuff up. The following code is where I call the recursion:
//Find Solute
try{if(iterations == 0){ //RECONDITION::: iterations is equal to zero at start of program and is static!
remaining = Whitespace.removePreceding(remaining);
String unused = remaining.substring(0);
InterpretInput solute = new InterpretInput(remaining);
solute.begin();
solute.fixSoluteAmount();
soluteAmount = solute.getSolventAmount();
isSolution = true;
++iterations;
}}catch(Exception ex){
}
finally{
System.out.println("Debugging point D");
findNumber();
fixSolventAmount();
fixSoluteAmount();
}
You'll find "Debugging point D" above, this is printed a ton of times so it apparently is going after the recursion with a bunch of objects, and the rest of the code is screwed up because of this. I just need someone experienced to point out how this is flawed as one iteration of recursion.
If you need the entire class, I'll also copy / paste that bellow, but it's almost 200 lines... so yeah... (I know I shouldn't make classes that long but this object needed a lot of stuff in it).
import java.util.ArrayList;
public class InterpretInput {
/**
* #param remaining - The string that was input, what's left to analyze
*/
/** Variables */
private String remaining; //The string input by the user, containing what's left to analyze
private static int iterations = 0;
//Solvent Info
private double solventAmount; //The amount of the solvent expressed as final in MOLES
private M solventAmountMeas; //The measurement used in solventAmount
private double solventConc; //The concentration of the solvent
private M solventConcMeas; //The measurement used in solventConc
private E[] solventCompound; //The compound of the solvent
private E[] water = {E.H, E.H, E.O};
//Solute Info
private double soluteAmount; //The amount of solute in the solution
//Type of Data
private boolean isElement = false; //Determines if the information input is only an element
private boolean hasAmount = false; //Determines if the information input has an amount of solvent
private boolean isSolution = false; //determines if the information input is a solution
private int identificationNumber;
/** Constructor */
public InterpretInput (String remain){
remaining = remain;
}
/** Mutator Methods
* #throws Exception */
public void begin() throws Exception{
//Find Measurement
FindMeasurements measureObject = new FindMeasurements(remaining);
while (measureObject.exists() == true){
measureObject.determineNumber();
measureObject.determineMeasurement();
double solventAmountTemp = measureObject.getAmount();
M solventAmountMeasTemp = measureObject.getMeasurement();
if( (solventAmountMeasTemp.getType()) == 3 ){
isSolution = true;
solventConc = solventAmountTemp;
solventConcMeas = solventAmountMeasTemp;
}else{
hasAmount = true;
solventAmount = solventAmountTemp;
solventAmountMeas = solventAmountMeasTemp;
}
remaining = measureObject.getRemaining();
}
//Find Compound
FindCompound comp = new FindCompound(remaining);
comp.getCompound();
solventCompound = comp.getValue();
remaining = comp.getRemaining();
if (solventCompound.length == 1)
isElement = true;
//Find Solute
try{if(iterations == 0){
remaining = Whitespace.removePreceding(remaining);
String unused = remaining.substring(0);
InterpretInput solute = new InterpretInput(remaining);
solute.begin();
solute.fixSoluteAmount();
soluteAmount = solute.getSolventAmount();
isSolution = true;
++iterations;
}}catch(Exception ex){
}
finally{
System.out.println("Debugging point D");
findNumber();
fixSolventAmount();
fixSoluteAmount();
}
}
public void fixSoluteAmount() throws Exception {
fixSolventAmount();
}
public void fixSolventAmount() throws Exception {
switch (identificationNumber){ //VIEW findNumber TO SEE INDEX OF THESE CASES
case 1:{
//In this situation, there would be nothing to change to begin with
break;
}
case 2:{
//In this situation, there would be nothing to change to begin with
break;
}
case 3:{
solventAmount *= solventAmountMeas.ofBase();
switch (solventAmountMeas.getType()){
case 1:{ //volume
if (!solventCompound.equals(water))
throw new Exception();
else{
solventAmount *= 1000; //Convert 1000g for every 1L
double molarMass = 0;
for (E e : solventCompound)
molarMass += e.atomicMass();
solventAmount /= molarMass; //convert to moles
}
}
case 2:{ //mass
double molarMass = 0;
for (E e : solventCompound)
molarMass += e.atomicMass();
solventAmount /= molarMass; //convert to moles
}
}
}
case 4:{
if(solventAmountMeas.equals(M.m)){
throw new Exception(); //I AM TAKING OUT THIS FEATURE, IT WILL BE TOO DIFFICULT TO IMPLEMENT
//BASICALLY, YOU CANNOT USE MOLALITY IN THIS PROGRAM ANYMORE
}
}
case 5:{
if(solventAmountMeas.equals(M.m))
throw new Exception(); //I AM TAKING OUT THIS FEATURE, IT WILL BE TOO DIFFICULT TO IMPLEMENT
//BASICALLY, YOU CANNOT USE MOLALITY IN THIS PROGRAM ANYMORE
double molarMass = 0;
for (E e : solventCompound)
molarMass += e.atomicMass();
solventAmount /= molarMass; //convert to moles
}
}
}
public void findNumber(){
/**
* 1 = Element
* 2 = Compound
* 3 = measured amount of compound
* 4 = specific concentration of solution
* 5 = Measured amount of specific concentration of solution
* */
if(isElement==true)
identificationNumber = 1;
else if(isSolution == false && hasAmount == false)
identificationNumber = 2;
else if(isSolution == false && hasAmount == true)
identificationNumber = 3;
else if(isSolution == true && hasAmount == false)
identificationNumber = 4;
else
identificationNumber = 5;
}
/** Accessory Methods */
public double getSolventAmount(){
return solventAmount;
}
public double getSoluteAmount(){
return soluteAmount;
}
public double getConcentration(){
return solventConc;
}
public E[] returnCompound(){
return solventCompound;
}
}
Your Begin function appears to call itself prior to incrementing the iterations variable. This will cause an infinite recursion. See my HERE notes in the code below.
//Find Solute
try{if(iterations == 0){
remaining = Whitespace.removePreceding(remaining);
String unused = remaining.substring(0);
InterpretInput solute = new InterpretInput(remaining);
// HERE - calls itself again, prior to incrementing
// iterations variable
solute.begin();
solute.fixSoluteAmount();
soluteAmount = solute.getSolventAmount();
isSolution = true;
// HERE - iterations is incremented, but too late
++iterations;
}}catch(Exception ex){
}
To resolve the recursion issue, you should increment iterations prior to the begin call.
The code here is pretty messy; it's a little difficult to figure out what the goal is. What are you trying to do with the string in InterpretInput, and why does it take such a complex solution (recursively built objects) as opposed to a loop or even just a recursive method?
Beyond that, however, it doesn't appear that there should be any way for your recursion to break. The only valid way for it to do so is if iterations != 0, which is never true because the ONLY time iterations is ever incremented is after the recursive call. Thus, I think the only reason the program terminates at all is that you overflow the stack, but the exception is caught by the empty catch block. Try printing something out in that block; I bet that's where the code is going even if you don't expect it to.
In one of my java applications, there is a field where the user is supposed to enter a time. I know, I can simply keep it as a normal JTextField and at the time of validation I can check the value and so on...
But I think there is a better way than that..
For example, if this is a matter with a date, then JXDatePicker (which comes with swingx) can be used very handily. Eventually, the users selection is guaranteed to be a date.
Likewise, there must be a better way for time selection too. Will someone share your knowledge if you know a better solution.
Any thought is appreciated. Thank you!
you could use a JFormattedTextField have a look at the example below, this will create a JFormattedTextField which will accept only numbers and put them in the form XXhXXminXXs then add a ActionListener to the JFormattedTextField and in that attempt to parse to a valid time object when ENTER for now I only show the use of the JFormattedTextField and the ActionListener I didn't attempt to convert or any converting and parsing for valid time:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.text.MaskFormatter;
public class FormattedTextFieldExample {
public FormattedTextFieldExample() {
initComponents();
}
private void initComponents() {
JFrame frame = new JFrame("JFormattedTextField Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MaskFormatter mask = null;
try {
mask = new MaskFormatter("##h##min##s");//the # is for numeric values
mask.setPlaceholderCharacter('#');
} catch (ParseException e) {
e.printStackTrace();
}
//
// Create a formatted text field that accept a valid time.
//
final JFormattedTextField timeField = new JFormattedTextField(mask);
//Add ActionListener for when enter is pressed
timeField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
Object source = ae.getSource();
if (source == timeField) {
//parse to a valid time here
System.out.println(timeField.getText());
}
}
});
frame.add(timeField);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new FormattedTextFieldExample();
}
});
}
}
Multiple JSpinner instances allowing to select hours, minutes and seconds
Use a JFormattedTextField with some improvements to provide immediate user feedback (for example color the background red as soon as the input becomes invalid)
I did a prototype of a TimeField some time ago, it still needs a little work, but the concept is pretty basic.
It basically presents two JTextFields, one for the hour and one for the minutes and makes them appear to be a single field.
Through the use of some DocumentFilters, it restricts the users input.
I did this some time ago and it needs work, but the basic idea is there...if you have problems, you will need to try and figure it out yourself ;)
/**
*
* #author MadProgrammer
*/
public class TimeField extends javax.swing.JPanel {
// The time of day...
public enum TimeOfDay {
AM,
PM
}
private HourDocumentFilter hourDocumentFilter;
private MinuteDocumentFilter minDocumentFilter;
private HourKeyHandler hourKeyHandler;
private MinuteKeyHandler minuteKeyHandler;
private HourFocusHandler hourFocusHandler;
private MinuteFocusHandler minuteFocusHandler;
private boolean use24HourClock;
private ActionHandler actionHandler;
/**
* Creates new form TimeField
*/
public TimeField() {
initComponents();
pnlFields.setBorder(new CompoundBorder(UIManager.getBorder("TextField.border"),new EmptyBorder(0, 2, 0, 2)));
set24HourClock(false);
setTime(new Date());
fldHour.addKeyListener(new HourKeyHandler());
}
#Override
public void addNotify() {
super.addNotify();
// Add all the required functionality to make this thing work...
((AbstractDocument) fldHour.getDocument()).setDocumentFilter(getHourDocumentFilter());
((AbstractDocument) fldMin.getDocument()).setDocumentFilter(getMinuteDocumentFilter());
fldHour.addFocusListener(getHourFocusHandler());
fldMin.addFocusListener(getMinuteFocusHandler());
fldHour.addKeyListener(getHourKeyHandler());
fldMin.addKeyListener(getMinuteKeyHandler());
fldHour.addActionListener(getActionHandler());
fldMin.addActionListener(getActionHandler());
cmbTimeOfDay.addActionListener(getActionHandler());
}
#Override
public void removeNotify() {
// Clean up our listeners...
((AbstractDocument) fldHour.getDocument()).setDocumentFilter(null);
((AbstractDocument) fldMin.getDocument()).setDocumentFilter(null);
fldHour.removeFocusListener(getHourFocusHandler());
fldMin.removeFocusListener(getMinuteFocusHandler());
fldHour.removeKeyListener(getHourKeyHandler());
fldMin.removeKeyListener(getMinuteKeyHandler());
fldHour.removeActionListener(getActionHandler());
fldMin.removeActionListener(getActionHandler());
cmbTimeOfDay.removeActionListener(getActionHandler());
super.removeNotify();
}
/**
* Adds an action listener to the component. Actions are fired when the user
* presses the enter key
*
* #param listener
*/
public void addActionListener(ActionListener listener) {
listenerList.add(ActionListener.class, listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(ActionListener.class, listener);
}
/**
* Returns the field that is acting as the hour editor
*
* #return
*/
public JTextField getHourEditor() {
return fldHour;
}
/**
* Returns the field that is acting as the minute editor
*
* #return
*/
public JTextField getMinuteEditor() {
return fldMin;
}
/**
* Returns the combo box that provides the time of day selection
*
* #return
*/
public JComboBox getTimeOfDayEditor() {
return cmbTimeOfDay;
}
/**
* Returns the internal action handler. This handler monitors actions on the
* individual components and merges them into one.
*
* #return
*/
protected ActionHandler getActionHandler() {
if (actionHandler == null) {
actionHandler = new ActionHandler();
}
return actionHandler;
}
/**
* Returns the hour key listener
*
* #return
*/
protected HourKeyHandler getHourKeyHandler() {
if (hourKeyHandler == null) {
hourKeyHandler = new HourKeyHandler();
}
return hourKeyHandler;
}
/**
* Returns the minute key listener
*
* #return
*/
protected MinuteKeyHandler getMinuteKeyHandler() {
if (minuteKeyHandler == null) {
minuteKeyHandler = new MinuteKeyHandler();
}
return minuteKeyHandler;
}
/**
* Returns the document filter used to filter the hour field
*
* #return
*/
protected HourDocumentFilter getHourDocumentFilter() {
if (hourDocumentFilter == null) {
hourDocumentFilter = new HourDocumentFilter();
}
return hourDocumentFilter;
}
/**
* Returns the document filter user to filter the minute field
*
* #return
*/
protected MinuteDocumentFilter getMinuteDocumentFilter() {
if (minDocumentFilter == null) {
minDocumentFilter = new MinuteDocumentFilter();
}
return minDocumentFilter;
}
/**
* Returns the focus listener used to monitor the hour field
*
* #return
*/
protected HourFocusHandler getHourFocusHandler() {
if (hourFocusHandler == null) {
hourFocusHandler = new HourFocusHandler();
}
return hourFocusHandler;
}
/**
* Used the focus listener used to monitor the minute field
*
* #return
*/
protected MinuteFocusHandler getMinuteFocusHandler() {
if (minuteFocusHandler == null) {
minuteFocusHandler = new MinuteFocusHandler();
}
return minuteFocusHandler;
}
/**
* Sets the time based on the supplied date
*
* #param date
*/
public void setTime(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int hour = cal.get(Calendar.HOUR);
int min = cal.get(Calendar.MINUTE);
int dayPart = cal.get(Calendar.AM_PM);
TimeOfDay timeOfDay = TimeOfDay.AM;
switch (dayPart) {
case Calendar.PM:
timeOfDay = TimeOfDay.PM;
break;
}
setTime(hour, min, timeOfDay);
}
/**
* Sets the time based on a 24 hour clock. The field does not need to be in 24
* hour mode to use this method, the method will automatically correct the
* hour appropriately.
*
* #param hour
* #param min
*/
public void setTime(int hour, int min) {
hour = correctHour(hour);
min = correctMinute(min);
TimeOfDay timeOfDay = TimeOfDay.AM;
if (hour >= 12) {
timeOfDay = TimeOfDay.PM;
}
setTime(hour, min, timeOfDay);
}
/**
* Corrects the minute value to make sure it is within allowable ranges.
*
* For example, if you pass in 90 the method, it will automatically correct
* the value to 30, discard the overflow.
*
* This will not effect the hour value...although this might be worth
* consideration in the future
*
* #param min
* #return
*/
protected int correctMinute(int min) {
// Make sure the value is positive.
// If we were interested in altering the hour value as well, we wouldn't
// want to do this...
if (min < 0) {
min += (min * -2);
}
// Correct the minute value....
if (min > 59) {
// How many hours fit into this value
float part = min / 60f;
part = (float) (part - Math.floor(part)); // Get remainder
min = (int) (60 * part); // Calculate the number of minutes...
}
return min;
}
/**
* Basically, this method will attempt to correct the hour value and bring the
* it into range of a single day.
*
* We are basically going to try and figure out how many parts of the day that
* the hour falls in and make it equal to a single day...
*
* That is, if the hour is 35, it's actually 1.458... days, which is roughly 1
* day and 11 hours. We are only interested in the 11 hours, cause the date is
* irrelevant to us
*
* #param hour
* #return
*/
protected int correctHour(int hour) {
if (hour < 0) {
hour += (hour * -2);
}
if (hour > 23) {
float part = hour / 24f;
part = (float) (part - Math.floor(part));
hour = (int) (24 * part);
}
return hour;
}
/**
* Sets the time value for this field...
*
* #param hour
* #param min
* #param timeOfDay
*/
public void setTime(int hour, int min, TimeOfDay timeOfDay) {
hour = correctHour(hour);
min = correctMinute(min);
// Now that we have a correct hour value, we need to know if it will
// actually fit within the correct part of the day...
switch (timeOfDay) {
case AM:
cmbTimeOfDay.setSelectedIndex(0);
break;
case PM:
cmbTimeOfDay.setSelectedIndex(1);
break;
}
if (!is24HourClock()) {
if (hour > 12) {
hour -= 12;
}
} else {
if (hour < 12 && timeOfDay.equals(TimeOfDay.PM)) {
hour += 12;
}
}
fldHour.setText(pad(Integer.toString(hour), 2));
fldMin.setText(pad(Integer.toString(min), 2));
}
public int getHour() {
return Integer.parseInt(getHourEditor().getText());
}
public int getMinute() {
return Integer.parseInt(getMinuteEditor().getText());
}
public TimeOfDay getTimeOfDay() {
TimeOfDay tod = null;
switch (cmbTimeOfDay.getSelectedIndex()) {
case 0:
tod = TimeOfDay.AM;
break;
case 1:
tod = TimeOfDay.PM;
break;
}
return tod;
}
/**
* Sets if we should be using 24 or 12 hour clock. This basically configures
* the time of day field and the validation ranges of the various fields
*
* #param value
*/
public void set24HourClock(boolean value) {
if (value != use24HourClock) {
use24HourClock = value;
cmbTimeOfDay.setVisible(!use24HourClock);
if (cmbTimeOfDay.getSelectedIndex() == 1) {
setTime(getHour() + 12, getMinute(), getTimeOfDay());
}
invalidate();
firePropertyChange("24HourClock", !use24HourClock, value);
}
}
/**
* Returns if this is using a 24 or 12 hour clock
*
* #return
*/
public boolean is24HourClock() {
return use24HourClock;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
cmbTimeOfDay = new javax.swing.JComboBox();
pnlFields = new javax.swing.JPanel();
lblSeperator = new javax.swing.JLabel();
fldHour = new javax.swing.JTextField();
fldMin = new javax.swing.JTextField();
addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
doFocusGained(evt);
}
});
setLayout(new java.awt.GridBagLayout());
cmbTimeOfDay.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"am", "pm"}));
cmbTimeOfDay.setBorder(null);
cmbTimeOfDay.setEditor(null);
cmbTimeOfDay.setOpaque(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
add(cmbTimeOfDay, gridBagConstraints);
pnlFields.setBackground(new java.awt.Color(255, 255, 255));
pnlFields.setLayout(new java.awt.GridBagLayout());
lblSeperator.setText(":");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
pnlFields.add(lblSeperator, gridBagConstraints);
fldHour.setBorder(null);
fldHour.setColumns(2);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
pnlFields.add(fldHour, gridBagConstraints);
fldMin.setBorder(null);
fldMin.setColumns(2);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
pnlFields.add(fldMin, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
add(pnlFields, gridBagConstraints);
}// </editor-fold>
private void doFocusGained(java.awt.event.FocusEvent evt) {
fldHour.requestFocus();
}
// Variables declaration - do not modify
private javax.swing.JComboBox cmbTimeOfDay;
private javax.swing.JTextField fldHour;
private javax.swing.JTextField fldMin;
private javax.swing.JLabel lblSeperator;
private javax.swing.JPanel pnlFields;
// End of variables declaration
/**
* Moves the focus forward to the next field.
*
* This is used to provide "automatic" focus movement
*/
protected void moveFocusForward() {
if (fldHour.hasFocus()) {
fldMin.requestFocus();
} else if (fldMin.hasFocus()) {
cmbTimeOfDay.requestFocus();
}
}
/**
* Moves the focus backwards to the previous field.
*
* This is used to provide "automatic" focus movement
*/
protected void moveFocusBackward() {
if (fldMin.hasFocus()) {
fldHour.requestFocus();
} else if (cmbTimeOfDay.hasFocus()) {
fldMin.requestFocus();
}
}
/**
* Fires the action performed event to all registered listeners
*
* #param evt
*/
protected void fireActionPerformed(ActionEvent evt) {
List<ActionListener> lstListeners = Arrays.asList(listenerList.getListeners(ActionListener.class));
if (!lstListeners.isEmpty()) {
Collections.reverse(lstListeners);
for (ActionListener listener : lstListeners) {
listener.actionPerformed(evt);
}
}
}
/**
* Hour key handler, used to monitor "special" keys for the hour field.
*
* This looks for the user pressing the ":" key and the right arrow key in
* order to perform special navigation
*/
protected class HourKeyHandler extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
boolean numLock = false;
try {
// Get the state of the nums lock
numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
} catch (Exception exp) {
}
// Move focus forward if the user presses the ":"
if (e.getKeyCode() == KeyEvent.VK_SEMICOLON && e.isShiftDown()) {
moveFocusForward();
// Move focus forward if the user pressed the left arrow key
} else if ((e.getKeyCode() == KeyEvent.VK_NUMPAD6 && !numLock) || e.getKeyCode() == KeyEvent.VK_RIGHT) {
// If we are in the last edit position
if (fldHour.getCaretPosition() >= 2) {
moveFocusForward();
// Or we are in the first edit position and the field only contains a single character
} else if (fldHour.getText().trim().length() == 1 && fldHour.getCaretPosition() == 1) {
moveFocusForward();
}
}
}
}
/**
* Minute key handler, used to monitor "special" keys for the hour field.
*
* This looks for the user pressing the left arrow key in order to perform
* special navigation
*/
protected class MinuteKeyHandler extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
boolean numLock = false;
try {
numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
} catch (Exception exp) {
}
if ((e.getKeyCode() == KeyEvent.VK_NUMPAD4 && !numLock) || e.getKeyCode() == KeyEvent.VK_LEFT) {
// Only want to move backwards if we are at the first edit position
if (fldMin.getCaretPosition() == 0) {
moveFocusBackward();
}
}
}
}
/**
* Hour field focus handler. This watches for focus lost events a
* automatically pads the field with a leading "0" if the field is only 1
* character in length
*/
protected class HourFocusHandler extends FocusAdapter {
#Override
public void focusLost(FocusEvent e) {
String text = fldHour.getText();
if (text.length() < 2) {
text = pad(text, 2);
fldHour.setText(text);
}
}
}
/**
* Minute field focus handler, watches for focus lost events and automatically
* adds a "0" to the end of the field if it is only 1 character in length
*/
protected class MinuteFocusHandler extends FocusAdapter {
#Override
public void focusLost(FocusEvent e) {
String text = fldMin.getText();
if (text.length() < 2) {
fldMin.setText(text + "0");
}
}
}
/**
* The document filter used to filter the hour field.
*/
protected class HourDocumentFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
System.out.println("insert: offset = " + offset + "; text = " + text);
super.insertString(fb, offset, text, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
try {
boolean isAcceptable = false;
boolean passOnFocus = false;
int strLength = text.length();
// We convert the value here to make sure it's a number...
int value = Integer.parseInt(text);
// If the length of the string been replaced is only 1 character
if (strLength == 1) {
// If we are at the start of the editing position
if (offset == 0) {
// What clock type are we using...
if (!is24HourClock()) {
// only accept 0 or 1...
if (value <= 1) {
isAcceptable = true;
}
} else if (value <= 2) {
isAcceptable = true;
}
// If we are at the second editing position
} else if (offset == 1) {
// Get the preceeding value, should be 0, 1 or 2
String upperPart = fb.getDocument().getText(0, 1);
// Convert the value to an int
int upperValue = Integer.parseInt(upperPart);
// The acceptable range of values for the given position
int lowerRange = 0;
int upperRange = 9;
// Which clock are we using
if (is24HourClock()) {
// If the first value is 2, we can only accept values from 0-3 (20-23)
if (upperValue == 2) {
upperRange = 3;
}
} else {
// 12 hour clock
// If the first value is 1, we can only accept values from 0-2 (10-12)
if (upperValue == 1) {
upperRange = 2;
}
}
// Is the value within accpetable range...
if (value >= lowerRange && value <= upperRange) {
isAcceptable = true;
}
// Pass on focus (only if the value is accepted)
passOnFocus = true;
}
} else {
// First, we need to trim the value down to a maximum of 2 characters
// Need to know at what offest...
// 2 - offset..
// offset == 0, length = 2 - offset = 2
// offset == 1, length = 2 - offset = 1
strLength = 2 - offset;
String timeText = text.substring(offset, strLength);
value = Integer.parseInt(timeText);
// this will only work if we are using a 24 hour clock
if (value >= 0 && value <= 23) {
while (value > 12 && is24HourClock()) {
value -= 12;
}
// Pad out the text if required
text = pad(value, 2);
isAcceptable = true;
}
}
if (isAcceptable) {
super.replace(fb, offset, length, text, attrs);
if (passOnFocus) {
moveFocusForward();
}
}
} catch (NumberFormatException exp) {
}
}
}
/**
* The document filter used to filter the minute field.
*/
protected class MinuteDocumentFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
System.out.println("insert: offset = " + offset + "; text = " + text);
super.insertString(fb, offset, text, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
try {
boolean isAcceptable = false;
boolean passOnFocus = false;
// How long is the text been added
int strLength = text.length();
// Convert the value to an integer now and save us the hassel
int value = Integer.parseInt(text);
// If the length is only 1, probably a new character has been added
if (strLength == 1) {
// The valid range of values we can accept
int upperRange = 9;
int lowerRange = 0;
if (offset == 0) {
// If we are at the first edit position, we can only accept values
// from 0-5 (50 minutes that is)
upperRange = 5;
} else if (offset == 1) {
// Second edit position...
// Every thing is valid here...
// We want to pass on focus if the clock is in 12 hour mode
passOnFocus = !is24HourClock();
}
// Is the value acceptable..
if (value >= lowerRange && value <= upperRange) {
isAcceptable = true;
}
} else {
// Basically, we are going to trim the value down to at max 2 characters
// Need to know at what offest...
// 2 - offset..
// offset == 0, length = 2 - offset = 2
// offset == 1, length = 2 - offset = 1
strLength = 2 - offset;
String timeText = text.substring(offset, strLength);
value = Integer.parseInt(timeText);
if (value >= 0 && value <= 59) {
// Pad out the value as required
text = pad(value, 2);
isAcceptable = true;
}
}
if (isAcceptable) {
super.replace(fb, offset, length, text, attrs);
if (passOnFocus) {
moveFocusForward();
}
}
} catch (NumberFormatException exp) {
}
}
}
/**
* This is a simple "pass" on action handler...
*/
protected class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
ActionEvent evt = new ActionEvent(TimeField.this, e.getID(), e.getActionCommand(), e.getModifiers());
fireActionPerformed(evt);
}
}
public static String pad(long lValue, int iMinLength) {
return pad(Long.toString(lValue), 2);
}
public static String pad(int iValue, int iMinLength) {
return pad(Integer.toString(iValue), iMinLength);
}
public static String pad(String sValue, int iMinLength) {
StringBuilder sb = new StringBuilder(iMinLength);
sb.append(sValue);
while (sb.length() < iMinLength) {
sb.insert(0, "0");
}
return sb.toString();
}
}
why should someone chose this bulky implementation over above given DavidKroukamp's simple solution. Also, this has some bugs when manually entering numbers. I prefer DavidKroukamp's way..
Simple answer, validation. JFormattedTextField does not valid the values, but only limits the fact that you can enter digits, you could just as easily enter 99:99 into a JFormattedTextField. The intention of this implementation is to provide real time validation while providing simple data entry requirements
I don't see any reasons for multiplay JSpinner, or JFormattedTextField, this doens't make me any sence, those concepts aren't user-friendly, have look at:
use Java Calendar
use simple JSpinner with SpinnerDateModel