// Table.java package net.sf.gogui.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Map; import java.util.TreeMap; /** Table of string elements. */ public class Table { /** Error thrown on invalid table location argument. */ public static class InvalidLocation extends ErrorMessage { public InvalidLocation(String message) { super(message); } } /** Error thrown on invalid file format when reading a table. */ public static class InvalidFormat extends ErrorMessage { public InvalidFormat(String message) { super(message); } } /** Error thrown if table element has not the expected type. */ public static class InvalidElement extends ErrorMessage { public InvalidElement(String message) { super(message); } } public Table() { m_columnTitles = new ArrayList<String>(); m_numberColumns = 0; } public Table(ArrayList<String> columnTitles) { m_columnTitles = columnTitles; m_numberColumns = columnTitles.size(); } public String get(int column, int row) { return getRow(row).get(column); } public String get(String columnTitle, int row) throws InvalidLocation { return get(getColumnIndex(columnTitle), row); } public double getDouble(int column, int row) throws InvalidLocation, InvalidElement { try { String s = get(column, row); if (s == null) s = ""; return Double.parseDouble(s); } catch (NumberFormatException e) { throw new InvalidElement("Expected floating point number in " + "table (column=" + column + ", row=" + row + ")"); } } public double getDouble(String columnTitle, int row) throws InvalidLocation, InvalidElement { return getDouble(getColumnIndex(columnTitle), row); } public int getInt(int column, int row) throws InvalidElement { try { String s = get(column, row); if (s == null) s = ""; return Integer.parseInt(s); } catch (NumberFormatException e) { throw new InvalidElement("Expected integer in table (column=" + column + ", row=" + row + ")"); } } public int getInt(String columnTitle, int row) throws InvalidLocation, InvalidElement { return getInt(getColumnIndex(columnTitle), row); } public int getColumnIndex(String column) throws InvalidLocation { for (int i = 0; i < m_numberColumns; ++i) { String title = getColumnTitle(i); if (title.equals(column)) return i; } throw new InvalidLocation("No such column in table: " + column); } public String getColumnTitle(int index) { return m_columnTitles.get(index); } @SuppressWarnings("unchecked") public ArrayList<String> getColumnTitles() { return (ArrayList<String>)m_columnTitles.clone(); } public int getNumberColumns() { return m_columnTitles.size(); } public int getNumberRows() { return m_rows.size(); } /** Get meta information. @param key the property key @return The property value or null, if it dows not exist */ public String getProperty(String key) { return getProperty(key, null); } /** Get meta information. @param key the property key @param def the default value, if this property does not exist */ public String getProperty(String key, String def) { if (! hasProperty(key)) return def; return m_properties.get(key); } public boolean hasProperty(String key) { return m_properties.containsKey(key); } public void read(File file) throws FileNotFoundException, IOException, InvalidFormat { read(new FileReader(file)); } public void read(Reader reader) throws IOException, InvalidFormat { BufferedReader bufferedReader = new BufferedReader(reader); m_lineNumber = 0; m_propertiesRead = false; String line = null; while ((line = bufferedReader.readLine()) != null) { ++m_lineNumber; handleLine(line); } bufferedReader.close(); } public void save(File file) throws IOException { FileWriter writer = new FileWriter(file); try { save(writer); } finally { writer.close(); } } public void save(Writer out) throws IOException { save(out, true); } public void save(Writer out, boolean withHeader) throws IOException { if (withHeader) { for (Map.Entry<String,String> entry : m_properties.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); out.write("# " + key + ": " + value + "\n"); } out.write("#\n#"); for (int i = 0; i < m_numberColumns; ++i) { out.write(getColumnTitle(i)); if (i < m_numberColumns - 1) out.write('\t'); else out.write('\n'); } } for (int i = 0; i < m_rows.size(); ++i) { ArrayList<String> row = m_rows.get(i); for (int j = 0; j < m_numberColumns; ++j) { String value = row.get(j); if (value != null) out.write(value); if (j < m_numberColumns - 1) out.write('\t'); else out.write('\n'); } } } /** Set element in last row. @param column Column in last row. @param value The value (must not contain newlines or tabs). */ public void set(int column, String value) { assert m_lastRow.get(column) == null; // Values containing newlines and tabs are not supported by save() // yet assert value == null || value.indexOf("\n") < 0; assert value == null || value.indexOf("\t") < 0; m_lastRow.set(column, value); } public void set(String column, int value) throws InvalidLocation { set(column, Integer.toString(value)); } public void set(String column, double value) throws InvalidLocation { set(column, Double.toString(value)); } public void set(String column, String value) throws InvalidLocation { set(getColumnIndex(column), value); } /** Set meta information. @param key the property key @param value the property value */ public Object setProperty(String key, String value) { return m_properties.put(key, value); } public void startRow() { ArrayList<String> row = new ArrayList<String>(m_numberColumns); for (int i = 0; i < m_numberColumns; ++i) row.add(null); m_rows.add(row); m_lastRow = row; } public void sortByIntColumn(int column, int rowBegin, int rowEnd) throws InvalidElement { for (int row1 = rowBegin; row1 < rowEnd - 1; ++row1) for (int row2 = row1 + 1; row2 < rowEnd; ++row2) if (getInt(column, row2) < getInt(column, row1)) { ArrayList<String> tmp = m_rows.get(row1); m_rows.set(row1, m_rows.get(row2)); m_rows.set(row2, tmp); } } public void sortByIntColumn(String columnTitle, int rowBegin, int rowEnd) throws InvalidElement, InvalidLocation { sortByIntColumn(getColumnIndex(columnTitle), rowBegin, rowEnd); } private boolean m_propertiesRead; private int m_lineNumber; private int m_numberColumns; private final Map<String,String> m_properties = new TreeMap<String,String>(); private final ArrayList<String> m_columnTitles; private ArrayList<String> m_lastRow; private final ArrayList<ArrayList<String>> m_rows = new ArrayList<ArrayList<String>>(); private void addColumnTitle(String columnTitle) { m_columnTitles.add(columnTitle); ++m_numberColumns; } private ArrayList<String> getRow(int index) { return m_rows.get(index); } private void handleComment(String comment) { comment = comment.trim(); if (m_propertiesRead) { String[] array = comment.split("\\t"); for (int i = 0; i < array.length; ++i) addColumnTitle(array[i]); return; } if (comment.equals("")) { m_propertiesRead = true; return; } int pos = comment.indexOf(':'); if (pos < 0) { System.err.println("Invalid line " + m_lineNumber + ": " + comment); return; } String key = comment.substring(0, pos).trim(); String value = comment.substring(pos + 1).trim(); setProperty(key, value); } private void handleLine(String line) throws InvalidFormat { line = line.trim(); if (line.startsWith("#")) { handleComment(line.substring(1)); return; } String[] array = line.split("\\t"); if (array.length > getNumberColumns()) throw new InvalidFormat("Invalid line " + m_lineNumber + ": " + line); startRow(); for (int i = 0; i < array.length; ++i) set(i, array[i]); } }