· 6 years ago · Dec 06, 2019, 07:44 PM
1package eg.edu.alexu.csd.oop.db;
2
3import java.io.File;
4import java.sql.SQLException;
5import java.util.*;
6
7public class DB implements Database {
8 private volatile static DB obj;
9 private static final String ANSI_RESET = "\u001B[0m";
10 private static final String ANSI_RED = "\u001B[31m";
11 private String path;
12 private LinkedList<Table> tables = new LinkedList<>();
13 private Mark mark = new Mark();
14 private List<Object> data;
15 private String[] actualHeaders;
16 private Class[] actualTypes;
17 private String directoryOfDatabases = "DataBasesDirectory";
18 private String selectedTableName;
19 private Class[] selectedTypes;
20 private String[] selectedHeaders;
21
22 public Class[] getSelectedTypes() {
23 return selectedTypes;
24 }
25
26 public String[] getSelectedHeaders() {
27 return selectedHeaders;
28 }
29
30 public DB() {
31
32 }
33
34 /**
35 * to use sigelton oop-pattern
36 */
37 public static DB getInstance()
38 {
39 if (obj == null)
40 {
41 // To make thread safe
42 synchronized (DB.class)
43 {
44 // check again as multiple threads
45 // can reach above step
46 if (obj==null)
47 obj = new DB();
48 }
49 }
50 return obj;
51 }
52
53
54 @Override
55 public String createDatabase(String databaseName, boolean dropIfExists) {
56
57// File direct = new File(directoryOfDatabases);
58// if (!direct.exists()) direct.mkdirs();
59// databaseName = directoryOfDatabases + System.getProperty("file.separator") + databaseName;
60 File file = new File(databaseName);
61 boolean exist = file.exists();
62 if (exist && !dropIfExists) { //if exist and not drop >> use it
63 this.path = databaseName;
64 try {
65 tables = ReadTables.ReadTables(databaseName);
66 } catch (Exception e) {
67 System.out.println(ANSI_RED + e.getMessage() + ANSI_RESET);
68 }
69 return databaseName;
70 } else if (exist) { //if exist and drop >> drop the old then create new one and use it
71 dropDirectory(databaseName);
72 createDirectory(databaseName);
73 tables = new LinkedList<>();
74 this.path = databaseName;
75 return databaseName;
76 }
77 if (createDirectory(databaseName)) { //if not exists create it
78 this.path = databaseName;
79 tables = new LinkedList<>();
80 return databaseName;
81 } else return databaseName;
82 }
83
84 private void dropDirectory(String path) { //this function can delete a directory and all its contents
85 File oldData = new File(path);
86 String[] entries = oldData.list();
87 if (entries != null) {
88 for (String s : entries) {
89 dropDirectory(oldData.getPath() + System.getProperty("file.separator") + s);
90 }
91 }
92// else {
93// if (!path.contains(".xml") && !path.contains(".xsd"))
94// System.out.println(ANSI_RED + "Data base doesn't exist!!" + ANSI_RESET);
95// }
96 oldData.delete();
97 }
98
99 private boolean createDirectory(String path) { //this function creates a directory witt this path
100 File directory = new File(path);
101 return directory.mkdirs();
102 }
103
104 @Override
105 public boolean executeStructureQuery(String query) throws SQLException {
106 query = query.replaceAll("\'", "\"");
107 if (query.matches("(?i)^create .+")) { //starts with create
108 LinkedList<Object> resultOfQuery = Parser.parseCreate(query);
109 if (resultOfQuery == null || (resultOfQuery.size() != 3 && resultOfQuery.size() != 5)) {
110 throw new SQLException("Wrong create command");
111 }
112 if ((Boolean) resultOfQuery.get(0)) { //create DataBase
113 String dataName = (String) resultOfQuery.get(1);
114 Boolean drop = (Boolean) resultOfQuery.get(2);
115 createDatabase(dataName, drop);
116 } else { //create a table
117 if (path == null) {
118 throw new SQLException("no database initialized");
119 }
120 String tableName = (String) resultOfQuery.get(1);
121 File tablePath = new File(path + System.getProperty("file.separator") + tableName);
122 if (tablePath.exists()) {
123 System.out.println("table is already existed");
124 return false;
125 }
126 String[] namesOfColumns = (String[]) resultOfQuery.get(3);
127 String[] types = (String[]) resultOfQuery.get(4);
128 if (tableName == null || namesOfColumns[0] == null || types[0] == null) {
129 System.out.println(ANSI_RED + "Wrong table initialization!!" + ANSI_RESET);
130 return false;
131 }
132 Table t = new Table(tableName, namesOfColumns, types);
133 addTable(t);
134 XML.convertIntoXml(path, t);
135 }
136 } else if (query.matches("(?i)^drop.+")) { //starts with drop
137 LinkedList<Object> resultOfQuery = Parser.parseDrop(query);
138 if (resultOfQuery == null || resultOfQuery.size() != 2) {
139 throw new SQLException("wrong query");
140 }
141 if ((Boolean) resultOfQuery.get(0)) { //drop DataBase
142 String dataName = (String) resultOfQuery.get(1);
143 dropDirectory( dataName);
144 this.tables = new LinkedList<>();
145 this.path = null;
146
147 } else { //drop a table
148 String tableName = (String) resultOfQuery.get(1);
149 Table t = getTable(tableName);
150 if (t == null) {
151 throw new SQLException("table name not exist!");
152 }
153 removeTable(t);
154 dropDirectory(path + System.getProperty("file.separator") + tableName);
155 }
156 }
157 return true;
158 }
159
160 @Override
161 public Object[][] executeQuery(String query) throws SQLException {
162 query = query.replaceAll("\'", "\"");
163 LinkedList<Object> result = Parser.parseSelect(query);
164 if (result == null) {
165 throw new SQLException("wrong input format");
166 }
167 LinkedList<String> columns = new LinkedList<>(Arrays.asList((String[]) result.get(0)));
168 String tableName = (String) result.get(1);
169 String condition = (String) result.get(2);
170 LinkedList<String> headers = new LinkedList<>(Arrays.asList(getTable(tableName).getHeaders()));
171 if (columns.getFirst().equals("*")) {
172 columns = headers;
173 }
174 // when ahmed finished it
175 LinkedList<Object[]> references = new LinkedList<>();
176 if (!condition.equals("")) {
177 Mark mark = new Mark();
178 try {
179 references = mark.getData(condition, getTable(tableName));
180 } catch (Exception e) {
181 System.out.println(ANSI_RED + e.getMessage() + ANSI_RESET);
182 return null;
183 }
184 } else {
185 references.addAll(getTable(tableName).getTable());
186 }
187 Object[][] arr = new Object[references.size()][columns.size()];
188 LinkedList<Integer> columnsIndexes = new LinkedList<>();
189 for (String column : columns) {
190 columnsIndexes.add(headers.indexOf(column));
191 } // now i have the indexes of the columns to be selected
192 selectedTableName = tableName;
193 this.selectedHeaders = columns.toArray(new String[columns.size()]);
194 this.selectedTypes = new Class[columns.size()];
195 ArrayList<String> originalHeaders = new ArrayList<>(Arrays.asList(getSelectedTable().getHeaders()));
196 ArrayList<Class> originalTypes = new ArrayList<>(Arrays.asList(getSelectedTable().getTypes()));
197 int h = 0;
198 for (String column : columns) {
199 int index = originalHeaders.indexOf(column);
200 this.selectedTypes[h++] = originalTypes.get(index);
201 }
202 int k = 0;
203 for (int j = 0; j < headers.size(); j++) {
204 if (columnsIndexes.contains(j)) { // j is a column to be selected
205 int i = 0;
206 for (Object record : references) { // i is an index of a record selected
207 arr[i++][k] = ((Object[]) record)[j];
208 }
209 k++;
210 }
211 }
212 return arr;
213 }
214
215 /**
216 * This method like a dirctor, it makes tasks distributions...
217 * @param query
218 * the query needed to be excuted.
219 * @return int
220 * it returns number of operated cols and -1 if no col hasn't inserted or deleted...
221 */
222 @Override
223 public int executeUpdateQuery(String query) throws Exception {
224 query = query.replaceAll("\'", "\"");
225 if(query.matches("(?i)^\\s*INSERT\\s+INTO\\s.+$"))
226 return this.excuteInsert(query);
227 if(query.matches("(?i)^\\s*DELETE\\s+FROM\\s.+$"))
228 return this.excuteDelete(query);
229 if(query.matches("(?i)^\\s*UPDATE\\s+.+$"))
230 return this.excuteUpdate(query);
231 return 0;
232 }
233
234 /**
235 * @param
236 * query of type string the query
237 * @return
238 * Integer define the modified row in the table
239 * this method takes the query and then get the main data to be inserted
240 * checks if the table already exits
241 * checks if the number of scheme objects equals the insertions objects
242 * checks data types of the the insertions
243 * rearrange the data to make it right to according to the scheme
244 */
245 private int excuteInsert(String query) throws SQLException, Exception{
246
247
248 data = Parser.parseInsert(query);
249 if(data == null) {
250 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
251 return 0;
252 }
253 Table operateOnTable = this.existance(data.get(0).toString());
254
255 if(operateOnTable != null) {
256 String[] headers = (String[])data.get(1);
257 if(headers==null){
258 headers=operateOnTable.getHeaders();
259 }
260 toLowerCaseConverter(headers, headers.length);
261 String[] values = (String[])data.get(2);
262 if(headers.length != values.length) {
263 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
264 return 0;
265 }
266 actualHeaders = operateOnTable.getHeaders();
267 toLowerCaseConverter(actualHeaders, actualHeaders.length);
268 actualTypes = operateOnTable.getTypes();
269 if(this.containsTheseHeaders(headers, values)) {
270 String[] strings = this.rearrange(headers, values);
271 operateOnTable.addX(convertToObjects(strings));
272 XML.convertIntoXml(this.path, operateOnTable);
273 return 1;
274 }
275 else {
276 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
277 return 0;
278 }
279 } else {
280 System.out.println(ANSI_RED + "The table is still not being created!!" + ANSI_RESET);
281 return 0;
282 }
283 }
284
285 private Object[] convertToObjects(String[] str) throws Exception {
286 Object[] obj = new Object[str.length];
287 int i = 0;
288 for(String temp : str) {
289 if(temp == null)
290 throw new Exception("Bad Input!!");
291 if(temp.matches("^-?[0-9]+$")){
292 obj[i++] = Integer.parseInt(temp);
293 }
294 else if(temp.matches("(?i).*true|false.*"))
295 obj[i++] = Boolean.parseBoolean(temp);
296 else
297 obj[i++] = temp;
298 }
299 return obj;
300 }
301
302 private int excuteDelete(String query) throws Exception {
303 data = Parser.parseDelete(query);
304 if(data == null) {
305 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
306 return 0;
307 }
308 Table deleteFromTable = this.existance(data.get(0).toString());
309 if(deleteFromTable != null) {
310 if(deleteFromTable.getTable().size() == 0) {
311 throw new Exception("This operation is denied for an empty table!!");
312 }
313 if(data.get(1).equals("")) {
314 int holeSize = deleteFromTable.getTable().size();
315 deleteFromTable.emptyTheTable();
316 try {
317 XML.convertIntoXml(this.path, deleteFromTable);
318 } catch(SQLException e) {}
319 return holeSize;
320 } else {
321 LinkedList<Object[]> deletedRows = mark.getData(data.get(1).toString(), deleteFromTable);
322 for(Object[] O : deletedRows) deleteFromTable.removeRecord(O);
323 try {
324 XML.convertIntoXml(this.path, deleteFromTable);
325 } catch(SQLException e) {}
326 return deletedRows.size();
327 }
328 } else
329 System.out.println(ANSI_RED + "The table is still not being created" + ANSI_RESET);
330 return 0;
331 }
332
333 private int excuteUpdate(String query) throws Exception {
334 LinkedList<Object[]> markedList = new LinkedList<>();
335 data = Parser.parseUpdate(query);
336 if(data == null) {
337 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
338 return 0;
339 }
340 Table updatedTable = this.existance(data.get(0).toString());
341 if(updatedTable != null) {
342 if(updatedTable.getTable().size() == 0) {
343 return 0;
344 }
345 String[] cols = (String[])data.get(2);
346 String[] newValues = (String[])data.get(3);
347 actualHeaders = updatedTable.getHeaders();
348 actualTypes = updatedTable.getTypes();
349 toLowerCaseConverter(cols,cols.length);
350 toLowerCaseConverter(actualHeaders,actualHeaders.length);
351 if(cols.length != newValues.length) {
352 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
353 return 0;
354 }
355 if(this.containsTheseHeaders(cols, newValues)) {
356 if(data.get(1).toString() != "") {
357 markedList = mark.getData(data.get(1).toString(), updatedTable);
358 for(Object[] iterate : markedList) {
359 Object[] newRowToBeExclusivelyAdded = this.createNewRows(iterate, cols, newValues);
360 updatedTable.updateRecord(iterate, newRowToBeExclusivelyAdded);
361 try {
362 XML.convertIntoXml(this.path, updatedTable);
363 } catch(SQLException e) {}
364 }
365 }
366 else {
367 updatedTable.updateHoleTable(this.convertToObjects(newValues), this.getIndexes(cols));
368 try {
369 XML.convertIntoXml(this.path, updatedTable);
370 } catch(SQLException e) {}
371 return updatedTable.getSize();
372 }
373 } else {
374 System.out.println(ANSI_RED + "Bad Input!!" + ANSI_RESET);
375 return 0;
376 }
377 } else
378 throw new SQLException("The table is still not being created!!");
379 return markedList.size();
380 }
381
382 private Object[] createNewRows(final Object[] oldy, String[] cols, String[] newValues) throws Exception {
383 Object[] news = convertToObjects(newValues);
384 LinkedList<String> actualHeadersAsList = new LinkedList<>(Arrays.asList(actualHeaders));
385 Object[] newie = new Object[oldy.length];
386 System.arraycopy(oldy, 0, newie, 0, oldy.length);
387 for(int i = 0, index; i < cols.length; ++i) {
388 index = actualHeadersAsList.indexOf(cols[i]);
389 newie[index] = news[i];
390 }
391 return newie;
392 }
393
394 private int[] getIndexes(String[] cols) {
395 LinkedList<String> actualHeadersAsList = new LinkedList<String>(Arrays.asList(actualHeaders));
396 int[] indexes = new int[cols.length];
397 for(int i = 0, j = 0; i < cols.length; ++i) {
398 if(actualHeadersAsList.contains(cols[i]))
399 indexes[j++] = actualHeadersAsList.indexOf(cols[i]) + 1;
400 }
401 return indexes;
402 }
403
404 private Table existance(String tableName) {
405 return this.getTable(tableName);
406 }
407
408 private Boolean containsTheseHeaders(String[] headers, String[] values) {
409 LinkedList<String> actualHeadersAsList = new LinkedList<String>(Arrays.asList(actualHeaders));
410 int i;
411 for(i = 0; i < headers.length; ++i) {
412 if(actualHeadersAsList.contains(headers[i])) {
413 int index = actualHeadersAsList.indexOf(headers[i]);
414 if(actualTypes[index].toString().matches("(?i)class\\s+java\\.lang\\.Integer")) {
415 if(values[i].matches("^-?[0-9]+$")){
416 continue;
417 }
418 else {
419 System.out.println(ANSI_RED + "Not Matched Types!!" + ANSI_RESET);
420 return false;
421 }
422 }
423 else if(actualTypes[index].toString().matches("(?i)class\\s+java\\.lang\\.Boolean")) {
424 if(values[i].matches("(?i).*(false)|(true).*")) {
425 continue;
426 }
427 else {
428 System.out.println(ANSI_RED + "Not Matched Types!!" + ANSI_RESET);
429 return false;
430 }
431 }
432 } else
433 return false;
434 }
435 return i == headers.length;
436 }
437
438 private String[] rearrange(String[] headers, String[] values) {
439 LinkedList<String> headersList = new LinkedList<String>(Arrays.asList(headers));
440
441 String[] dummy = new String[actualHeaders.length];
442 for(int i = 0; i < headersList.size(); ++i){
443 int index = Arrays.asList(actualHeaders).indexOf((headersList.get(i)));
444 if(index != -1)
445 dummy[index] = values[i];
446 }
447 return dummy;
448 }
449
450 public Table getSelectedTable() {
451 for (Table table : tables) {
452 if (table.getName().equals(this.selectedTableName)) {
453 return table;
454 }
455 }
456 return null;
457 }
458
459 private Table getTable(String tableName){
460 for (Table table : tables) {
461 if (table.getName().equalsIgnoreCase(tableName)) {
462 return table;
463 }
464 }
465 return null;
466 }
467
468 String schema(){
469 StringBuilder stringBuilder = new StringBuilder("");
470 Iterator<Table> tableIterator = tables.listIterator();
471 while (tableIterator.hasNext()){
472 stringBuilder.append(tableIterator.next());
473 if (tableIterator.hasNext()){
474 stringBuilder.append('\n');
475 }
476 }
477 return stringBuilder.toString();
478 }
479
480 private void addTable(Table table){
481 this.tables.add(table);
482 }
483
484 private void removeTable (Table table){
485 this.tables.remove(table);
486 }
487
488 private void toLowerCaseConverter(String[] arr, int length) {
489 for(int i = 0; i < length; ++i)
490 arr[i] = arr[i].toLowerCase();
491 }
492
493}