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}