2

Following on from my question last night: SwingPropertyChangeSupport to dynamically update JTextArea, I'm trying to refresh a GUI after reading in a text file.

I have an array which holds integers of two digits. I can update it dynamically by typing a string of digits into a text area, then choosing which index to modify. The string is broken into two-digit pieces and added sequentially. After typing in the String of characters and clicking a button, this method is called:

public void modifyArray() {

    // show dialog to retrieve entered address
    addressToModify = (String) JOptionPane
            .showInputDialog("At which location?");

    // convert to integer if decimal address entered
    memAddress = Integer.parseInt(addressToModify);
    arrayForUpdate.instructionsIn(codeIn.getText(), memAddress);
}

This part of my code now works as expected. That is, if I type in "123456" and then enter location "0" (or "0000"), the display is updated to something like:

Index     0000   Value:   12
Index     0001   Value:   34
Index     0002   Value:   56
Index     0003   Value:   00

I also have a text file containing a String comprised of four digits donating the array index, followed by a series of two-digit values to be added to the array. I can load this via a gui button/file chooser. The contents of the example file are:

0000123456

I have the following methods to process the file:

    public void readRecord(File fileName) {

    // create file reader
    try {
        FileReader reader = null;
        try {
            // open input file
            reader = new FileReader(fileName);

            // create scanner to read from file reader
            Scanner in = new Scanner(reader);

            // read each line and remove whitespace
            while (in.hasNextLine()) {
                String line = in.nextLine().trim();
                parseRecord(line);
            }

        } finally {
            // close reader assuming it was successfully opened
            if (reader != null)
                reader.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
    }
  }

public void parseRecord(String record) {

    // create address substring from start, 4 long
    String addrString = record.substring(0, 3);
    int s1Address = Integer.parseInt(addrString);

    // create binary data substring (4 from start, up to end)
    String dataString = record.substring(4, record.length());

    // pass data string as parameter to InstructionsIn method
    arrayForUpdate.instructionsIn(dataString, s1Address);
}

In both of the scenarios above, the "instructionsIn" method is called. In the first scenario this results in the display being updated but in the second it doesn't and I can't figure out why. I was hoping someone might be able to spot something I'm missing.

Here is a complete, simplified version of the code which runs:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.event.SwingPropertyChangeSupport;

public class PropertyChangeExample extends JFrame implements ActionListener {

private static final long serialVersionUID = 1L;
private String addressToModify, mList;
private int memAddress;
private JTextArea codeIn, displayOutput;
private S_Record sRec;
private JButton loadButton, modifyArrayButton;
private FocusListener focusListener;
private JPanel displayPanel;
private ArrayForUpdate arrayForUpdate = new ArrayForUpdate();

public static void main(String[] arg) {
    PropertyChangeExample display = new PropertyChangeExample();
    display.setVisible(true);
}

public PropertyChangeExample() {
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(450, 170);
    layoutLeft();
    layoutDisplay();
    layoutBottom();
}

public void layoutDisplay() {
    displayPanel = new JPanel();
    add(displayPanel, BorderLayout.CENTER);
    displayOutput = new JTextArea(32, 38);
    displayPanel.add(displayOutput);
    displayOutput.addFocusListener(focusListener);

    mList = arrayForUpdate.getBoundProperty();
    displayOutput.setText(mList);

    arrayForUpdate.addPropertyChangeListener(new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent pcEvt) {
            if (pcEvt.getPropertyName().equals(
                    ArrayForUpdate.BOUND_PROPERTY)) {
                mList = (pcEvt.getNewValue().toString());
                displayOutput.setText(mList);
            }
        }
    });
}

public void layoutLeft() {
    JPanel left = new JPanel();
    add(left, BorderLayout.WEST);
    codeIn = new JTextArea(10, 7);
    left.add(codeIn, BorderLayout.NORTH);
    codeIn.addFocusListener(focusListener);
}

public void layoutBottom() {
    JPanel bottom = new JPanel();
    bottom.setBackground(Color.LIGHT_GRAY);
    loadButton = new JButton("Load file");
    loadButton.addActionListener(this);
    bottom.add(loadButton);
    add(bottom, BorderLayout.SOUTH);
    modifyArrayButton = new JButton("Add value to array");
    modifyArrayButton.addActionListener(this);
    bottom.add(modifyArrayButton);
}

public void actionPerformed(ActionEvent ae) {
    if (ae.getSource() == modifyArrayButton) {
        modifyArray();
    }

    if (ae.getSource() == loadButton) {
        processInputFile();
    }
}

public void modifyArray() {
    addressToModify = (String) JOptionPane
            .showInputDialog("At which location?");

    // convert to integer if decimal address entered
    memAddress = Integer.parseInt(addressToModify);
    arrayForUpdate.instructionsIn(codeIn.getText(), memAddress);
}

public void processInputFile() {

    sRec = new S_Record();
    JFileChooser chooser = new JFileChooser();
    int returnVal = chooser.showOpenDialog(getParent());
    if (returnVal == JFileChooser.APPROVE_OPTION) {
        // create the file
        File file = chooser.getSelectedFile();

        // pass to readRecord method in S_Record class
        sRec.readRecord(file);
    }
 }
}

class S_Record {

private ArrayForUpdate arrayForUpdate = new ArrayForUpdate();

public void readRecord(File fileName) {

    // create file reader
    try {
        FileReader reader = null;
        try {
            // open input file
            reader = new FileReader(fileName);

            // create scanner to read from file reader
            Scanner in = new Scanner(reader);

            // read each line and remove whitespace
            while (in.hasNextLine()) {
                String line = in.nextLine().trim();
                parseRecord(line);
            }
        } finally {
            // close reader assuming it was successfully opened
            if (reader != null)
                reader.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void parseRecord(String record) {

    // create address substring from start, 4 long
    String addrString = record.substring(0, 3);

    int s1Address = Integer.parseInt(addrString);

    // create binary data substring (4 from start, up to end)
    String dataString = record.substring(4, record.length());

    // pass data string as parameter to InstructionsIn method
    arrayForUpdate.instructionsIn(dataString, s1Address);
 }
}

class ArrayForUpdate {

public static final String BOUND_PROPERTY = "bound property";
private String boundProperty = "";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
        this);
private int[] myArray;
private final int MEM_LOCATIONS = 6;
/** StringBuilder object for displaying memory */
private StringBuilder mList;

public ArrayForUpdate() {

    myArray = new int[MEM_LOCATIONS];
    for (int i = 0; i < myArray.length; i++) {
        myArray[i] = 0;
    }
    setArrayyDisplayString();
}

/** 
 * method to create formatted string of array
 */
public void setArrayyDisplayString() {

    // create StringBuilder for display in memory tab
    mList = new StringBuilder();
    for (int i = 0; i < myArray.length; i++) {

        mList.append(String.format("%10s %04x %10s %02x", "Index:   ", i,
                "Value:  ", myArray[i]));
        mList.append("\n");
    }
    setBoundProperty(mList.toString());
}

/**
 * This method takes in a string passed through from the GUI
 */
public void instructionsIn(String codeIn, int loc) {

    String code = codeIn.trim();
    int len = code.length();
    int chunkLength = 2; // the length of each chunk of code
    int i = 0;

    // traverse entered code and split into 2 digit chunks
    for (i = 0; i < len; i += chunkLength) {

        String chunk = code.substring(i, Math.min(len, i + chunkLength));
        int oc = Integer.parseInt(chunk, 16);

        // add the data to the array
        setArrayData(loc, oc);
        loc++;
    }
}

/**   
 * method to add data to the array
 */
public void setArrayData(int a, int memData) {
    myArray[a] = memData;
    setArrayyDisplayString();
}

public String getBoundProperty() {
    return boundProperty;
}

/**
 * Method to implement changes to array for display
 * 
 * @param boundProperty - the String representing the memory array
 */
public void setBoundProperty(String boundProperty) {
    String oldValue = this.boundProperty;
    String newValue = boundProperty;
    this.boundProperty = newValue;
    spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
}

public void addPropertyChangeListener(PropertyChangeListener listener) {
    spcSupport.addPropertyChangeListener(listener);
 }
}
Community
  • 1
  • 1
Robert
  • 5,278
  • 43
  • 65
  • 115

1 Answers1

3

This looks to be a problem of references.

You appear to have at least two ArrayForUpdate objects, one that is listened to by the GUI, and another completely distinct instance that is held inside of the S_Record class. The latter one is the one that is getting the information from the file while the former one, the one being listened to by the GUI is not. The solution: make sure that there is only one ArrayForUpdate object that is used in both places. Perhaps you can pass a reference of the GUI's instance into your S_Record object via its constructor?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you very much. That appears to have worked a treat. Your help has been much appreciated these last weeks, sir! – Robert Aug 08 '12 at 21:40
  • 1
    @Robert: you're welcome. Now don't forget to do your file reading business in a background thread. – Hovercraft Full Of Eels Aug 08 '12 at 21:40
  • Apologies, I'm pretty new to programming and am not sure what you mean by a background thread? – Robert Aug 08 '12 at 21:48
  • 1
    You need to take care that any long-running tasks not be performed on the main Swing event thread, else you could render your GUI unresponsive. Please have a look at the Swing threading tutorial: [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) – Hovercraft Full Of Eels Aug 08 '12 at 22:23
  • Thanks again. I didn't know about any of that. – Robert Aug 08 '12 at 22:36