SuspendableGetRows.java

1
package com.jsql.model.suspendable;
2
3
import com.jsql.model.InjectionModel;
4
import com.jsql.model.bean.database.AbstractElementDatabase;
5
import com.jsql.model.bean.database.Table;
6
import com.jsql.model.bean.util.Interaction;
7
import com.jsql.model.bean.util.Request;
8
import com.jsql.model.exception.AbstractSlidingException;
9
import com.jsql.model.exception.InjectionFailureException;
10
import com.jsql.model.exception.LoopDetectedSlidingException;
11
import com.jsql.model.exception.StoppedByUserSlidingException;
12
import com.jsql.model.injection.strategy.AbstractStrategy;
13
import com.jsql.util.LogLevelUtil;
14
import com.jsql.util.StringUtil;
15
import org.apache.commons.lang3.StringUtils;
16
import org.apache.commons.text.StringEscapeUtils;
17
import org.apache.logging.log4j.LogManager;
18
import org.apache.logging.log4j.Logger;
19
20
import java.net.URLDecoder;
21
import java.nio.charset.StandardCharsets;
22
import java.util.ArrayList;
23
import java.util.List;
24
import java.util.regex.Matcher;
25
import java.util.regex.Pattern;
26
import java.util.regex.PatternSyntaxException;
27
28
import static com.jsql.model.accessible.DataAccess.*;
29
import static com.jsql.model.injection.vendor.model.VendorYaml.LIMIT;
30
31
/**
32
 * Get data as chunks by performance query from SQL request.
33
 * 
34
 * <pre>
35
 * Single row format: \4[0-9A-F]*\5[0-9A-F]*c?\4
36
 * Row separator: \6
37
 * Tape example: \4xxRow#Xxx\5x\4\6\4xxRow#X+1xx\5x\4\6...\4\1\3\3\7</pre>
38
 * 
39
 * MID and LIMIT move two sliding windows in a 2D array tape in that order.
40
 * MID skips characters when collected, then LIMIT skips lines when collected.
41
 * The process can be interrupted by the user (stop/pause).
42
 */
43
public class SuspendableGetRows extends AbstractSuspendable {
44
45
    /**
46
     * Log4j logger sent to view.
47
     */
48
    private static final Logger LOGGER = LogManager.getRootLogger();
49
50
    public SuspendableGetRows(InjectionModel injectionModel) {
51
        super(injectionModel);
52
    }
53
54
    @Override
55
    public String run(Object... args) throws AbstractSlidingException {
56
        
57
        // TODO Map class
58
        String initialSqlQuery = (String) args[0];
59
        String[] sourcePage = (String[]) args[1];
60
        boolean isMultipleRows = (Boolean) args[2];
61
        int countRowsToFind = (Integer) args[3];
62
        AbstractElementDatabase elementDatabase = (AbstractElementDatabase) args[4];
63
        String metadataInjectionProcess = (String) args[5];
64
        
65 1 1. run : removed call to com/jsql/util/ThreadUtil::put → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().put(elementDatabase, this);
66
67
        AbstractStrategy strategy = this.injectionModel.getMediatorStrategy().getStrategy();
68
        
69
        // Fix #14417
70 1 1. run : negated conditional → NO_COVERAGE
        if (strategy == null) {
71
            return StringUtils.EMPTY;
72
        }
73
        
74
        // Stop injection if all rows are found, skip rows and characters collected
75
        var slidingWindowAllRows = new StringBuilder();
76
        var slidingWindowCurrentRow = new StringBuilder();
77
        
78
        String previousChunk = StringUtils.EMPTY;
79
        var countAllRows = 0;
80
        var charPositionInCurrentRow = 1;
81
        var countInfiniteLoop = 0;
82
        
83
        String queryGetRows = this.getQuery(initialSqlQuery, countAllRows);
84
        
85
        while (true) {
86
87 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::checkSuspend → NO_COVERAGE
            this.checkSuspend(strategy, slidingWindowAllRows, slidingWindowCurrentRow);
88
            
89
            sourcePage[0] = strategy.inject(queryGetRows, Integer.toString(charPositionInCurrentRow), this, metadataInjectionProcess);
90
            
91
            // Parse all the data we have retrieved
92
            Matcher regexLeadFound = this.parseLeadFound(sourcePage[0], strategy.getPerformanceLength());
93
            Matcher regexTrailOnlyFound = this.parseTrailOnlyFound(sourcePage[0]);
94
            
95
            if (
96 3 1. run : negated conditional → NO_COVERAGE
2. run : negated conditional → NO_COVERAGE
3. run : negated conditional → NO_COVERAGE
                (!regexLeadFound.find() || regexTrailOnlyFound.find())
97
                && isMultipleRows
98 1 1. run : negated conditional → NO_COVERAGE
                && StringUtils.isNotEmpty(slidingWindowAllRows.toString())
99
            ) {
100
                
101 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                this.sendProgress(countRowsToFind, countRowsToFind, elementDatabase);
102
                break;
103
            }
104
105
            // Add the result to the data already found.
106
            // Fix #40947: OutOfMemoryError on append()
107
            // Fix #95382: IllegalArgumentException on URLDecoder.decode()
108
            try {
109
                String currentChunk = regexLeadFound.group(1);
110
                currentChunk = decodeUnicode(currentChunk);
111
                currentChunk = decodeUrl(currentChunk);
112
113
                countInfiniteLoop = this.checkInfinite(countInfiniteLoop, previousChunk, currentChunk, slidingWindowCurrentRow, slidingWindowAllRows);
114
                
115
                previousChunk = currentChunk;
116
                slidingWindowCurrentRow.append(currentChunk);
117 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendChunk → NO_COVERAGE
                this.sendChunk(currentChunk);
118
                
119
            } catch (IllegalArgumentException | IllegalStateException | OutOfMemoryError e) {
120 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::endInjection → NO_COVERAGE
                this.endInjection(elementDatabase, e);
121
            }
122
123
            // Check how many rows we have collected from the beginning of that chunk
124
            int countChunkRows = this.getCountRows(slidingWindowCurrentRow);
125
126 2 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
2. run : Replaced integer addition with subtraction → NO_COVERAGE
            this.sendProgress(countRowsToFind, countAllRows + countChunkRows, elementDatabase);
127
128
            // End of rows detected: \1\3\3\7
129
            // => \4xxxxxxxx\500\4\6\4...\4\1\3\3\7
130 2 1. run : negated conditional → NO_COVERAGE
2. run : changed conditional boundary → NO_COVERAGE
            if (
131
                countChunkRows > 0
132 1 1. run : negated conditional → NO_COVERAGE
                || slidingWindowCurrentRow.toString().matches("(?s).*"+ TRAIL_RGX +".*")
133
            ) {
134
                
135 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrapeTrailJunk → NO_COVERAGE
                this.scrapeTrailJunk(slidingWindowCurrentRow);
136
                
137
                slidingWindowAllRows.append(slidingWindowCurrentRow);
138
                
139 1 1. run : negated conditional → NO_COVERAGE
                if (isMultipleRows) {
140
                    
141 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE
                    this.scrap(slidingWindowAllRows);
142 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE
                    this.scrap(slidingWindowCurrentRow);
143
144 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::appendRowFixed → NO_COVERAGE
                    this.appendRowFixed(slidingWindowAllRows, slidingWindowCurrentRow);
145
146
                    countAllRows = this.getCountRows(slidingWindowAllRows);
147
148 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                    this.sendProgress(countRowsToFind, countAllRows, elementDatabase);
149
150
                    // Ending condition: every expected rows have been retrieved.
151 1 1. run : negated conditional → NO_COVERAGE
                    if (countAllRows == countRowsToFind) {
152
                        
153
                        break;
154
                    }
155
156
                    // Add the LIMIT statement to the next SQL query and reset variables.
157
                    // Put the character cursor to the beginning of the line, and reset the result of the current query
158
                    queryGetRows = this.getQuery(initialSqlQuery, countAllRows);
159
160 1 1. run : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
                    slidingWindowCurrentRow.setLength(0);
161
162
                } else {
163
                    
164 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                    this.sendProgress(countRowsToFind, countRowsToFind, elementDatabase);
165
                    break;
166
                }
167
            }
168
169 1 1. run : Replaced integer addition with subtraction → NO_COVERAGE
            charPositionInCurrentRow = slidingWindowCurrentRow.length() + 1;
170
        }
171
        
172 1 1. run : removed call to com/jsql/util/ThreadUtil::remove → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().remove(elementDatabase);
173
174 1 1. run : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::run → NO_COVERAGE
        return slidingWindowAllRows.toString();
175
    }
176
177
    private String decodeUrl(String currentChunk) {
178
179 1 1. decodeUrl : negated conditional → NO_COVERAGE
        if (!this.injectionModel.getMediatorUtils().getPreferencesUtil().isUrlDecodeDisabled()) {
180
            try {
181 1 1. decodeUrl : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE
                return URLDecoder.decode(currentChunk, StandardCharsets.UTF_8);  // Transform %00 entities to text
182
            } catch (IllegalArgumentException e) {
183
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, "Decoding fails on UT8, keeping raw result");
184
            }
185
        }
186 1 1. decodeUrl : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE
        return currentChunk;
187
    }
188
189
    private String decodeUnicode(String currentChunk) {
190
191 1 1. decodeUnicode : negated conditional → NO_COVERAGE
        if (!this.injectionModel.getMediatorUtils().getPreferencesUtil().isUnicodeDecodeDisabled()) {
192 1 1. decodeUnicode : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE
            return StringEscapeUtils.unescapeJava(  // transform \u0000 entities to text
193
                currentChunk.replaceAll("\\\\u.{0,3}$", "")  // remove incorrect entities
194
            );
195
        }
196 1 1. decodeUnicode : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE
        return currentChunk;
197
    }
198
199
    private String getQuery(String initialSqlQuery, int countAllRows) {
200 1 1. getQuery : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::getQuery → NO_COVERAGE
        return initialSqlQuery.replace(LIMIT, this.injectionModel.getMediatorVendor().getVendor().instance().sqlLimit(countAllRows));
201
    }
202
203
    private void appendRowFixed(StringBuilder slidingWindowAllRows, StringBuilder slidingWindowCurrentRow) {
204
        
205
        // Check either if there is more than 1 row and if there is less than 1 complete row
206
        var regexAtLeastOneRow = Pattern.compile(
207
            String.format(
208
                "%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]%s%s%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$",
209
                MODE,
210
                ENCLOSE_VALUE_RGX,
211
                SEPARATOR_CELL_RGX,
212
                ENCLOSE_VALUE_RGX
213
            )
214
        )
215
        .matcher(slidingWindowCurrentRow);
216
        
217
        var regexRowIncomplete = Pattern.compile(
218
            MODE
219
            + ENCLOSE_VALUE_RGX
220
            + "[^\\x01-\\x03\\x05-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$"
221
        )
222
        .matcher(slidingWindowCurrentRow);
223
224
        // If there is more than 1 row, delete the last incomplete one in order to restart properly from it at the next loop,
225
        // else if there is 1 row but incomplete, mark it as cut with the letter c
226 1 1. appendRowFixed : negated conditional → NO_COVERAGE
        if (regexAtLeastOneRow.find()) {
227
            
228
            var allLine = slidingWindowAllRows.toString();
229 1 1. appendRowFixed : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
            slidingWindowAllRows.setLength(0);
230
            slidingWindowAllRows.append(
231
                Pattern.compile(
232
                    MODE
233
                    + ENCLOSE_VALUE_RGX
234
                    + "[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$"
235
                )
236
                .matcher(allLine)
237
                .replaceAll(StringUtils.EMPTY)
238
            );
239
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Chunk unreliable, reloading row part...");
240
241 1 1. appendRowFixed : negated conditional → NO_COVERAGE
        } else if (regexRowIncomplete.find()) {
242
            slidingWindowAllRows.append(StringUtil.hexstr("05")).append("1").append(StringUtil.hexstr("0804"));
243
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Chunk unreliable, keeping row parts only");
244
        }
245
    }
246
247
    private void scrapeTrailJunk(StringBuilder slidingWindowCurrentRow) {
248
        
249
        // Remove everything after chunk
250
        // => \4xxxxxxxx\500\4\6\4...\4 => \1\3\3\7junk
251
        var currentRow = slidingWindowCurrentRow.toString();
252 1 1. scrapeTrailJunk : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
        slidingWindowCurrentRow.setLength(0);
253
        slidingWindowCurrentRow.append(
254
            Pattern.compile(MODE + TRAIL_RGX +".*")
255
            .matcher(currentRow)
256
            .replaceAll(StringUtils.EMPTY)
257
        );
258
    }
259
260
    private int getCountRows(StringBuilder slidingWindowCurrentRow) {
261
        
262
        var regexAtLeastOneRow = Pattern.compile(
263
            String.format(
264
                "%s(%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?\\x08?%s)",
265
                MODE,
266
                ENCLOSE_VALUE_RGX,
267
                SEPARATOR_QTE_RGX,
268
                ENCLOSE_VALUE_RGX
269
            )
270
        )
271
        .matcher(slidingWindowCurrentRow);
272
        
273
        var nbCompleteLine = 0;
274 1 1. getCountRows : negated conditional → NO_COVERAGE
        while (regexAtLeastOneRow.find()) {
275 1 1. getCountRows : Changed increment from 1 to -1 → NO_COVERAGE
            nbCompleteLine++;
276
        }
277
        
278 1 1. getCountRows : replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::getCountRows → NO_COVERAGE
        return nbCompleteLine;
279
    }
280
281
    private void endInjection(AbstractElementDatabase searchName, Throwable e) throws InjectionFailureException {
282
        
283
        // Premature end of results
284
        // if it's not the root (empty tree)
285 1 1. endInjection : negated conditional → NO_COVERAGE
        if (searchName != null) {
286
            
287
            var request = new Request();
288 1 1. endInjection : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.END_PROGRESS);
289 1 1. endInjection : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            request.setParameters(searchName);
290 1 1. endInjection : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
291
        }
292
293
        var messageError = new StringBuilder("Fetching fails: no data to parse");
294
        
295 1 1. endInjection : negated conditional → NO_COVERAGE
        if (searchName != null) {
296
            messageError.append(" for ").append(StringUtil.detectUtf8(searchName.toString()));
297
        }
298
        
299 3 1. endInjection : negated conditional → NO_COVERAGE
2. endInjection : negated conditional → NO_COVERAGE
3. endInjection : changed conditional boundary → NO_COVERAGE
        if (searchName instanceof Table && searchName.getChildCount() > 0) {
300
            messageError.append(", check Network tab for logs");
301
        }
302
        
303
        throw new InjectionFailureException(messageError.toString(), e);
304
    }
305
306
    private void sendChunk(String currentChunk) {
307
        
308
        var request = new Request();
309 1 1. sendChunk : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.MESSAGE_CHUNK);
310 1 1. sendChunk : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(
311
            Pattern.compile(MODE + TRAIL_RGX +".*")
312
            .matcher(currentChunk)
313
            .replaceAll(StringUtils.EMPTY)
314
            .replace("\\n", "\\\\\\n")
315
            .replace("\\r", "\\\\\\r")
316
            .replace("\\t", "\\\\\\t")
317
        );
318
        
319 1 1. sendChunk : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
320
    }
321
322
    // TODO pb for same char string like aaaaaaaaaaaaa...aaaaaaaaaaaaa
323
    // detected as infinite
324
    private int checkInfinite(
325
        int loop,
326
        String previousChunk,
327
        String currentChunk,
328
        StringBuilder slidingWindowCurrentRow,
329
        StringBuilder slidingWindowAllRows
330
    ) throws LoopDetectedSlidingException {
331
        
332
        int infiniteLoop = loop;
333
        
334 1 1. checkInfinite : negated conditional → NO_COVERAGE
        if (previousChunk.equals(currentChunk)) {
335
            
336 1 1. checkInfinite : Changed increment from 1 to -1 → NO_COVERAGE
            infiniteLoop++;
337 2 1. checkInfinite : changed conditional boundary → NO_COVERAGE
2. checkInfinite : negated conditional → NO_COVERAGE
            if (infiniteLoop >= 20) {
338
                
339 1 1. checkInfinite : removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE
                this.stop();
340
                
341
                throw new LoopDetectedSlidingException(
342
                    slidingWindowAllRows.toString(),
343
                    slidingWindowCurrentRow.toString()
344
                );
345
            }
346
        }
347
        
348 1 1. checkInfinite : replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::checkInfinite → NO_COVERAGE
        return infiniteLoop;
349
    }
350
351
    private Matcher parseTrailOnlyFound(String sourcePage) {
352
353
        String sourcePageUnicodeDecoded = decodeUnicode(sourcePage);
354
355
        // TODO: prevent to find the last line directly: MODE + LEAD + .* + TRAIL_RGX
356
        // It creates extra query which can be endless if not nullified
357 1 1. parseTrailOnlyFound : replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseTrailOnlyFound → NO_COVERAGE
        return Pattern.compile(
358
            String.format("(?s)%s(?i)%s", LEAD, TRAIL_RGX)
359
        )
360
        .matcher(sourcePageUnicodeDecoded);
361
    }
362
363
    /**
364
     * After ${lead} tag, gets characters between 1 and maxPerf
365
     * performanceQuery() gets 65536 characters or less
366
     * ${lead}blahblah1337      ] : end or limit+1
367
     * ${lead}blahblah      blah] : continue substr()
368
     */
369
    private Matcher parseLeadFound(String sourcePage, String performanceLength) throws InjectionFailureException {
370
        
371
        Matcher regexAtLeastOneRow;
372
        
373
        try {
374
            regexAtLeastOneRow = Pattern.compile(
375
                String.format("(?s)%s(?i)(.{1,%s})", LEAD, performanceLength)
376
            )
377
            .matcher(sourcePage);
378
        } catch (PatternSyntaxException e) {
379
            // Fix #35382 : PatternSyntaxException null on SQLi(.{1,null})
380
            throw new InjectionFailureException("Row parsing failed using capacity", e);
381
        }
382
        
383 1 1. parseLeadFound : replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseLeadFound → NO_COVERAGE
        return regexAtLeastOneRow;
384
    }
385
386
    private void checkSuspend(
387
        AbstractStrategy strategy,
388
        StringBuilder slidingWindowAllRows,
389
        StringBuilder slidingWindowCurrentRow
390
    ) throws StoppedByUserSlidingException, InjectionFailureException {
391 1 1. checkSuspend : negated conditional → NO_COVERAGE
        if (this.isSuspended()) {
392
            throw new StoppedByUserSlidingException(
393
                slidingWindowAllRows.toString(),
394
                slidingWindowCurrentRow.toString()
395
            );
396 1 1. checkSuspend : negated conditional → NO_COVERAGE
        } else if (strategy == null) {
397
            // Fix #1905 : NullPointerException on injectionStrategy.inject()
398
            throw new InjectionFailureException("Undefined strategy");
399
        }
400
    }
401
402
    private void scrap(StringBuilder slidingWindowAllRows) {
403
        
404
        // Remove everything not properly attached to the last row:
405
        // 1. very start of a new row: XXXXX\4[\6\4]$
406
        // 2. very end of the last row: XXXXX[\500]$
407
408
        var allRowsLimit = slidingWindowAllRows.toString();
409 1 1. scrap : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
        slidingWindowAllRows.setLength(0);
410
        slidingWindowAllRows.append(
411
            Pattern.compile(
412
                String.format(
413
                    "%s(%s%s|%s\\d*)$",
414
                    MODE,
415
                    SEPARATOR_CELL_RGX,
416
                    ENCLOSE_VALUE_RGX,
417
                    SEPARATOR_QTE_RGX
418
                )
419
            )
420
            .matcher(allRowsLimit)
421
            .replaceAll(StringUtils.EMPTY)
422
        );
423
    }
424
425
    private void sendProgress(int numberToFind, int countProgress, AbstractElementDatabase searchName) {
426 3 1. sendProgress : negated conditional → NO_COVERAGE
2. sendProgress : negated conditional → NO_COVERAGE
3. sendProgress : changed conditional boundary → NO_COVERAGE
        if (numberToFind > 0 && searchName != null) {
427
            
428
            var request = new Request();
429 1 1. sendProgress : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.UPDATE_PROGRESS);
430 1 1. sendProgress : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            request.setParameters(searchName, countProgress);
431 1 1. sendProgress : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
432
        }
433
    }
434
    
435
    public static List<List<String>> parse(String rows) throws InjectionFailureException {
436
        
437
        // Parse all the data we have retrieved
438
        var regexSearch = Pattern.compile(
439
                String.format(
440
                    "%s%s([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?)%s([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?)(\\x08)?%s",
441
                    MODE,
442
                    ENCLOSE_VALUE_RGX,
443
                    SEPARATOR_QTE_RGX,
444
                    ENCLOSE_VALUE_RGX
445
                )
446
            )
447
            .matcher(rows);
448
449 1 1. parse : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
450
            throw new InjectionFailureException();
451
        }
452
        
453
        regexSearch.reset();
454
455
        var rowsFound = 0;
456
        List<List<String>> listValues = new ArrayList<>();
457
458
        // Build a 2D array of strings from the data we have parsed
459
        // => row number, occurrence, value1, value2...
460 1 1. parse : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
461
            
462
            String values = regexSearch.group(1);
463
            var instances = Integer.parseInt(regexSearch.group(2));
464
465
            listValues.add(new ArrayList<>());
466 1 1. parse : Replaced integer addition with subtraction → NO_COVERAGE
            listValues.get(rowsFound).add(Integer.toString(rowsFound + 1));
467
            listValues.get(rowsFound).add("x"+ instances);
468
            
469
            for (String cellValue: values.split("\\x7F", -1)) {
470
                listValues.get(rowsFound).add(cellValue);
471
            }
472
473 1 1. parse : Changed increment from 1 to -1 → NO_COVERAGE
            rowsFound++;
474
        }
475
        
476 1 1. parse : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetRows::parse → NO_COVERAGE
        return listValues;
477
    }
478
}

Mutations

65

1.1
Location : run
Killed by : none
removed call to com/jsql/util/ThreadUtil::put → NO_COVERAGE

70

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

87

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::checkSuspend → NO_COVERAGE

96

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

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

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

98

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

101

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

117

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendChunk → NO_COVERAGE

120

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::endInjection → NO_COVERAGE

126

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

2.2
Location : run
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

130

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

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

132

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

135

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrapeTrailJunk → NO_COVERAGE

139

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

141

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE

142

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE

144

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::appendRowFixed → NO_COVERAGE

148

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

151

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

160

1.1
Location : run
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

164

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

169

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

172

1.1
Location : run
Killed by : none
removed call to com/jsql/util/ThreadUtil::remove → NO_COVERAGE

174

1.1
Location : run
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::run → NO_COVERAGE

179

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

181

1.1
Location : decodeUrl
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE

186

1.1
Location : decodeUrl
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE

191

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

192

1.1
Location : decodeUnicode
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE

196

1.1
Location : decodeUnicode
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE

200

1.1
Location : getQuery
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::getQuery → NO_COVERAGE

226

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

229

1.1
Location : appendRowFixed
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

241

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

252

1.1
Location : scrapeTrailJunk
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

274

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

275

1.1
Location : getCountRows
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

278

1.1
Location : getCountRows
Killed by : none
replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::getCountRows → NO_COVERAGE

285

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

288

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

289

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

290

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

295

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

299

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

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

3.3
Location : endInjection
Killed by : none
changed conditional boundary → NO_COVERAGE

309

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

310

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

319

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

334

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

336

1.1
Location : checkInfinite
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

337

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

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

339

1.1
Location : checkInfinite
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE

348

1.1
Location : checkInfinite
Killed by : none
replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::checkInfinite → NO_COVERAGE

357

1.1
Location : parseTrailOnlyFound
Killed by : none
replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseTrailOnlyFound → NO_COVERAGE

383

1.1
Location : parseLeadFound
Killed by : none
replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseLeadFound → NO_COVERAGE

391

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

396

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

409

1.1
Location : scrap
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

426

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

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

3.3
Location : sendProgress
Killed by : none
changed conditional boundary → NO_COVERAGE

429

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

430

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

431

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

449

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

460

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

466

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

473

1.1
Location : parse
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

476

1.1
Location : parse
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetRows::parse → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.16.1