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 |
|
130 |
1.1 |
|
198 |
1.1 |
|
205 |
1.1 |
|
215 |
1.1 |
|
216 |
1.1 |
|
217 |
1.1 |
|
219 |
1.1 |
|
236 |
1.1 |
|
240 |
1.1 |
|
241 |
1.1 |
|
242 |
1.1 |
|
274 |
1.1 |
|
275 |
1.1 |
|
276 |
1.1 |
|
278 |
1.1 |
|
287 |
1.1 |
|
297 |
1.1 |
|
298 |
1.1 |
|
299 |
1.1 |
|
301 |
1.1 |
|
320 |
1.1 |
|
321 |
1.1 |
|
322 |
1.1 |
|
343 |
1.1 |
|
357 |
1.1 |
|
358 |
1.1 |
|
359 |
1.1 |
|
361 |
1.1 |
|
368 |
1.1 |
|
377 |
1.1 |
|
378 |
1.1 |
|
379 |
1.1 |
|
381 |
1.1 |
|
402 |
1.1 |
|
403 |
1.1 |
|
404 |
1.1 |
|
420 |
1.1 |
|
421 |
1.1 |
|
431 |
1.1 |
|
432 |
1.1 |
|
433 |
1.1 |
|
436 |
1.1 |
|
437 |
1.1 |
|
438 |
1.1 |
|
440 |
1.1 |
|
464 |
1.1 |
|
472 |
1.1 |
|
474 |
1.1 |
|
478 |
1.1 |
|
486 |
1.1 2.2 |
|
490 |
1.1 2.2 |
|
501 |
1.1 |
|
507 |
1.1 |
|
508 |
1.1 2.2 |
|
513 |
1.1 |
|
521 |
1.1 |