I've been programming with java for about 4 - 5 months now, And I have nothing to complain about. It's great. While i'm still in my learning stages, I've come pretty far, for the amount of time, i've put into it.
Ok, I've made a Calculator class. Normally, I work very neatly with Polymorphism (OO) and try to make relevant classes as possible.
Only this time, I run into problems with the buttons of the Calculator, when I separate the Calculator class into separated classes..
I.E. : What I want to do is, make an Calculator class, a ButtonHandler class, and a ActionListener class and to not write so much Operants I also want a ScriptEngineManager class (js).
But when ever I separate the Calculator java class into these classes, The buttons won't react and I get:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at calc_758D6.NumButtonActionListener.actionPerformed(NumButtonActionListener.java:29)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6527)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
at java.awt.Component.processEvent(Component.java:6292)
at java.awt.Container.processEvent(Container.java:2234)
at java.awt.Component.dispatchEventImpl(Component.java:4883)
at java.awt.Container.dispatchEventImpl(Container.java:2292)
at java.awt.Component.dispatchEvent(Component.java:4705)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
at java.awt.Container.dispatchEventImpl(Container.java:2278)
at java.awt.Window.dispatchEventImpl(Window.java:2739)
at java.awt.Component.dispatchEvent(Component.java:4705)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
at java.awt.EventQueue.access$400(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.awt.EventQueue$3.run(EventQueue.java:691)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:719)
at java.awt.EventQueue$4.run(EventQueue.java:717)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
But I find this strange because I had used 'implements ActionListener' and also did I use 'extends Calculator'
so the object should be linked correctly, to work properly, right?
If someone could pls tell me how to separate this Calculator class into different classes, that would be great.
This is my code:
package calculator;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Calculator extends JApplet {
// ;which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.
public static final long serialVersionUID = 1;
// boolean A flag whether the text in the antwoordCalculatieEntryfield is the result of a calculation
private boolean antwoordCalculatieEntryfield;
//Container The container of our app
private Container con;
//JPanel The 'root' panel
private JPanel panel;
//JTextField The field in which the calculations are created
private JTextField entryField;
//double Memory
private double defaultMemory = 0;
//new
private Menu editDropDownMenu;
private Menu viewDropDownMenu;
private Menu helpDropDownMenu;
private MenuBar menuBalk;
private MenuItem optie1, optie2, optie3;
private MenuItem viewOptie1, viewOptie2;
private MenuItem helpOptie;
/**
* This method is called when the app is an applet
* This will prepare for the app being used as an in-browser applet
*/
public void init() {
this.con = getContentPane();
createGUI();
}
/**
* This method is called when the app is started from the jar file
* This will prepare the app for being used as a standalone application
*/
public void go() {
// Create a frame and set the container
JFrame frame = new JFrame();
menuBalk = new MenuBar();
editDropDownMenu = new Menu ("Edit");
viewDropDownMenu = new Menu ("View");
helpDropDownMenu = new Menu ("Help");
this.con = frame.getContentPane();
// Build the GUI
createGUI();
frame.setLocation(400,400);
frame.setResizable(false);
frame.setMenuBar(menuBalk);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 250);
frame.setVisible(true);
}
/**
* Create the GUI
*/
private void createGUI() {
panel = new JPanel(new BorderLayout());
entryField = new JTextField("0.");
panel.add(entryField, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new BorderLayout(10,0));
// Build the different panels
buttonPanel.add(buildNorthPanel(), BorderLayout.NORTH);
buttonPanel.add(buildWestPanel(), BorderLayout.WEST);
buttonPanel.add(buildNumPanel(), BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.CENTER);
con.add(panel);
/**
* De menubalk componenten realiseren en benoemen.
*/
optie1 = new MenuItem(" Copy ");
optie2 = new MenuItem(" Paste ");
optie3 = new MenuItem(" Quit ");
viewOptie1 = new MenuItem("Default");
viewOptie2 = new MenuItem("Scientific");
helpOptie = new MenuItem("Info");
/**
* Toevoegen van edit/view/help componenten aan de menus.
*/
editDropDownMenu.add(optie1);
editDropDownMenu.add(optie2);
editDropDownMenu.addSeparator();
editDropDownMenu.add(optie3);
viewDropDownMenu.add(viewOptie1);
viewDropDownMenu.add(viewOptie2);
helpDropDownMenu.add(helpOptie);
/**
* aan het menubalkje de edit/view/help dropdown opties toevoegen.
*/
menuBalk.add(editDropDownMenu);
menuBalk.add(viewDropDownMenu);
menuBalk.add(helpDropDownMenu);
/**
* Build the northPanel
*
* @return JPanel The created panel
*/
private JPanel buildNorthPanel() {
JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
// We have a placeholder on the top right
JPanel placeHolder = new JPanel();
placeHolder.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
placeHolder.setBorder(BorderFactory.createLoweredBevelBorder());
p.add(placeHolder);
createNorthButton("Backspace", p).addActionListener(new BackspaceActionListener());
createNorthButton("CE", p).addActionListener(new CEActionListener());
createNorthButton("C", p).addActionListener(new CActionListener());
return p;
}
/**
* Build the westPanel
*
* @return JPanel The created panel
*/
private JPanel buildWestPanel() {
JPanel p = new JPanel(new GridLayout(4, 1, 5, 5));
p.setBorder(new EmptyBorder(0, 3, 3, 0));
createWestButton("MC", p);
createWestButton("MR", p);
createWestButton("MS", p);
createWestButton("M+", p);
return p;
}
/**
* Build the numPanel
*
* @return JPanel The created panel
*/
private JPanel buildNumPanel() {
JPanel p = new JPanel(new GridLayout(4, 5, 5, 5));
p.setBorder(new EmptyBorder(0, 0, 4, 4));
createNumButton("7", p);
createNumButton("8", p);
createNumButton("9", p);
createNumButton("/", p);
createNumButton("sqrt", p);
createNumButton("4", p);
createNumButton("5", p);
createNumButton("6", p);
createNumButton("*", p);
createNumButton("%", p);
createNumButton("1", p);
createNumButton("2", p);
createNumButton("3", p);
createNumButton("-", p);
createNumButton("1/x", p);
createNumButton("0", p);
createNumButton("+/-", p);
createNumButton(",", p);
createNumButton("+", p);
createNumButton("=", p);
return p;
}
/**
* Calculate the result from a string containing a calculation
*/
public double calcResult(String text) {
// We use the build-in JavaScript engine to calculate the result
// this saves us a lot of weird conversions and complex calculations
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("JavaScript");
// Do some pre processing to allow javascript to do something with it
text = text.replace("sqrt(", "Math.sqrt(");
try {
return (double) engine.eval(text);
} catch (ScriptException e) {
JOptionPane.showMessageDialog(null, "Er was een probleem met het evalueren van uw input", "foutmelding", JOptionPane.ERROR_MESSAGE);
}
return 0.0;
}
/**
* This will run when the application is run from the jar file
*
* @param args Arguments
*/
public static void main(String[] args) {
Calculator app = new Calculator();
app.go();
}
/**
* Create a button
*
* @param String Contents
* @param JPanel The panel to add
* @return JButton The created button
*/
private JButton createButton(String contents, JPanel p) {
JButton b = new JButton(contents);
b.setMargin(new Insets(0,0,0,0));
p.add(b);
return b;
}
/**
* Create a button for the NumPanel
*
* @param String Contents
* @param JPanel The panel to add
* @return JButton The created button
*/
private JButton createNumButton(String contents, JPanel p) {
JButton b = createButton(contents, p);
b.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
b.addActionListener(new NumButtonActionListener());
return b;
}
/**
* Create a button for the WestPanel
*
* @param String Contents
* @param JPanel The panel to add
* @return JButton The created button
*/
private JButton createWestButton(String contents, JPanel p) {
JButton b = createButton(contents, p);
b.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
b.addActionListener(new MemoryActionListener());
return b;
}
/**
* Create a button for the NorthPanel
*
* @param String Contents
* @param JPanel The panel to add
* @return JButton The created button
*/
private JButton createNorthButton(String contents, JPanel p) {
JButton b = createButton(contents, p);
b.setPreferredSize(new Dimension(100, BUTTON_HEIGHT));
return b;
}
/**
* Check if string b is a number
*
* @param String The string to check
* @return boolean String is a number or not
*/
public boolean isNumber(String b) {
try {
Double.parseDouble(b);
} catch(NumberFormatException ex) {
return false;
}
return true;
}
/**
* This class handles the buttons in the 'numPanel'
*/
class NumButtonActionListener implements ActionListener {
/**
* Responds to a click on the button
*
* @param ActionEvent The event object
*/
public void actionPerformed(ActionEvent ev) {
JButton source = (JButton) ev.getSource();
String sourceText = source.getText();
String origText = calcField.getText();
String textToAdd = "";
// If the text equals the default text, clear it
if(origText.equals(DEFAULT_TEXT)) {
calcField.setText("");
}
// If you just calculated something and wish to use the result
// we don't want to clear the text for you, the text should only
// be cleared when you enter a new number
if (fieldIsResult && isNumber(sourceText)) {
calcField.setText("");
fieldIsResult = false;
} else {
fieldIsResult = false;
}
// Some buttons have special uses
if(sourceText.equals("=")) { // Calculate the result
calcField.setText(""+calcResult(calcField.getText()));
fieldIsResult = true;
} else if(sourceText.equals("sqrt")) { // Square root needs a '('
textToAdd = "sqrt(";
} else if(sourceText.equals("+/-") && origText.length() > 0) { // Toggle negative/non-negative number
char firstChar = origText.charAt(0);
if(firstChar == '-') {
calcField.setText(origText.substring(1, origText.length()));
} else {
calcField.setText("-"+origText);
}
} else { // Just append the button text
textToAdd = sourceText;
}
if( ! textToAdd.equals("")) { // If there is something that needs to be appended
calcField.setText(calcField.getText() + textToAdd);
}
}
}
/**
* This class handles the backspace button
*/
class BackspaceActionListener implements ActionListener {
/**
* Responds to a click on the button
*
* @param ActionEvent The event object
*/
public void actionPerformed(ActionEvent ev) {
String newText = calcField.getText();
// If there are characters
if(newText.length() > 0) {
// Remove the last one
newText = newText.substring(0, newText.length() - 1);
}
calcField.setText(newText);
}
}
/**
* This class handles the C button
*/
class CActionListener implements ActionListener {
/**
* Responds to a click on the button
*
* @param ActionEvent The event object
*/
public void actionPerformed(ActionEvent ev) {
// C will simply clear the field
calcField.setText("");
}
}
/**
* This class handles the CE button
*/
class CEActionListener implements ActionListener {
/**
* Responds to a click on the button
*
* @param ActionEvent The event object
*/
public void actionPerformed(ActionEvent ev) {
// C will simply clear the field
String newText = calcField.getText().replaceAll("[0-9]+$", "");
calcField.setText(newText);
}
}
/**
* This class handles all the memory actions
*/
class MemoryActionListener implements ActionListener {
/**
* Respons to a click on the button
*
* @param ActionEvent The event object
*/
public void actionPerformed(ActionEvent ev) {
JButton source = (JButton) ev.getSource();
String sourceText = source.getText();
if(sourceText.equals("MC")) {
memory = 0.0; // Clear memory
} else if (sourceText.equals("MR")) {
calcField.setText(""+memory);
fieldIsResult = true;
} else if (sourceText.equals("MS") && fieldIsResult) {
memory = Double.parseDouble(calcField.getText());
} else if (sourceText.equals("M+") && fieldIsResult) {
memory = calcResult(memory+"+"+calcField.getText());
}
}
}
}
And this is how for instance the class 'NumButtonActionListener' looks like, when I separate it.
public class NumButtonActionListener extends Calculator implements ActionListener {
// boolean A flag whether the text in the antwoordCalculatieEntryfield is the result of a calculation
private boolean antwoordCalculatieEntryfield;
//JTextField The field in which the calculations are created
private JTextField entryField;
/**
* Responds to a click on the button
*
* @param ActionEvent The event object
*/
//line 25
public void actionPerformed(ActionEvent ev) {
JButton source = (JButton) ev.getSource();
String sourceText = source.getText();
//line 29
String origText = entryField.getText();
String textToAdd = "";
// If the text equals the default text, clear it
if(origText.equals("0.")) {
entryField.setText("");
}
// If you just calculated something and wish to use the result
// we don't want to clear the text for you, the text should only
// be cleared when you enter a new number
if (antwoordCalculatieEntryfield && isNumber(sourceText)) {
entryField.setText("");
antwoordCalculatieEntryfield = false;
} else {
antwoordCalculatieEntryfield = false;
}
// Some buttons have special uses
if(sourceText.equals("=")) { // Calculate the result
entryField.setText(""+calcResult(entryField.getText()));
antwoordCalculatieEntryfield = true;
} else if(sourceText.equals("sqrt")) { // Square root needs a '('
textToAdd = "sqrt(";
} else if(sourceText.equals("+/-") && origText.length() > 0) { // Toggle negative/non-negative number
char firstChar = origText.charAt(0);
if(firstChar == '-') {
entryField.setText(origText.substring(1, origText.length()));
} else {
entryField.setText("-"+origText);
}
} else { // Just append the button text
textToAdd = sourceText;
}
if( ! textToAdd.equals("")) { // If there is something that needs to be appended
entryField.setText(entryField.getText() + textToAdd);
}
}
/**
* Check if string b is a number
*
* @param b The string to check
* @return boolean String is a number or not
*/
public boolean isNumber(String b) {
try {
Double.parseDouble(b);
} catch(NumberFormatException ex) {
return false;
}
return true;
}
/**
* Calculate the result from a string containing a calculation
*/
public double calcResult(String text) {
// We use the build-in JavaScript engine to calculate the result
// this saves us a lot of weird conversions and complex calculations
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("JavaScript");
// Do some pre processing to allow javascript to do something with it
text = text.replace("sqrt(", "Math.sqrt(");
try {
return (double) engine.eval(text);
} catch (ScriptException e) {
JOptionPane.showMessageDialog(null, "Er was een probleem met het evalueren van uw input", "foutmelding", JOptionPane.ERROR_MESSAGE);
}
return 0.0;
}
}