Java Multithreading and Reading from Serial: How to Properly Suspend and Resume? - java

I am trying to write a basic Java Swing application that works as such: an Arduino writes two CSV strings to the COM port every second, I read that data and parse it, and then display the data in a GUI. With some help from the official Arduino tutorials, I am at the point where I can read and parse the data coming in from the serial port, and display it. However, I would be able to pause and resume the GUI (while still reading from serial) using a key on the keyboard. I was able to pause the program, but cannot figure out how to resume it. I've been looking around online for a while now to no avail. If someone could point me in the right direction, I'd sincerely appreciate it. I'm rather new to threads and the Arduino, but have some basic Java experience. Here is the code below, which is broken up into three classes (one to read from serial, one for the GUI, and a main class to run everything. Thanks!
Here is the "Display" class:
//Imports
import java.awt.*;
import javax.swing.*;
import java.util.Random;
import java.lang.Thread;
import java.awt.event.*;
import java.util.ArrayList;
import java.awt.Toolkit.*;
//Class Definition
public class Display extends JPanel implements Runnable {
//Variables
private volatile Thread t;
private volatile boolean threadSuspended;
//Data Strings
private String data;
private String carData;
private String gpsData;
//Car Data
private int event;
private int engineRPM;
private int cvtRPM;
private double kph;
private int brake;
private double accel_x;
private double accel_y;
private double accel_z;
private double gyro_x;
private double gyro_y;
private double gyro_z;
//GPS Data
private String sentence;
private int time;
private String status;
private double latitude;
private String latitudeDir;
private double longitude;
private String longitudeDir;
private double knots;
private double angle;
private int date;
private double magVar;
private char magVarDir;
private String checksum;
//Constructor
public Display() {
super.setSize(800,800);
setLayout(new BorderLayout());
addKeyListener(new KeyHandler());
setFocusable(true);
start();
}
public void start() {
t = new Thread(this);
t.start();
threadSuspended = false;
}
public synchronized void stop() {
t = null;
notify();
threadSuspended = true;
}
#Override
public void run() {
Thread thisThread = Thread.currentThread();
while(t == thisThread) {
try {
Thread.sleep(1000);
synchronized(this) {
while (threadSuspended && t == thisThread) {
wait();
}
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
repaint();
save();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
kph = SerialReader.getKPH();
System.out.println(kph);
g.drawString("Speed of Car: " + kph, 35, 35);
}
//Writes values to a file and updates average values
public void save() {
//Nothing here yet
}
private class KeyHandler extends KeyAdapter {
#Override
public synchronized void keyPressed(KeyEvent ke) {
ke.consume();
int key = ke.getKeyCode();
if (key == KeyEvent.VK_SPACE) {
threadSuspended = !threadSuspended;
if (!threadSuspended) {
notify();
}
}
}
}
}
Here is the "SerialReader" class:
//Imports
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.Enumeration;
//Class definition
public class SerialReader implements SerialPortEventListener {
//Data Strings
private static String data;
private static String carData;
private static String gpsData;
//Car Data
private static int event;
private static int engineRPM;
private static int cvtRPM;
private static double kph;
private static int brake;
private static double accel_x;
private static double accel_y;
private static double accel_z;
private static double gyro_x;
private static double gyro_y;
private static double gyro_z;
//GPS Data
private static String sentence;
private static int time;
private static String status;
private static double latitude;
private static String latitudeDir;
private static double longitude;
private static String longitudeDir;
private static double knots;
private static double angle;
private static int date;
private static double magVar;
private static char magVarDir;
private static String checksum;
//Variables
SerialPort serialPort;
private static final String[] PORT_NAMES = {"/dev/tty.usbserial-A9007UX1", "/dev/ttyUSB0", "COM3"};
private BufferedReader input;
private OutputStream output;
private static final int TIME_OUT = 2000;
private static final int DATA_RATE = 9600;
//Constructor
public SerialReader() {}
//Opens port
public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
if (portId == null) {
System.out.println("Could not find COM port.");
return;
}
try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);
// set port parameters
serialPort.setSerialPortParams(DATA_RATE,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
// open the streams
input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
output = serialPort.getOutputStream();
// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
//Do nothing
}
}
//Called when port is no longer in use
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
System.out.println("Port Closed");
}
}
//Reads data from serial port
public synchronized void serialEvent(SerialPortEvent spe) {
if (spe.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
data = input.readLine();
//System.out.println(data);
if (data.charAt(0) == '$') {
gpsData = data;
parseGPSData(data);
} else {
carData = data;
parseCarData(data);
}
} catch (Exception e) {
//Do nothing
}
}
}
//Parse car data
public void parseCarData(String data) {
String[] info = data.split(",");
event = Integer.parseInt(info[0]);
engineRPM = Integer.parseInt(info[1]);
cvtRPM = Integer.parseInt(info[2]);
kph = Double.parseDouble(info[3]);
brake = Integer.parseInt(info[4]);
accel_x = Double.parseDouble(info[5]);
accel_y = Double.parseDouble(info[6]);
accel_z = Double.parseDouble(info[7]);
gyro_x = Double.parseDouble(info[8]);
gyro_y = Double.parseDouble(info[9]);
gyro_z = Double.parseDouble(info[10]);
}
//Parse GPS data
public void parseGPSData(String data) {
String[] info = data.split(",");
sentence = info[0];
int time = Integer.parseInt(info[1]);
status = info[2];
latitude = Double.parseDouble(info[3]);
latitudeDir = info[4];
longitude = Double.parseDouble(info[5]);
longitudeDir = info[6];
knots = Double.parseDouble(info[7]);
angle = Double.parseDouble(info[8]);
date = Integer.parseInt(info[9]);
magVar = Double.parseDouble(info[10]);
magVarDir = info[11].charAt(0);
checksum = info[11].substring(1);
}
//Accessor Methods
public static String getCarData() {
return carData;
}
public static String getGPSData() {
return gpsData;
}
public static int getEvent() {
return event;
}
public static int getEngineRPM() {
return engineRPM;
}
public static int getCvtRPM() {
return cvtRPM;
}
public static double getKPH() {
return kph;
}
public static int getBrake() {
return brake;
}
public static double getAccel_X() {
return accel_x;
}
public static double getAccel_Y() {
return accel_y;
}
public static double getAccel_Z() {
return accel_z;
}
public static double getGyro_X() {
return gyro_x;
}
public static double getGyro_Y() {
return gyro_y;
}
public static double getGyro_Z() {
return gyro_z;
}
public static String getSentence() {
return sentence;
}
public static int getTime() {
return time;
}
public static String getStatus() {
return status;
}
public static double getLatitude() {
return latitude;
}
public static String getLatitudeDir() {
return latitudeDir;
}
public static double getLongitude() {
return longitude;
}
public static String getLongitudeDir() {
return longitudeDir;
}
public static double getKnots() {
return knots;
}
public static double getAngle() {
return angle;
}
public static int getDate() {
return date;
}
public static double getMagVar() {
return magVar;
}
public static char getMagVarDir() {
return magVarDir;
}
public static String getChecksum() {
return checksum;
}
}
And lastly, the "Main" class:
//Imports
import java.awt.*;
import javax.swing.*;
//Class Definition
public class Main {
//Variables
private static JFrame frame;
private static Display display;
private static Container pane;
private static Dimension dim;
private static SerialReader sr;
private static Thread t;
//Run the program
public static void main(String[] args) {
sr = new SerialReader();
sr.initialize();
t = new Thread() {
public void run() {
try {
Thread.sleep(1000000);
} catch (InterruptedException ie) {
//Do nothing
}
}
};
t.start();
System.out.println("Started");
//Set look and feel to that of OS
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
frame = new JFrame("Baja GUI, Version 0.1");
frame.setSize(800,800);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display = new Display();
pane = frame.getContentPane();
pane.add(display);
//Place frame in the middle of the screen
dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation(dim.width/2-frame.getSize().width/2, dim.height/2-frame.getSize().height/2);
frame.setVisible(true);
}
}

Related

Having problems with concurrency and outputobjectstream (java)

So, I have a university project in java in which I'm making a bomberman game that should be run on a server. I'm having problems because of some timers that are creating new threads that are changing the object Map while I'm trying to send it.
I tried to use the synchronize(Map) on the code blocks of the timers and the outputstream but didn't work
I also have a problem where I want to keep my class Image_Library as a transient because it doesn't need to be sent over the stream, but when I mark the ArrayLists inside as such they're just considered null instead.
Here's some code:
public class GameHandler extends Thread{
private ArrayList<Client> Players = new ArrayList<Client>();
private ArrayList<Bomber> Characters = new ArrayList<Bomber>();
public GameLogic L;
transient private Image_Library lib;
private Map m;
private ArrayList<Integer> Actions = new ArrayList<Integer>();
GameHandler(Client x1, Client x2) {
x1.AddToGame(this);
x2.AddToGame(this);
Players.add(x1);
Players.add(x2);
}
#Override
public void run() {
boolean init=true;
for(int i=0;i<Players.size();i++) {
Create_New_Socket(Players.get(i));
}
Timer tt = new Timer();
tt.schedule(new Player_Info_Query(),0,200);
while(true) {
if(!init){
}
else {
int i;
for(i=0;i<Players.size();i++)
if(Players.get(i).GetBomber()==null)
break;
if(i==Players.size()) {
init=false;
tt.cancel();
try {
init_game();
for(i=0;i<Players.size();i++) {
Players.get(i).dos.writeUTF("game_start");
}
Timer tt2 = new Timer();
tt2.schedule(new Update_Task(),100);
} catch (SlickException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
private class Player_Info_Query extends TimerTask{
#Override
public void run() {
for(int i=0;i<Players.size();i++) {
if(Players.get(i).GetBomber()!=null)
continue;
try {
Players.get(i).Request_Client_Player();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private class Update_Task extends TimerTask{
#Override
public void run() {
// TODO Auto-generated method stub
try {
while(Actions.size()!=0) {
L.Action(Actions.get(0));
Actions.remove(0);
}
lib.Run_Changes();
} catch (SlickException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
for(int i=0;i<Players.size();i++)
try {
Players.get(i).Send_Map(m);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Timer tt2 = new Timer();
tt2.schedule(new Update_Task(),100);
}
}
private void Create_New_Socket(Client x){
try {
x.objectsocket = new Socket(x.datasocket.getInetAddress(),x.outputsocket);
System.out.println("SOCKET CREATED " + x.objectsocket);
x.oos = new ObjectOutputStream(x.objectsocket.getOutputStream());
x.ois = new ObjectInputStream(x.objectsocket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// obtaining input and out streams
}
private void init_game() throws SlickException {
lib = new Image_Library();
Layout_Logic map_gen = new Layout_Logic(lib);
m = map_gen.Generate_Standard_Map();
L=new GameLogic(lib,m);
Characters = new ArrayList<Bomber>();
for(int i=0;i<Players.size();i++){
Characters.add(Players.get(i).GetBomber());
}
L.Place_Characters(Characters);
}
public void Buffer_Input(int key){
Actions.add(key);
}
}
package GameLogic;
import java.io.Serializable;
import java.util.ArrayList;
import org.newdawn.slick.SlickException;
public class Image_Library implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8459759623730666317L;
private ArrayList<Element> Flagged = new ArrayList<Element>();
private ArrayList<String> Flagged_images = new ArrayList<String>();
public Map m;
public void Flag_For_Change(Element x,String img){
Flagged.add(x);
Flagged_images.add(img);
}
public void Run_Changes() throws SlickException {
while(Flagged.size()!=0 && Flagged_images.size()!=0) {
Flagged.get(0).Set_Image(Flagged_images.get(0));
Flagged.remove(0);
Flagged_images.remove(0);
}
}
}
package GameLogic;
import org.newdawn.slick.SlickException;
import java.util.Timer;
import java.util.TimerTask;
public class Bomber extends Element{
/**
*
*/
private static final long serialVersionUID = -9085303141098935687L;
transient protected boolean bomb_cd;
transient protected boolean Walking_cd;
static private int cooldown = 500; //milliseconds
static private int bomb_cooldown=1000;
static protected int move_res = 5;
protected int progression_count=move_res;
protected int direction; //0-down 1-left 2-up 3-right
static protected String StopDown="StopDown";
static protected String Down1="Down1";
static protected String Down2="Down2";
static protected String StopUp="StopUp";
static protected String Up1="Up1";
static protected String Up2="Up2";
static protected String StopLeft="StopLeft";
static protected String Left1="Left1";
static protected String Left2="Left2";
static protected String StopRight="StopRight";
static protected String Right1="Right1";
static protected String Right2="Right2";
protected int upkey,downkey,rightkey,leftkey,actionkey;
private int player;
public Bomber(int x,int y,Image_Library lib,Map m,int c1,int c2,int k1,int k2, int k3, int k4,int k5,int p) throws SlickException{
Coordinate tmp = new Coordinate(x,y);
Coord=tmp;
Solid=true;
this.lib=lib;
this.m=m;
img=StopDown;
upkey=k1;
downkey=k2;
leftkey=k3;
rightkey=k4;
actionkey=k5;
player=p;
img = StopDown;
bomb_cd=false;
Walking_cd=false;
//direction=0;
GUI_Scale=64;
}
public void MoveUp() {
Coord.MoveUp();
direction=2;
Start_Walking();
}
public void MoveRight() {
Coord.MoveRight();
direction=3;
Start_Walking();
}
public void MoveLeft() {
Coord.MoveLeft();
direction=1;
Start_Walking();
}
public void MoveDown() {
Coord.MoveDown();
direction=0;
Start_Walking();
}
private void Start_Walking(){
Walking_cd=true;
Timer tt = new Timer();
tt.schedule(new Walking(this),0,cooldown/move_res);
progression_count=0;
}
public void Used_Bomb(){
bomb_cd=true;
Timer tt = new Timer();
tt.schedule(new Bomb_Cd(), bomb_cooldown);
}
public boolean Can_Use_Bomb(){
return !bomb_cd;
}
public boolean Can_Walk() {
return !Walking_cd;
}
public boolean Death_Check(){
return m.Has_Explosion(Coord.getX(),Coord.getY());
}
private class Bomb_Cd extends TimerTask{
public void run(){
synchronized(m) {
bomb_cd=false;
}
}
}
private class Walking extends TimerTask{
transient private boolean feet=false;
private Bomber person;
Walking (Bomber person){
this.person=person;
bomb_cd=true;
}
public void run(){
progression_count++;
if(feet) {
if(direction==0)
lib.Flag_For_Change(person,Down1);
if(direction==1)
lib.Flag_For_Change(person,Left1);
if(direction==2)
lib.Flag_For_Change(person,Up1);
if(direction==3)
lib.Flag_For_Change(person,Right1);
}
else {
if(direction==0)
lib.Flag_For_Change(person,Down2);
if(direction==1)
lib.Flag_For_Change(person,Left2);
if(direction==2)
lib.Flag_For_Change(person,Up2);
if(direction==3)
lib.Flag_For_Change(person,Right2);
}
feet=!feet;
if(progression_count==move_res){
Walking_cd=false;
if(direction==0)
lib.Flag_For_Change(person,StopDown);
if(direction==1)
lib.Flag_For_Change(person,StopLeft);
if(direction==2)
lib.Flag_For_Change(person,StopUp);
if(direction==3)
lib.Flag_For_Change(person,StopRight);
bomb_cd=false;
this.cancel();
}
}
}
}

Pass inputstream variable between classes

I have three classes that I am working with.
The first class opens the GPS and reads the data.
The second parses the data.
The third is going to be the parsed data in a GUI.
My question is how do I pass the global variable gpsData to classC from NMEA?
I am using code that I found online for the gps data.
It looks like the NMEA class is dependent on serialGps and serialGps is dependent on NMEA.
I need to pass the inputLine which sets the global variable "gpsData" contained in the NMEA class to a third class which will be my GUI.
This is the serialGps class
package gpsData;
import com.fazecast.jSerialComm.SerialPort;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#SuppressWarnings({"unused", "WeakerAccess"})
public class SerialGps {
public interface StateListener {
void onGpsStateUpdated(NMEA.GpsState state);
}
private String portName;
private int baudRate;
private boolean isRunning = false;
private List<StateListener> stateListeners = new ArrayList<>();
public SerialGps(String portName, int baudRate) {
this.portName = portName;
this.baudRate = baudRate;
}
public SerialGps(String portName) {
this(portName, 4800);
}
public void addStateListener(StateListener stateListener) {
stateListeners.add(stateListener);
}
public void start() {
NMEA nmea = new NMEA();
SerialPort[] serialPorts = SerialPort.getCommPorts();
SerialPort gpsPort = null;
for (SerialPort serialPort : serialPorts) {
if (serialPort.getDescriptivePortName().toLowerCase().contains("serial")) {
gpsPort = serialPort;
}
}
if (gpsPort == null) {
System.out.println("failed to find gps serial port");
return;
}
System.out.println("using serial port: " + gpsPort.getDescriptivePortName());
gpsPort.setBaudRate(4800);
gpsPort.openPort();
InputStream inStream = gpsPort.getInputStream();
if (inStream == null) {
System.out.println("opening port " + gpsPort.getDescriptivePortName() + " failed");
return;
}
Thread thread = new Thread(() -> {
String inputLine = "";
isRunning = true;
while (isRunning) {
try {
if (inStream.available() > 0) {
char b = (char) inStream.read();
if (b == '\n') {
NMEA.GpsState gpsState = nmea.getUpdatedStatus(inputLine);
updateState(gpsState);
inputLine = "";
} else {
inputLine += b;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
}
public void stop() throws InterruptedException {
isRunning = false;
}
private void updateState(NMEA.GpsState gpsState) {
stateListeners.forEach(stateListener -> stateListener.onGpsStateUpdated(gpsState));
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SerialGps app = new SerialGps("/dev/cu.usbserial", 4800);
app.start();
}
}
The NMEA class:
package gpsData;
import java.io.IOException;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class NMEA {
public static String gpsData = null;
interface SentenceParser {
boolean parse(String[] tokens, GpsState position);
}
private class GPGGA implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[2], tokens[3]);
position.lon = Longitude2Decimal(tokens[4], tokens[5]);
position.quality = Integer.parseInt(tokens[6]);
position.altitude = Float.parseFloat(tokens[9]);
return true;
}
}
private class GPGGL implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.lat = Latitude2Decimal(tokens[1], tokens[2]);
position.lon = Longitude2Decimal(tokens[3], tokens[4]);
position.time = Float.parseFloat(tokens[5]);
return true;
}
}
private class GPRMC implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[3], tokens[4]);
position.lon = Longitude2Decimal(tokens[5], tokens[6]);
position.velocity = Float.parseFloat(tokens[7]);
position.dir = Float.parseFloat(tokens[8]);
return true;
}
}
private class GPVTG implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.dir = Float.parseFloat(tokens[3]);
return true;
}
}
private class GPRMZ implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.altitude = Float.parseFloat(tokens[1]);
return true;
}
}
private static float Latitude2Decimal(String lat, String NS) {
float result = (Float.parseFloat(lat.substring(2)) / 60.0f) + Float.parseFloat(lat.substring(0, 2));
if (NS.startsWith("S")) {
result *= -1.0f;
}
return result;
}
private static float Longitude2Decimal(String lon, String WE) {
float med = (Float.parseFloat(lon.substring(3)) / 60.0f) + Float.parseFloat(lon.substring(0, 3));
if (WE.startsWith("W")) {
med *= -1.0f;
}
return med;
}
#SuppressWarnings("WeakerAccess")
public class GpsState {
public float time = 0.0f;
public float lat = 0.0f;
public float lon = 0.0f;
public boolean hasFix = false;
public int quality = 0;
public float dir = 0.0f;
public float altitude = 0.0f;
public float velocity = 0.0f;
void updatefix() {
hasFix = quality > 0;
}
public String toString() {
return String.format("POSITION: lat: %f, lon: %f, time: %f, Q: %d, dir: %f, alt: %f, vel: %f", lat, lon, time, quality, dir, altitude, velocity);
}
}
private GpsState position = new GpsState();
private static final Map<String, SentenceParser> sentenceParsers = new HashMap<>();
NMEA() {
sentenceParsers.put("GPGGA", new GPGGA());
sentenceParsers.put("GPGGL", new GPGGL());
sentenceParsers.put("GPRMC", new GPRMC());
sentenceParsers.put("GPRMZ", new GPRMZ());
sentenceParsers.put("GPVTG", new GPVTG());
}
GpsState getUpdatedStatus(String inputLine) {
if (inputLine.startsWith("$")) {
//System.out.println(inputLine);
String nmea = inputLine.substring(1);
String[] tokens = nmea.split(",");
String type = tokens[0];
this.gpsData = inputLine;
//System.out.println(gpsData);
if (sentenceParsers.containsKey(type)) {
try {
sentenceParsers.get(type).parse(tokens, position);
} catch (Exception e) {}
}
position.updatefix();
//System.out.println(gpsData);
}
return position;
}
public static String passer() {
String gpsData2 = gpsData;
return gpsData2;
}
}
classC
package gpsData;
public class classC {
static String inputLinet = null;
public void print() {
inputLinet = NMEA.gpsData;
if (inputLinet.startsWith("$")) {
System.out.print(inputLinet);
}
}
}

How to load a game using file/object input stream

I have written save / Load methods (not sure entirely if the save works but the file 'minesweepersavestate.ser' appears in my project folder after calling saveGame()). I want to try to get the load to work because at current it doesn't seem to do anything.
Here are my functions:
public void saveGame(){
GameBoard b = new GameBoard();
try {
System.out.println("Creating File/Object output stream...");
FileOutputStream fileOut = new FileOutputStream("minesweepersavestate.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
System.out.println("Writing GameBoard Object...");
out.writeObject(b);
System.out.println("Save Successful...\n");
out.close();
fileOut.close();
} catch(FileNotFoundException e) {
System.out.println("no");
e.printStackTrace();
} catch (IOException e) {
System.out.println("no");
e.printStackTrace();
}
}
public void loadBoard()
{
GameBoard b = null;
try {
System.out.println("Creating File/Object input stream...");
FileInputStream fileIn = new FileInputStream("minesweepersavestate.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
System.out.println("Loading GameBoard Object...");
b = (GameBoard)in.readObject();
System.out.println("Load Successful...\n");
in.close();
fileIn.close();
} catch (ClassNotFoundException e) {
System.out.println("Class not found\n");
e.printStackTrace();
} catch(FileNotFoundException e) {
System.out.println("File not found\n");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
How can I alter this method so that when i call the loadBoard() method, it brings back the game in the state it was saved?
Edit:
GameBoard Class:
public class GameBoard extends JFrame implements Serializable
{
//MENU ITEMS
private JFrame frame;
private JMenuBar menubar = new JMenuBar();
private JTable table;
//FIELDS USED FOR GAMEPLAY
private int x;
private int y;
private boolean mineTrue;
private boolean triedTrue;
private static int boardsize = 8;
private int numberOfMines = 10;
public static final int Empty = 0;
public static final int Mine = -1;
public static final int Flag = -2;
public static final int FMine = -3;
public static final int RevealedMine = -4;
public static final int RevealedEmpty = -5;
// SIZE OF BUTTONS
private int gridsize = 45;
// ARRAY FOR THE BUTTONS
private JButton[][] buttons;
private int[][] board;
//VARIABLE USED FOR LABELS
private static int noGamesPlayed = 0;
private static int noGamesWon = 0;
private int mine = 10;
private int minesLeft = 10;
private static int score = 1;
private static String playername;
// GAME STATUS
private boolean gamegoing = true;
// GAME LABELS
private JLabel space = new JLabel("");
private JTextField nameEnter = new JTextField("Enter name here: ");
private JButton saveName = new JButton("Play");
private JLabel namelabel = new JLabel("Player 1: ");
private JLabel scorelabel = new JLabel("0 points ");
private JLabel status = new JLabel("Game in Progress: ");
private JLabel gamesPlayed = new JLabel("Games Played: " + noGamesPlayed);
private JLabel gamesWon = new JLabel("Games Won : " + noGamesWon);
private JLabel noMines = new JLabel("Number of Mines: " + minesLeft);
/**
* Constructor
*/
public GameBoard() { }
/**
*Making the game Frame
*/
private void makeFrame() { }
// MAKING THE GAME MENU BAR
public void makeMenuBar(){
}
public void setx(int pmeter) { }
public void sety(int pmeter) { }
public int getx() { }
public int gety() { }
public void settried(boolean paramBoolean) { }
public boolean gettried() { }
public void setmine(boolean paramBoolean) { }
public boolean getmine() { }
//ASSIGN MINES TO RANDOM LOCATION
public void assignmines() { }
// *********************************GAME CONTROLS************
private void quit() { }
//RESETS THE BOARD
public void reset() { }
public void newGame() { }
public void biggerBoard() { }
public void changeDifficulty() { }
public void saveGame() { }
public void loadBoard() { }
// LOSING THE GAME ALERT
public void lose() { }
// WINNING THE GAME ALERT
public void win() { }
// UPDATING THE SCORE
public void updatescore() { }
public void gamesPlayed() { }
public void UpdateName() { }
public void updateGamesPlayed() { }
public void updateGamesWon() { }
//WHAT VALUES THE CHARACTER HAVE
static char whichCharacter(int value) { }
//This method shows how many mines are around the spot that was clicked
static int minesAround(int[][] board, int row, int col) { }
// This method takes in an x and y value and defines what should happen when the user clicks there.
public void click(int rows, int cols) { }
//ACTION WHEN USER CLICKS ON A BUTTON INNER CLASS
private class MouseListener extends MouseAdapter {
private int x = 0;
private int y = 0;
public MouseListener(int row, int col) { }
public void mouseClicked(MouseEvent e) { }
}
}
You need to make sure that all your fields in your class are Serializable as this tutorial suggests. Of particular note are the requirements for a class to be Serializable:
Notice that for a class to be serialized successfully, two conditions must be met:
The class must implement the java.io.Serializable interface.
All of the fields in the class must be serializable. If a field is not serializable, it must be marked transient.
This means you'll have to do some digging in the Javadocs for all the fields in your GameBoard class. For instance, I did a quick search on JFrame, and there seems to be a bit of a nuance when it comes to
saving a JFrame. When you retrieve the state of your saved game, it might be better just to recreate the GUI from scratch rather than to rely upon Serialization to do it for you. This thread also seems to concur that it's not a good idea to rely on Java to restore a GUI for you, but gives a suggestion as to how to possibly make it work:
once deserialised, you will need to show the frame with frame.setVisible(true);

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException in my vector3 class

Ok so I get this null Pointer exception and neither myself nor my lecturer can figure it out. (he reckons its bad logic and I'm inclined to agree)
I'm making my own 3d object and rendering it with direct world to screenpace co-ords for x and y. This gives me exactly what I want for the excercise and I am able to refine the numbers to get smooth rendering without dropping many frames on my course computer (meaning I cant reproduce the null pointer for my lecturer) but as soon as I run it on beast machine at home the code frames appear to be going much faster, and the cube I'm rendering flashes on and off repeatedly while spitting the null pointer exception to the console(even when I manipulate the numbers to slow it all down). It never crashes though.
Here are the classes that are being used.
Viewport.java(attatched to a JFrame)
package com.my3d.graphics;
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import com.my3d.geometry.Cube;
import com.my3d.utils.Props;
public class Viewport extends Canvas implements Runnable
{
private static final long serialVersionUID = 1L;
private static final String DEFAULT_NAME = "Viewport";
private static int viewportCount = 0;
private static final BasicStroke stroke = new BasicStroke(0.5f);
private Cube cube;
private boolean running;
private String name;
private int number;
public Viewport()
{
super();
//name the Viewport
if(viewportCount < 10)
setThisName(DEFAULT_NAME +"_0" +viewportCount);
else
setThisName(DEFAULT_NAME +"_" +viewportCount);
//set Identity
setNumber(viewportCount);
//increment the Viewport counter
viewportCount++;
cube = new Cube();
cube.printCubeVertices();
cube.rotateZ(20);
cube.printCubeVertices();
cube.rotateX(20);
cube.printCubeVertices();
running = true;
}
public Viewport(String n)
{
setThisName(n);
viewportCount++;
cube = new Cube();
//cube.printCubeVertices();
//cube.rotateZ(20);
// cube.printCubeVertices();
//cube.rotateX(20);
//cube.printCubeVertices();
running = true;
}
public void tick()
{
//cube.rotateY(0.0001);
//cube.rotateX(0.0001);
repaint();
tock();
}
private void tock()
{
tick();
}
public void setThisName(String n)
{
name = n;
}
private void setNumber(int n)
{
number = n;
}
public Cube getCube()
{
return cube;
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(stroke);
Vertex[] verts = cube.getVerts();
for(int i = 0; i < verts.length; i++)
{
ArrayList<Vertex> links = verts[i].getLinks();
for(int j = 0; j < links.size(); j++)
{
Line2D l = new Line2D.Double(verts[i].getPosition().x()+500,/**/verts[i].getPosition().y()+400,/*|*/links.get(j).getPosition().x()+500,/**/links.get(j).getPosition().y()+400);
g2.setColor(Color.RED);
g2.draw(l);
}
}
}
#Override
public void run()
{
int frame = 0;
while(running)
{
if(frame == 1)
{
cube.rotateY(0.0004);
cube.rotateZ(-0.0005);
cube.rotateX(-0.0003);
}
if(frame > 200000)
{
//cube.rotateY(0.0001);
//cube.rotateZ(0.000015);
repaint();
frame = 0;
}
frame++;
}
}
}
Cube.java
package com.my3d.geometry;
import java.util.ArrayList;
import com.my3d.graphics.Vector3;
import com.my3d.graphics.Vertex;
public class Cube
{
private static final String DEFAULT_NAME = "Cube";
private static final double DEFAULT_SIZE = 100;
private static int cubeCount = 0;
private Vertex[] corners;
private Vector3 position;
private Vector3 rotation;
private String name;
private int number;
public Cube()
{
if(cubeCount < 10)
setName(DEFAULT_NAME +"_0" +cubeCount);
else
setName(DEFAULT_NAME +"_" +cubeCount);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
corners = new Vertex[8];
for(int i = 0; i < 8; i++)
corners[i] = new Vertex();
corners[0].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[1].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[2].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[3].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[4].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[5].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[6].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[7].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[0].addLink(corners[1]);
corners[1].addLink(corners[2]);
corners[2].addLink(corners[3]);
corners[3].addLink(corners[0]);
corners[0].addLink(corners[4]);
corners[1].addLink(corners[5]);
corners[2].addLink(corners[6]);
corners[3].addLink(corners[7]);
corners[4].addLink(corners[5]);
corners[5].addLink(corners[6]);
corners[6].addLink(corners[7]);
corners[7].addLink(corners[4]);
// corners[0].addLink(corners[5]);
// corners[1].addLink(corners[6]);
// corners[2].addLink(corners[7]);
// corners[3].addLink(corners[4]);
//
// corners[0].addLink(corners[2]);
// corners[5].addLink(corners[7]);
setPosition(new Vector3());
}
public Cube(String n)
{
setName(n);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
}
////////////////////////////////////////////////////////////////
//Setters
////////////////////////////////////////////////////////////////
private void setName(String n)
{
name = n;
}
private void setPosition(Vector3 v)
{
position = v;
}
public Vertex[] getVerts()
{
return corners;
}
public void printCubeVertices()
{
for(int i = 0; i < 8; i++)
System.out.println(corners[i].getPosition().toString());
}
public void rotateZ(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempZ = corners[i].getPosition().z();
double newX = (corners[i].getPosition().x()-position.x())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().x()-position.x())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,newY,tempZ));
}
}
public void rotateX(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempX = corners[i].getPosition().x();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(tempX,newY,newZ));
}
}
public void rotateY(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempY = corners[i].getPosition().y();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().x()-position.x())*Math.sin(degrees);
double newX = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().x()-position.x())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,tempY,newZ));
}
}
}
Vertex.java
package com.my3d.graphics;
import java.util.ArrayList;
public class Vertex
{
private Vector3 position;
private ArrayList<Vertex> linked = new ArrayList<Vertex>();
public Vertex()
{
setPosition(new Vector3(0.0,0.0,0.0));
}
public Vertex(Vector3 v)
{
setPosition(v);
}
public void setPosition(Vector3 pos)
{
position = pos;
}
public void addLink(Vertex v)
{
linked.add(v);
}
public void removeLink(Vertex v)
{
linked.remove(v);
}
/////////////////////////////////////////////////////////////////
//Getters
/////////////////////////////////////////////////////////////////
public Vector3 getPosition()
{
return position;
}
public ArrayList<Vertex> getLinks()
{
return linked;
}
}
Vector3.java
package com.my3d.graphics;
public class Vector3
{
private double[] xyz;
// public double x;
// public double y;
// public double z;
public Vector3()
{
xyz = new double []{0.0,0.0,0.0};
// x = xyz[0];
// y = xyz[1];
// z = xyz[2];
}
public Vector3(double x,double y,double z)
{
xyz = new double []{x,y,z};
// xyz[0] = x;
// xyz[1] = y;
// xyz[2] = z;
}
public Vector3(double[] array)
{
try
{
if(array.length > 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too big by (" +(array.length - 3) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
if(array.length < 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too small by (" +(3 - array.length) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
xyz[0] = array[0];
xyz[1] = array[1];
xyz[2] = array[2];
}
catch(RuntimeException e)
{
System.out.println(e);
}
}
///////////////////////////////////////////////////////////////////////////
public void x(double a)
{
xyz[0] = a;
}
public void y(double a)
{
xyz[1] = a;
}
public void z(double a)
{
xyz[2] = a;
}
public double x()
{
return xyz[0];
}
public double y()
{
return xyz[1];
}
public double z()
{
return xyz[2];
}
public void normalize()
{
double length = Math.sqrt((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]) + (xyz[2] * xyz[2]));
xyz[0] = xyz[0]/length;
xyz[1] = xyz[1]/length;
xyz[2] = xyz[2]/length;
}
public String toString()
{
return "(" + xyz[0] + "," + xyz[1] + "," + xyz[2] + ")";
}
}
The best I can do is trace it to public double x(){return xyz[0];}
in the Vector3 class when the paint method is called in the viewport class but when I try and step through they are never null. I get the impression that the jvm is catching up with itself while trying to reassign new positions to the vertices during the rotation.
Edit: Stacktrace
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.my3d.graphics.Vector3.x(Vector3.java:70)
at com.my3d.graphics.Viewport.paint(Viewport.java:107)
at java.awt.Canvas.update(Unknown Source)
at sun.awt.RepaintArea.updateComponent(Unknown Source)
at sun.awt.RepaintArea.paint(Unknown Source)
at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Additional Info:
Manager.java
package com.my3d.core;
import java.awt.Color;
import com.my3d.graphics.Viewport;
import com.my3d.graphics.Wind;
import com.my3d.utils.Props;
public class Manager {
/**
* #param args
*/
public static void main(String[] args)
{
Props.loadPropsFromFile();
Wind wind = new Wind();
wind.setVisible(true);
Viewport view = new Viewport("Main view");
view.setPreferredSize(Props.getWindowSize());
view.setBackground(Color.LIGHT_GRAY);
wind.add(view);
view.setVisible(true);
wind.pack();
view.run();
}
}
Wind.java
package com.my3d.graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import com.my3d.utils.Props;
import javax.swing.JFrame;
public class Wind extends JFrame
{
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean fullScreen = false;
//Constructor
public Wind()
{
super(Props.getDefaultTitle() +Props.getTitleSplash());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fullScreen = Props.getFullScreen();
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setBounds(0,0,Props.getWindowWidth(),Props.getWindowHeight());
setLocationRelativeTo(null);
}
}
public void setTitle(String s)
{
//if(Props.getRandomSplashes())
super.setTitle(Props.getDefaultTitle() +s);
}
public void setRandomTitle()
{
if(Props.getRandomSplashes())
Props.setTitleSplash(Props.randomizeSplash());
super.setTitle(Props.getDefaultTitle() +Props.getTitleSplash());
}
public void setFullScreen(boolean b)
{
fullScreen = b;
}
//this method wont work >>
public void refactorWindow()
{
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setSize(Props.getWindowSize());
setLocationRelativeTo(null);
}
}
}
Props.java
package com.my3d.utils;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class Props
{
private static String gameTitle;
private static String splashTitle;
private static Dimension windowSize;
//private static int gameState;
private static boolean fullScreen;
private static boolean randomSplashes;
private static BufferedImage tileSheet;
///////////////////////////////////////////////////////////////
//Getters
///////////////////////////////////////////////////////////////
public static Dimension getWindowSize()
{
return windowSize;
}
public static int getWindowWidth()
{
return (int) windowSize.getWidth();
}
public static int getWindowHeight()
{
return (int) windowSize.getHeight();
}
public static String getTitleSplash()
{
return splashTitle;
}
public static String getDefaultTitle()
{
return gameTitle;
}
public static boolean getFullScreen()
{
return fullScreen;
}
public static boolean getRandomSplashes()
{
return randomSplashes;
}
public static BufferedImage getTileSheet()
{
return tileSheet;
}
/////////////////////////////////////////////////////////////////////
//Setters
/////////////////////////////////////////////////////////////////////
public static void setWindowSize(int width, int height)
{
windowSize = new Dimension(width,height);
}
public static void setTitleSplash(String s)
{
splashTitle = s;
}
private static void setDefaultTitle(String s)
{
gameTitle = s;
}
////////////////////////////////////////////////////////////////////
//loader
////////////////////////////////////////////////////////////////////
public static void loadPropsFromFile()
{
File file = new File("res\\config\\props.props");
try
{
Scanner input = new Scanner(file);
String lInput = input.nextLine().trim();
setWindowSize(Integer.parseInt(lInput.substring(lInput.indexOf('=')+1,lInput.indexOf(','))),Integer.parseInt(lInput.substring(lInput.indexOf(',')+1,lInput.length())));
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
fullScreen = true;
else
fullScreen = false;
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
randomSplashes = true;
setTitleSplash(randomizeSplash());
}
else
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
setTitleSplash("");
randomSplashes = false;
}
lInput = input.nextLine().trim();
try
{
tileSheet = ImageIO.read(new File(loadFromDirectory(lInput.substring(lInput.indexOf('=')+1,lInput.length()))));
}
catch (IOException e)
{
System.out.println("Tilesheet Cannot be found.");
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
///////////////////////////////////////////////////////////////////
//Utility
///////////////////////////////////////////////////////////////////
public static String loadFromDirectory(String key)
{
String s = "";
File sFile = new File("res\\config\\dirs.dir");
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
String st = sInput.nextLine().trim();
if(key.equals(st.substring(1,st.length())));
s = sInput.nextLine().trim();
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return s;
}
public static String randomizeSplash()
{
if(randomSplashes)
{
File sFile = new File("res\\config\\splash.props");
ArrayList<String> sArray = new ArrayList<String>();
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
sArray.add(sInput.nextLine().trim());
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Random rand = new Random();
String s = sArray.get(rand.nextInt(sArray.size()));
splashTitle = s;
return s;
}
else
return "";
}
}
props.props
windowSize =1000,800
startInFullscreen =false
randomSplashes =false
defaultTitle =3D Prototype
tileSheet =tileSheet
dirs.dir
<startGame>
res\\img\\button\\Start_game_text.png
res\\img\\button\\Start_game_active.png
res\\img\\button\\Start_game_click.png
<quitGame>
res\\img\\button\\Quit_game_text.png
res\\img\\button\\Quit_game_active.png
res\\img\\button\\Quit_game_click.png
<menuBackground>
res\\img\\Menu_background.png
<resumeGame>
res\\img\\button\\Resume_game_text.png
res\\img\\button\\Resume_game_active.png
res\\img\\button\\Resume_game_click.png
<tileSheet>
res\\img\\Final_Sub_Hunt.png
Update: mark your field xyz in Vector3 as final. My reading of the JMM says that, without a synchronizer or final, it is legal for the Event Dispatch Thread (EDT), from whence your stack trace comes, to see that non-final field as not yet initialized by the constructor when the constructor is run in a separate (your Viewport.run()) thread. See this SO question or the JMM FAQ.
The correct place to look in the JLS for this is in Section 17.5 (though you really need to read all of 17, and a lot of times, to start to grok this):
An object is considered to be completely initialized when its
constructor finishes. A thread that can only see a reference to an
object after that object has been completely initialized is guaranteed
to see the correctly initialized values for that object's final
fields.
Note that the same guarantee does not hold for non-final fields; if you want safety there, you will need to use synchronization edges of some kind (explicit synchronizers, volatile, whatever.)
Related discussion from Alex Miller, who I think putters around StackOverflow as https://stackoverflow.com/users/7671/alex-miller and might poke his head in if we say hello.) And a related SO question.
Original Answer
Looks like you have an error in your Vector3(double[]) constructor, which never initializes the double[] xyz field. Only place I could find where that field would not be initialized and yield an NPE.
I can't tell from your code how many threads you have running around. If you are creating instances of Vector3 in one thread and placing them into a collection while they are being displayed by a separate rendering thread, it is possible you are observing them in a state of partial construction. Generally things are arranged to make this unlikely (I can't remember off the top of my head what the memory model says about publishing partially-constructed objects, but it can happen if you let this slip out of the constructor; I don't see that happening here.)
JA
catch(RuntimeException e)
{
System.out.println(e);
}
This segment allows you to create a Vector3 without properly initialising the array. That's a potiential problem.
Both of the non-default constructors make no attempt to check x, y or z is null before assigning them.
I would start there, then consider unit tests.

Customize listfield item on BlackBerry

I have a simple list field class which shows the default menu when I select a particular listitem. I want to customize the listfield item, so that when an item is selected a new screen is pushed onto the stack. I am overridding trackwheeel() method, but am not able to make it work.
import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import java.util.Vector;
public class TeamListScreen extends UiApplication
{
public static void main(String[] args)
{
TeamListScreen theApp = new TeamListScreen();
theApp.enterEventDispatcher();
}
public TeamListScreen()
{
pushScreen(new ListFieldScreen());
}
}
class ListFieldScreen extends MainScreen
{
private ListField _listField;
private Vector _listElements;
int listFieldIndex = -1;
public ListFieldScreen()
{
setTitle("List Field Sample");
_listElements = new Vector();
_listField = new ListField();
ListCallback _callback = new ListCallback();
_listField.setCallback(_callback);
_listField.setRowHeight(45);
//_listField.setChangeListener(this);
add(_listField);
initializeList();
_listField = new ListField(_listElements.size()) {
protected void drawFocus(Graphics graphics, boolean on) {
}
protected boolean trackwheelClick(int status, int time) {
listFieldIndex = _listField.getSelectedIndex();
if (listFieldIndex < 0) {
listFieldIndex = 0;
}
try {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(new LiveScreen());
// UiApplication.getUiApplication().popScreen(getActiveScreen());
}
});
} catch (Exception e) {
}
return true;
}
};
}
private void initializeList()
{
String itemOne = "List item one";
String itemTwo = "List item two";
_listElements.addElement(itemOne);
_listElements.addElement(itemTwo);
reloadList();
}
private void reloadList()
{
_listField.setSize(_listElements.size());
}
private class ListCallback implements ListFieldCallback
{
public void drawListRow(ListField list, Graphics g, int index, int y, int w)
{
String text = (String)_listElements.elementAt(index);
g.drawText(text, 0, y, 0, w);
}
public Object get(ListField list, int index)
{
return _listElements.elementAt(index);
}
public int indexOfList(ListField list, String prefix, int string)
{
return _listElements.indexOf(prefix, string);
}
public int getPreferredWidth(ListField list)
{
return Display.getWidth();
}
}
}
trackwheelClick() function is deprecated, you should use navigationClick() instead.
BTW, you don't need to use UiApplication.getUiApplication().invokeLater, because it is already running in the event queue.

Categories