001package csheets.ext.database.core; 002 003import java.sql.SQLException; 004import java.util.*; 005 006import csheets.core.Cell; 007import csheets.core.formula.compiler.FormulaCompilationException; 008 009/** 010 * A class that deals with all the data going into and from the databases (UML 011 * facade pattern) 012 * 013 * @author João Carreira 014 */ 015public class DatabaseFacade extends Observable { 016 private DBConnectionStrategy adapter; 017 private String tableName; 018 private Cell[][] cells; 019 private int[] pk; 020 021 /** 022 * constructor 023 */ 024 public DatabaseFacade() { 025 } 026 027 /** 028 * creates a connection to a database driver 029 * 030 * @param url 031 * path to the driver 032 * @param user 033 * username 034 * @param pass 035 * password 036 */ 037 public void createConnection(String url, String user, String pass, 038 String dbName) throws SQLException, ClassNotFoundException, 039 Exception { 040 /* gets factory instance */ 041 DBConnectionFactory factory = DBConnectionFactory.getInstance(); 042 /* gets the corresponding adapter based on the adaptee class name */ 043 adapter = factory.getDBTechnology(dbName); 044 adapter.createConnection(url, user, pass); 045 } 046 047 /** 048 * exports data to a database 049 * 050 * @param cells 051 * cells to be exported 052 * @param tableName 053 * table's name in the database 054 */ 055 public void exportData(Cell[][] cells, String tableName) { 056 this.tableName = tableName; 057 this.cells = cells; 058 adapter.createTable(tableName, cells); 059 adapter.disconnet(); 060 } 061 062 /** 063 * gets the table list of a given database 064 */ 065 public String[] getTableList() { 066 return adapter.getTableList(adapter.queryToArray()); 067 } 068 069 /** 070 * loads data from a table of the database 071 * 072 * @param tableName 073 * name of the table 074 */ 075 public String[][] loadTable(String tableName) { 076 return adapter.getTableContent(tableName); 077 } 078 079 /** 080 * Starts a new sync with database 081 * 082 * @param user 083 * username 084 * @param pass 085 * username's password 086 * @param cells 087 * cells to be sync 088 * @param tableName 089 * name of the table 090 * @param url 091 * database url 092 * @param observer 093 * the observer object 094 */ 095 public void startSync(String user, String pass, Cell[][] cells, 096 String tableName, String url, Observer observer) { 097 addObserver(observer); 098 adapter.createTable(tableName, cells); 099 100 CellDatabase[][] cellsTemp = new CellDatabase[cells.length - 1][cells[0].length]; 101 while (true) { 102 try { 103 temporaryStructure(cells, cellsTemp); 104 adapter.disconnet(); 105 106 Thread.sleep(30000); 107 adapter.createConnection(url, user, pass); 108 String[][] serverCells = loadTable(tableName); 109 for (int i = 0; i < cellsTemp.length; i++) { 110 int indexServ = findLine(cellsTemp[i][0].getRow(), 111 serverCells); 112 int indexApp = findLine(cellsTemp[i][0].getRow(), cells); 113 checkLine(serverCells[indexServ], cells[indexApp], 114 cellsTemp[i], tableName, cells[0]); 115 } 116 117 } catch (InterruptedException e) { 118 } catch (FormulaCompilationException e) { 119 } catch (ClassNotFoundException e) { 120 } catch (SQLException e) { 121 } 122 } 123 } 124 125 /** 126 * Creates a temporary structure of sync cells 127 * 128 * @param cells 129 * cells of application 130 * @param cellsTemp 131 * temporary cells 132 */ 133 private void temporaryStructure(Cell[][] cells, CellDatabase[][] cellsTemp) { 134 for (int i = 1; i < cells.length; i++) { 135 for (int j = 0; j < cells[i].length; j++) { 136 cellsTemp[i - 1][j] = new CellDatabase( 137 cells[i][j].getContent(), cells[i][j].getAddress() 138 .getRow(), cells[i][j].getAddress().getColumn()); 139 } 140 } 141 } 142 143 /** 144 * Method that will check if the line changes 145 * 146 * @param lineServer 147 * line in the server 148 * @param cellApp 149 * line in the app 150 * @param cellTemp 151 * line in the temporary cell 152 * @param tableName 153 * name of the table 154 * @param cellHeader 155 * the header of the cell table 156 * @throws FormulaCompilationException 157 * throws if the wrong formula was entered 158 */ 159 private void checkLine(String[] lineServer, Cell[] cellApp, 160 CellDatabase[] cellTemp, String tableName, Cell[] cellHeader) 161 throws FormulaCompilationException { 162 for (int i = 0; i < cellApp.length; i++) { 163 if (cellApp[i].getContent().equals(cellTemp[i].getContent()) 164 && !cellTemp[i].getContent().equals(lineServer[i + 1])) { 165 cellApp[i].setContent(lineServer[i + 1]); 166 } else if (!cellApp[i].getContent() 167 .equals(cellTemp[i].getContent()) 168 && cellTemp[i].getContent().equals(lineServer[i + 1])) { 169 adapter.updateRow(tableName, cellHeader[i].getContent(), 170 cellApp[i].getContent(), lineServer[0]); 171 } else if (!cellApp[i].getContent() 172 .equals(cellTemp[i].getContent()) 173 && !cellTemp[i].getContent().equals(lineServer[i + 1])) { 174 ObserverMessages obs = new ObserverMessages(lineServer[i + 1], 175 cellApp[i].getContent()); 176 setChanged(); 177 notifyObservers(obs); 178 clearChanged(); 179 if (obs.getDecision() == 0) { 180 cellApp[i].setContent(lineServer[i + 1]); 181 } else if (obs.getDecision() == 1) { 182 adapter.updateRow(tableName, cellHeader[i].getContent(), 183 cellApp[i].getContent(), lineServer[0]); 184 } 185 } 186 } 187 188 } 189 190 /** 191 * Find the index of the line in the server cells 192 * 193 * @param row 194 * the row of the line to be sync 195 * @param serverCells 196 * the server cells 197 * @return the index of the line of the server 198 */ 199 private int findLine(int row, String[][] serverCells) { 200 for (int i = 1; i < serverCells.length; i++) { 201 if (Integer.parseInt(serverCells[i][0]) == row) { 202 return i; 203 } 204 } 205 return 0; 206 } 207 208 /** 209 * Find the index of the line in the application cells 210 * 211 * @param row 212 * the row of the line to be sync 213 * @param cells 214 * the application cells 215 * @return the index of the line of the client 216 */ 217 private int findLine(int row, Cell[][] cells) { 218 for (int i = 0; i < cells.length; i++) { 219 if (cells[i][0].getAddress().getRow() == row) { 220 return i; 221 } 222 } 223 return 0; 224 } 225 226 /** 227 * converts selected spreadsheet content in a 2D String array 228 * 229 * @param cells 230 * selected cells in spreadsheet 231 * @return 2D array with content in selected cells 232 */ 233 public String[][] cellsTo2dArray(Cell[][] cells) { 234 /* we add an additional row to temp so we can store the row number */ 235 String[][] temp = new String[cells.length][cells[0].length + 1]; 236 237 /* position [0][0] must always have LINHA */ 238 temp[0][0] = "LINHA"; 239 240 for (int i = 0; i < temp.length; i++) { 241 /* if it's [i][0] other than [0][0] then we go get the row */ 242 if (i > 0) { 243 temp[i][0] = Integer 244 .toString(cells[i][0].getAddress().getRow()); 245 } 246 /* the rest of the columns are filled with cells conent */ 247 for (int j = 1; j < temp[0].length; j++) { 248 temp[i][j] = cells[i][j - 1].getContent().toString(); 249 } 250 } 251 return temp; 252 } 253 254 /** 255 * compares if there's any difference in content between the selected cells 256 * in the spreadsheet and the ones imported from the DB 257 * 258 * @param tableData 259 * content imported from BD 260 * @param selectedCells 261 * cells selected in the spreadsheet 262 * @return true if there's any difference, false if they're equal 263 */ 264 public boolean compareCellsWithDB(String[][] tableData, 265 String[][] selectedCells) { 266 /* 267 * to avoid getting the array out of bounds index in need to strict the 268 * comparison only in the range of the smaller array (we won't have 269 * trouble with columns as we only get here if both arrays have the same 270 * number of cols) 271 */ 272 273 int lessCols; 274 if (tableData.length < selectedCells.length) { 275 lessCols = tableData.length; 276 /* 277 * is this case we're certain we can return true because having one 278 * more row implies that we have different information when compared 279 * to the DB 280 */ 281 return true; 282 } else { 283 lessCols = selectedCells.length; 284 } 285 286 for (int i = 0; i < lessCols; i++) { 287 for (int j = 0; j < selectedCells[0].length; j++) { 288 if (!tableData[i][j].equals(selectedCells[i][j])) { 289 return true; 290 } 291 } 292 } 293 return false; 294 } 295 296 /** 297 * updates a database table based on the selected cells 298 * 299 * @param tableName 300 * target table to be updated 301 * @param tableData 302 * 2D array with current table data 303 * @param selectedCells 304 * 2D array with selected cells in spreadsheet 305 */ 306 public void updateTable(String tableName, String[][] tableData, String[][] selectedCells, Cell [][]cells) 307 { 308 this.cells = cells; 309 int tableDataRows = tableData.length; 310 int selectedCellsRows = selectedCells.length; 311 312// System.out.println("tableDataRows = " + tableDataRows); 313// System.out.println("selectedCellsRows = " + selectedCellsRows); 314 315 /* 316 * if selected cells have more rows than table data then we need to at 317 * least insert new data into the database 318 */ 319 if (selectedCellsRows > tableDataRows) 320 { 321 /* creating a new array with only the "extra" rows in the spreadsheet */ 322 int startIndex = tableDataRows; 323// System.out.println("startIndex = " + startIndex); 324 String [][]newRows = new String[selectedCells.length - startIndex][tableData[0].length]; 325 for(int i = 0; i < newRows.length; i++) 326 { 327 for(int j = 0; j < newRows[0].length; j++) 328 { 329 newRows[i][j] = selectedCells[i + startIndex][j]; 330// System.out.println(newRows[i][j]); 331 } 332// System.out.println("-----------"); 333 334 } 335 /* inserts the "extra" selected cells in the database */ 336 adapter.insertNewData(tableName, newRows); 337 /* at this point we need to make sure the rows are equal in both 338 * tables so we call the following methods before update takes place */ 339 selectedCells = cellsTo2dArray(cells); 340 tableData = loadTable(tableName); 341 updateEqualRows(tableName, tableData, selectedCells); 342 } 343 344// /* 345// * if selected cells have less rows than table data then we need to at 346// * least remove a record from the database 347// */ 348// else if (selectedCellsRows < tableDataRows) 349// { 350// 351// } 352 353 /* if row count is the same then we only need to update the table */ 354 else 355 { 356 updateEqualRows(tableName, tableData, selectedCells); 357 } 358 359 } 360 361 /** 362 * updates a database table when selected cells in the spreadsheet and database's 363 * table have the same column number 364 * @param tableName database's table 365 * @param tableData 2D array with database's data 366 * @param selectedCells 2D array with spreadsheet's data 367 */ 368 private void updateEqualRows(String tableName, String[][] tableData, String[][] selectedCells) 369 { 370 String[][] modifiedCells = new String[selectedCells.length][selectedCells[0].length]; 371// int cont = 0; 372 for (int i = 0; i < selectedCells.length; i++) 373 { 374 for (int j = 0; j < selectedCells[0].length; j++) 375 { 376 if (!selectedCells[i][j].toString().equals(tableData[i][j].toString())) 377 { 378 adapter.updateRow(tableName, tableData[0][j], selectedCells[i][j], tableData[i][0]); 379// cont++; 380// System.out.println("GOT HERE: " + cont); 381// System.out.println("table name: " + tableName); 382// System.out.println("table data 0j: " + tableData[0][j]); 383// System.out.println("selected cells ij: " + selectedCells[i][j]); 384// System.out.println("tabledata i0: " + tableData[i][0]); 385// System.out.println("\n"); 386 } 387 } 388// cont = 0; 389 } 390 } 391 392 /** 393 * updates a database table by deleting unselected rows and editing modified rows 394 * @param tableName table name in database 395 * @param tableData content from database 396 * @param selectedCells content sppreadsheet 397 * @param cells selected cells 398 */ 399 public void updateTableWithDeletion(String tableName, String[][] tableData, String[][] selectedCells, Cell[][] cells) 400 { 401 int startIndex = selectedCells.length; 402 int endIndex = tableData.length; 403 404 String []toDelete = new String[endIndex - startIndex]; 405 406 for(int i = 0; i < toDelete.length; i++) 407 { 408 toDelete[i] = tableData[i + startIndex][0]; 409 } 410 411 adapter.deleteRows(tableName, toDelete); 412 413 //TODO 414// selectedCells = cellsTo2dArray(cells); 415// tableData = loadTable(tableName); 416// updateEqualRows(tableName, tableData, selectedCells); 417 } 418}