How would I refactor repetitive code? - java

Apologies for the poor title, but I can't really think of an informative title for this.
I'll be straight forward here:
setName(); // set the name field
validate(); // submit the form and check if form has error
setName();
setAge();
validate();
setName();
setAge();
setHeight();
validate();
setName();
setAge();
setHeight();
setGender();
validate();
// the list goes on...
I am testing a form on a website and the form should not allow any empty fields, however upon a submit button click any fields that I've entered in before gets cleared, which is why I need to set the previous fields all over again like the above code.
What would be a more elegant way to write this? I was thinking of a loop but then I could not think of the right conditions...

You can create a Runnable instance for each of the actions and put them in a list.
In Java 8+:
List<Runnable> runnables = new ArrayList<>();
runnables.add(() -> setName());
runnables.add(() -> setAge());
runnables.add(() -> setHeight());
// ... etc
For earlier versions of Java, you can use anonymous classes:
runnables.add(new Runnable() { #Override public void run() { setName(); } });
// ... etc
and then execute the Runnable instances in a sublist of runnables:
for (int i = 0; i < runnables.size(); ++i) {
for (Runnable fn : runnables.subList(0, i + 1)) {
fn.run();
}
validate();
}
Demo

I'm unsure as to whether I fully understand your question (perhaps a pastebin as to a working example would help), however perhaps you could have a boolean for each field that checks as to whether the field has been modified since the last check, and then you set field based on the state of these booleans?
The checking method would then reset all booleans to false (or whatever method actually submits the changes).

Related

Is there a way to add interface instances to an ArrayList in Java?

I found this
In which the accepted answer is almost perfect, but I would like to instead add the elements to an ArrayList. Is this possible?
I'll try to be concise: I'm creating a JavaFX application and I have an ArrayList containing all my TextFields. I used this list to add an event handler for the Action event to each field, and this handler calls a method that moves focus to the next field in the list (this way the user can press return and will navigate to the next field automatically).
In the same loop I also add an event listener to each field, so that if a field loses focus another method is called to update the form. In this way the user can navigate however they choose (return, tab, mouse click, etc.), and the form will update automatically without the need for a button click when they are finished entering data.
Now, in the update method, I determine which field triggered the event and validate the text in the field and parse a double from it. I then need to do something with that data. Here is where I want to use my ArrayList of "worker functions." I can simply create a parallel ArrayList that matches the index order of the field traversal ArrayList, and call the correct update method.
I want to do it this way because each TextField is tied to a specific member of a specific object for example - the TextField qtyOrdered would be tied to the workOrder.qtyOrdered member, and the TextField materialWidth would be tied to the masterRoll.materialWidth member. These objects would of course have their own accessors and mutators, so I wanted to use the interface to create all these methods beforehand and call the correct one based on the index of the TextField.
I want something like this (pseudocode):
ArrayList<Worker> workerMethods = new ArrayList<>();
workerMethods.add(new Worker() {
public void work(double input) {
workOrder.setQtyOrdered(input);
}
});
//Add remaining methods...
method updateForm(TextField caller)
{
get callerIndex;
// validate user input
if (valid)
workerMethods.get(callerIndex).work(input);
}
In fact, using the method I linked to, this works with arrays like so:
workerMethods[callerIndex].update(callerValue);
However, when I use the same syntax for adding to an ArrayList instead it obviously does not work.
I would like to use an ArrayList instead of an array if possible. This is because some fields in the form are removed from the traversableFields ArrayList when they are deactivated so that field traversal will skip these fields when they are hidden from view, and resume in the correct order when they are visible. For example, if the user wants to estimate the length of material on the master roll, they can click a CheckBox to show those fields.
The Action event handler for that CheckBox would then call my updateControls() method which would enable the required fields, set them to visible, and add them to the list of traversable fields. If they uncheck it, the reverse happens.
If I am using an array to store my update methods, the indexes may not match the indexes of my traversableFields List at any given time.
Edit: Added my actual code below (this is a simplified version I am using in a small test environment).
Code for creating the list of traversable TextFields:
// ArrayList of traversable TextFields for form navigation.
private ArrayList<TextField> traversableFields = new ArrayList<>();
// Add all TextFields to the ArrayList.
traversableFields.addAll(Arrays.asList(field1, field2, field3));
// Loop through the ArrayList to add handlers/listeners.
for (int i = 0; i < traversableFields.size(); i++) {
// Get the Control at index i in the ArrayList.
TextField currentControl = traversableFields.get(i);
// Add an event handler so that when the user presses return we can
// move to the next field in the ArrayList.
currentControl.addEventHandler(ActionEvent.ACTION, e ->
moveToNextField(e));
// Add a listener so that if a field loses focus we update the form.
currentControl.focusedProperty().
addListener((obs, wasFocused, isNowFocused) ->
{
if (!isNowFocused) {
updateForm(currentControl);
}
});
}
Code for creating the array of worker methods:
interface Update { public void update(double input); }
class Label1 { public void update(double input) { label1.setText(String.valueOf(input)); } }
class Label2 { public void update(double input) { label2.setText(String.valueOf(input)); } }
class Label3 { public void update(double input) { label3.setText(String.valueOf(input)); } }
Label1 l1 = new Label1();
Label2 l2 = new Label2();
Label3 l3 = new Label3();
Update[] updateFunctions = new Update[] {
new Update() { public void update(double input) { l1.update(input); } },
new Update() { public void update(double input) { l2.update(input); } },
new Update() { public void update(double input) { l3.update(input); } }
};
The snippet above is basically exactly what they did in the link I posted. As I mentioned earlier, this works. I can call updateFunctions[callerIndex].update(callerValue); and it does what I want. Here is the method called by the event handler:
// Move to the next field in the form. Called by the Action Event for each
// field in the traversableFields ArrayList.
private void moveToNextField(ActionEvent event)
{
// Get the TextField that triggered the event.
TextField caller = (TextField)event.getSource();
// Get the index of this Control from the ArrayList.
int callerIndex = traversableFields.indexOf(caller);
// If we have reached the end of the list then we move to the
// first field in the list.
if (callerIndex == traversableFields.size() - 1)
traversableFields.get(0).requestFocus();
// Otherwise move to the next field.
else
traversableFields.get(++callerIndex).requestFocus();
}
and here is the method called by the focus listener:
// Update the form. This will contain the majority of functional code
// and will be called automatically when any TextField loses focus.
private void updateForm(TextField caller)
{
// Get the index of the TextField
int callerIndex = traversableFields.indexOf(caller);
// Get the CSS id of the TextField for testing purposes.
String callerID = caller.getId();
// Get the TextField contents.
String callerText = caller.getText();
// Flag variable.
boolean fieldEmpty = callerText.equals("");
// Double for storing parsed input.
double callerValue = 0;
// If the field is not empty, ensure that it contains valid data before
// proceeding.
if (!fieldEmpty)
{
try
{
callerValue = Validation.getDouble(callerText);
updateFunctions[callerIndex].update(callerValue);
clearError(caller);
}
catch (Exception e)
{
// If the input was invalid, alert the user and move focus
// back to the offending TextField.
System.out.println("Invalid input.");
markEntryInvalid(caller);
caller.requestFocus();
}
}
// Trace statements.
System.out.println("updateForm() called by " + callerID);
System.out.println("Parsed value was " + callerValue);
}
However, I want to use an ArrayList instead of an array. But if I do this:
ArrayList<Update> updateMethods = new ArrayList<>();
updateMethods.add(new Update() { public void update(double input) { l1.update(input); } });
I get <identifier> expected error.
If I understand your question correctly, what you want to know is how to initialize an ArrayList with a given collection of values?
That would be done like this:
List<Workers> myWorkers = new ArrayList<>(
Arrays.asList(
new Worker() { ... },
new Worker() { ... }
)
);
Arrays.asList(element1, element2, ...) returns an immutable list of these elements. And the constructor of ArrayList can take a Collection to initialize the list.
Of course, you can also create the list and add single elements. The resulting list is the same:
List<Workers> myWorkers = new ArrayList<>();
myWorkers.add(new Worker() { ... });
myWorkers.add(new Worker() { ... });
In both cases, the resulting list can be modified using add and remove methods.
Two notes on the usecase, though:
First, a Map might be better suited for your usecase, because you don't need to take care about indexes:
// initialization:
Map<TextField, Worker> workers = new HashMap<TextField, Worker>();
workers.put(textfield1, worker1);
workers.put(textfield2, worker2);
// ...
void updateForm(TextField caller) {
workers.get(caller).work();
}
Second, if your Worker interface only has one method, since Java 8 you can use it as a functional interface with a closure. So the initialization would look like this:
Map<TextField, Worker> workers = new HashMap<TextField, Worker>();
workers.put(textfield1, input -> { /* do something with input here */ });
workers.put(textfield2, input -> { /* do something with input here */ });
And maybe your Worker interface is even not needed and you can just use Consumer<InputType> instead.
You can create List<Runnable> and execute the code in the current thread or in the parallel thread:
List<Runnable> workerMethods = List.of(
() -> System.out.println("worker 1"),
() -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get(0).run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get(1)).start(); // worker 2
Similarly, you can create Map<String, Runnable>:
Map<String, Runnable> workerMethods = Map.of(
"w1", () -> System.out.println("worker 1"),
"w2", () -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get("w1").run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get("w2")).start(); // worker 2
Might I suggest an entirely different approach?
Why not use the user data field of the TextField to point to your own object that can handle all of your needs.
e.g.
class FieldContext {
TextField previous;
TextField next;
Update updater;
// add whatever else you need, e.g. validation interface etc.
}
so when you get an event from the TextField you can just call:
FieldContext fieldCtx = (FieldContext)((TextField)event.getSource()).getUserData();
and then you are free to handle whatever specific event processing is needed having the context information for the TexTField.
I find a lot of people overlook the fact that you have a field for user data on the controls. I find it can simplify things quite a bit

JPanel not visible

I've been having a problem with my program that has been driving me crazy and I cannot understand why this is happening. I have a GUI that when the "Add" button is pressed, a new system listing appears (Which is a class called SystemPanel that extends JPanel and cotains system details, which is created and then put into the Frame's main panel.)
To put it shortly, when I try to add a new SystemPanel, it does not appear for whatever reason. I have code using JSch that connects to the system and verifies whether its processes are online or not, but the line of code that does this is after the creation of the SystemPanel. It is only after the code for testing the processes of the system are executed that the SystemPanel becomes visible, and I can't understand why this is the case. Here is the code for adding a new SystemPanel:
public void actionPerformed(ActionEvent e) {
//If the "Add" button is pressed
if (e.getActionCommand() == "Add") {
PopupWindow popup = new PopupWindow(this);
popup.setVisible(true);
String[] results = popup.getResults();
if (results[0] != null && results[1] != null && results[2] != null && results[3] != null && results[4] != null) {
SystemPanel newSystem = new SystemPanel(this, results[0], results[1], results[2], results[3], results[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
System.out.println("Did the stuff");
boolean[] status = SystemChecker.checkOnline(results[0], results[1], results[2], results[3]);
}
}
}
The PopupWindow is a custom JDialog that allows the user to enter the required information which is returned in a String array and is used to create a new SystemPanel. The checkOnline function grabs the user's inputs and uses them to connect to the system and determine whether the processes are working or not, and returns the results into a boolean array, true for working, false for not.
What's even weirder is that I have another part of my program that reads from an .ini file to obtain existing systems and then creates SystemPanels based on the data that it reads. Through this method, the SystemPanels are added the way I want and work perfectly for some reason, even though the code for adding the panels is hardly any different. Code:
for (int i = 0; i < systems.size(); i++) {
SystemPanel newSystem = new SystemPanel(this, systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3], systems.get(i)[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
}
for (int i = 0; i < lineNum; i++) {
boolean[] status = SystemChecker.checkOnline(systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3]);
systemsList.get(i).updateIcons(status);
}
This code grabs the details from the file and then makes the SystemPanels based on those details. Here, all of the SystemPanels are added and show up before the connection is tested, which is what I want to happen when I add one normally.
Why is it that the SystemPanel doesn't appear until the connection is tested, even though the code for displaying the SystemPanel is executed before the connection test? Any help would be greatly appreciated, thanks.
Try it of the current event queue handling, on which actionPerformed is done.
public void actionPerformed(ActionEvent e) {
EventQueue.invokeLater(() -> { ... your code here ... });
}
Also you cannot add the same component to two parents, every component object has a single parent (container).
(Java 8 notation)

Java Checkbox Action

I have a check box and when I create an Action script from the Netbeans' design, it creates a function like;
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
total=8.99f;
xc = "XCheese";
exTop++;
calculateTotal(total);
updateTextArea();
}
This works perfectly, but I want to set everything to zero when the jCheckBox1 is unchecked, if I uncheck it the way the code is now, no changes appear.
It is an sample of code. Hope it will help you.
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
if(checkBox.isSelected() ){
total=8.99f;
xc = "XCheese";
exTop++;
calculateTotal(total);
updateTextArea();
}else{
// set everything zero here.
}
}
Start by taking a look at How to Use Buttons, Check Boxes, and Radio Buttons
Basically, the ActionListener will be called when ever the check box is selected (checked) or unselected (unchecked). You need to check the state of the check box when ever the method is called.
Take a look at AbstractButton#isSelected which will tell you the (in this case) the checked state of the JCheckBox

Need to split a thread so it runs in multiple threads. Also need to figure out a synchronized issue - JAVA

This program is about showing the oldest, youngest ect person in a network.
I need to figure out how I can improve it, so I dont get the ConcurrentModificationException. I get this when I ask for displaying more of these multiple time, like asking for youngest, oldest, and make it refresh to tell me whos the current youngest.
public void randomIncreaseCoupling(int amount, double chance, double inverseChance) {
randomChangeCoupling(amount,chance,inverseChance,true);
}
public void randomDecreaseCoupling(int amount, double chance, double inverseChance) {
randomChangeCoupling(amount,chance,inverseChance,false);
This code is used in the network to randomly change the date outcome.
Also, I have this running in a Thread currently, but I need to fasten it, so I need to run each of the 'functions' to run in their own Thread.
The Class MainController is starting the Thread by:
public void startEvolution() {
if (display == null)
throw new Error("Controller not initialized before start");
evolutionThread = new NetworkEvolutionSimulator(network, display);
evolutionThread.start();
}
When I click on any button ex a button to show me the oldest in this network, it is done by:
public void startOldest() {
if (display == null)
throw new Error("Not properly initialized");
int order = display.getDistanceFor(Identifier.OLDEST);
Set<Person> oldest = network.applyPredicate(PredicateFactory.IS_OLDEST,
order);
display.displayData(Identifier.OLDEST, summarize(order, oldest));
I tried to make it like:
public void startOldest() {
if (display == null)
throw new Error("Not properly initialized");
int order = display.getDistanceFor(Identifier.OLDEST);
Set<Person> oldest = network.applyPredicate(PredicateFactory.IS_OLDEST,
order);
display.displayData(Identifier.OLDEST, summarize(order, oldest));
evolutionThread2 = new NetworkEvolutionSimulator(network, display);
evolutionThread2.start();
But this starts main thread over and over when I press the button. What I want is that this specific function and the others when I press the cercain button it has to start each of them in their own threads so I will be able to use more than one of them at a time. How shall I do this?
I can explain more if needed.
Thanks in advance.
My first post, so sorry if I didn't follow a specific rule.
You could use the synchronized keyword -
The synchronized keyword can be used to mark several types of code blocks:
Instance methods
Static methods
Code blocks inside instance methods
Code blocks inside static methods
Everywhere you're using your set oldest you could add a synchronized code block like this
synchronized(oldest) { ... }

Prepend beginning to object name and set text in java

I'm trying to pick up java quickly and looking for a way to set the text of a number of labels in my java app.
What I have is a java app that starts\stops\checks status of windows services. I have a method, which is passed an array of these service names and each of these services has a corresponding label that contains it's status. For example, DummyService1 is contained in the array and there is a label called txt_DummyService1. My method (short version) does the following
public static void Checker(String Array[])
{
//check status of DummyService1
"txt_"+DummyService.Text = "started";
}
I realize that this isn't the way that you do this, but could anybody help me out with the best way to do this?
There's no way to generate a "variable" name from a String in this manner. Yes, you might use reflection, but that already rasies questions about the quality of the design.
Instead. Place each label into a Map keyed by it's name.
private Map<String, JLabel> labelLookup = new HashMap<>(25); // Instance variable.
In you constructor (or where ever you build your UI), add each label to the Map.
/* Other UI code */
labelLookup.put("DummyService1", txt_DummyService1);
Now, when you need to do you changes, simply look up the label by it's name
// You had better have a VERY good reason for making this static...
public void checker(String services[])
{
for (String service : services) {
JLabel label = labelLookup.get(service);
if (label != null) {
label.setText("Started");
}
}
}
For example...
Actually I was looking for something more like the following
public static void Checker()
{
try
{
Object Instance = getClass().getDeclaredField("txt_DummyService").get(this);
Method m = Instance.getClass().getMethod("setText",String.class);
m.invoke(Instance,"started");
}
catch(Exception e)
{
//exception handling
}
}
You cannot manipulate variable names at runtime since these are only available to the compiler. One solution to your problem is to keep a Map<String, JLabel> (assuming you are using JLabel and not some other component) to associate a name with each JLabel. I'm sure there are several other possible solutions depending on the exact design of your code.

Categories