DnDList.java

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.list;
12
13
import com.jsql.util.I18nUtil;
14
import com.jsql.util.LogLevelUtil;
15
import com.jsql.view.swing.util.UiUtil;
16
import org.apache.commons.io.FilenameUtils;
17
import org.apache.commons.lang3.StringUtils;
18
import org.apache.logging.log4j.LogManager;
19
import org.apache.logging.log4j.Logger;
20
21
import javax.swing.*;
22
import java.awt.event.*;
23
import java.io.BufferedReader;
24
import java.io.File;
25
import java.io.FileReader;
26
import java.io.IOException;
27
import java.nio.charset.StandardCharsets;
28
import java.util.ArrayList;
29
import java.util.List;
30
31
/**
32
 * A list supporting drag and drop.
33
 */
34
public class DnDList extends JList<ItemList> {
35
    
36
    /**
37
     * Log4j logger sent to view.
38
     */
39
    private static final Logger LOGGER = LogManager.getRootLogger();
40
    
41
    /**
42
     * Model for the JList.
43
     */
44
    protected final DefaultListModel<ItemList> listModel;
45
    
46
    /**
47
     * List of default items.
48
     */
49
    private final transient List<ItemList> defaultList;
50
    
51
    /**
52
     * Create a JList decorated with drag/drop features.
53
     * @param newList List to decorate
54
     */
55
    public DnDList(List<ItemList> newList) {
56
        
57
        this.defaultList = newList;
58
59
        this.listModel = new DefaultListModel<>();
60
61
        for (ItemList path: newList) {
62 1 1. <init> : removed call to javax/swing/DefaultListModel::addElement → NO_COVERAGE
            this.listModel.addElement(path);
63
        }
64
65 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::setModel → NO_COVERAGE
        this.setModel(this.listModel);
66
67
        ListCellRenderer<ItemList> renderer = new RendererComplexCell();
68 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::setCellRenderer → NO_COVERAGE
        this.setCellRenderer(renderer);
69
        
70 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::initializeActionMap → NO_COVERAGE
        this.initializeActionMap();
71 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::initializeListener → NO_COVERAGE
        this.initializeListener();
72
        
73 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::setDragEnabled → NO_COVERAGE
        this.setDragEnabled(true);
74 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::setDropMode → NO_COVERAGE
        this.setDropMode(DropMode.INSERT);
75
76
        // Set Drag and Drop
77 1 1. <init> : removed call to com/jsql/view/swing/list/DnDList::setTransferHandler → NO_COVERAGE
        this.setTransferHandler(new ListTransfertHandler());
78
    }
79
80
    private void initializeListener() {
81
        
82 1 1. initializeListener : removed call to com/jsql/view/swing/list/DnDList::addMouseListener → NO_COVERAGE
        this.addMouseListener(new MouseAdapterMenuAction(this));
83
84
        // Allows color change when list loses/gains focus
85 1 1. initializeListener : removed call to com/jsql/view/swing/list/DnDList::addFocusListener → NO_COVERAGE
        this.addFocusListener(new FocusListener() {
86
                
87
            @Override
88
            public void focusLost(FocusEvent arg0) {
89 1 1. focusLost : removed call to com/jsql/view/swing/list/DnDList::repaint → NO_COVERAGE
                DnDList.this.repaint();
90
            }
91
            
92
            @Override
93
            public void focusGained(FocusEvent arg0) {
94 1 1. focusGained : removed call to com/jsql/view/swing/list/DnDList::repaint → NO_COVERAGE
                DnDList.this.repaint();
95
            }
96
        });
97
        
98
        // Allows deleting values
99 1 1. initializeListener : removed call to com/jsql/view/swing/list/DnDList::addKeyListener → NO_COVERAGE
        this.addKeyListener(new KeyAdapter() {
100
            @Override
101
            public void keyPressed(KeyEvent arg0) {
102 1 1. keyPressed : negated conditional → NO_COVERAGE
                if (arg0.getKeyCode() == KeyEvent.VK_DELETE) {
103 1 1. keyPressed : removed call to com/jsql/view/swing/list/DnDList::removeSelectedItem → NO_COVERAGE
                    DnDList.this.removeSelectedItem();
104
                }
105
            }
106
        });
107
    }
108
109
    private void initializeActionMap() {
110
        
111
        // Transform Cut, selects next value
112
        var listActionMap = this.getActionMap();
113
        
114 1 1. initializeActionMap : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        listActionMap.put(TransferHandler.getCutAction().getValue(Action.NAME), new AbstractAction() {
115
            @Override
116
            public void actionPerformed(ActionEvent e) {
117
                
118 1 1. actionPerformed : negated conditional → NO_COVERAGE
                if (DnDList.this.getSelectedValuesList().isEmpty()) {
119
                    return;
120
                }
121
                
122
                List<ItemList> selectedValues = DnDList.this.getSelectedValuesList();
123
                List<ItemList> siblings = new ArrayList<>();
124
                
125
                for (ItemList value: selectedValues) {
126
                    
127
                    int valueIndex = DnDList.this.listModel.indexOf(value);
128
129 3 1. actionPerformed : changed conditional boundary → NO_COVERAGE
2. actionPerformed : negated conditional → NO_COVERAGE
3. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
                    if (valueIndex < DnDList.this.listModel.size() - 1) {
130 1 1. actionPerformed : Replaced integer addition with subtraction → NO_COVERAGE
                        siblings.add(DnDList.this.listModel.get(valueIndex + 1));
131 2 1. actionPerformed : negated conditional → NO_COVERAGE
2. actionPerformed : changed conditional boundary → NO_COVERAGE
                    } else if (valueIndex > 0) {
132 1 1. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
                        siblings.add(DnDList.this.listModel.get(valueIndex - 1));
133
                    }
134
                }
135
136 1 1. actionPerformed : removed call to javax/swing/Action::actionPerformed → NO_COVERAGE
                TransferHandler.getCutAction().actionPerformed(e);
137
                
138
                for (ItemList sibling: siblings) {
139 1 1. actionPerformed : removed call to com/jsql/view/swing/list/DnDList::setSelectedValue → NO_COVERAGE
                    DnDList.this.setSelectedValue(sibling, true);
140
                }
141
            }
142
        });
143
144 1 1. initializeActionMap : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        listActionMap.put(
145
            TransferHandler.getCopyAction().getValue(Action.NAME),
146
            TransferHandler.getCopyAction()
147
        );
148
        
149 1 1. initializeActionMap : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        listActionMap.put(
150
            TransferHandler.getPasteAction().getValue(Action.NAME),
151
            TransferHandler.getPasteAction()
152
        );
153
    }
154
155
    /**
156
     * Delete selected items from the list.
157
     */
158
    public void removeSelectedItem() {
159
        
160 1 1. removeSelectedItem : negated conditional → NO_COVERAGE
        if (this.getSelectedValuesList().isEmpty()) {
161
            return;
162
        }
163
164
        List<ItemList> selectedValues = this.getSelectedValuesList();
165
        
166
        for (ItemList itemSelected: selectedValues) {
167
            
168
            int indexOfItemSelected = this.listModel.indexOf(itemSelected);
169
            this.listModel.removeElement(itemSelected);
170
            
171 1 1. removeSelectedItem : negated conditional → NO_COVERAGE
            if (indexOfItemSelected == this.listModel.getSize()) {
172 2 1. removeSelectedItem : Replaced integer subtraction with addition → NO_COVERAGE
2. removeSelectedItem : removed call to com/jsql/view/swing/list/DnDList::setSelectedIndex → NO_COVERAGE
                this.setSelectedIndex(indexOfItemSelected - 1);
173
            } else {
174 1 1. removeSelectedItem : removed call to com/jsql/view/swing/list/DnDList::setSelectedIndex → NO_COVERAGE
                this.setSelectedIndex(indexOfItemSelected);
175
            }
176
        }
177
        
178
        try {
179
            var rectangle = this.getCellBounds(
180
                this.getMinSelectionIndex(),
181
                this.getMaxSelectionIndex()
182
            );
183
            
184 1 1. removeSelectedItem : negated conditional → NO_COVERAGE
            if (rectangle != null) {
185 1 1. removeSelectedItem : removed call to com/jsql/view/swing/list/DnDList::scrollRectToVisible → NO_COVERAGE
                this.scrollRectToVisible(
186
                    this.getCellBounds(
187
                        this.getMinSelectionIndex(),
188
                        this.getMaxSelectionIndex()
189
                    )
190
                );
191
            }
192
        } catch (NullPointerException e) {
193
            // Report NullPointerException #1571 : manual scroll elsewhere then run action
194
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
195
        }
196
    }
197
198
    /**
199
     * Load a file into the list (drag/drop or copy/paste).
200
     * @param filesToImport
201
     * @param position
202
     */
203
    public void dropPasteFile(final List<File> filesToImport, int position) {
204
        
205 1 1. dropPasteFile : negated conditional → NO_COVERAGE
        if (filesToImport.isEmpty()) {
206
            return;
207
        }
208
        
209
        for (File fileToImport : filesToImport) {
210
            
211
            // Report NoSuchMethodError #1617
212
            if (
213
                !FilenameUtils
214
                .getExtension(fileToImport.getPath())
215 1 1. dropPasteFile : negated conditional → NO_COVERAGE
                .matches("txt|csv|ini")
216
            ) {
217
                
218
                // Fix #42832: ClassCastException on showMessageDialog()
219
                try {
220 1 1. dropPasteFile : removed call to javax/swing/JOptionPane::showMessageDialog → NO_COVERAGE
                    JOptionPane.showMessageDialog(
221
                        this.getTopLevelAncestor(),
222
                        I18nUtil.valueByKey("LIST_IMPORT_ERROR_LABEL"),
223
                        I18nUtil.valueByKey("LIST_IMPORT_ERROR_TITLE"),
224
                        JOptionPane.ERROR_MESSAGE,
225
                        UiUtil.ICON_ERROR
226
                    );
227
                } catch (ClassCastException e) {
228
                    LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
229
                }
230
                
231
                return;
232
            }
233
        }
234
235
        var options = new String[] {
236
            I18nUtil.valueByKey("LIST_IMPORT_CONFIRM_REPLACE"),
237
            I18nUtil.valueByKey("LIST_IMPORT_CONFIRM_ADD"),
238
            I18nUtil.valueByKey("LIST_ADD_VALUE_CANCEL")
239
        };
240
        
241
        int answer = JOptionPane.showOptionDialog(
242
            this.getTopLevelAncestor(),
243
            I18nUtil.valueByKey("LIST_IMPORT_CONFIRM_LABEL"),
244
            I18nUtil.valueByKey("LIST_IMPORT_CONFIRM_TITLE"),
245
            JOptionPane.YES_NO_CANCEL_OPTION,
246
            JOptionPane.QUESTION_MESSAGE,
247
            null,
248
            options,
249
            options[2]
250
        );
251
        
252
        int startPosition = position;
253
254 2 1. dropPasteFile : negated conditional → NO_COVERAGE
2. dropPasteFile : negated conditional → NO_COVERAGE
        if (answer != JOptionPane.YES_OPTION && answer != JOptionPane.NO_OPTION) {
255
            return;
256
        }
257
        
258 1 1. dropPasteFile : negated conditional → NO_COVERAGE
        if (answer == JOptionPane.YES_OPTION) {
259
            
260 1 1. dropPasteFile : removed call to javax/swing/DefaultListModel::clear → NO_COVERAGE
            this.listModel.clear();
261
            startPosition = 0;
262
        }
263
        
264
        int startPositionFinal = startPosition;
265
        
266 2 1. lambda$dropPasteFile$0 : removed call to com/jsql/view/swing/list/DnDList::addItems → NO_COVERAGE
2. dropPasteFile : removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE
        SwingUtilities.invokeLater(() -> this.addItems(filesToImport, startPositionFinal));
267
    }
268
269
    private void addItems(final List<File> filesToImport, int startPosition) {
270
        
271
        int endPosition = startPosition;
272
        
273
        for (File file: filesToImport) {
274
            endPosition = this.initializeItems(endPosition, file);
275
        }
276
277 1 1. addItems : negated conditional → NO_COVERAGE
        if (!this.listModel.isEmpty()) {
278 2 1. addItems : Replaced integer subtraction with addition → NO_COVERAGE
2. addItems : removed call to com/jsql/view/swing/list/DnDList::setSelectionInterval → NO_COVERAGE
            DnDList.this.setSelectionInterval(startPosition, endPosition - 1);
279
        }
280
        
281
        try {
282 1 1. addItems : removed call to com/jsql/view/swing/list/DnDList::scrollRectToVisible → NO_COVERAGE
            DnDList.this.scrollRectToVisible(
283
                DnDList.this.getCellBounds(
284
                    DnDList.this.getMinSelectionIndex(),
285
                    DnDList.this.getMaxSelectionIndex()
286
                )
287
            );
288
        } catch (NullPointerException e) {
289
            // Report NullPointerException #1571 : manual scroll elsewhere then run action
290
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
291
        }
292
    }
293
294
    private int initializeItems(int startPosition, File file) {
295
        
296
        int endPosition = startPosition;
297
        
298
        try (
299
            var fileReader = new FileReader(file, StandardCharsets.UTF_8);
300
            var bufferedReader = new BufferedReader(fileReader)
301
        ) {
302
            
303
            String line;
304 1 1. initializeItems : negated conditional → NO_COVERAGE
            while ((line = bufferedReader.readLine()) != null) {
305
                if (
306 3 1. initializeItems : changed conditional boundary → NO_COVERAGE
2. initializeItems : negated conditional → NO_COVERAGE
3. initializeItems : negated conditional → NO_COVERAGE
                    StringUtils.isNotEmpty(line)
307
                    // Fix Report #60
308
                    && 0 <= endPosition
309 2 1. initializeItems : changed conditional boundary → NO_COVERAGE
2. initializeItems : negated conditional → NO_COVERAGE
                    && endPosition <= this.listModel.size()
310
                ) {
311 2 1. initializeItems : removed call to com/jsql/view/swing/list/DnDList::addItem → NO_COVERAGE
2. initializeItems : Changed increment from 1 to -1 → NO_COVERAGE
                    this.addItem(endPosition++, line);
312
                }
313
            }
314
        } catch (IOException e) {
315
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
316
        }
317
        
318 1 1. initializeItems : replaced int return with 0 for com/jsql/view/swing/list/DnDList::initializeItems → NO_COVERAGE
        return endPosition;
319
    }
320
    
321
    public void restore() {
322
        
323 1 1. restore : removed call to javax/swing/DefaultListModel::clear → NO_COVERAGE
        this.listModel.clear();
324
        
325
        for (ItemList path: this.defaultList) {
326 1 1. restore : removed call to javax/swing/DefaultListModel::addElement → NO_COVERAGE
            this.listModel.addElement(path);
327
        }
328
    }
329
    
330
    public void addItem(int endPosition, String line) {
331 1 1. addItem : removed call to javax/swing/DefaultListModel::add → NO_COVERAGE
        this.listModel.add(endPosition, new ItemList(line.replace("\\", "/")));
332
    }
333
}

Mutations

62

1.1
Location : <init>
Killed by : none
removed call to javax/swing/DefaultListModel::addElement → NO_COVERAGE

65

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setModel → NO_COVERAGE

68

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setCellRenderer → NO_COVERAGE

70

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::initializeActionMap → NO_COVERAGE

71

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::initializeListener → NO_COVERAGE

73

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setDragEnabled → NO_COVERAGE

74

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setDropMode → NO_COVERAGE

77

1.1
Location : <init>
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setTransferHandler → NO_COVERAGE

82

1.1
Location : initializeListener
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::addMouseListener → NO_COVERAGE

85

1.1
Location : initializeListener
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::addFocusListener → NO_COVERAGE

89

1.1
Location : focusLost
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::repaint → NO_COVERAGE

94

1.1
Location : focusGained
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::repaint → NO_COVERAGE

99

1.1
Location : initializeListener
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::addKeyListener → NO_COVERAGE

102

1.1
Location : keyPressed
Killed by : none
negated conditional → NO_COVERAGE

103

1.1
Location : keyPressed
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::removeSelectedItem → NO_COVERAGE

114

1.1
Location : initializeActionMap
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

118

1.1
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

129

1.1
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

130

1.1
Location : actionPerformed
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

131

1.1
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

132

1.1
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

136

1.1
Location : actionPerformed
Killed by : none
removed call to javax/swing/Action::actionPerformed → NO_COVERAGE

139

1.1
Location : actionPerformed
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setSelectedValue → NO_COVERAGE

144

1.1
Location : initializeActionMap
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

149

1.1
Location : initializeActionMap
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

160

1.1
Location : removeSelectedItem
Killed by : none
negated conditional → NO_COVERAGE

171

1.1
Location : removeSelectedItem
Killed by : none
negated conditional → NO_COVERAGE

172

1.1
Location : removeSelectedItem
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : removeSelectedItem
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setSelectedIndex → NO_COVERAGE

174

1.1
Location : removeSelectedItem
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setSelectedIndex → NO_COVERAGE

184

1.1
Location : removeSelectedItem
Killed by : none
negated conditional → NO_COVERAGE

185

1.1
Location : removeSelectedItem
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::scrollRectToVisible → NO_COVERAGE

205

1.1
Location : dropPasteFile
Killed by : none
negated conditional → NO_COVERAGE

215

1.1
Location : dropPasteFile
Killed by : none
negated conditional → NO_COVERAGE

220

1.1
Location : dropPasteFile
Killed by : none
removed call to javax/swing/JOptionPane::showMessageDialog → NO_COVERAGE

254

1.1
Location : dropPasteFile
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : dropPasteFile
Killed by : none
negated conditional → NO_COVERAGE

258

1.1
Location : dropPasteFile
Killed by : none
negated conditional → NO_COVERAGE

260

1.1
Location : dropPasteFile
Killed by : none
removed call to javax/swing/DefaultListModel::clear → NO_COVERAGE

266

1.1
Location : lambda$dropPasteFile$0
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::addItems → NO_COVERAGE

2.2
Location : dropPasteFile
Killed by : none
removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE

277

1.1
Location : addItems
Killed by : none
negated conditional → NO_COVERAGE

278

1.1
Location : addItems
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : addItems
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::setSelectionInterval → NO_COVERAGE

282

1.1
Location : addItems
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::scrollRectToVisible → NO_COVERAGE

304

1.1
Location : initializeItems
Killed by : none
negated conditional → NO_COVERAGE

306

1.1
Location : initializeItems
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : initializeItems
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : initializeItems
Killed by : none
negated conditional → NO_COVERAGE

309

1.1
Location : initializeItems
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : initializeItems
Killed by : none
negated conditional → NO_COVERAGE

311

1.1
Location : initializeItems
Killed by : none
removed call to com/jsql/view/swing/list/DnDList::addItem → NO_COVERAGE

2.2
Location : initializeItems
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

318

1.1
Location : initializeItems
Killed by : none
replaced int return with 0 for com/jsql/view/swing/list/DnDList::initializeItems → NO_COVERAGE

323

1.1
Location : restore
Killed by : none
removed call to javax/swing/DefaultListModel::clear → NO_COVERAGE

326

1.1
Location : restore
Killed by : none
removed call to javax/swing/DefaultListModel::addElement → NO_COVERAGE

331

1.1
Location : addItem
Killed by : none
removed call to javax/swing/DefaultListModel::add → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.16.1