001    package org.springframework.richclient.settings.support;
002    
003    import javax.swing.JTable;
004    import javax.swing.table.TableColumn;
005    
006    import org.apache.commons.logging.Log;
007    import org.apache.commons.logging.LogFactory;
008    import org.springframework.richclient.settings.Settings;
009    import org.springframework.util.Assert;
010    import org.springframework.util.StringUtils;
011    
012    public class TableMemento implements Memento {
013            private static final Log logger = LogFactory.getLog(TableMemento.class);
014    
015            private static final String COLUMN_WIDTHS = "columnWidths";
016    
017            private static final String COLUMN_ORDER = "columnOrder";
018    
019            private static final String SELECTED_ROWS = "selectedRows";
020    
021            private static final String ANCHOR = "anchor";
022    
023            private static final String LEAD = "lead";
024    
025            private JTable table;
026    
027            private String key;
028    
029            public TableMemento(JTable table, String key) {
030                    Assert.notNull(table, "Table cannot be null");
031                    Assert.isTrue(StringUtils.hasText(key) || StringUtils.hasText(table.getName()),
032                                    "Key is empty or table has no name");
033    
034                    if (!StringUtils.hasText(key)) {
035                            key = table.getName();
036                    }
037                    
038                    this.table = table;
039                    this.key = key;
040            }
041    
042            public TableMemento(JTable table) {
043                    this(table, null);
044            }
045    
046            public void saveState(Settings settings) {
047                    saveSelectedRows(settings);
048                    saveColumnOrder(settings);
049                    saveColumnWidths(settings);
050            }
051    
052            void saveColumnWidths(Settings settings) {
053                    StringBuffer sb = new StringBuffer();
054                    int columnCount = table.getColumnCount();
055                    for (int i = 0; i < columnCount; i++) {
056                            sb.append(table.getColumnModel().getColumn(i).getWidth());
057                            if (i < columnCount - 1) {
058                                    sb.append(",");
059                            }
060                    }
061                    settings.setString(key + "." + COLUMN_WIDTHS, sb.toString());
062            }
063    
064            void saveColumnOrder(Settings settings) {
065                    StringBuffer sb = new StringBuffer();
066                    for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
067                            TableColumn column = table.getColumnModel().getColumn(i);
068                            sb.append(column.getModelIndex());
069                            if (i < table.getColumnModel().getColumnCount() - 1) {
070                                    sb.append(",");
071                            }
072                    }
073                    settings.setString(key + "." + COLUMN_ORDER, sb.toString());
074            }
075    
076            void saveSelectedRows(Settings settings) {
077                    String settingsKey = key + "." + SELECTED_ROWS;
078                    if (settings.contains(settingsKey)) {
079                            settings.remove(settingsKey);
080                    }
081    
082                    if (table.getSelectedRowCount() > 0) {
083                            settings.setInt(key + "." + ANCHOR, table.getSelectionModel().getAnchorSelectionIndex());
084                            settings.setInt(key + "." + LEAD, table.getSelectionModel().getLeadSelectionIndex());
085                    }
086    
087                    String selectionString = ArrayUtil.asIntervalString(table.getSelectedRows());
088                    if (selectionString.length() > 0) {
089                            settings.setString(settingsKey, selectionString);
090                    }
091            }
092    
093            public void restoreState(Settings settings) {
094                    restoreColumnOrder(settings);
095                    restoreColumnWidths(settings);
096                    restoreSelectedRows(settings);
097            }
098    
099            void restoreColumnWidths(Settings settings) {
100                    table.getSelectionModel().clearSelection();
101                    String widthSetting = settings.getString(key + "." + COLUMN_WIDTHS);
102                    if (StringUtils.hasText(widthSetting)) {
103    
104                            String[] stringWidths = widthSetting.split(",");
105    
106                            try {
107                                    int[] widths = ArrayUtil.toIntArray(stringWidths);
108    
109                                    if (widths.length == table.getColumnCount()) {
110                                            for (int i = 0; i < widths.length; i++) {
111                                                    table.getColumnModel().getColumn(i).setWidth(widths[i]);
112                                                    table.getColumnModel().getColumn(i).setPreferredWidth(widths[i]);
113                                            }
114                                    } else {
115                                            logger.warn("Unable to restore column widths, table has " + table.getColumnCount() + " columns, "
116                                                            + widths.length + " columns stored in settings");
117                                    }
118                            } catch (IllegalArgumentException e) {
119                                    logger.warn("Unable to restore column widths", e);
120                            }
121                    }
122            }
123    
124            void restoreSelectedRows(Settings settings) {
125                    table.getSelectionModel().clearSelection();
126                    if (settings.contains(key + "." + SELECTED_ROWS)) {
127                            String selection = settings.getString(key + "." + SELECTED_ROWS);
128                            if (StringUtils.hasText(selection)) {
129                                    String[] parts = selection.split(",");
130    
131                                    // find max row, so we can check before restoring row selections
132                                    String lastPart = parts[parts.length - 1];
133                                    int maxRow = -1;
134                                    if (lastPart.indexOf('-') >= 0) {
135                                            maxRow = Integer.parseInt(lastPart.substring(lastPart.indexOf('-')));
136                                    } else {
137                                            maxRow = Integer.parseInt(lastPart);
138                                    }
139                                    if (maxRow <= table.getRowCount() - 1) {
140                                            for (int i = 0; i < parts.length; i++) {
141                                                    if (parts[i].indexOf('-') >= 0) {
142                                                            String[] tmp = parts[i].split("-");
143                                                            table.addRowSelectionInterval(Integer.parseInt(tmp[0]), Integer.parseInt(tmp[1]));
144                                                    } else {
145                                                            int index = Integer.parseInt(parts[i]);
146                                                            table.addRowSelectionInterval(index, index);
147                                                    }
148                                            }
149                                    } else {
150                                            logger.warn("Unable to restore row selection, table has " + table.getRowCount()
151                                                            + " rows, setting has max row " + maxRow);
152                                    }
153                            }
154                    }
155    
156                    if (settings.contains(key + "." + ANCHOR)) {
157                            table.getSelectionModel().setAnchorSelectionIndex(settings.getInt(key + "." + ANCHOR));
158                    }
159                    if (settings.contains(key + "." + LEAD)) {
160                            table.getSelectionModel().setLeadSelectionIndex(settings.getInt(key + "." + LEAD));
161                    }
162            }
163    
164            void restoreColumnOrder(Settings settings) {
165                    table.getSelectionModel().clearSelection();
166                    String orderSetting = settings.getString(key + "." + COLUMN_ORDER);
167                    if (StringUtils.hasText(orderSetting)) {
168                            String[] stringColumns = orderSetting.split(",");
169    
170                            try {
171                                    int[] columns = ArrayUtil.toIntArray(stringColumns);
172    
173                                    if (columns.length == table.getColumnCount()) {
174                                            for (int i = 0; i < columns.length; i++) {
175                                                    table.moveColumn(getPosition(table, columns[i]), i);
176                                            }
177                                    } else {
178                                            logger.warn("Unable to restore column order, table has " + table.getColumnCount() + " columns, "
179                                                            + columns.length + " columns stored in settings");
180                                    }
181                            } catch (IllegalArgumentException e) {
182                                    logger.warn("Unable to restore column order.", e);
183                            }
184                    }
185            }
186    
187            /**
188             * Returns the position of the column for the given model index. The model
189             * index remains constant, but the position changes as the columns are
190             * moved.
191             * 
192             * @param table
193             *            the table
194             * @param modelIndex
195             *            the modelIndex
196             * @return the position
197             */
198            private static int getPosition(JTable table, int modelIndex) {
199                    for (int i = 0; i < table.getColumnCount(); i++) {
200                            TableColumn column = table.getColumnModel().getColumn(i);
201                            if (column.getModelIndex() == modelIndex) {
202                                    return i;
203                            }
204                    }
205                    throw new IllegalArgumentException("No column with modelIndex " + modelIndex + " found");
206            }
207    
208            protected String getKey() {
209                    return key;
210            }
211    
212            public JTable getTable() {
213                    return table;
214            }
215    }