DataAccess.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.model.accessible;
12
13
import com.jsql.model.InjectionModel;
14
import com.jsql.model.bean.database.AbstractElementDatabase;
15
import com.jsql.model.bean.database.Column;
16
import com.jsql.model.bean.database.Database;
17
import com.jsql.model.bean.database.Table;
18
import com.jsql.model.bean.util.Interaction;
19
import com.jsql.model.bean.util.Request;
20
import com.jsql.model.exception.AbstractSlidingException;
21
import com.jsql.model.exception.InjectionFailureException;
22
import com.jsql.model.exception.JSqlException;
23
import com.jsql.model.suspendable.SuspendableGetRows;
24
import com.jsql.util.I18nUtil;
25
import com.jsql.util.LogLevelUtil;
26
import org.apache.commons.lang3.StringUtils;
27
import org.apache.logging.log4j.LogManager;
28
import org.apache.logging.log4j.Logger;
29
30
import java.util.ArrayList;
31
import java.util.List;
32
import java.util.regex.Pattern;
33
34
/**
35
 * Database resource object to read name of databases, tables, columns and values
36
 * using most suited injection strategy.
37
 */
38
public class DataAccess {
39
    
40
    /**
41
     * Log4j logger sent to view.
42
     */
43
    private static final Logger LOGGER = LogManager.getRootLogger();
44
    
45
    /**
46
     * Regex characters marking the end of the result of an injection.
47
     * Process stops when this schema is encountered:
48
     * <pre>SQLix01x03x03x07
49
     */
50
    public static final String TRAIL_RGX = "\\x01\\x03\\x03\\x07";
51
    
52
    /**
53
     * Regex character used between each table cells.
54
     * Expected schema of multiple table cells :
55
     * <pre>
56
     * x04[table cell]x05[number of occurrences]x04x06x04[table cell]x05[number of occurrences]x04
57
     */
58
    public static final String SEPARATOR_CELL_RGX = "\\x06";
59
    
60
    /**
61
     * Regex character used between the table cell and the number of occurrence of the cell text.
62
     * Expected schema of a table cell data is
63
     * <pre>x04[table cell]x05[number of occurrences]x04
64
     */
65
    public static final String SEPARATOR_QTE_RGX = "\\x05";
66
67
    /**
68
     * Regex character enclosing a table cell returned by injection.
69
     * It allows to detect the correct end of a table cell data during parsing.
70
     * Expected schema of a table cell data is
71
     * <pre>x04[table cell]x05[number of occurrences]x04
72
     */
73
    public static final String ENCLOSE_VALUE_RGX = "\\x04";
74
    
75
    public static final String LEAD = "SqLi";
76
    public static final String SHELL_LEAD = "${shell.lead}";
77
    public static final String TRAIL = "iLQS";
78
    public static final String SHELL_TRAIL = "${shell.trail}";
79
    
80
    /**
81
     * Regex keywords corresponding to multiline and case-insensitive match.
82
     */
83
    public static final String MODE = "(?si)";
84
    
85
    /**
86
     * Regex schema describing a table cell with firstly the cell content and secondly the number of occurrences
87
     * of the cell text, separated by the reserved character x05 in hexadecimal.
88
     * The range of characters from x01 to x1F are not printable ASCII characters used to parse the data and exclude
89
     * printable characters during parsing.
90
     * Expected schema of a table cell data is
91
     * <pre>x04[table cell]x05[number of occurrences]x04
92
     */
93
    public static final String CELL_TABLE = "([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)"+ SEPARATOR_QTE_RGX +"([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)(\\x08)?";
94
95
    private final InjectionModel injectionModel;
96
    
97
    public DataAccess(InjectionModel injectionModel) {
98
        this.injectionModel = injectionModel;
99
    }
100
    
101
    /**
102
     * Get general database informations.<br>
103
     * => version{%}database{%}user{%}CURRENT_USER
104
     */
105
    public void getDatabaseInfos() {
106
        
107
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_INFORMATIONS"));
108
        
109
        var sourcePage = new String[]{ StringUtils.EMPTY };
110
111
        var resultToParse = "";
112
        
113
        try {
114
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
115
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlInfos(),
116
                sourcePage,
117
                false,
118
                0,
119
                AbstractElementDatabase.MOCK,
120
                "metadata"
121
            );
122
        } catch (AbstractSlidingException e) {
123
            resultToParse = getPartialResultAndLog(e, resultToParse);
124
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
125
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
126
        }
127
128 1 1. getDatabaseInfos : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(resultToParse)) {
129
            
130 1 1. getDatabaseInfos : removed call to com/jsql/model/InjectionModel::sendResponseFromSite → NO_COVERAGE
            this.injectionModel.sendResponseFromSite("Incorrect metadata", sourcePage[0].trim());
131
        }
132
        
133
        try {
134
            String versionDatabase = resultToParse.split(ENCLOSE_VALUE_RGX)[0].replaceAll("\\s+", StringUtils.SPACE);
135
            String nameDatabase = resultToParse.split(ENCLOSE_VALUE_RGX)[1];
136
            String username = resultToParse.split(ENCLOSE_VALUE_RGX)[2];
137
            
138
            var infos = String.format(
139
                "Database [%s] on %s [%s] for user [%s]",
140
                nameDatabase,
141
                this.injectionModel.getMediatorVendor().getVendor(),
142
                versionDatabase,
143
                username
144
            );
145
            
146
            LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, infos);
147
            
148
        } catch (ArrayIndexOutOfBoundsException e) {
149
150
            LOGGER.log(
151
                LogLevelUtil.CONSOLE_ERROR,
152
                String.format("%s: %s", I18nUtil.valueByKey("LOG_DB_METADATA_INCORRECT"), resultToParse)
153
            );
154
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, I18nUtil.valueByKey("LOG_DB_METADATA_WARN"));
155
        }
156
    }
157
158
    /**
159
     * Get database names and table counts and send them to the view.<br>
160
     * Use readable text (not hexa) and parse this pattern:<br>
161
     * => hh[database name 1]jj[table count]hhgghh[database name 2]jj[table count]hhggh...hi<br>
162
     * Data window can be cut before the end of the request but the process helps to obtain
163
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
164
     * @return list of databases found
165
     * @throws JSqlException when injection failure or stopped by user
166
     */
167
    public List<Database> listDatabases() throws JSqlException {
168
        
169
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_DATABASES"));
170
        List<Database> databases = new ArrayList<>();
171
        String resultToParse = StringUtils.EMPTY;
172
        
173
        try {
174
            var sourcePage = new String[]{ StringUtils.EMPTY };
175
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
176
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlDatabases(),
177
                sourcePage,
178
                true,
179
                0,
180
                AbstractElementDatabase.MOCK,
181
                "databases"
182
            );
183
        } catch (AbstractSlidingException e) {
184
            resultToParse = getPartialResultAndLog(e, resultToParse);
185
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
186
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
187
        }
188
189
        // Parse all data we have retrieved
190
        var regexSearch = Pattern.compile(
191
                MODE
192
                + ENCLOSE_VALUE_RGX
193
                + CELL_TABLE
194
                + ENCLOSE_VALUE_RGX
195
            )
196
            .matcher(resultToParse);
197
198 1 1. listDatabases : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
199
            throw new InjectionFailureException("No match while injecting databases");
200
        }
201
        
202
        regexSearch.reset();
203
204
        // Build an array of Database objects from the data we have parsed
205 1 1. listDatabases : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
206
            
207
            String databaseName = regexSearch.group(1);
208
            String tableCount = regexSearch.group(2);
209
210
            var newDatabase = new Database(databaseName, tableCount);
211
            databases.add(newDatabase);
212
        }
213
214
        var request = new Request();
215 1 1. listDatabases : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.ADD_DATABASES);
216 1 1. listDatabases : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(databases);
217 1 1. listDatabases : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
218
        
219 1 1. listDatabases : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listDatabases → NO_COVERAGE
        return databases;
220
    }
221
222
    /**
223
     * Get tables name and row count and send them to the view.<br>
224
     * Use readable text (not hexa) and parse this pattern:<br>
225
     * => hh[table name 1]jj[rows count]hhgghh[table name 2]jj[rows count]hhggh...hi<br>
226
     * Data window can be cut before the end of the request but the process helps to obtain
227
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
228
     * @param database which contains tables to find
229
     * @return list of tables found
230
     * @throws JSqlException when injection failure or stopped by user
231
     */
232
    public List<Table> listTables(Database database) throws JSqlException {
233
        
234
        // Reset stoppedByUser if list of Databases is partial
235
        // and some Tables are still reachable
236 1 1. listTables : removed call to com/jsql/model/InjectionModel::setIsStoppedByUser → NO_COVERAGE
        this.injectionModel.setIsStoppedByUser(false);
237
        
238
        // Inform the view that database has just been used
239
        var requestStartProgress = new Request();
240 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestStartProgress.setMessage(Interaction.START_PROGRESS);
241 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestStartProgress.setParameters(database);
242 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestStartProgress);
243
244
        var tableCount = Integer.toString(database.getChildCount());
245
        
246
        String resultToParse = StringUtils.EMPTY;
247
        
248
        try {
249
            var pageSource = new String[]{ StringUtils.EMPTY };
250
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
251
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlTables(database),
252
                pageSource,
253
                true,
254
                Integer.parseInt(tableCount),
255
                database,
256
                "tables"
257
            );
258
        } catch (AbstractSlidingException e) {
259
            resultToParse = getPartialResultAndLog(e, resultToParse);
260
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
261
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
262
        }
263
264
        // Parse all the data we have retrieved
265
        var regexSearch = Pattern.compile(
266
                MODE
267
                + ENCLOSE_VALUE_RGX
268
                + CELL_TABLE
269
                + ENCLOSE_VALUE_RGX
270
            )
271
            .matcher(resultToParse);
272
        
273
        var requestEndProgress = new Request();
274 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_PROGRESS);
275 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(database);
276 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
277
        
278 1 1. listTables : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
279
            throw new InjectionFailureException("No match while injecting tables");
280
        }
281
        
282
        regexSearch.reset();
283
        
284
        List<Table> tables = new ArrayList<>();
285
        
286
        // Build an array of Table objects from the data we have parsed
287 1 1. listTables : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
288
            
289
            String tableName = regexSearch.group(1);
290
            String rowCount = regexSearch.group(2);
291
            
292
            var newTable = new Table(tableName, rowCount, database);
293
            tables.add(newTable);
294
        }
295
        
296
        var requestAddTables = new Request();
297 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestAddTables.setMessage(Interaction.ADD_TABLES);
298 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestAddTables.setParameters(tables);
299 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestAddTables);
300
        
301 1 1. listTables : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listTables → NO_COVERAGE
        return tables;
302
    }
303
304
    /**
305
     * Get column names and send them to the view.<br>
306
     * Use readable text (not hexa) and parse this pattern with 2nd member forced to 31 (1 in ascii):<br>
307
     * => hh[column name 1]jj[31]hhgghh[column name 2]jj[31]hhggh...hi<br>
308
     * Data window can be cut before the end of the request but the process helps to obtain
309
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
310
     * @param table which contains columns to find
311
     * @return list of columns found
312
     * @throws JSqlException when injection failure or stopped by user
313
     */
314
    public List<Column> listColumns(Table table) throws JSqlException {
315
        
316
        List<Column> columns = new ArrayList<>();
317
        
318
        // Inform the view that table has just been used
319
        var requestStartProgress = new Request();
320 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestStartProgress.setMessage(Interaction.START_INDETERMINATE_PROGRESS);
321 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestStartProgress.setParameters(table);
322 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestStartProgress);
323
324
        String resultToParse = StringUtils.EMPTY;
325
        
326
        try {
327
            var pageSource = new String[]{ StringUtils.EMPTY };
328
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
329
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlColumns(table),
330
                pageSource,
331
                true,
332
                0,
333
                table,
334
                "columns"
335
            );
336
        } catch (AbstractSlidingException e) {
337
            resultToParse = getPartialResultAndLog(e, resultToParse);
338
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
339
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
340
        }
341
342
        // Build SQLite columns
343 1 1. listColumns : negated conditional → NO_COVERAGE
        if (this.injectionModel.getMediatorVendor().isSqlite()) {
344
            resultToParse = this.injectionModel.getMediatorVendor().getSqlite().transformSqlite(resultToParse);
345
        }
346
        
347
        // Parse all the data we have retrieved
348
        var regexSearch = Pattern.compile(
349
                MODE
350
                + ENCLOSE_VALUE_RGX
351
                + CELL_TABLE
352
                + ENCLOSE_VALUE_RGX
353
            )
354
            .matcher(resultToParse);
355
356
        var requestEndProgress = new Request();
357 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_INDETERMINATE_PROGRESS);
358 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(table);
359 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
360
361 1 1. listColumns : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
362
            throw new InjectionFailureException("No match while injecting columns");
363
        }
364
365
        regexSearch.reset();
366
367
        // Build an array of Column objects from the data we have parsed
368 1 1. listColumns : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
369
            
370
            String nameColumn = regexSearch.group(1);
371
372
            var column = new Column(nameColumn, table);
373
            columns.add(column);
374
        }
375
376
        var requestAddColumns = new Request();
377 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestAddColumns.setMessage(Interaction.ADD_COLUMNS);
378 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestAddColumns.setParameters(columns);
379 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestAddColumns);
380
        
381 1 1. listColumns : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listColumns → NO_COVERAGE
        return columns;
382
    }
383
384
    /**
385
     * Get table values and count each occurrences and send them to the view.<br>
386
     * Values are on clear text (not hexa) and follows this window pattern<br>
387
     * => hh[value 1]jj[count]hhgghh[value 2]jj[count]hhggh...hi<br>
388
     * Data window can be cut before the end of the request but the process helps to obtain
389
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
390
     * @param columnsBean choice by the user
391
     * @return a 2x2 table containing values by columns
392
     * @throws JSqlException when injection failure or stopped by user
393
     */
394
    public String[][] listValues(List<Column> columnsBean) throws JSqlException {
395
        
396
        var databaseBean = (Database) columnsBean.get(0).getParent().getParent();
397
        var tableBean = (Table) columnsBean.get(0).getParent();
398
        int rowCount = columnsBean.get(0).getParent().getChildCount();
399
400
        // Inform the view that table has just been used
401
        var request = new Request();
402 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.START_PROGRESS);
403 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(tableBean);
404 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
405
406
        // Build an array of column names
407
        List<String> columnsName = new ArrayList<>();
408
        for (AbstractElementDatabase columnBean: columnsBean) {
409
            columnsName.add(columnBean.toString());
410
        }
411
412
        // From that array, build the SQL fields nicely
413
        // => col1{%}col2...
414
        // ==> trim(ifnull(`col1`,0x00)),0x7f,trim(ifnull(`Col2`,0x00))...
415
        String[] columns = columnsName.toArray(new String[0]);
416
417
        List<List<String>> listValues = this.getRows(databaseBean, tableBean, rowCount, columns);
418
419
        // Add the default title to the columns: row number, occurrence
420 1 1. listValues : removed call to java/util/List::add → NO_COVERAGE
        columnsName.add(0, StringUtils.EMPTY);
421 1 1. listValues : removed call to java/util/List::add → NO_COVERAGE
        columnsName.add(0, StringUtils.EMPTY);
422
423
        String[][] table = this.getTable(columnsName, listValues);
424
425
        columns = columnsName.toArray(new String[0]);
426
        
427
        // Group the columns names, values and Table object in one array
428
        var objectData = new Object[]{ columns, table, tableBean };
429
430
        var requestCreateValuesTab = new Request();
431 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestCreateValuesTab.setMessage(Interaction.CREATE_VALUES_TAB);
432 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestCreateValuesTab.setParameters(objectData);
433 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestCreateValuesTab);
434
435
        var requestEndProgress = new Request();
436 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_PROGRESS);
437 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(tableBean);
438 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
439
        
440 1 1. listValues : replaced return value with null for com/jsql/model/accessible/DataAccess::listValues → NO_COVERAGE
        return table;
441
    }
442
443
    private List<List<String>> getRows(Database database, Table table, int rowCount, String[] columns) throws InjectionFailureException {
444
        
445
        String resultToParse = StringUtils.EMPTY;
446
        
447
        try {
448
            var pageSource = new String[]{ StringUtils.EMPTY };
449
            
450
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
451
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlRows(columns, database, table),
452
                pageSource,
453
                true,
454
                rowCount,
455
                table,
456
                "rows"
457
            );
458
        } catch (AbstractSlidingException e) {  // Catch all exceptions but prevent detecting bug
459
            resultToParse = getPartialResultAndLog(e, resultToParse);
460
        } catch (Exception e) {
461
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
462
        }
463
464 1 1. getRows : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::getRows → NO_COVERAGE
        return SuspendableGetRows.parse(resultToParse);
465
    }
466
467
    private static String getPartialResultAndLog(AbstractSlidingException e, String resultToParse) {
468
469
        LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
470
471
        // Get pieces of data already retrieved instead of losing them
472 1 1. getPartialResultAndLog : negated conditional → NO_COVERAGE
        if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
473
            resultToParse = e.getSlidingWindowAllRows();
474 1 1. getPartialResultAndLog : negated conditional → NO_COVERAGE
        } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
475
            resultToParse = e.getSlidingWindowCurrentRows();
476
        }
477
478 1 1. getPartialResultAndLog : replaced return value with "" for com/jsql/model/accessible/DataAccess::getPartialResultAndLog → NO_COVERAGE
        return resultToParse;
479
    }
480
481
    private String[][] getTable(List<String> columnsName, List<List<String>> values) {
482
        
483
        // Build a proper 2D array from the data
484
        var table = new String[values.size()][columnsName.size()];
485
        
486 2 1. getTable : changed conditional boundary → NO_COVERAGE
2. getTable : negated conditional → NO_COVERAGE
        for (var indexRow = 0 ; indexRow < values.size() ; indexRow++) {
487
            
488
            var isIncomplete = false;
489
            
490 2 1. getTable : negated conditional → NO_COVERAGE
2. getTable : changed conditional boundary → NO_COVERAGE
            for (var indexColumn = 0 ; indexColumn < columnsName.size() ; indexColumn++) {
491
                try {
492
                    table[indexRow][indexColumn] = values.get(indexRow).get(indexColumn);
493
                } catch (IndexOutOfBoundsException e) {
494
                    
495
                    isIncomplete = true;
496
                    LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, I18nUtil.valueByKey("LOG_LIST_VALUES_INCOMPLETE"));
497
                    LOGGER.log(LogLevelUtil.IGNORE, e);
498
                }
499
            }
500
            
501 1 1. getTable : negated conditional → NO_COVERAGE
            if (isIncomplete) {
502
                
503
                int logIndexRow = indexRow;
504
                LOGGER.log(
505
                    LogLevelUtil.CONSOLE_ERROR,
506
                    "{}{}: ",
507 1 1. lambda$getTable$2 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$2 → NO_COVERAGE
                    () -> I18nUtil.valueByKey("LOG_LIST_VALUES_TOO_LONG"),
508 2 1. lambda$getTable$3 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$3 → NO_COVERAGE
2. lambda$getTable$3 : Replaced integer addition with subtraction → NO_COVERAGE
                    () -> logIndexRow + 1
509
                );
510
511
                LOGGER.log(
512
                    LogLevelUtil.CONSOLE_ERROR,
513 1 1. lambda$getTable$4 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$4 → NO_COVERAGE
                    () -> String.join(
514
                        ", ",
515
                        values.get(logIndexRow).toArray(new String[0])
516
                    )
517
                );
518
            }
519
        }
520
        
521 1 1. getTable : replaced return value with null for com/jsql/model/accessible/DataAccess::getTable → NO_COVERAGE
        return table;
522
    }
523
}

Mutations

128

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

130

1.1
Location : getDatabaseInfos
Killed by : none
removed call to com/jsql/model/InjectionModel::sendResponseFromSite → NO_COVERAGE

198

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

205

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

215

1.1
Location : listDatabases
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

216

1.1
Location : listDatabases
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

217

1.1
Location : listDatabases
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

219

1.1
Location : listDatabases
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listDatabases → NO_COVERAGE

236

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/InjectionModel::setIsStoppedByUser → NO_COVERAGE

240

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

241

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

242

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

274

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

275

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

276

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

278

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

287

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

297

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

298

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

299

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

301

1.1
Location : listTables
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listTables → NO_COVERAGE

320

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

321

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

322

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

343

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

357

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

358

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

359

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

361

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

368

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

377

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

378

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

379

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

381

1.1
Location : listColumns
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listColumns → NO_COVERAGE

402

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

403

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

404

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

420

1.1
Location : listValues
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

421

1.1
Location : listValues
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

431

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

432

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

433

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

436

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

437

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

438

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

440

1.1
Location : listValues
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::listValues → NO_COVERAGE

464

1.1
Location : getRows
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::getRows → NO_COVERAGE

472

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

474

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

478

1.1
Location : getPartialResultAndLog
Killed by : none
replaced return value with "" for com/jsql/model/accessible/DataAccess::getPartialResultAndLog → NO_COVERAGE

486

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

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

490

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

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

501

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

507

1.1
Location : lambda$getTable$2
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$2 → NO_COVERAGE

508

1.1
Location : lambda$getTable$3
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$3 → NO_COVERAGE

2.2
Location : lambda$getTable$3
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

513

1.1
Location : lambda$getTable$4
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$4 → NO_COVERAGE

521

1.1
Location : getTable
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::getTable → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.16.1