1 | /******************************************************************************* | |
2 | * Copyhacked (H) 2012-2020. | |
3 | * This program and the accompanying materials | |
4 | * are made available under no term at all, use it like | |
5 | * you want, but share and discuss about it | |
6 | * every time possible with every body. | |
7 | * | |
8 | * Contributors: | |
9 | * ron190 at ymail dot com - initial implementation | |
10 | ******************************************************************************/ | |
11 | package com.jsql.view.swing.table; | |
12 | ||
13 | import com.jsql.util.LogLevelUtil; | |
14 | import com.jsql.view.swing.popupmenu.JPopupMenuTable; | |
15 | import com.jsql.view.swing.scrollpane.JScrollIndicator; | |
16 | import com.jsql.view.swing.tab.ButtonClose; | |
17 | import com.jsql.view.swing.text.JTextFieldPlaceholder; | |
18 | import com.jsql.view.swing.util.UiStringUtil; | |
19 | import org.apache.commons.lang3.StringUtils; | |
20 | import org.apache.logging.log4j.LogManager; | |
21 | import org.apache.logging.log4j.Logger; | |
22 | ||
23 | import javax.swing.*; | |
24 | import javax.swing.event.DocumentEvent; | |
25 | import javax.swing.event.DocumentListener; | |
26 | import javax.swing.table.DefaultTableCellRenderer; | |
27 | import javax.swing.table.TableCellRenderer; | |
28 | import javax.swing.table.TableModel; | |
29 | import javax.swing.table.TableRowSorter; | |
30 | import java.awt.*; | |
31 | import java.awt.event.*; | |
32 | import java.util.Comparator; | |
33 | import java.util.HashSet; | |
34 | import java.util.Set; | |
35 | import java.util.regex.Pattern; | |
36 | ||
37 | /** | |
38 | * Display a table for database values. Add keyboard shortcut, mouse icon, text | |
39 | * and header formatting. | |
40 | */ | |
41 | public class PanelTable extends JPanel { | |
42 | | |
43 | /** | |
44 | * Log4j logger sent to view. | |
45 | */ | |
46 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
47 | | |
48 | /** | |
49 | * Table to display in the panel. | |
50 | */ | |
51 | private final JTable tableValues; | |
52 | ||
53 | /** | |
54 | * Create a panel containing a table to display injection values. | |
55 | * | |
56 | * @param data Array 2D with injection table data | |
57 | * @param columnNames Names of columns from database | |
58 | */ | |
59 | public PanelTable(String[][] data, String[] columnNames) { | |
60 | | |
61 | super(new BorderLayout()); | |
62 | ||
63 | this.tableValues = new JTable(data, columnNames) { | |
64 | @Override | |
65 | public boolean isCellEditable(int row, int column) { | |
66 |
1
1. isCellEditable : replaced boolean return with true for com/jsql/view/swing/table/PanelTable$1::isCellEditable → NO_COVERAGE |
return false; |
67 | } | |
68 | }; | |
69 | ||
70 |
1
1. <init> : removed call to javax/swing/JTable::setAutoResizeMode → NO_COVERAGE |
this.tableValues.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); |
71 |
1
1. <init> : removed call to javax/swing/JTable::setSelectionMode → NO_COVERAGE |
this.tableValues.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
72 |
1
1. <init> : removed call to javax/swing/JTable::setColumnSelectionAllowed → NO_COVERAGE |
this.tableValues.setColumnSelectionAllowed(true); |
73 |
1
1. <init> : removed call to javax/swing/JTable::setRowHeight → NO_COVERAGE |
this.tableValues.setRowHeight(20); |
74 |
1
1. <init> : removed call to javax/swing/JTable::setRowSelectionAllowed → NO_COVERAGE |
this.tableValues.setRowSelectionAllowed(true); |
75 |
1
1. <init> : removed call to javax/swing/JTable::setCellSelectionEnabled → NO_COVERAGE |
this.tableValues.setCellSelectionEnabled(true); |
76 |
1
1. <init> : removed call to javax/swing/JTable::setGridColor → NO_COVERAGE |
this.tableValues.setGridColor(Color.LIGHT_GRAY); |
77 | ||
78 |
1
1. <init> : removed call to com/jsql/view/swing/table/PanelTable::initializeRenderer → NO_COVERAGE |
this.initializeRenderer(); |
79 | ||
80 |
1
1. <init> : removed call to javax/swing/table/JTableHeader::setReorderingAllowed → NO_COVERAGE |
this.tableValues.getTableHeader().setReorderingAllowed(false); |
81 | ||
82 |
1
1. <init> : removed call to com/jsql/view/swing/table/PanelTable::initializeMouseEvent → NO_COVERAGE |
this.initializeMouseEvent(); |
83 |
1
1. <init> : removed call to com/jsql/view/swing/table/PanelTable::initializeTabShortcut → NO_COVERAGE |
this.initializeTabShortcut(); |
84 | ||
85 | var columnAdjuster = new AdjusterTableColumn(this.tableValues); | |
86 |
1
1. <init> : removed call to com/jsql/view/swing/table/AdjusterTableColumn::adjustColumns → NO_COVERAGE |
columnAdjuster.adjustColumns(); |
87 | ||
88 | final TableRowSorter<TableModel> rowSorter = new TableRowSorter<>(this.tableValues.getModel()); | |
89 |
1
1. <init> : removed call to javax/swing/JTable::setRowSorter → NO_COVERAGE |
this.tableValues.setRowSorter(rowSorter); |
90 | | |
91 |
1
1. <init> : removed call to com/jsql/view/swing/table/PanelTable::initializeTableScroller → NO_COVERAGE |
this.initializeTableScroller(); |
92 |
1
1. <init> : removed call to com/jsql/view/swing/table/PanelTable::initializePanelSearch → NO_COVERAGE |
this.initializePanelSearch(rowSorter); |
93 | ||
94 | Comparator<Object> comparatorNumeric = new ComparatorColumn<>(); | |
95 | | |
96 |
2
1. <init> : changed conditional boundary → NO_COVERAGE 2. <init> : negated conditional → NO_COVERAGE |
for (var i = 0 ; i < this.tableValues.getColumnCount() ; i++) { |
97 |
1
1. <init> : removed call to javax/swing/table/TableRowSorter::setComparator → NO_COVERAGE |
rowSorter.setComparator(i, comparatorNumeric); |
98 | } | |
99 | } | |
100 | ||
101 | private void initializeMouseEvent() { | |
102 | | |
103 |
1
1. initializeMouseEvent : removed call to javax/swing/JTable::setDragEnabled → NO_COVERAGE |
this.tableValues.setDragEnabled(true); |
104 | ||
105 |
1
1. initializeMouseEvent : removed call to javax/swing/JTable::addMouseListener → NO_COVERAGE |
this.tableValues.addMouseListener(new MouseAdapter() { |
106 | @Override | |
107 | public void mousePressed(MouseEvent e) { | |
108 | | |
109 | PanelTable.this.tableValues.requestFocusInWindow(); | |
110 | ||
111 |
1
1. mousePressed : negated conditional → NO_COVERAGE |
if (SwingUtilities.isRightMouseButton(e)) { |
112 | | |
113 | // Keep selection when multiple cells are selected, | |
114 | // move focus only | |
115 | var p = e.getPoint(); | |
116 | ||
117 | var rowNumber = PanelTable.this.tableValues.rowAtPoint(p); | |
118 | var colNumber = PanelTable.this.tableValues.columnAtPoint(p); | |
119 | ||
120 | DefaultListSelectionModel modelRow = (DefaultListSelectionModel) PanelTable.this.tableValues.getSelectionModel(); | |
121 | DefaultListSelectionModel modelColumn = (DefaultListSelectionModel) PanelTable.this.tableValues.getColumnModel().getSelectionModel(); | |
122 | ||
123 |
1
1. mousePressed : removed call to javax/swing/DefaultListSelectionModel::moveLeadSelectionIndex → NO_COVERAGE |
modelRow.moveLeadSelectionIndex(rowNumber); |
124 |
1
1. mousePressed : removed call to javax/swing/DefaultListSelectionModel::moveLeadSelectionIndex → NO_COVERAGE |
modelColumn.moveLeadSelectionIndex(colNumber); |
125 | } | |
126 | } | |
127 | }); | |
128 | } | |
129 | ||
130 | private void initializeRenderer() { | |
131 | | |
132 | final TableCellRenderer cellRendererHeader = this.tableValues.getTableHeader().getDefaultRenderer(); | |
133 | final var cellRendererDefault = new DefaultTableCellRenderer(); | |
134 | | |
135 |
1
1. initializeRenderer : removed call to javax/swing/table/JTableHeader::setDefaultRenderer → NO_COVERAGE |
this.tableValues.getTableHeader().setDefaultRenderer( |
136 | (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) -> { | |
137 | | |
138 | JLabel label = (JLabel) cellRendererHeader.getTableCellRendererComponent( | |
139 | table, | |
140 | UiStringUtil.detectUtf8HtmlNoWrap(StringUtils.SPACE + value + StringUtils.SPACE), | |
141 | isSelected, | |
142 | hasFocus, | |
143 | row, | |
144 | column | |
145 | ); | |
146 | | |
147 |
1
1. lambda$initializeRenderer$0 : removed call to javax/swing/JLabel::setBorder → NO_COVERAGE |
label.setBorder( |
148 | BorderFactory.createCompoundBorder( | |
149 | BorderFactory.createMatteBorder(1, 0, 1, 1, Color.LIGHT_GRAY), | |
150 | BorderFactory.createEmptyBorder(0, 5, 0, 5) | |
151 | ) | |
152 | ); | |
153 | | |
154 |
1
1. lambda$initializeRenderer$0 : replaced return value with null for com/jsql/view/swing/table/PanelTable::lambda$initializeRenderer$0 → NO_COVERAGE |
return label; |
155 | } | |
156 | ); | |
157 | | |
158 |
1
1. initializeRenderer : removed call to javax/swing/JTable::setDefaultRenderer → NO_COVERAGE |
this.tableValues.setDefaultRenderer( |
159 | this.tableValues.getColumnClass(2), | |
160 | (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) -> { | |
161 | | |
162 | // Prepare cell value to be utf8 inspected | |
163 |
1
1. lambda$initializeRenderer$1 : negated conditional → NO_COVERAGE |
String cellValue = value != null ? value.toString() : StringUtils.EMPTY; |
164 | | |
165 | // Fix #90481: NullPointerException on getTableCellRendererComponent() | |
166 | try { | |
167 |
1
1. lambda$initializeRenderer$1 : replaced return value with null for com/jsql/view/swing/table/PanelTable::lambda$initializeRenderer$1 → NO_COVERAGE |
return cellRendererDefault.getTableCellRendererComponent( |
168 | table, UiStringUtil.detectUtf8HtmlNoWrap(cellValue), isSelected, hasFocus, row, column | |
169 | ); | |
170 | } catch (NullPointerException e) { | |
171 | | |
172 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
173 | return null; | |
174 | } | |
175 | } | |
176 | ); | |
177 | } | |
178 | ||
179 | private void initializeTableScroller() { | |
180 | | |
181 | var scroller = new JScrollIndicator(this.tableValues); | |
182 |
1
1. initializeTableScroller : removed call to javax/swing/JScrollPane::setBorder → NO_COVERAGE |
scroller.getScrollPane().setBorder(BorderFactory.createEmptyBorder(0, 0, -1, -1)); |
183 |
1
1. initializeTableScroller : removed call to javax/swing/JScrollPane::setViewportBorder → NO_COVERAGE |
scroller.getScrollPane().setViewportBorder(BorderFactory.createEmptyBorder(0, 0, -1, -1)); |
184 | | |
185 | AdjustmentListener singleItemScroll = adjustmentEvent -> { | |
186 | | |
187 | // The user scrolled the List (using the bar, mouse wheel or something else): | |
188 |
1
1. lambda$initializeTableScroller$2 : negated conditional → NO_COVERAGE |
if (adjustmentEvent.getAdjustmentType() == AdjustmentEvent.TRACK) { |
189 | | |
190 |
1
1. lambda$initializeTableScroller$2 : removed call to java/awt/Adjustable::setBlockIncrement → NO_COVERAGE |
adjustmentEvent.getAdjustable().setBlockIncrement(100); |
191 |
1
1. lambda$initializeTableScroller$2 : removed call to java/awt/Adjustable::setUnitIncrement → NO_COVERAGE |
adjustmentEvent.getAdjustable().setUnitIncrement(100); |
192 | } | |
193 | }; | |
194 | ||
195 |
1
1. initializeTableScroller : removed call to javax/swing/JScrollBar::addAdjustmentListener → NO_COVERAGE |
scroller.getScrollPane().getVerticalScrollBar().addAdjustmentListener(singleItemScroll); |
196 |
1
1. initializeTableScroller : removed call to javax/swing/JScrollBar::addAdjustmentListener → NO_COVERAGE |
scroller.getScrollPane().getHorizontalScrollBar().addAdjustmentListener(singleItemScroll); |
197 | ||
198 | var tableFixedColumn = new FixedColumnTable(); | |
199 |
1
1. initializeTableScroller : removed call to com/jsql/view/swing/table/FixedColumnTable::fixColumnSize → NO_COVERAGE |
tableFixedColumn.fixColumnSize(2, scroller.getScrollPane()); |
200 | | |
201 |
1
1. initializeTableScroller : removed call to com/jsql/view/swing/table/PanelTable::add → NO_COVERAGE |
this.add(scroller, BorderLayout.CENTER); |
202 | } | |
203 | ||
204 | private void initializePanelSearch(final TableRowSorter<TableModel> rowSorter) { | |
205 | | |
206 | final var panelSearch = new JPanel(new BorderLayout()); | |
207 |
1
1. initializePanelSearch : removed call to javax/swing/JPanel::setBorder → NO_COVERAGE |
panelSearch.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); |
208 | | |
209 | final JTextField textFilter = new JTextFieldPlaceholder("Find in table"); | |
210 |
1
1. initializePanelSearch : removed call to javax/swing/JPanel::add → NO_COVERAGE |
panelSearch.add(textFilter, BorderLayout.CENTER); |
211 | ||
212 | Action actionShowSearchTable = new ActionShowSearch(panelSearch, textFilter); | |
213 |
1
1. initializePanelSearch : removed call to javax/swing/ActionMap::put → NO_COVERAGE |
this.tableValues.getActionMap().put("search", actionShowSearchTable); |
214 |
1
1. initializePanelSearch : removed call to javax/swing/InputMap::put → NO_COVERAGE |
this.tableValues.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK), "search"); |
215 | ||
216 | Action actionCloseSearch = new ActionCloseSearch(textFilter, panelSearch, this); | |
217 |
1
1. initializePanelSearch : removed call to javax/swing/ActionMap::put → NO_COVERAGE |
textFilter.getActionMap().put("close", actionCloseSearch); |
218 |
1
1. initializePanelSearch : removed call to javax/swing/InputMap::put → NO_COVERAGE |
textFilter.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); |
219 | ||
220 | // Fix #43974: PatternSyntaxException on regexFilter() => Pattern.quote() | |
221 |
1
1. initializePanelSearch : removed call to javax/swing/text/Document::addDocumentListener → NO_COVERAGE |
textFilter.getDocument().addDocumentListener(new DocumentListener() { |
222 | | |
223 | private void insertUpdateFixed() { | |
224 | | |
225 | String text = textFilter.getText(); | |
226 | ||
227 |
1
1. insertUpdateFixed : negated conditional → NO_COVERAGE |
if (text.trim().isEmpty()) { |
228 |
1
1. insertUpdateFixed : removed call to javax/swing/table/TableRowSorter::setRowFilter → NO_COVERAGE |
rowSorter.setRowFilter(null); |
229 | } else { | |
230 |
1
1. insertUpdateFixed : removed call to javax/swing/table/TableRowSorter::setRowFilter → NO_COVERAGE |
rowSorter.setRowFilter(RowFilter.regexFilter("(?i)" + Pattern.quote(text))); |
231 | } | |
232 | } | |
233 | | |
234 | @Override | |
235 | public void insertUpdate(DocumentEvent e) { | |
236 |
1
1. insertUpdate : removed call to com/jsql/view/swing/table/PanelTable$3::insertUpdateFixed → NO_COVERAGE |
this.insertUpdateFixed(); |
237 | } | |
238 | ||
239 | @Override | |
240 | public void removeUpdate(DocumentEvent e) { | |
241 |
1
1. removeUpdate : removed call to com/jsql/view/swing/table/PanelTable$3::insertUpdateFixed → NO_COVERAGE |
this.insertUpdateFixed(); |
242 | } | |
243 | ||
244 | @Override | |
245 | public void changedUpdate(DocumentEvent e) { | |
246 | throw new UnsupportedOperationException("Not supported yet."); | |
247 | } | |
248 | }); | |
249 | ||
250 |
1
1. initializePanelSearch : removed call to javax/swing/JTable::setComponentPopupMenu → NO_COVERAGE |
this.tableValues.setComponentPopupMenu(new JPopupMenuTable(this.tableValues, actionShowSearchTable)); |
251 | | |
252 | JButton buttonCloseSearch = new ButtonClose(); | |
253 |
1
1. initializePanelSearch : removed call to javax/swing/JButton::addActionListener → NO_COVERAGE |
buttonCloseSearch.addActionListener(actionCloseSearch); |
254 |
1
1. initializePanelSearch : removed call to javax/swing/JPanel::add → NO_COVERAGE |
panelSearch.add(buttonCloseSearch, BorderLayout.EAST); |
255 | | |
256 |
1
1. initializePanelSearch : removed call to com/jsql/view/swing/table/PanelTable::add → NO_COVERAGE |
this.add(panelSearch, BorderLayout.SOUTH); |
257 | | |
258 |
1
1. initializePanelSearch : removed call to javax/swing/JPanel::setVisible → NO_COVERAGE |
panelSearch.setVisible(false); |
259 | } | |
260 | ||
261 | private void initializeTabShortcut() { | |
262 | | |
263 |
1
1. initializeTabShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE |
this.tableValues.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( |
264 | KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), | |
265 | null | |
266 | ); | |
267 |
1
1. initializeTabShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE |
this.tableValues.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( |
268 | KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK), | |
269 | null | |
270 | ); | |
271 | ||
272 | Set<AWTKeyStroke> forward = new HashSet<>( | |
273 | this.tableValues.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS) | |
274 | ); | |
275 | forward.add(KeyStroke.getKeyStroke("TAB")); | |
276 |
1
1. initializeTabShortcut : removed call to javax/swing/JTable::setFocusTraversalKeys → NO_COVERAGE |
this.tableValues.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forward); |
277 | | |
278 | Set<AWTKeyStroke> backward = new HashSet<>( | |
279 | this.tableValues.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS) | |
280 | ); | |
281 | backward.add(KeyStroke.getKeyStroke("shift TAB")); | |
282 |
1
1. initializeTabShortcut : removed call to javax/swing/JTable::setFocusTraversalKeys → NO_COVERAGE |
this.tableValues.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backward); |
283 | } | |
284 | ||
285 | /** | |
286 | * Select every cell. | |
287 | */ | |
288 | public void selectTable() { | |
289 |
1
1. selectTable : removed call to javax/swing/JTable::selectAll → NO_COVERAGE |
this.tableValues.selectAll(); |
290 | } | |
291 | ||
292 | /** | |
293 | * Perform copy event on current table. | |
294 | */ | |
295 | public void copyTable() { | |
296 | | |
297 | var nev = new ActionEvent(this.tableValues, ActionEvent.ACTION_PERFORMED, "copy"); | |
298 |
1
1. copyTable : removed call to javax/swing/Action::actionPerformed → NO_COVERAGE |
this.tableValues.getActionMap().get(nev.getActionCommand()).actionPerformed(nev); |
299 | } | |
300 | ||
301 | | |
302 | // Getter and setter | |
303 | | |
304 | public JTable getTableValues() { | |
305 |
1
1. getTableValues : replaced return value with null for com/jsql/view/swing/table/PanelTable::getTableValues → NO_COVERAGE |
return this.tableValues; |
306 | } | |
307 | } | |
Mutations | ||
66 |
1.1 |
|
70 |
1.1 |
|
71 |
1.1 |
|
72 |
1.1 |
|
73 |
1.1 |
|
74 |
1.1 |
|
75 |
1.1 |
|
76 |
1.1 |
|
78 |
1.1 |
|
80 |
1.1 |
|
82 |
1.1 |
|
83 |
1.1 |
|
86 |
1.1 |
|
89 |
1.1 |
|
91 |
1.1 |
|
92 |
1.1 |
|
96 |
1.1 2.2 |
|
97 |
1.1 |
|
103 |
1.1 |
|
105 |
1.1 |
|
111 |
1.1 |
|
123 |
1.1 |
|
124 |
1.1 |
|
135 |
1.1 |
|
147 |
1.1 |
|
154 |
1.1 |
|
158 |
1.1 |
|
163 |
1.1 |
|
167 |
1.1 |
|
182 |
1.1 |
|
183 |
1.1 |
|
188 |
1.1 |
|
190 |
1.1 |
|
191 |
1.1 |
|
195 |
1.1 |
|
196 |
1.1 |
|
199 |
1.1 |
|
201 |
1.1 |
|
207 |
1.1 |
|
210 |
1.1 |
|
213 |
1.1 |
|
214 |
1.1 |
|
217 |
1.1 |
|
218 |
1.1 |
|
221 |
1.1 |
|
227 |
1.1 |
|
228 |
1.1 |
|
230 |
1.1 |
|
236 |
1.1 |
|
241 |
1.1 |
|
250 |
1.1 |
|
253 |
1.1 |
|
254 |
1.1 |
|
256 |
1.1 |
|
258 |
1.1 |
|
263 |
1.1 |
|
267 |
1.1 |
|
276 |
1.1 |
|
282 |
1.1 |
|
289 |
1.1 |
|
298 |
1.1 |
|
305 |
1.1 |