Java Tutorial/Intermediate GUIs/Working with tables/Locode viewer
Appearance
Locode viewer
[edit | edit source]This is a small program that downloads the list of UN location codes from the United Nations Economic Commission for Europe (UNECE) and displays the table of location codes with a special TableSorter class published by Oracle. The table sorter can sort by multiple rows (hold the CTRL key and select another row by clicking on the header to sort by a second or third row).
Exercises
[edit | edit source]- Improve the table model and the rendering of table cells. What can be improved?
- Add a filter that can filter the table with regular expressions.
- Display location codes on a globe or allow to select regions on a map.
(Attention: If you write a program using location codes or the table sorter please pay attention to either license.)
Source code
[edit | edit source]package org.wikiversity.java_tutorial;
import java.awt.BorderLayout;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class Locode
{
public final static String LOCODE_URL = "http://live.unece.org/fileadmin/DAM/cefact/locode/loc111csv.zip";
private final static int[] WIDTH = {
50, 50, 50, 250, 250, 50, 120, 50, 120, 50, 250, 50
};
public static void main (String[] args)
{
new Locode ().run (args);
}
private void run (String[] args)
{
File locodeArchive = file ("~/.un/locode/loc111csv.zip");
locodeArchive.getParentFile ().mkdirs ();
downloadArchive (locodeArchive);
if (!locodeArchive.exists ())
{
System.err.println ("Archive " + LOCODE_URL + " is not available.");
System.exit (1);
}
DefaultTableModel model = readDataFromArchive (locodeArchive);
System.out.println (model.getRowCount () + " rows.");
displayTable (model);
}
private void displayTable (DefaultTableModel model)
{
JFrame frame = new JFrame ("UN Location Codes");
frame.setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE);
frame.setLayout (new BorderLayout ());
frame.setSize (1024, 500);
JTable table = new JTable ();
JScrollPane js = new JScrollPane (table);
js.setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
js.setVerticalScrollBarPolicy (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
frame.add (js, BorderLayout.CENTER);
table.getTableHeader ().setReorderingAllowed (true);
table.getTableHeader ().setResizingAllowed (true);
TableSorter sorter = new TableSorter (model);
table.setModel (sorter);
sorter.setTableHeader (table.getTableHeader ());
for (int i=0; i<12; i++)
table.addColumn (new TableColumn (i, WIDTH[i]));
frame.setVisible (true);
}
private final static File file (String path)
{
if (path.charAt (0) == '~')
path = System.getProperty ("user.home") + path.substring (1);
return new File (path);
}
private void downloadArchive (File locodeArchive)
{
long lastModified = locodeArchive.lastModified ();
try {
URL url = new URL (LOCODE_URL);
URLConnection c = url.openConnection ();
if (c.getLastModified () <= lastModified)
return;
byte buffer[] = new byte[0xffff];
OutputStream os = null;
InputStream is = c.getInputStream ();
try {
int bytesRead;
os = new FileOutputStream (locodeArchive);
for (;;)
{
if ((bytesRead = is.read (buffer)) == -1)
break;
os.write (buffer, 0, bytesRead);
System.out.print (".");
System.out.flush ();
}
System.out.println ();
}
catch (Exception ex) {
closeSilently (os);
closeSilently (is);
}
}
catch (Exception ex) {
System.err.println ("Failed to download file: " + ex);
}
}
private DefaultTableModel readDataFromArchive (File inputFile)
{
DefaultTableModel model = new DefaultTableModel ();
Vector<Object> v = model.getDataVector ();
InputStream is = null;
JarFile file = null;
BufferedReader br;
String line, nextLine;
Object[] row;
try {
file = new JarFile (inputFile);
for (Enumeration<JarEntry> e = file.entries (); e.hasMoreElements (); )
{
JarEntry je = (JarEntry) e.nextElement ();
if (!je.getName ().toLowerCase ().endsWith (".csv"))
continue;
System.out.println ("Reading: " + je.getName ());
is = file.getInputStream (je);
br = new BufferedReader (new InputStreamReader (is));
loop:
while ((line = br.readLine ()) != null)
{
row = csvSplit (line);
while (row.length < 12) {
System.out.println ("Unexpected number of fields: " + row.length + ", reading next line for more input.");
if ((nextLine = br.readLine ()) == null)
break loop;
line += nextLine;
row = csvSplit (line);
printRow (line, row);
}
v.addElement (new Vector<Object> (Arrays.asList (row)));
}
}
}
catch (Exception ex) {
closeSilently ((Closeable) file);
closeSilently (is);
}
return model;
}
private void printRow (String line, Object[] row)
{
System.out.println ("---- " + line);
for (int i=0; i<row.length; i++)
{
System.out.println ("[" + i + "]: " + row[i]);
}
}
private void closeSilently (Closeable c)
{
if (c != null) {
try {
c.close ();
}
catch (IOException ex) { /* intentionally ignored */ }
}
}
private final static String[] STRING_ARRAY = new String[0];
private final static String[] csvSplit (String s)
{
ArrayList<String> a = new ArrayList<String> ();
char[] chars = s.toCharArray ();
int p1 = 0, p2;
for (;;)
{
if ((p2 = csvIndexOf (chars, p1)) == -1) {
a.add (trimQuotes (s.substring (p1)));
break;
}
String sub = s.substring (p1, p2);
p1 = p2 + 1;
a.add (trimQuotes (sub));
}
if (a.size () == 0)
return STRING_ARRAY;
return (String[]) a.toArray (STRING_ARRAY);
}
private final static String trimQuotes (String s)
{
if (s.startsWith ("\"") && s.endsWith ("\"")) {
return s.substring (1, s.length () - 1);
}
return s;
}
private final static int csvIndexOf (char[] str, int pos)
{
boolean notInsideQuote = true;
for (int i=pos; i<str.length; i++)
{
if (str[i] == '"')
notInsideQuote = !notInsideQuote;
if (str[i] == ',' && notInsideQuote)
return i;
}
return -1;
}
}