001package jmri.jmrix.pricom.pockettester;
002
003import java.util.Vector;
004import javax.swing.JButton;
005import javax.swing.JLabel;
006import javax.swing.JTable;
007import javax.swing.JTextField;
008import javax.swing.table.TableCellEditor;
009import javax.swing.table.TableColumnModel;
010import jmri.util.table.ButtonEditor;
011import jmri.util.table.ButtonRenderer;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * Table data model for display of DCC packet contents
017 *
018 * @author Bob Jacobsen Copyright (C) 2005
019 */
020public class PacketDataModel extends javax.swing.table.AbstractTableModel {
021
022    static public final int ADDRESSCOLUMN = 0;
023    static public final int TYPECOLUMN = 1;
024    static public final int DETAILCOLUMN = 2;
025    static public final int MONITORBUTTONCOLUMN = 3;
026
027    static public final int NUMCOLUMN = 4;
028
029    /**
030     * Returns the number of rows to be displayed. This can vary depending on
031     * what has been seen
032     * @return number of rows
033     */
034    @Override
035    public int getRowCount() {
036        return keys.size();
037    }
038
039    @Override
040    public int getColumnCount() {
041        return NUMCOLUMN;
042    }
043
044    @Override
045    public String getColumnName(int col) {
046        switch (col) {
047            case ADDRESSCOLUMN:
048                return Bundle.getMessage("ColumnAddress");
049            case TYPECOLUMN:
050                return Bundle.getMessage("ColumnType");
051            case DETAILCOLUMN:
052                return Bundle.getMessage("ColumnDetails");
053            case MONITORBUTTONCOLUMN:
054                return "";   // no heading, as button is clear
055            default:
056                return "unknown";
057        }
058    }
059
060    @Override
061    public Class<?> getColumnClass(int col) {
062        switch (col) {
063            case ADDRESSCOLUMN:
064            case TYPECOLUMN:
065            case DETAILCOLUMN:
066                return String.class;
067            case MONITORBUTTONCOLUMN:
068                return JButton.class;
069            default:
070                return null;
071        }
072    }
073
074    @Override
075    public boolean isCellEditable(int row, int col) {
076        switch (col) {
077            case MONITORBUTTONCOLUMN:
078                return true;
079            default:
080                return false;
081        }
082    }
083
084    static final Boolean True = Boolean.valueOf(true);
085    static final Boolean False = Boolean.valueOf(false);
086
087    @Override
088    public Object getValueAt(int row, int col) {
089
090        switch (col) {
091            case ADDRESSCOLUMN:  // slot number
092                return addresses.elementAt(row);
093            case TYPECOLUMN:  //
094                return types.elementAt(row);
095            case DETAILCOLUMN:  //
096                return details.elementAt(row);
097            case MONITORBUTTONCOLUMN:  //
098                return Bundle.getMessage("ButtonTrace");
099            default:
100                log.error("internal state inconsistent with table request for {} {}", row, col);
101                return null;
102        }
103    }
104
105    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
106                        justification="better to keep cases in column order rather than to combine")
107    public int getPreferredWidth(int col) {
108        switch (col) {
109            case ADDRESSCOLUMN:
110                return new JTextField(8).getPreferredSize().width;
111            case TYPECOLUMN:
112                return new JTextField(12).getPreferredSize().width;
113            case DETAILCOLUMN:
114                return new JTextField(12).getPreferredSize().width;
115            case MONITORBUTTONCOLUMN:
116                return new JButton("Details").getPreferredSize().width;
117            default:
118                return new JLabel(" <unknown> ").getPreferredSize().width;
119        }
120    }
121
122    @Override
123    public void setValueAt(Object value, int row, int col) {
124        switch (col) {
125            case MONITORBUTTONCOLUMN:
126                MonitorFrame f = new MonitorFrame();
127                try {
128                    f.initComponents();
129                    f.setFilter((String) getValueAt(row, ADDRESSCOLUMN));
130                    source.addListener(f);
131                } catch (Exception ex) {
132                    log.error("starting MonitorFrame caught exception: {}", ex.toString());
133                }
134                f.setVisible(true);
135
136                return;
137            default:
138                return;
139        }
140    }
141
142    /**
143     * Configure a table to have our standard rows and columns.
144     * This is optional, in that other table formats can use this table model.
145     * But we put it here to help keep it consistent.
146     *
147     * @param slotTable the table to configure.
148     */
149    public void configureTable(JTable slotTable) {
150        // allow reordering of the columns
151        slotTable.getTableHeader().setReorderingAllowed(true);
152
153        // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541)
154        slotTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
155
156        // resize columns as requested
157        for (int i = 0; i < slotTable.getColumnCount(); i++) {
158            int width = getPreferredWidth(i);
159            slotTable.getColumnModel().getColumn(i).setPreferredWidth(width);
160        }
161        slotTable.sizeColumnsToFit(-1);
162
163        // install a button renderer & editor in the "DISP" column for freeing a slot
164        setColumnToHoldButton(slotTable, PacketDataModel.MONITORBUTTONCOLUMN);
165
166    }
167
168    void setColumnToHoldButton(JTable slotTable, int column) {
169        TableColumnModel tcm = slotTable.getColumnModel();
170        // install the button renderers & editors in this column
171        ButtonRenderer buttonRenderer = new ButtonRenderer();
172        tcm.getColumn(column).setCellRenderer(buttonRenderer);
173        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
174        tcm.getColumn(column).setCellEditor(buttonEditor);
175        // ensure the table rows, columns have enough room for buttons
176        slotTable.setRowHeight(new JButton("  " + getValueAt(1, column)).getPreferredSize().height);
177        slotTable.getColumnModel().getColumn(column)
178                .setPreferredWidth(new JButton("  " + getValueAt(1, column)).getPreferredSize().width);
179    }
180
181    public void dispose() {
182    }
183
184    public void asciiFormattedMessage(String m) {
185        String key = getKey(m);
186        if (key == null) {
187            return;  // ignore this input
188        }
189        String address = getPrefix(m);
190        String type = getType(m);
191        String detail = getDetails(m);
192
193        // see if exists
194        int index = keys.indexOf(key);
195
196        if (index == -1) {
197            // not present, add
198            keys.addElement(key);
199            addresses.addElement(address);
200            types.addElement(type);
201            details.addElement(detail);
202
203            index = keys.indexOf(key);
204            fireTableRowsInserted(index, index);
205        } else {
206            // index has been set, update  
207            keys.setElementAt(key, index);
208            addresses.setElementAt(address, index);
209            types.setElementAt(type, index);
210            details.setElementAt(detail, index);
211            fireTableRowsUpdated(index, index);
212        }
213    }
214
215    DataSource source;
216
217    public void setSource(DataSource d) {
218        source = d;
219    }
220
221    public void reset() {
222        int count = keys.size();
223        keys = new Vector<String>();
224        addresses = new Vector<String>();
225        types = new Vector<String>();
226        details = new Vector<String>();
227        fireTableRowsDeleted(0, count - 1);
228    }
229
230    Vector<String> keys = new Vector<String>();
231    Vector<String> addresses = new Vector<String>();
232    Vector<String> types = new Vector<String>();
233    Vector<String> details = new Vector<String>();
234
235    /**
236     * Find the display key from the current input line. A later input line that
237     * maps to the same key will overwrite the earlier line.
238     * <p>
239     * The current implementation is address+type, so that separate lines will
240     * be used for each type sent to the same address.
241     *
242     * @param s Current line of input
243     * @return null if not to be displayed, e.g. no address
244     */
245    String getKey(String s) {
246        if (!s.startsWith("ADR=")) {
247            return null;
248        } else {
249            return s.substring(0, 22);
250        }
251    }
252
253    /**
254     * Find the address (1st column) from the current input line
255     * @param s Current line of input
256     * @return address 
257     */
258    String getPrefix(String s) {
259        return s.substring(0, 8);
260    }
261
262    /**
263     * Find the message type (2nd column) from the current input line. Should
264     * not be called if getPrefix has returned null.
265     *
266     * @param s Current line of input
267     * @return null if not to be displayed, e.g. too short
268     */
269    String getType(String s) {
270        return s.substring(9, 22);
271    }
272
273    /**
274     * Find the message arguments (3rd column) from the current input line.
275     * Should not be called if getPrefix has returned null.
276     *
277     * @param s Current line of input
278     * @return null if not to be displayed, e.g. too short
279     */
280    String getDetails(String s) {
281        return s.substring(23, s.length() - 1);
282    }
283
284    private final static Logger log = LoggerFactory.getLogger(PacketDataModel.class);
285
286}