I have a counter x that I want to invoke in two separate ActionListeners. When I try to make x into final, I can't increment using x++;. I tried to make x within the nest, but then I can't use the same value in the other ActionListener. Code is as follows:
buttonIn.addActionListener(new ActionListener() {
String reportDate = "";
int x = 0;
public void actionPerformed(ActionEvent e)
{
DateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm aa");
Date time = new GregorianCalendar().getTime();
reportDate = df.format(time);
String confirm = name.getText() + " has checked in at " + reportDate;
timeLabel.setText(confirm);
timeLabel.setVisible(true);
String action = "Time In";
reportData[x][0] = name.getText();
reportData[x][1] = "Time In";
reportData[x][2] = reportDate;
x++;
System.out.println(x);
}
});
buttonOut.addActionListener(new ActionListener() {
String reportDate = "";
public void actionPerformed(ActionEvent e)
{
DateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm aa");
Date time = new GregorianCalendar().getTime();
reportDate = df.format(time);
String confirm = name.getText() + " has checked out at " + reportDate;
timeLabel.setText(confirm);
timeLabel.setVisible(true);
reportData[x][0] = name.getText();
reportData[x][1] = "Time Out";
reportData[x][2] = reportDate;
x++;
}
});
One simple option is to use AtomicInteger instead - then the variable can be final, but you can still increment the wrapped value. So:
final AtomicInteger counter = new AtomicInteger(0);
buttonIn.addActionListener(new ActionListener() {
// Within here, you can use counter.get and counter.incrementAndGet
});
buttonOut.addActionListener(new ActionListener() {
// Within here, you can use counter.get and counter.incrementAndGet
});
I'd also strongly consider extracting that code into a separate class though - almost all the code is the same, so you should be able to remove the duplication by parameterizing the differences. So you'd end up with something like:
AtomicInteger counter = new AtomicInteger(0);
buttonIn.addActionListener(new ReportListener(
counter, reportData, "%s has checked in at %s", "Time In"));
buttonOut.addActionListener(new ReportListener(
counter, reportData, "%s has checked out at %s", "Time Out"));
(Where ReportListener is the new class implementing ActionListener.)
Additionally:
I strongly suspect you want to use HH rather than hh in your SimpleDateFormat
Consider which time zone and locale you want to use in your SimpleDateFormat, and specify them explicitly for clarity
To get the current time, just call new Date() rather than creating a calendar and extracting the date from it
There's no obvious reason for having reportDate as an instance variable
For testability, I'd encourage you to use some sort of Clock interface, with an implementation provided by dependency injection, so you can fake time appropriately
Consider using Joda Time for all date/time work; it's much cleaner than the built-in date/time API
Related
Alright I got something like this:
public void menu() {
final Form menu = new Form("Menu");
menu.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
Button confirm = new Button("Confirm");
Container creditCardContainer = new Container(new GridLayout(1, 3));
final TextField num1 = new TextField(3);
final TextField num2 = new TextField(3);
final TextField num3 = new TextField(3);
num1.setConstraint(TextArea.NUMERIC);
num2.setConstraint(TextArea.NUMERIC);
num3.setConstraint(TextArea.NUMERIC);
creditCardContainer.addComponent(num1);
creditCardContainer.addComponent(num2);
creditCardContainer.addComponent(num3);
Validator v = new Validator();
v.addConstraint(num1, new LengthConstraint(2));
v.addConstraint(num2, new LengthConstraint(2));
v.addConstraint(num3, new LengthConstraint(4));
automoveToNext(num1, num2);
automoveToNext(num2, num3);
menu.add(creditCardContainer);
menu.add(confirm);
v.addSubmitButtons(confirm);
menu.show();
confirm.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
String getdate = num1.getText() + "/" + num2.getText() + "/" + num3.getText();
System.out.println(getdate);
new StateMachine("/theme");
}
});
}
}
private void automoveToNext(final TextField current, final TextField next) {
current.addDataChangedListener(new DataChangedListener() {
public void dataChanged(int type, int index) {
if(current.getText().length() == 3) {
Display.getInstance().stopEditing(current);
String val = current.getText();
current.setText(val.substring(0, 2));
next.setText(val.substring(2));
Display.getInstance().editString(next, 3, current.getConstraint(), next.getText());
}
}
});
}
Notice that addDataChangeListener is deprecated so I had to change it to addDataChangedListener instead.
I think there is something wrong in my code, because when I run it in the Codename One Simulator, it still allow me to type letters, even with the code below:
num1.setConstraint(TextArea.NUMERIC);
num2.setConstraint(TextArea.NUMERIC);
num3.setConstraint(TextArea.NUMERIC);
Also when I finish typing the date, my confirm button doesn't get highlighted as it should be. Please someone help me to fix it.
Obs: My date is intended to be dd/MM/yyyy
We don't support direct field masking as native text field input doesn't handle that very well. You have 2 options I can think of:
Use Date Picker which launches a great device native UI to pick the date. Notice it's not great in the simulator but on Android/iOS it would look good.
Use 3 text fields and automatically move to the next as you type like we did for this credit card input sample: http://www.codenameone.com/blog/validation-regex-masking.html
Using
try {
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date date = format.parse("");
}catch (ParseException e) {
System.out.println("Wrong format.");
}
to check if the date is valid format.
I created method for parsing a view different date formats during data import (400 K records). My method catches ParseException and trying to parse date with next format when it's different.
Question: Is better way(and faster) to set correct date format during data import?
private static final String DMY_DASH_FORMAT = "dd-MM-yyyy";
private static final String DMY_DOT_FORMAT = "dd.MM.yyyy";
private static final String YMD_DASH_FORMAT = "yyyy-MM-dd";
private static final String YMD_DOT_FORMAT = "yyyy.MM.dd";
private static final String SIMPLE_YEAR_FORMAT = "yyyy";
private final List<String> dateFormats = Arrays.asList(YMD_DASH_FORMAT, DMY_DASH_FORMAT,
DMY_DOT_FORMAT, YMD_DOT_FORMAT);
private Date parseDateFromString(String date) throws ParseException {
if (date.equals("0")) {
return null;
}
if (date.length() == 4) {
SimpleDateFormat simpleDF = new SimpleDateFormat(SIMPLE_YEAR_FORMAT);
simpleDF.setLenient(false);
return new Date(simpleDF.parse(date).getTime());
}
for (String format : dateFormats) {
SimpleDateFormat simpleDF = new SimpleDateFormat(format);
try {
return new Date(simpleDF.parse(date).getTime());
} catch (ParseException exception) {
}
}
throw new ParseException("Unknown date format", 0);
}
If you're running single threaded, an obvious improvement is to create the SimpleDateFormat objects only once. In a multithreaded situation using ThreadLocal<SimpleDateFormat> would be required.
Also fix your exception handling. It looks like it's written by someone who shouldn't be trusted to import any data.
For a similar problem statememt , i had used time4j library in the past. Here is an example. This uses the following dependencies given below as well
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import net.time4j.PlainDate;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.MultiFormatParser;
import net.time4j.format.expert.ParseLog;
import net.time4j.format.expert.PatternType;
public class MultiDateParser {
static final MultiFormatParser<PlainDate> MULTI_FORMAT_PARSER;
static {
ChronoFormatter<PlainDate> style1 = ChronoFormatter.ofDatePattern("dd-MM-yyyy", PatternType.CLDR,
Locale.GERMAN);
ChronoFormatter<PlainDate> style2 = ChronoFormatter.ofDatePattern("dd.MM.yyyy", PatternType.CLDR, Locale.US);
ChronoFormatter<PlainDate> style3 = ChronoFormatter.ofDatePattern("yyyy-MM-dd", PatternType.CLDR, Locale.US);
ChronoFormatter<PlainDate> style4 = ChronoFormatter.ofDatePattern("yyyy.MM.dd", PatternType.CLDR, Locale.US);
//this is not supported
//ChronoFormatter<PlainDate> style5 = ChronoFormatter.ofDatePattern("yyyy", PatternType.CLDR, Locale.US);
MULTI_FORMAT_PARSER = MultiFormatParser.of(style1, style2, style3, style4);
}
public List<PlainDate> parse() throws ParseException {
String[] input = { "11-09-2001", "09.11.2001", "2011-11-01", "2011.11.01", "2012" };
List<PlainDate> dates = new ArrayList<>();
ParseLog plog = new ParseLog();
for (String s : input) {
plog.reset(); // initialization
PlainDate date = MULTI_FORMAT_PARSER.parse(s, plog);
if (date == null || plog.isError()) {
System.out.println("Wrong entry found: " + s + " at position " + dates.size() + ", error-message="
+ plog.getErrorMessage());
} else {
dates.add(date);
}
}
System.out.println(dates);
return dates;
}
public static void main(String[] args) throws ParseException {
MultiDateParser mdp = new MultiDateParser();
mdp.parse();
}
}
<dependency>
<groupId>net.time4j</groupId>
<artifactId>time4j-core</artifactId>
<version>4.19</version>
</dependency>
<dependency>
<groupId>net.time4j</groupId>
<artifactId>time4j-misc</artifactId>
<version>4.19</version>
</dependency>
The case yyyy will have to be handled differently as it is not a date. May be similar logic that you have used (length ==4) is a choice.
The above code returns , you can check a quick perf run to see if this scales for the 400k records you have.
Wrong entry found: 2012 at position 4, error-message=Not matched by any format: 2012
[2001-09-11, 2001-11-09, 2011-11-01, 2011-11-01]
Talking about 400K records, it might be reasonable to do some "bare hands" optimization here.
For example: if your incoming string has a "-" on position 5, then you know that the only (potentially) matching format would be "yyyy-MM-dd". If it is "."; you know that it is the other format that starts yyyy.
So, if you really want to optimize, you could fetch that character and see what it is. Could save 3 attempts of parsing with the wrong format!
Beyond that: I am not sure if sure if "dd" means that your other dates start with "01" ... or if "1.1.2016" would be possible, too. If all your dates always use two digits for dd/mm; then you can repeat that game - as you would fetch on position 3 - to choose between "dd...." and "dd-....".
Of course; there is one disadvantage - if you follow that idea, you are very much "hard-coding" the expected formats into your code; so adding other formats will become harder. On the other hand; you would save a lot.
Finally: the other thing that might greatly speed up things would be to use stream operations for reading/parsing that information; because then you could look into parallel streams, and simply exploit the ability of modern hardware to process 4, 8, 16 dates in parallel.
I am trying to understand the need of using ThreadLocal. Lot of people mention ThreadLocal should be used to provide a Per-Thread SimpleDateFormat, but they do not mention how will a mangled SimpleDateFormat will be seen if ThreadLocal is not used. I try the following code, seems it is just fine, I don't see a mangled SimpleDateFormat.
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadLocalTest {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
private static final Date TODAY = new Date();
private static final String expected = "07/09/2016";
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
#Override
public void run() {
for (int j = 0; j < 1000; j++) {
String real = dateFormat.format(TODAY);
if (!real.equals(expected)) {
throw new RuntimeException("Mangled SimpleDateFormat");
}
}
}
}).start();
}
}
}
How can I produce a exception like NumberFormatException because I don't use a ThreadLocal ?
The crucial point is: the SimpleDateFormat implementation is not thread safe.
That doesn't mean that it will throw an exception.
It is worse: maybe, occasionally the shared formatter will simply give you wrong output!
You know, if "multi-threading issues" would nicely throw exceptions at you ... people would be much less afraid of them. Because we would have a direct hint that something went wrong.
Instead, things go wrong - and unnoticed.
Suggestion: enhance your test to
always format the same Date object
check that the result of formatting that Date is as expected (for example by comparing it against the result of an initial, first formatting operation)
And of course: only print mismatches, so that notice when they happen. Or better: throw your own exception on mismatch!
EDIT: turns out that the "better" way to enforce inconsistencies is to not use formatting but parsing!
Finally, to address another comment: of course, inconsistencies can only occur for objects that are shared between multiple threads. When each thread has its own format object, than there is no sharing; thus no problem.
Just run these code, you will get "java.lang.NumberFormatException". If not occur, run a few more times
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class ThreadLocalDemo1 implements Runnable {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + simpleDateFormat.toPattern());
try {
System.out.println("Formatted date is " + simpleDateFormat.parse("2013-05-24 06:02:20"));
} catch (ParseException pe) {
pe.printStackTrace();
}
System.out.println("=========================================================");
}
}
}
Date formats are not thread safe.
I think if you format the same day you can't reproduce, you should use 2 different dates or format also second and dates that have different seconds etc. Date format uses a Calendar under the hood on which it sets the date. If the first thread sets a date and start formatting the string and another thread with a different date comes and sets it on the same calendar, you will get wrong output.
Following code produces an exception/error:
final Date today = new Date();
String expectedToday = dateFormat.format(today);
Date yesterday = new Date(today.getTime() - TimeUnit.DAYS.toMillis(1));
String expectedYesterday = dateFormat.format(yesterday);
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
String format = dateFormat.format(today);
if (!expectedToday.equals(format)) {
System.out.println("error: " + format + " " + expectedToday);//Throw exception if you want
}
format = dateFormat.format(yesterday);
if (!expectedYesterday.equals(format)) {
System.out.println("error: " + format + " " + expectedYesterday);//Throw exception if you want
}
}
}
}).start();
}
One of the ways that SimpleDateFormat is not thread safe is that it has an internal calendar field, which holds a Calendar object. Pretty much the first thing that SimpleDateFormat does before actually formatting the date is call this.calendar.setTime(theDateYouPassedIn), no synchronization or locks. I'm not sure if this is the only way, but it should be fairly straightforward to inspect the code.
So, one way to get SimpleDateFormat to fail is to use dates that will produce differing output in different threads. Here is an example:
public class NotThreadSafe
{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
public static void main(String[] args) {
Date dref = new Date();
// Dates for yesterday and tomorrow
Date[] ds = new Date[] {
new Date(dref.getTime() - (24L * 60L * 60L * 1000L)),
new Date(dref.getTime() + (24L * 60L * 60L * 1000L))
};
String[] refs = new String[ds.length];
for (int i = 0; i < ds.length; ++i) {
// How the corresponding ds[i] should be formatted
refs[i] = dateFormat.format(ds[i]);
}
for (int i = 0; i < 100; i++) {
// Every even numbered thread uses ds[0] and refs[0],
// odd numbered threads use ds[1] and refs[1].
int index = (i % 2);
final Date d = ds[index];
final String ref = refs[index];
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
String s = dateFormat.format(d);
if (!ref.equals(s)) {
throw new IllegalStateException("Expected: " + ref + ", got: " + s);
}
}
}
}).start();
}
}
}
As the comments show, every even numbered thread will format yesterday's date, and odd numbered threads will use tomorrows date.
If you run this, threads will pretty much immediately start committing suicide by throwing exceptions until such time as you have only a handful left, likely all formatting the same date.
I am creating a project in which I am supposed to take date of birth of any person. So for that I have taken 3 combo boxes: date, month and year. But how I will know that the Date which is going to be inserted is valid because the number of day is different-different months and years is different.
And is there any ready made GUI component for taking dates from users?
I am designing using Swing package.
My sample code is
import java.awt.*;
import javax.swing.*;
public class Pro1 extends JFrame
{
JComboBox dd, mm, yy;
JLabel doblbl;
Container con;
public Pro1()
{
con = getContentPane();
doblbl = new JLabel("Date of Birth :-");
doblbl.setHorizontalAlignment(SwingConstants.CENTER);
doblbl.setFont(new Font("Arial",Font.BOLD,17));
doblbl.setForeground(Color.blue);
doblbl.setOpaque(true);
doblbl.setBackground(new Color(230,180,230));
dd = new JComboBox();
mm = new JComboBox();
yy = new JComboBox();
for(int i = 1; i<=31; i++)
dd.addItem("" + i);
for(int i = 1; i<=12; i++)
mm.addItem("" + i);
for(int i = 1960; i<=2014; i++)
yy.addItem("" + i);
con.setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
int i = 120;
doblbl.setBounds(30,i+=40,270,30);
int j = 120;
dd.setBounds(350,j+=40,50,30);
mm.setBounds(420,j,50,30);
yy.setBounds(480,j,70,30);
con.add(doblbl);
con.add(dd);
con.add(mm);
con.add(yy);
setSize(1500,800);
setVisible(true);
con.setBackground(new Color(125,80,140));
}
public static void main(String s[])
{
Pro1 p1 = new Pro1();
}
}
You can use SimpleDateFormat to parse or format the date as dd/MM/yyyy.
Sample code:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
String dateString = "30/02/2014"; // form date string in above format
String formattedDate = sdf.format(sdf.parse(dateString));
if (formattedDate.equals(dateString)) {
System.out.println("valid date");
} else {
System.out.println("Invalid date"); // It's an invalid date
}
Consider offering the user a JSpinner with a SpinnerDateModel, then it is easier for the user to enter, or scroll through and select, a valid date.
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class PromptForDate {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {}
SpinnerDateModel dateModel = new SpinnerDateModel(
new Date(), null, null, Calendar.DAY_OF_MONTH );
JSpinner dateSpinner = new JSpinner(dateModel);
dateSpinner.setEditor(
new JSpinner.DateEditor(dateSpinner, "dd/MM/yyyy"));
JOptionPane.showMessageDialog(
null, dateSpinner, "D.O.B.?",
JOptionPane.QUESTION_MESSAGE);
System.out.println(dateModel.getValue());
}
};
SwingUtilities.invokeLater(r);
}
}
And is there any ready made GUI component for taking dates from users?
Yes, there are several options in standard API and third-party libraries, as exposed in this answer: JSpinner + SpinnerDateModel or JCalendar or JXDatePicker. These components will also solve this other problem:
But how i will know that the date which is going to be inserted is
valid because the number of dates in different-different months and
years is different.
Off-topic
About this:
con.setLayout(null);
...
dd.setBounds(350,j+=40,50,30);
mm.setBounds(420,j,50,30);
yy.setBounds(480,j,70,30);
...
con.add(dd);
con.add(mm);
con.add(yy);
While using Null layout is perfectly possible, Swing is designed to be used along with layout managers and thus null layout is discouraged. See this topic Why is it frowned upon to use a null layout in SWING?.
I'm probably being stupid here...but I need help with this one! Basically i need to do a .contains("message") to determine if the key already contains the incoming message.
Thanks in advance!
EDIT: Just as a note, i do not want it to do anything if it already exists! Currently its not adding it to the list.
EDIT2: the date will not matter for the incoming message because the incoming message does not have the date portion.
private Map<Integer,List<String>> map = new HashMap<Integer,List<String>>();
public synchronized void addToProblemList(String incomingMessage, int storeNumber){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
String formattedDate = sdf.format(date);
if(map.get(storeNumber)==null){
map.put(storeNumber, new ArrayList<String>());
}
for(String lookForText : map.get(storeNumber)){
if(lookForText.contains(incomingMessage)){
}else if(!lookForText.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
}
}
It used to look like this, but it always added it:
public synchronized void addToProblemList(String incomingMessage, int storeNumber){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
String formattedDate = sdf.format(date);
if(map.get(storeNumber)==null){
map.put(storeNumber, new ArrayList<String>());
}
if(map.get(storeNumber).contains(incomingMessage)==true){
//Do nothing
}
if (map.get(storeNumber).contains(incomingMessage)==false){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
What you are adding to the list is a key of the store number and an empty array list,
So the first message for the store you add to the list is empty, therefore your for loop will not execute as it has no elements to iterate.
So add this
if(map.get(storeNumber)==null){
ArrayList<String> aList = new ArrayList<String>();
aList.add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
map.put(storeNumber, aList);
}
Note map.get(storeNumber).contains(incomingMessage)==true you dont need to boolean comparison here as contains() returns a boolean.
The reason this original approach of yours wouldn't have worked is doing a List.contains() means you are doing an check to see if the list contains an exact matching string which it would not have since when you have added the String it also contained "\nTime of incident: "+formattedDate+"\n... which I suspect would not have matched just incomingMessage
You have this:
for(String lookForText : map.get(storeNumber)){
if(lookForText.contains(incomingMessage)){
}else if(!lookForText.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
}
Try this instead:
List<String> messages = map.get(storeNumber);
if(!messages.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}