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