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