Java Tutorial/Intermediate GUIs/Working with tables/Locode viewer

From Wikiversity
Jump to navigation Jump to search

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;
    }
}