Posts Tagged ‘Swing

16
Oct
13

Java Debugging using a Custom AlertFrame

This page describes how to create a simple Alert Frame to display java variable during application runtime. It is similar to the alert window commonly displayed in javaScript. The AlertFrame can be used during development as well as in your final application.

An example would be to display the results of a database query you have run, or to display results of a web service call that was made. The AlertFrame has capability to display not only simple wrapper types but also complex beans. It inspects and finds getter methods using reflection and displays the results in the appropriate format. It also allows the user to click further into the bean to explore deeper.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Code

The following is the code for the the AlertFrame.

vi src/main/java/AlertFrame.java

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;


/**
 * Displays List, Properties, and Table data depending on which constructor is
 * called. Constructors for Strings, Array of Strings, Maps of Strings and List
 * of Maps are available.
 */
public class AlertFrame extends JFrame {
	private static final long serialVersionUID = 1L;

	private static final GraphicsDevice gd = GraphicsEnvironment
			.getLocalGraphicsEnvironment().getDefaultScreenDevice();

	private JTextField selectedRowIndex;
	private JTextField selectedColIndex;
	private MapListTableModel mapListTableModel;
	private JTextArea textArea = new JTextArea();
	
	private void initCommon() {
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}
	public <T> AlertFrame(List<T> list) {
        initCommon();
		final JTextArea jTextArea = new JTextArea();
		jTextArea.setPreferredSize(new Dimension(300, 300));
		final DefaultListModel listModel = new DefaultListModel();
		int i = 0;
		for (Object value : list) {
			listModel.add(i++, value);
		}
		JList jList = new JList(listModel);
		getContentPane().add(new JScrollPane(jList),
				BorderLayout.CENTER);
		final JScrollPane scrollPane = new JScrollPane(jTextArea);
		getContentPane().add(scrollPane,BorderLayout.SOUTH);
		setSize(gd.getDisplayMode().getWidth() / 2, gd.getDisplayMode()
				.getWidth() / 2);

		jList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
					if (((JList) e.getSource()).isSelectedIndex(i)) {
						jTextArea.setText(String.valueOf(listModel.get(i)));
						scrollPane.invalidate(); // TODO: fix this.
					}
				}
			}
		});
		
		jList.addMouseListener(new MouseAdapter() {
		    public void mouseClicked(MouseEvent evt) {
		        JList list = (JList)evt.getSource();
		        if (evt.getClickCount() == 2) {
		            int index = list.locationToIndex(evt.getPoint());
		            Object obj = listModel.get(index);
		            new AlertFrame(obj);
		        } 
		    }
		});	
		
		showCentered(this);		
	}
	public <T> AlertFrame(Set<T> set) {
		this(new ArrayList<T>(set));
	}
	/**
	 * Tabular data (simple text) with an ordered list of columns.
	 */
	public <T> AlertFrame(List<Map<String, T>> mapList,
			List<String> colNameList) {
        initCommon();
		this.mapListTableModel = new MapListTableModel<T>(mapList, colNameList);

		selectedRowIndex = new JTextField();
		selectedRowIndex.getDocument().addDocumentListener(new DL(this));
		selectedColIndex = new JTextField();
		selectedColIndex.getDocument().addDocumentListener(new DL(this));
		// textArea.setPreferredSize()
		setSize(800, 600);

		JTable jTable = new JTable(mapListTableModel);
		// handle selection events and callbacks here.
		jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		ListSelectionModel rsm = jTable.getSelectionModel();
		rsm.addListSelectionListener(new SelectionDebugger(selectedRowIndex,
				rsm));

		ListSelectionModel csm = jTable.getColumnModel().getSelectionModel();
		csm.addListSelectionListener(new SelectionDebugger(selectedColIndex,
				csm));

		jTable.setRowSelectionAllowed(false);
		jTable.setColumnSelectionAllowed(false);
		jTable.setCellSelectionEnabled(true);

		getContentPane().add(new JScrollPane(jTable), BorderLayout.CENTER);
		getContentPane().add(new JScrollPane(textArea), BorderLayout.SOUTH);

		pack();
		showCentered(this);
		setVisible(true);
	}
	public AlertFrame(Number value) {
		this(String.valueOf(value));
	}
	public AlertFrame(Object obj) {
        initCommon();
		try {
			Map<String,Object> beanMap = BeanUtils.describe(obj);
		for(String key : beanMap.keySet()) {
			beanMap.put(key, PropertyUtils.getSimpleProperty(obj, key));
		}
			processMap(beanMap);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
	}
	public AlertFrame(String value) {
        initCommon();
		textArea.setText(value);
		getContentPane().add(new JScrollPane(textArea));
		setSize(gd.getDisplayMode().getWidth() / 2, gd.getDisplayMode()
				.getWidth() / 2);
		showCentered(this);
	}

	public AlertFrame(String[] values) {
        initCommon();
		final JTextArea jTextArea = new JTextArea();
		jTextArea.setPreferredSize(new Dimension(300, 300));
		final DefaultListModel listModel = new DefaultListModel();
		int i = 0;
		for (Object value : values) {
			listModel.add(i++, String.valueOf(value));
		}
		JList jList = new JList(listModel);
		getContentPane().add(new JScrollPane(jList),
				BorderLayout.SOUTH);
		final JScrollPane scrollPane = new JScrollPane(jTextArea);
		getContentPane().add(scrollPane,BorderLayout.CENTER);
		setSize(gd.getDisplayMode().getWidth() / 2, gd.getDisplayMode()
				.getWidth() / 2);

		jList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
//				if (e.getValueIsAdjusting() == false || e.getFirstIndex() == -1) {
//					return;
//				}
				for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
					if (((JList) e.getSource()).isSelectedIndex(i)) {
						jTextArea.setText(String.valueOf(listModel.get(i)));
						scrollPane.invalidate(); // TODO: fix this.
					}
				}
			}
		});
		showCentered(this);
	}

	public <T> AlertFrame(final Map<String, T> map) {
        initCommon();
		processMap(map);
	}

	private <T> void processMap(final Map<String, T> map) {
		final DefaultListModel listModel = new DefaultListModel();

		int i = 0;
		for (Object value : new TreeSet<Object>(map.keySet())) {
			listModel.add(i++, value);
		}
		JList jList = new JList(listModel);
		jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		jList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
//				if (e.getValueIsAdjusting() == false || e.getFirstIndex() == -1) {
//					return;
//				}
				for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
					if (((JList) e.getSource()).isSelectedIndex(i)) {
						textArea.setText(String.valueOf(map.get(listModel.get(i))));
					}
				}
			}
		});
		jList.addMouseListener(new MouseAdapter() {
		    public void mouseClicked(MouseEvent evt) {
		        JList list = (JList)evt.getSource();
		        if (evt.getClickCount() == 2) {
		            int index = list.locationToIndex(evt.getPoint());
		            Object obj = map.get(listModel.get(index));
		            new AlertFrame(obj);
		        } 
		    }
		});		
		
		// jList.add
		getContentPane().add(new JScrollPane(jList), BorderLayout.NORTH);
		getContentPane().add(new JScrollPane(textArea), BorderLayout.CENTER);
		setSize(gd.getDisplayMode().getWidth() / 2, gd.getDisplayMode()
				.getWidth() / 2);
		showCentered(this);
	}
	public JTextArea getTextArea() {
		return textArea;
	}

	class DL implements DocumentListener {
		private AlertFrame parent;

		public DL(AlertFrame parent) {
			this.parent = parent;
		}

		public void removeUpdate(DocumentEvent event) {
			changed(event);
		}

		public void insertUpdate(DocumentEvent event) {
			changed(event);
		}

		public void changedUpdate(DocumentEvent event) {
			changed(event);
		}

		private void changed(DocumentEvent event) {
			if (!"".equals(parent.selectedRowIndex.getText())
					&& !"".equals(parent.selectedColIndex.getText())) {
				int intSelectedRowIndex = Integer
						.parseInt(parent.selectedRowIndex.getText());
				int intSelectedColIndex = Integer
						.parseInt(parent.selectedColIndex.getText());
				textArea.setText(String.valueOf(mapListTableModel.getValueAt(
						intSelectedRowIndex, intSelectedColIndex)));
			}
		}
	}
	public static void showCentered(JFrame frame) {
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
		final int x = (gd.getDisplayMode().getWidth() - frame.getWidth()) / 2;
		final int y = (gd.getDisplayMode().getHeight() - frame.getHeight()) / 2;
		frame.setLocation(x, y);
		frame.setVisible(true);
	}	

}

class SelectionDebugger implements ListSelectionListener {
	private ListSelectionModel listModel;
	private JTextField target;

	public SelectionDebugger(JTextField target, ListSelectionModel lsm) {
		this.listModel = lsm;
		this.target = target;
	}

	public void valueChanged(ListSelectionEvent lse) {
		if (!lse.getValueIsAdjusting()) {
			int[] selection = getSelectedIndices(
					listModel.getMinSelectionIndex(),
					listModel.getMaxSelectionIndex());
			if (selection.length == 0) {
			} else {
				for (int i = 0; i < selection.length; i++) {
					String text = String.valueOf(selection[i]);
					if (!"".equals(text.trim())) {
						target.setText(text);
					}
				}
			}

		}
	}

	protected int[] getSelectedIndices(int start, int stop) {
		if ((start == -1) || (stop == -1)) {
			// no selection, so return an empty array
			return new int[0];
		}
		int guesses[] = new int[stop - start + 1];
		int index = 0;
		// manually walk thru these
		for (int i = start; i <= stop; i++) {
			if (listModel.isSelectedIndex(i)) {
				guesses[index++] = i;
			}
		}
		int realthing[] = new int[index];
		System.arraycopy(guesses, 0, realthing, 0, index);
		return realthing;
	}
}

class MapListTableModel <T> extends AbstractTableModel {
	private static final long serialVersionUID = 1L;
	private List<Map<String, T>> mapList = new ArrayList<Map<String, T>>();
	private List<String> colNameList = new ArrayList<String>();

	public MapListTableModel(List<Map<String, T>> mapList,
			List<String> colNameList) {
		super();
		this.mapList = mapList;
		this.colNameList = colNameList;
	}

	public int getColumnCount() {
		return colNameList.size();
	}

	public String getColumnName(int columnIndex) {
		return colNameList.get(columnIndex);
	}

	public int getRowCount() {
		return mapList.size();
	}

	public Object getValueAt(int rowIndex, int columnIndex) {
		String columnName = getColumnName(columnIndex);
		Map<String, T> map = mapList.get(rowIndex);
		if (map == null)
			return null;
		return map.get(columnName);
	}
}

Sample Swing Test Application

vi src/main/java/AlertFrameTest.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class AlertFrameTest {
	public static void main(String args[]) throws Exception {
		testValue();
		testValueSet();
		testValueList();
		testValueMap();
		testValueListMap();		
	}

	/**
	 * Displays a single dialog with the value displayed. 
	 * 
	 * @throws Exception
	 */
	private static void testValue() throws Exception {
		new AlertFrame(123);
		new AlertFrame(123.5);
		new AlertFrame(new Integer(123));
		// Strings
		new AlertFrame("Testing Single String");
		// beans 
		// uses reflection to get a map of all the getters.
		// displays a jListbox with the getters.
		// clicking displays toString on bottom jTextArea.
		// double clicking opens new AlertFrame (value).
		new AlertFrame(new UserBean(1, "Bob", new UserBean(0, "Manager", null)));
	}

	/**
	 * Displays Dialog with a JListbox.
	 */
	private static void testValueSet() {
		// wrapped numbers 
		// clicking displays toString on bottom jTextArea.
		// double clicking does nothing.
		Set<Integer> intSet = new HashSet<Integer>();
		intSet.add(1);
		intSet.add(2);
		new AlertFrame(intSet);
		// Strings
		// clicking displays toString on bottom jTextArea.
		// double clicking does nothing.
		Set<String> set = new HashSet<String>();
		set.add("a");
		set.add("b");
		new AlertFrame(set);
		// beans 
		// clicking displays toString on bottom jTextArea.
		// double clicking opens new AlertFrame (value).
		Set<UserBean> userSet = new HashSet<UserBean>();
		userSet.add(new UserBean(1, "Bob", new UserBean(0, "Manager", null)));
		userSet.add(new UserBean(1, "Ted", new UserBean(0, "Manager", null)));
		new AlertFrame(userSet);
	}
	public static void testValueList() {	
		// Strings
		// clicking displays toString on bottom jTextArea.
		// double clicking does nothing.
		List<String> set = new ArrayList<String>();
		set.add("a");
		set.add("b");
		new AlertFrame(set);
		// beans 
		// clicking displays toString on bottom jTextArea.
		// double clicking opens new AlertFrame (value).
		List<UserBean> userSet = new ArrayList<UserBean>();
		userSet.add(new UserBean(1, "Bob", new UserBean(0, "Manager", null)));
		userSet.add(new UserBean(1, "Ted", new UserBean(0, "Manager", null)));
		new AlertFrame(userSet);
	}
	/**
	 * This is for properties. It displays a list box with the map keys with
	 * details displayed in a text area on the bottom.
	 */
	public static void testValueMap() {	
		// wrapped numbers (as map values)
		// clicking displays toString on bottom jTextArea.		
		// double clicking does nothing.
		Map <String, Integer> numericMap = new HashMap<String,Integer>();
		numericMap.put("a", 123);
		numericMap.put("b", 456);
		new AlertFrame(numericMap);
		// Strings (as map values)
		// clicking displays toString on bottom jTextArea.
		// double clicking does nothing.
		Map <String, String> stringMap = new HashMap<String,String>();
		stringMap.put("a", "string a");
		stringMap.put("b", "String b");
		new AlertFrame(stringMap);
		// beans (as map values)		
		// clicking displays toString on bottom jTextArea.
		// double clicking opens new AlertFrame (value).
		Map <String, UserBean> beanMap = new HashMap<String,UserBean>();
		beanMap.put("a", new UserBean(1, "Bob", new UserBean(0, "Manager", null)));
		beanMap.put("b", new UserBean(1, "Ted", new UserBean(0, "Manager", null)));
		new AlertFrame(beanMap);
	}
	/**
	 * This displays a table with columns that are based on map keys with 
	 * details displayed in the text area on the bottom.
	 */
	public static void testValueListMap() {	
		{
			// wrapped numbers (as table cell values)
			// clicking displays toString on bottom jTextArea.
			// double clicking does nothing.
			List<Map<String,Integer>> mapList = new ArrayList<Map<String,Integer>>();
			mapList.add(Collections.singletonMap("key", 123));
			mapList.add(Collections.singletonMap("key", 456));
			new AlertFrame(mapList, Arrays.asList(new String[] {"key"}));
		}{
			// Strings (as table cell values)
			// clicking displays toString on bottom jTextArea.
			// double clicking does nothing.
			List<Map<String,String>> mapList = new ArrayList<Map<String,String>>();
			mapList.add(Collections.singletonMap("key", "String-123"));
			mapList.add(Collections.singletonMap("key", "String-456"));			
			new AlertFrame(mapList, Arrays.asList(new String[] {"key"}));
		}{
			// beans (have beans as table cells)		
			// clicking displays toString on bottom jTextArea.
			// double clicking opens new AlertFrame (value).
			List<Map<String,Object>> mapList = new ArrayList<Map<String,Object>>();
			Map<String,Object> hmap = new HashMap<String,Object>();
			hmap.put("key", new UserBean(1, "Bob", new UserBean(0, "Manager", null)));
			hmap.put("key2", "test");
			hmap.put("key3", 123);
			mapList.add(hmap);
			hmap = new HashMap<String,Object>();
			hmap.put("key", new UserBean(1, "Ted", new UserBean(0, "Manager", null)));
			hmap.put("key2", "test");
			hmap.put("key3", 123);
			mapList.add(hmap);
			new AlertFrame(mapList, new ArrayList<String>(hmap.keySet()));
		}
	}
}

To run the app just type the following:

mvn exec:java -Dexec.mainClass=AlertFrame

Finally the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>alertFrame</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    <pluginRepositories>
      <pluginRepository>
        <id>numberformat-releases</id>
        <url>https://raw.github.com/numberformat/20130213/master/repo</url>
      </pluginRepository>
    </pluginRepositories>  
  
  <dependencies>
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.11</version>
  	</dependency>
  	<dependency>
  		<groupId>commons-beanutils</groupId>
  		<artifactId>commons-beanutils</artifactId>
  		<version>1.8.3</version>
  	</dependency>
  </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>github.numberformat</groupId>
                <artifactId>blog-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <configuration>
                <gitUrl>https://github.com/numberformat/wordpress/tree/master/20131013/${artifactId}</gitUrl>
                </configuration>
            <executions>
              <execution>
                <id>1</id>
                <phase>site</phase>
                <goals>
                  <goal>generate</goal>
                </goals>
              </execution>
            </executions>
            </plugin>
        </plugins>
    </build>  
</project>
Full downloadable source for this page is available here.



Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 78 other followers

July 2017
S M T W T F S
« Mar    
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

Blog Stats

  • 822,600 hits