org.yccheok.jstock.gui.JTableUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.yccheok.jstock.gui.JTableUtilities.java

Source

/*
 * JStock - Free Stock Market Software
 * Copyright (C) 2010 Yan Cheng CHEOK <yccheok@yahoo.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package org.yccheok.jstock.gui;

import java.awt.*;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import javax.swing.*;
import javax.swing.table.*;
//import org.jdesktop.swingx.table.DatePickerCellEditor;
import net.sf.nachocalendar.table.DateFieldTableEditor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.yccheok.jstock.gui.table.DateRendererDecoratorEx;
import org.yccheok.jstock.internationalization.GUIBundle;

public class JTableUtilities {

    /** Creates a new instance of JTableUtilities */
    private JTableUtilities() {
    }

    // Resize table column. User will still able to adjust the width manually.
    public static void makeTableColumnWidthFit(JTable jTable, int col, int margin) {
        makeTableColumnWidthFit(jTable, col, margin, false);
    }

    public static void makeTableColumnWidthFit(JTable jTable, int col, int margin, boolean locking) {
        // strategy - get max width for cells in column and
        // make that the preferred width
        TableColumnModel columnModel = jTable.getColumnModel();
        int maxwidth = 0;

        for (int row = 0; row < jTable.getRowCount(); row++) {
            TableCellRenderer rend = jTable.getCellRenderer(row, col);
            Object value = jTable.getValueAt(row, col);
            Component comp = rend.getTableCellRendererComponent(jTable, value, false, false, row, col);

            maxwidth = Math.max(comp.getPreferredSize().width + margin, maxwidth);
        } // for row

        TableColumn column = columnModel.getColumn(col);
        TableCellRenderer headerRenderer = column.getHeaderRenderer();
        if (headerRenderer == null) {
            headerRenderer = jTable.getTableHeader().getDefaultRenderer();
        }
        Object headerValue = column.getHeaderValue();
        Component headerComp = headerRenderer.getTableCellRendererComponent(jTable, headerValue, false, false, 0,
                col);

        maxwidth = Math.max(maxwidth, headerComp.getPreferredSize().width + margin);
        column.setPreferredWidth(maxwidth);
        if (locking) {
            // User will not able to adjust the width manually.
            column.setMinWidth(maxwidth);
            column.setMaxWidth(maxwidth);
        }
    }

    public static void setJTableOptions(JTable jTable, GUIOptions.JTableOptions jTableOptions) {
        if (jTableOptions.getColumnSize() <= 0) {
            // A bug introduced in version 1.0.5p. Due to incorrect
            // implementation of Utils.toXML, we may get a table option with 0
            // column turned on. To avoid all columns being turned off, we will
            // return early.
            return;
        }

        final Locale locale = jTableOptions.getLocale();
        // HACKING!
        boolean first_column_hacking_required = false;

        // Remove unwanted column. MUST BE DONE FIRST!
        for (int i = 0; i < jTable.getColumnCount(); i++) {
            final String name = jTable.getColumnName(i);

            // In to avoid the following situation.
            // 1. GUIOptions is being saved when we are in Chinese locale.
            // 2. Application is restarted in English locale.
            // 3. We are trying to compare English wording with Chinese wording.
            //
            // Before performing comparison, we shall convert name, to the locale
            // of table options.
            final java.util.List<String> keys = getKeys(name, Locale.getDefault());

            // Ensure correct resource file is being loaded.
            // When ResourceBundle.getBundle(..., locale) is being called, the
            // system will try to search in the following sequence.
            // 1. gui_<locale>.properties.
            // 2. gui_<default_locale>.properties.
            // 3. gui.properties.
            final Locale oldLocale = Locale.getDefault();
            Locale.setDefault(locale);
            try {
                final ResourceBundle bundle = ResourceBundle.getBundle("org.yccheok.jstock.data.gui", locale);
                Locale.setDefault(oldLocale);

                // Try all the keys.
                boolean found = false;
                for (String key : keys) {
                    final String translated_name = bundle.getString(key);
                    if (jTableOptions.contains(translated_name)) {
                        found = true;
                        break;
                    }
                }

                /* Remove any unwanted columns. */
                if (found == false) {
                    // HACKING!
                    // Some customers complain their first column of Watchlist,
                    // or Portfolio are being hidden. I'm not sure why that
                    // happen. YES! I have really no idea why that happen!
                    // This is a hacking way "if (i > 0)" to prevent such 
                    // problem. Shh...
                    if (i > 0) {
                        removeTableColumn(jTable, name);
                        i--;
                    } else {
                        first_column_hacking_required = true;
                    }
                }
            } finally {
                Locale.setDefault(oldLocale);
            }
        }

        final int optionsCount = jTableOptions.getColumnSize();
        final int tableCount = jTable.getColumnCount();

        // HACKING!
        // jTableOptions doesn't have first column information if first_column_hacking_required
        // is true. When perform column moving, we will start from 2nd column if
        // first_column_hacking_required is true. Assume first column will always
        // stay in first column.
        int target = first_column_hacking_required ? 1 : 0;

        /* Sort the columns according to user preference. */
        for (int i = 0; i < optionsCount; i++) {
            final String name = jTableOptions.getColumnName(i);
            final java.util.List<String> keys = getKeys(name, locale);
            assert (keys != null);

            int index = -1;
            for (int j = 0; j < tableCount; j++) {
                // Try all the keys.
                boolean found = false;
                String translated_name = null;
                for (String key : keys) {
                    translated_name = GUIBundle.getString(key);
                    if (jTable.getColumnName(j).equals(translated_name)) {
                        found = true;
                        break;
                    }
                }

                if (found) {
                    /* Restore width. */
                    jTable.getColumn(translated_name).setPreferredWidth(jTableOptions.getColumnWidth(i));

                    index = j;
                    break;
                }
            }

            if (index >= 0) {
                jTable.moveColumn(index, target++);
            }
        }
    }

    public static void removeTableColumn(JTable jTable, Object identifier) {
        TableColumn tableColumn = null;
        // If we try to getColumn which its identifier doesn't exist, 
        // IllegalArgumentException will be thrown.        
        try {
            tableColumn = jTable.getColumn(identifier);
        } catch (IllegalArgumentException ex) {
            log.error(null, ex);
        }

        if (tableColumn != null) {
            jTable.removeColumn(tableColumn);
        }
    }

    public static void insertTableColumnFromModel(JTable jTable, Object value, int clickedColumnIndex) {
        boolean isVisible = true;

        try {

            TableColumn tableColumn = jTable.getColumn(value);

        }

        catch (java.lang.IllegalArgumentException exp) {

            isVisible = false;

        }

        if (isVisible)
            return;

        TableModel tableModel = jTable.getModel();

        final int modelIndex = getModelColumnIndex(jTable, value);

        Class c = tableModel.getColumnClass(modelIndex);

        TableColumn tableColumn = new javax.swing.table.TableColumn(modelIndex, 0, jTable.getDefaultRenderer(c),
                jTable.getDefaultEditor(c));

        jTable.addColumn(tableColumn);

        makeTableColumnWidthFit(jTable, jTable.getColumnCount() - 1, 5);

        // If we right clicked on the 3rd column, and select a new column, we
        // would like the new column to be inserted into 4th column. Note that,
        // clickedColumnIndex will be < 0, if we right clicked on empty area.
        if (clickedColumnIndex < 0) {
            // Have it in the last column when we right clicked on empty area.
            jTable.moveColumn(jTable.getColumnCount() - 1, jTable.getColumnCount() - 1);
        } else {
            // +1, as we want our newly inserted column to be at the right of
            // clicked column.
            jTable.moveColumn(jTable.getColumnCount() - 1,
                    Math.min(jTable.getColumnCount() - 1, clickedColumnIndex + 1));
        }
    }

    public static int getModelColumnIndex(JTable jTable, Object value) {

        TableModel tableModel = jTable.getModel();

        if (tableModel instanceof StockTableModel) {

            return ((StockTableModel) tableModel).findColumn(value.toString());

        }

        try {

            TableColumn tableColumn = jTable.getColumn(value);

            return tableColumn.getModelIndex();

        } // Anti-pattern. We are depending on the exception throwing. Bad!

        catch (java.lang.IllegalArgumentException exp) {

            final int columnCount = tableModel.getColumnCount();

            for (int col = 0; col < columnCount; col++) {

                String s = tableModel.getColumnName(col);

                if (s.equals(value))

                    return col;

            }

        }

        return -1;

    }

    public static void scrollToVisible(JTable table, int rowIndex, int vColIndex) {
        if (!(table.getParent() instanceof JViewport)) {
            return;
        }

        JViewport viewport = (JViewport) table.getParent();

        // This rectangle is relative to the table where the
        // northwest corner of cell (0,0) is always (0,0).
        Rectangle rect = table.getCellRect(rowIndex, vColIndex, true);

        // The location of the viewport relative to the table
        Point pt = viewport.getViewPosition();

        // Translate the cell location so that it is relative
        // to the view, assuming the northwest corner of the
        // view is (0,0)
        rect.setLocation(rect.x - pt.x, rect.y - pt.y);

        // Scroll the area into view
        viewport.scrollRectToVisible(rect);
    }

    /**
     * Sets the editor/renderer for Date objects to provided JTable, for the specified column.
     * @param table JTable to set up
     * @param row Column to apply
     */
    public static void setDateEditorAndRendererForRow(JTable table, int row) {
        final TableColumn column = table.getColumnModel().getColumn(row);
        // SwingX's. Pretty but buggy.
        //column.setCellEditor(new DatePickerCellEditor());
        column.setCellEditor(new DateFieldTableEditor());
        final DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT);
        column.setCellRenderer(new DateRendererDecoratorEx(column.getCellRenderer(), format));
    }

    /**
     * Get the keys for a given string and locale.
     *
     * @param string the string for the desired key
     * @param locale the locale for the desired key
     * @return the keys for a given string and locale
     */
    private static java.util.List<String> getKeys(String string, Locale locale) {
        if (string2KeyMap.containsKey(locale)) {
            final Map<String, java.util.List<String>> string2Key = string2KeyMap.get(locale);
            final java.util.List<String> result = string2Key.get(string);
            if (result == null) {
                return java.util.Collections.EMPTY_LIST;
            }
            return result;
        }

        final Map<String, java.util.List<String>> string2Key = new HashMap<String, java.util.List<String>>();

        // Ensure correct resource file is being loaded.
        // When ResourceBundle.getBundle(..., locale) is being called, the
        // system will try to search in the following sequence.
        // 1. gui_<locale>.properties.
        // 2. gui_<default_locale>.properties.
        // 3. gui.properties.
        final Locale oldLocale = Locale.getDefault();
        Locale.setDefault(locale);
        try {
            final ResourceBundle bundle = ResourceBundle.getBundle("org.yccheok.jstock.data.gui", locale);

            final Enumeration<String> enumeration = bundle.getKeys();
            while (enumeration.hasMoreElements()) {
                final String key = enumeration.nextElement();
                final String str = bundle.getString(key);
                java.util.List list = string2Key.get(str);
                if (list == null) {
                    list = new ArrayList<String>();
                    string2Key.put(str, list);
                }
                list.add(key);
            }

            string2KeyMap.put(locale, string2Key);
        } finally {
            Locale.setDefault(oldLocale);
        }

        final java.util.List<String> result = string2Key.get(string);
        if (result == null) {
            return java.util.Collections.EMPTY_LIST;
        }
        return result;
    }

    private static final Log log = LogFactory.getLog(JTableUtilities.class);
    private static final Map<Locale, Map<String, java.util.List<String>>> string2KeyMap = new HashMap<Locale, Map<String, java.util.List<String>>>();
}