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