diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/BadDBFileException.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/BadDBFileException.java new file mode 100644 index 000000000..dba295158 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/BadDBFileException.java @@ -0,0 +1,9 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +public class BadDBFileException extends RuntimeException { + + public BadDBFileException(String msg) { + super(msg); + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ChangingCommand.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ChangingCommand.java new file mode 100644 index 000000000..e8e0fb14f --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ChangingCommand.java @@ -0,0 +1,24 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +enum CommandName { + PUT, REMOVE +}; + +public class ChangingCommand { + public CommandName cName; + public String[] args; + + public ChangingCommand(CommandName c, String arg1, String arg2) { + cName = c; + if (cName == CommandName.PUT) { + args = new String[2]; + args[0] = arg1; + args[1] = arg2; + } + if (cName == CommandName.REMOVE) { + args = new String[1]; + args[0] = arg1; + } + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/DBCollection.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/DBCollection.java new file mode 100644 index 000000000..b4938a472 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/DBCollection.java @@ -0,0 +1,89 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.util.List; +import java.io.File; +import java.io.IOException; + +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.strings.TableProvider; + +public class DBCollection implements TableProvider { + private String dirPath; + private FileMap maps; + private static final String INFO_FILE_NAME = "tables_info.dat"; + + public DBCollection(String dirPath) throws IllegalArgumentException { + this.dirPath = dirPath; + maps = null; + if (dirPath == null) { + throw new IllegalArgumentException("path is null"); + } + if (dirPath.endsWith(File.separator) && !dirPath.equals(File.separator)) { + this.dirPath = dirPath.substring(0, dirPath.length() - 1); + } + try { + maps = new FileMap(dirPath + File.separator + INFO_FILE_NAME); + } catch (BadDBFileException | IOException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + public List showTables() { // Actually doesn't + // throw anything. + return maps.list(); + } + + @Override + public Table getTable(String name) throws IllegalArgumentException { + String fullPath = maps.get(name); + if (fullPath == null) { + return null; + } + Table res; + try { + res = new TableWithTransactions(name); + } catch (BadDBFileException e) { + throw new IllegalArgumentException(e.getMessage()); + } + return res; + } + + @Override + public Table createTable(String name) { + // TODO Auto-generated method stub + if (maps.get(name) != null) { + return null; + } + maps.put(name, dirPath + File.separator + name); + File dir = new File(dirPath + File.separator + name); + if (!dir.mkdirs()) { + throw new IllegalArgumentException(); + } + TableWithTransactions res; + try { + res = new TableWithTransactions(dirPath + File.separator + name); + } catch (BadDBFileException e) { + maps.remove(name); + throw new IllegalStateException(); + } + return res; + } + + @Override + public void removeTable(String name) { + String tname = maps.get(name); + if (tname != null) { // Database found. + if (!Utils.removeDirectory(tname)) { + throw new IllegalStateException("Couldn't remove directory"); + } else { + maps.remove(name); + } + } else { // Database not found. + throw new IllegalStateException(); + } + } + + public void close() throws IOException { + maps.close(); + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/FileMap.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/FileMap.java new file mode 100644 index 000000000..d3895c46c --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/FileMap.java @@ -0,0 +1,158 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import ru.fizteh.fivt.storage.strings.Table; + +public class FileMap implements Table { + private Map table; + private File dbFile; + + public FileMap(String path) throws BadDBFileException, IOException { + table = new HashMap<>(); + dbFile = new File(path); + if (!dbFile.exists()) { + if (!dbFile.createNewFile()) { + throw new BadDBFileException("Couldn't create db file"); + } + } else { + if (!dbFile.isFile()) { + throw new BadDBFileException("Is not a file"); + } + } + if (!(dbFile.setReadable(true)) && dbFile.setWritable(true)) { + throw new BadDBFileException("Couldn't set rw options"); + } + DataInputStream in = new DataInputStream(new FileInputStream(dbFile)); + + while (true) { + String key = readString(in); + if (key == null) { + break; + } + String value = readString(in); + if (value == null) { + in.close(); + throw new BadDBFileException("Couldn't set rw options"); + } + table.put(key, value); + } + in.close(); + + } + + /** + * @return String read from file. If meets end of file, returns null. + * @throws BadDBFileException + * @throws IOException + * */ + + private String readString(DataInputStream in) throws IOException, + BadDBFileException { + final int sizeOfInt = 4; + int len; + StringBuilder res = new StringBuilder(); + if (in.available() >= sizeOfInt) { + len = in.readInt(); + if (0 != len % 2 || in.available() < len) { + in.close(); + throw new BadDBFileException("File was damaged"); + } + len /= 2; + while (len > 0) { + char curChar = in.readChar(); + res.append(curChar); + len--; + } + } else { + return null; + } + return res.toString(); + } + + public String put(String key, String value) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException(); + } + String res = table.get(key); + table.put(key, value); + return res; + } + + public String get(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException(); + } + String val = table.get(key); + return val; + } + + public List list() { + List res = new ArrayList<>(); + Set> tableSet = table.entrySet(); + for (Entry i : tableSet) { + res.add(i.getKey()); + } + return res; + } + + public String remove(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException(); + } + return table.remove(key); + } + + public void close() throws IOException { + DataOutputStream out = new DataOutputStream( + new FileOutputStream(dbFile)); + Set> tableSet = table.entrySet(); + for (Entry it : tableSet) { + writeData(out, it.getKey()); + writeData(out, it.getValue()); + } + out.flush(); + out.close(); + } + + private void writeData(DataOutputStream out, String toWrite) + throws IOException { + int len = toWrite.length(); + out.writeInt(len * 2); + out.writeChars(toWrite); + } + + public boolean isEmpty() { + return table.isEmpty(); + } + + public int size() { + return table.size(); + } + + @Override + public String getName() { + return dbFile.getName(); + } + + @Override + public int commit() { + return 0; + } + + @Override + public int rollback() { + return 0; + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Main.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Main.java new file mode 100644 index 000000000..cb08a6c23 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Main.java @@ -0,0 +1,155 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.io.IOException; +import java.util.Scanner; + +import ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter.DBInterpreter; +import ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter.HandlerReturn; +import ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter.HandlerReturnResult; + +public class Main { + + public static HandlerReturn commandSplitting(String command, + DBInterpreter inter) { + String[] firstSplitted = command.split(" "); + String[] toGive = new String[firstSplitted.length]; + int j = 0; + for (int i = 0; i < firstSplitted.length; i++) { + if (firstSplitted[i].length() > 0) { + toGive[j] = firstSplitted[i]; + j++; + } + } + if (0 == j) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, ""); + } + try { + return inter.handle(toGive, 0, j); + } catch (IOException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, + "Couldn't close provider"); + } + } + + public static void batchMode(String[] args, DBInterpreter inter) { + String currentLine = ""; + for (int i = 0; i < args.length; i++) { + currentLine += args[i] + " "; + } + String[] commands = currentLine.split(";"); + try { + for (int i = 0; i < commands.length; i++) { + if (commands[i].length() > 0) { + HandlerReturn ret = commandSplitting(commands[i], inter); + switch (ret.getVal()) { + case SUCCESS: + System.out.print(ret.getMessage()); + break; + case NO_SUCH_COMMAND: + System.out + .print("No such command: " + ret.getMessage()); + break; + case NOT_ENOUGH_PARAMETRES: + System.out.println("Not enough parametres for command " + + ret.getMessage()); + break; + case ERROR: + System.out.print("Error occured " + ret.getMessage()); + break; + case TABLE_NOT_CHOSEN: + System.out.println("Table not chosen"); + break; + case EXIT: + System.out.print(ret.getMessage()); + i = commands.length; + break; + default: + break; + } + } + } + + } catch (Exception e) { + System.err.println(e.getMessage()); + try { + inter.emergencyExit(); + System.err.println("Emergency exit succeeded"); + } catch (IOException e1) { + System.err.println("Emergency exit failed"); + } + System.exit(1); + } + try { + inter.handleExit(); + } catch (IOException e) { + System.exit(1); + } + System.exit(0); + } + + public static void main(String[] args) { + DBInterpreter inter; + try { + inter = new DBInterpreter(System.getProperty("fizteh.db.dir")); + } catch (IllegalArgumentException e1) { + return; + } + if (args.length > 0) { + batchMode(args, inter); + } + Scanner in = new Scanner(System.in); + + boolean contFlag = true; + try { + while (contFlag) { + System.out.print(" $ "); + String currentLine = in.nextLine(); + String[] commands = currentLine.split(";"); + for (int i = 0; i < commands.length; i++) { + if (commands[i].length() > 0) { + HandlerReturn ret = commandSplitting(commands[i], inter); + switch (ret.getVal()) { + case SUCCESS: + System.out.print(ret.getMessage()); + break; + case NO_SUCH_COMMAND: + System.out.print("No such command: " + + ret.getMessage()); + break; + case NOT_ENOUGH_PARAMETRES: + System.out + .println("Not enough parametres for command " + + ret.getMessage()); + break; + case ERROR: + System.out.print("Error occured " + + ret.getMessage()); + break; + case TABLE_NOT_CHOSEN: + System.out.println("no table"); + break; + case EXIT: + System.out.print(ret.getMessage()); + contFlag = false; + break; + default: + break; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + try { + inter.emergencyExit(); + System.err.println("Emergency exit succeeded"); + } catch (IOException e1) { + System.err.println("Emergency exit failed"); + } + } finally { + in.close(); + } + + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MultiFileHashMap.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MultiFileHashMap.java new file mode 100644 index 000000000..fca569dd8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MultiFileHashMap.java @@ -0,0 +1,191 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.TreeSet; + +import ru.fizteh.fivt.storage.strings.Table; + +public class MultiFileHashMap implements Table { + private static final int FILE_MAPS_CNT = 256; + private static final int DIRS_CNT = 16; + private static final String DIR_SUFFIX = ".dir"; + private static final String FILE_SUFFIX = ".dat"; + private File rootDir; + private BitSet openedMaps; + private FileMap[] maps; + private String name; + int size; + + public MultiFileHashMap(String path) throws BadDBFileException { + String[] splittedPath = path.split(File.separator); + name = splittedPath[splittedPath.length - 1]; + size = 0; + rootDir = Utils.safeMkDir(path); + openedMaps = new BitSet(FILE_MAPS_CNT); + openedMaps.clear(); + maps = new FileMap[FILE_MAPS_CNT]; + for (int i = 0; i < DIRS_CNT; ++i) { + String suffix = File.separator; + suffix += Integer.toString(i); + suffix += DIR_SUFFIX; + Utils.safeMkDir(path + suffix); + } + } + + public String put(String key, String value) throws IllegalArgumentException { + if (key == null || value == null) { + throw new IllegalArgumentException(); + } + int mapNum = Math.abs(key.hashCode() % FILE_MAPS_CNT); + if (openedMaps.get(mapNum)) { + return maps[mapNum].put(key, value); + } else { + String curPath = getPath(mapNum); + try { + maps[mapNum] = new FileMap(curPath); + } catch (IOException | BadDBFileException e) { + throw new BadDBFileException(e.getMessage()); + } + openedMaps.set(mapNum); + return maps[mapNum].put(key, value); + } + } + + public String get(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException(); + } + int mapNum = Math.abs(key.hashCode() % FILE_MAPS_CNT); + if (openedMaps.get(mapNum)) { + return maps[mapNum].get(key); + } else { + String curPath = getPath(mapNum); + try { + maps[mapNum] = new FileMap(curPath); + } catch (IOException | BadDBFileException e) { + throw new BadDBFileException(e.getMessage()); + } + openedMaps.set(mapNum); + return maps[mapNum].get(key); + } + } + + public String remove(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException(); + } + int mapNum = Math.abs(key.hashCode() % FILE_MAPS_CNT); + if (openedMaps.get(mapNum)) { + return maps[mapNum].remove(key); + } else { + String curPath = getPath(mapNum); + try { + maps[mapNum] = new FileMap(curPath); + } catch (IOException | BadDBFileException e) { + throw new BadDBFileException(e.getMessage()); + } + openedMaps.set(mapNum); + return maps[mapNum].remove(key); + } + } + + public List list() { + List res = new ArrayList(); + for (int i = 0; i < FILE_MAPS_CNT; i++) { + List t; + if (openedMaps.get(i)) { + t = maps[i].list(); + res.addAll(t); + } else { + String curPath = getPath(i); + try { + maps[i] = new FileMap(curPath); + } catch (IOException | BadDBFileException e) { + throw new BadDBFileException(e.getMessage()); + } + t = maps[i].list(); + res.addAll(t); + openedMaps.set(i); + } + } + return res; + + } + + public void exit() throws IOException { + TreeSet toDelete = new TreeSet(); + for (int i = 0; i < FILE_MAPS_CNT; i++) { + if (openedMaps.get(i)) { + if (maps[i].isEmpty()) { + toDelete.add(i); + } + maps[i].close(); + } + } + for (Integer num : toDelete) { + File tdel = new File(getPath(num)); + tdel.delete(); + } + for (int i = 0; i < DIRS_CNT; i++) { + String suffix = File.separator; + suffix += Integer.toString(i); + suffix += DIR_SUFFIX; + File tdir = new File(rootDir.getAbsolutePath() + suffix); + tdir.delete(); // Won't be deleted if there're any files. + } + } + + /** + * @return Path to database file by number of database + */ + private String getPath(int hash) { + int ndir = Math.abs(hash % DIRS_CNT); + int nfile = Math.abs(hash / DIRS_CNT % DIRS_CNT); + String path = rootDir.getAbsolutePath(); + String suffix = File.separator; + suffix += Integer.toString(ndir) + DIR_SUFFIX; + suffix += Integer.toString(nfile) + FILE_SUFFIX; + path += suffix; + + return path; + } + + @Override + public String getName() { + return name; + } + + @Override + public int size() { + int res = 0; + for (int i = 0; i < FILE_MAPS_CNT; ++i) { + if (openedMaps.get(i)) { + res += maps[i].size(); + } else { + String curPath = getPath(i); + try { + maps[i] = new FileMap(curPath); + res += maps[i].size(); + openedMaps.set(i); + } catch (IOException | BadDBFileException e) { + throw new BadDBFileException(e.getMessage()); + } + } + } + return res; + } + + @Override + public int commit() { + return 0; + } + + @Override + public int rollback() { + return 0; + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTable.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTable.java new file mode 100644 index 000000000..7c4147d14 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTable.java @@ -0,0 +1,53 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.util.Stack; + +public class MyTable extends MultiFileHashMap { + Stack opStack; + + public MyTable(String path) throws BadDBFileException { + super(path); + opStack = new Stack<>(); + } + + public String put(String key, String value) throws IllegalArgumentException { + String result = super.put(key, value); + if (result == null) { // If PUT has just pushed new value + opStack.push(new ChangingCommand(CommandName.REMOVE, key, "")); + } else { // If PUT has overwritten old value + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public String remove(String key) throws IllegalArgumentException { + String result = super.remove(key); + if (result != null) { // Key found. + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public int rollback() { + int cnt = opStack.size(); + while (!opStack.isEmpty()) { + ChangingCommand t = opStack.pop(); + if (t.cName == CommandName.PUT) { + super.put(t.args[0], t.args[1]); + } else { + super.remove(t.args[0]); + } + } + return cnt; + } + + public int commit() { + int cnt = opStack.size(); + opStack.clear(); + return cnt; + } + + public int getUnsavedChanges() { + return opStack.size(); + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTableProviderFactory.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTableProviderFactory.java new file mode 100644 index 000000000..b0326abf2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/MyTableProviderFactory.java @@ -0,0 +1,15 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; + +public class MyTableProviderFactory implements TableProviderFactory { + + @Override + public TableProvider create(String dir) throws IllegalArgumentException { + TableProvider res; + res = new DBCollection(dir); + return res; + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ReversableMFHM.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ReversableMFHM.java new file mode 100644 index 000000000..c64fbe5de --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/ReversableMFHM.java @@ -0,0 +1,53 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.util.Stack; + +public class ReversableMFHM extends MultiFileHashMap { + Stack opStack; + + public ReversableMFHM(String path) throws BadDBFileException { + super(path); + opStack = new Stack<>(); + } + + public String put(String key, String value) throws IllegalArgumentException { + String result = super.put(key, value); + if (result == null) { // If PUT has just pushed new value + opStack.push(new ChangingCommand(CommandName.REMOVE, key, "")); + } else { // If PUT has overwritten old value + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public String remove(String key) throws IllegalArgumentException { + String result = super.remove(key); + if (result != null) { // Key found. + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public int rollback() { + int cnt = opStack.size(); + while (!opStack.isEmpty()) { + ChangingCommand t = opStack.pop(); + if (t.cName == CommandName.PUT) { + super.put(t.args[0], t.args[1]); + } else { + super.remove(t.args[0]); + } + } + return cnt; + } + + public int commit() { + int cnt = opStack.size(); + opStack.clear(); + return cnt; + } + + public int getUnsavedChanges() { + return opStack.size(); + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/TableWithTransactions.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/TableWithTransactions.java new file mode 100644 index 000000000..5ac15596e --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/TableWithTransactions.java @@ -0,0 +1,53 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.util.Stack; + +public class TableWithTransactions extends MultiFileHashMap { + Stack opStack; + + public TableWithTransactions(String path) throws BadDBFileException { + super(path); + opStack = new Stack<>(); + } + + public String put(String key, String value) throws IllegalArgumentException { + String result = super.put(key, value); + if (result == null) { // If PUT has just pushed new value + opStack.push(new ChangingCommand(CommandName.REMOVE, key, "")); + } else { // If PUT has overwritten old value + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public String remove(String key) throws IllegalArgumentException { + String result = super.remove(key); + if (result != null) { // Key found. + opStack.push(new ChangingCommand(CommandName.PUT, key, result)); + } + return result; + } + + public int rollback() { + int cnt = opStack.size(); + while (!opStack.isEmpty()) { + ChangingCommand t = opStack.pop(); + if (t.cName == CommandName.PUT) { + super.put(t.args[0], t.args[1]); + } else { + super.remove(t.args[0]); + } + } + return cnt; + } + + public int commit() { + int cnt = opStack.size(); + opStack.clear(); + return cnt; + } + + public int getUnsavedChanges() { + return opStack.size(); + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Utils.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Utils.java new file mode 100644 index 000000000..236e4b5f3 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/Utils.java @@ -0,0 +1,51 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit; + +import java.io.File; + +public class Utils { + /** + * @return False is something was wrong + */ + static boolean removeDirectory(String path) { + File dir = new File(path); + if (!dir.exists()) { + return false; + } + if (!dir.isDirectory()) { + return false; + } + File[] flist = dir.listFiles(); + for (int i = 0; i < flist.length; i++) { + if (flist[i].isDirectory()) { + if (0 == flist[i].listFiles().length) { + if (!flist[i].delete()) { + return false; + } + } else { + if (!removeDirectory(flist[i].getAbsolutePath())) { + return false; + } + } + } else { + if (!flist[i].delete()) { + return false; + } + } + + } + return dir.delete(); + } + + static File safeMkDir(String path) throws BadDBFileException { + File dir = new File(path); + if (dir.exists()) { + if (!dir.isDirectory()) { + throw new BadDBFileException("File " + path + + " already exists and it's not a directory"); + } + } else { + dir.mkdir(); + } + return dir; + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/DBInterpreter.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/DBInterpreter.java new file mode 100644 index 000000000..836b9fc06 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/DBInterpreter.java @@ -0,0 +1,305 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.dmitry_morozov.junit.DBCollection; +import ru.fizteh.fivt.students.dmitry_morozov.junit.TableWithTransactions; +import ru.fizteh.fivt.students.dmitry_morozov.junit.MyTableProviderFactory; + +public class DBInterpreter { + private DBCollection provider; + private TableWithTransactions table; + private HashMap argsCount; + private TreeSet commandsForTable; + + public DBInterpreter(String path) { + TableProviderFactory factory = new MyTableProviderFactory(); + try { + provider = (DBCollection) factory.create(path); + if (provider == null) { + System.out.println("Usage: java -Dfizteh.db.dir="); + return; + } + } catch (IllegalArgumentException e) { + System.out + .println("Usage: java -Ddb.file=. Your path could have had incorrect value"); + throw new IllegalArgumentException(); + } + table = null; + argsCount = new HashMap<>(); + argsCount.put("use", 1); + argsCount.put("create", 1); + argsCount.put("drop", 1); + argsCount.put("put", 2); + argsCount.put("get", 1); + argsCount.put("remove", 1); + argsCount.put("list", 0); + argsCount.put("size", 0); + argsCount.put("rollback", 0); + argsCount.put("commit", 0); + argsCount.put("exit", 0); + argsCount.put("show", 1); + commandsForTable = new TreeSet<>(); + commandsForTable.add("put"); + commandsForTable.add("get"); + commandsForTable.add("remove"); + commandsForTable.add("list"); + commandsForTable.add("size"); + commandsForTable.add("rollback"); + commandsForTable.add("commit"); + + } + + public HandlerReturn handle(String[] comAndParams, int bIndex, int eIndex) + throws IOException { + if (comAndParams.length < 1) { + return new HandlerReturn(HandlerReturnResult.EXIT, ""); + } + String command = comAndParams[bIndex].toLowerCase(); + Integer expectedArgsCount = argsCount.get(command); + if (expectedArgsCount == null) { + return new HandlerReturn(HandlerReturnResult.NO_SUCH_COMMAND, + command + "\n"); + } else { + if (expectedArgsCount > eIndex - bIndex - 1) { + return new HandlerReturn( + HandlerReturnResult.NOT_ENOUGH_PARAMETRES, + comAndParams[bIndex]); + } + } + if (commandsForTable.contains(command) && table == null) { + return new HandlerReturn(HandlerReturnResult.TABLE_NOT_CHOSEN, ""); + } + switch (command) { + case "put": + return handlePut(comAndParams, bIndex + 1, eIndex); + case "get": + return handleGet(comAndParams, bIndex + 1, eIndex); + case "remove": + return handleRemove(comAndParams, bIndex + 1, eIndex); + case "list": + return handleList(); + case "size": + return handleSize(); + case "rollback": + return handleRollback(comAndParams, bIndex + 1, eIndex); + case "commit": + return handleCommit(comAndParams, bIndex + 1, eIndex); + case "use": + return handleUse(comAndParams, bIndex + 1, eIndex); + case "create": + return handleCreate(comAndParams, bIndex + 1, eIndex); + case "drop": + return handleDrop(comAndParams, bIndex + 1, eIndex); + case "exit": + return handleExit(); + case "show": + return handleShowTables(comAndParams, bIndex + 1, eIndex); + default: + return new HandlerReturn(HandlerReturnResult.NO_SUCH_COMMAND, + command + "\n"); + } + } + + public HandlerReturn handlePut(String[] comAndParams, int bIndex, int eIndex) { + String res; + try { + res = table.put(comAndParams[bIndex], comAndParams[bIndex + 1]); + } catch (IllegalArgumentException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, ""); + } + if (res == null) { + res = "new\n"; + } else { + res = "overwrite\n" + res + "\n"; + } + return new HandlerReturn(HandlerReturnResult.SUCCESS, res); + } + + public HandlerReturn handleGet(String[] comAndParams, int bIndex, int eIndex) { + String res; + try { + res = table.get(comAndParams[bIndex]); + } catch (IllegalArgumentException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, ""); + } + if (res == null) { + res = "not found\n"; + } else { + res = "found\n" + res + "\n"; + } + return new HandlerReturn(HandlerReturnResult.SUCCESS, res); + } + + public HandlerReturn handleRemove(String[] comAndParams, int bIndex, + int eIndex) { + String res; + String key = comAndParams[bIndex]; + try { + res = table.get(key); + } catch (IllegalArgumentException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, ""); + } + if (res == null) { + res = "not found\n"; + } else { + res = "removed\n"; + table.remove(key); + } + return new HandlerReturn(HandlerReturnResult.SUCCESS, res); + } + + public HandlerReturn handleList() { + String res; + res = String.join(", ", table.list()); + res += (res.equals("")) ? "" : "\n"; + return new HandlerReturn(HandlerReturnResult.SUCCESS, res); + } + + public HandlerReturn handleSize() { + return new HandlerReturn(HandlerReturnResult.SUCCESS, table.size() + + "\n"); + } + + public HandlerReturn handleRollback(String[] comAndParams, int bIndex, + int eIndex) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, table.rollback() + + "\n"); + } + + public HandlerReturn handleCommit(String[] comAndParams, int bIndex, + int eIndex) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, table.commit() + + "\n"); + } + + public HandlerReturn handleUse(String[] comAndParams, int bIndex, int eIndex) { + TableWithTransactions newTable; + try { + newTable = (TableWithTransactions) provider + .getTable(comAndParams[bIndex]); + } catch (IllegalArgumentException e1) { + return new HandlerReturn(HandlerReturnResult.ERROR, ""); + } + if (newTable == null) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, + comAndParams[bIndex] + " not exists\n"); + } + // If table isn't chosen. + if (table == null) { + table = newTable; + return new HandlerReturn(HandlerReturnResult.SUCCESS, "using " + + comAndParams[bIndex] + "\n"); + } + // If table is already chosen. + int unsaved = table.getUnsavedChanges(); + if (unsaved > 0) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, unsaved + + "unsaved changes\n"); + } + try { + table.exit(); + } catch (IOException e) { + System.err.println("Couldn't save" + table.getName() + "on disk"); + } + table = newTable; + return new HandlerReturn(HandlerReturnResult.SUCCESS, "using " + + comAndParams[bIndex] + "\n"); + } + + public HandlerReturn handleCreate(String[] comAndParams, int bIndex, + int eIndex) { + Table newTable; + try { + newTable = provider.createTable(comAndParams[bIndex]); + } catch (IllegalArgumentException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, + "wrong tablename\n"); + } + if (newTable == null) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, + comAndParams[bIndex] + " exists\n"); + } + return new HandlerReturn(HandlerReturnResult.SUCCESS, "created\n"); + } + + public HandlerReturn handleDrop(String[] comAndParams, int bIndex, + int eIndex) { + + try { + provider.removeTable(comAndParams[bIndex]); + } catch (IllegalStateException e) { + return new HandlerReturn(HandlerReturnResult.SUCCESS, + comAndParams[bIndex] + " not exists\n"); + } catch (IllegalArgumentException e) { + return new HandlerReturn(HandlerReturnResult.ERROR, + "wrong tablename\n"); + } + + return new HandlerReturn(HandlerReturnResult.SUCCESS, "dropped\n"); + } + + public HandlerReturn handleShowTables(String[] comAndParams, int bIndex, + int eIndex) { + if (!comAndParams[bIndex].toLowerCase().equals("tables")) { + return new HandlerReturn(HandlerReturnResult.NO_SUCH_COMMAND, + "show " + comAndParams[bIndex] + "\n"); + } + String res = ""; + List tableNames = provider.showTables(); + String tableName = table == null ? "" : table.getName(); + for (String curName : tableNames) { + if (tableName.equals(curName)) { + res += curName + " " + table.size() + "\n"; + } else { + TableWithTransactions curTable = (TableWithTransactions) provider + .getTable(curName); + res += curName + " " + curTable.size() + "\n"; + try { + curTable.exit(); + } catch (IOException e) { + System.err.println("Interrupted access to database: " + + curName); + } + } + } + return new HandlerReturn(HandlerReturnResult.SUCCESS, res); + } + + public HandlerReturn handleExit() throws IOException { + if (table == null) { + provider.close(); + return new HandlerReturn(HandlerReturnResult.EXIT, "goodbye\n"); + } + if (table.getUnsavedChanges() > 0) { + return new HandlerReturn(HandlerReturnResult.ERROR, + "there're unsaved changes\n"); + } + try { + table.exit(); + } catch (IOException e) { + System.err.println("Couldn't save " + table.getName()); + return new HandlerReturn(HandlerReturnResult.EXIT, ""); + } + provider.close(); + return new HandlerReturn(HandlerReturnResult.EXIT, "goodbye\n"); + } + + public void emergencyExit() throws IOException { + try { + if (table != null) { + table.exit(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + provider.close(); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturn.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturn.java new file mode 100644 index 000000000..9493d7bce --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturn.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter; + + +public class HandlerReturn { + HandlerReturnResult ret; + String message; + public HandlerReturn(HandlerReturnResult argRet, String argMessage) { + ret = argRet; + message = argMessage; + } + public String getMessage() { + return message; + } + + public HandlerReturnResult getVal() { + return ret; + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturnResult.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturnResult.java new file mode 100644 index 000000000..b5dc930de --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/interpreter/HandlerReturnResult.java @@ -0,0 +1,5 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.interpreter; + +public enum HandlerReturnResult { + EXIT, SUCCESS, NO_SUCH_COMMAND, NOT_ENOUGH_PARAMETRES, TABLE_NOT_CHOSEN, ERROR +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/DBCollectionTest.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/DBCollectionTest.java new file mode 100644 index 000000000..a293ec6ee --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/DBCollectionTest.java @@ -0,0 +1,94 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.tests; + +import java.io.File; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.dmitry_morozov.junit.MyTableProviderFactory; + +public class DBCollectionTest { + private TableProvider provider; + private String dirPath; + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void before() throws IOException { + TableProviderFactory factory = new MyTableProviderFactory(); + dirPath = tmpFolder.newFolder().getAbsolutePath(); + provider = factory.create(dirPath); + System.err.println(File.separator); + } + + @Test + public void createTable() { + provider.createTable("table"); + } + + @Test + public void createTableTwice() { + provider.createTable("table"); + Assert.assertNull(provider.createTable("table")); + } + + @Test(expected = IllegalArgumentException.class) + public void createTableNull() { + provider.createTable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void createWrong() throws IOException { + File file = new File(dirPath + "/qqqqqqqqq.smt"); + file.createNewFile(); + provider.createTable(file.getName()); + } + + @Test + public void getTable() { + provider.createTable("table"); + Assert.assertNotNull(provider.getTable("table")); + } + + @Test + public void getUnexistingTable() { + Assert.assertNull(provider.getTable("table")); + } + + @Test(expected = IllegalArgumentException.class) + public void getTableNull() { + Assert.assertNull(provider.getTable(null)); + } + + @Test + public void getTableWrong() throws IOException { + File file = new File(dirPath + "/qqqqqqqqq.smt"); + file.createNewFile(); + Assert.assertNull(provider.getTable(file.getName())); + } + + @Test + public void removeTable() { + provider.createTable("table"); + provider.removeTable("table"); + File removed = new File(dirPath + "/table"); + Assert.assertFalse(removed.exists()); + } + + @Test(expected = IllegalStateException.class) + public void removeUnexistingTable() { + provider.removeTable("table"); + } + + @Test(expected = IllegalArgumentException.class) + public void removeNull() { + provider.removeTable(null); + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableProviderFactoryTest.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableProviderFactoryTest.java new file mode 100644 index 000000000..cb9d9e3a7 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableProviderFactoryTest.java @@ -0,0 +1,39 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.tests; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.dmitry_morozov.junit.MyTableProviderFactory; + +public class MyTableProviderFactoryTest { + private TableProviderFactory factory; + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void before() { + factory = new MyTableProviderFactory(); + } + + @Test + public void create() throws IOException { + factory.create(tmpFolder.newFolder().getAbsolutePath()); + } + + @Test (expected = IllegalArgumentException.class) + public void createNull() { + factory.create(null); + } + + @Test (expected = IllegalArgumentException.class) + public void createWrong() throws IOException { + factory.create(tmpFolder.newFile().getAbsolutePath()); + } + +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableTest.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableTest.java new file mode 100644 index 000000000..3d0e42e16 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/MyTableTest.java @@ -0,0 +1,138 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.tests; + +import java.util.List; +import java.util.TreeSet; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.dmitry_morozov.junit.*; + +public class MyTableTest { + private Table table; + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void before() throws IOException { + TableProviderFactory factory = new MyTableProviderFactory(); + TableProvider provider = factory.create(tmpFolder.newFolder().getAbsolutePath()); + table = provider.createTable("table"); + } + + @Test + public void getNameTest() { + String actualName = table.getName(); + Assert.assertEquals("table", actualName); + } + + @Test + public void getNothingTest() { + Assert.assertNull(table.get("weather")); + } + + @Test + public void putTest() { + Assert.assertNull(table.put("weather", "sunny")); + Assert.assertEquals("sunny", table.put("weather", "snow")); + } + + @Test + public void getTest() { + table.put("weather", "rain"); + Assert.assertEquals("rain", table.get("weather")); + } + + @Test (expected = IllegalArgumentException.class) + public void getNullTest() { + table.get(null); + } + + @Test (expected = IllegalArgumentException.class) + public void putKeyNullTest() { + table.put(null, "smth"); + } + + @Test (expected = IllegalArgumentException.class) + public void putValueNullTest() { + table.put("null", null); + } + + @Test (expected = IllegalArgumentException.class) + public void putBothNullTest() { + table.put(null, null); + } + + @Test + public void removeTest() { + Assert.assertNull(table.remove("nothing")); + table.put("nothing", "something"); + Assert.assertEquals("something", table.remove("nothing")); + } + + @Test(expected = IllegalArgumentException.class) + public void removeNullTest() { + table.remove(null); + } + + @Test + public void rollbackTest() { + table.put("1", "one"); + table.put("2", "two"); + table.remove("3"); + table.remove("1"); + table.put("1", "uno"); + Assert.assertEquals(4, table.rollback()); + Assert.assertEquals(0, table.size()); + } + + @Test + public void commitTest() { + table.put("1", "one"); + table.put("2", "two"); + table.remove("3"); + table.remove("1"); + table.put("1", "uno"); + Assert.assertEquals(4, table.commit()); + Assert.assertNull(table.get("3")); + Assert.assertEquals("two", table.get("2")); + Assert.assertEquals("uno", table.get("1")); + } + + @Test + public void sizeTest() { + Assert.assertEquals(0, table.size()); + table.put("1", "one"); + Assert.assertEquals(1, table.size()); + table.remove("1"); + Assert.assertEquals(0, table.size()); + } + + @Test + public void listTest() { + table.put("1", "one"); + table.put("1", "one"); + table.put("1", "one"); + table.put("3", "one"); + table.put("4", "one"); + table.put("5", "one"); + table.remove("1"); + List list = table.list(); + TreeSet keySet = new TreeSet(); + keySet.addAll(list); + Assert.assertEquals(3, keySet.size()); + Assert.assertTrue(keySet.contains("3")); + Assert.assertTrue(keySet.contains("4")); + Assert.assertTrue(keySet.contains("5")); + Assert.assertFalse(keySet.contains("1")); + + } +} diff --git a/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/ReversableMFHPTest.java b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/ReversableMFHPTest.java new file mode 100644 index 000000000..f7b0e3222 --- /dev/null +++ b/src/ru/fizteh/fivt/students/dmitry_morozov/junit/tests/ReversableMFHPTest.java @@ -0,0 +1,133 @@ +package ru.fizteh.fivt.students.dmitry_morozov.junit.tests; + +import java.util.List; +import java.util.TreeSet; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.dmitry_morozov.junit.*; + +public class ReversableMFHPTest { + private Table table; + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void before() throws IOException { + TableProviderFactory factory = new MyTableProviderFactory(); + TableProvider provider = factory.create(tmpFolder.newFolder().getAbsolutePath()); + table = provider.createTable("table"); + } + + @Test + public void getNameTest() { + Assert.assertEquals("table", table.getName()); + } + + @Test + public void getNothingTest() { + Assert.assertNull(table.get("weather")); + } + + @Test + public void putTest() { + Assert.assertNull(table.put("weather", "sunny")); + Assert.assertEquals("sunny", table.put("weather", "snow")); + } + + @Test + public void getTest() { + table.put("weather", "rain"); + Assert.assertEquals("rain", table.get("weather")); + } + + @Test (expected = IllegalArgumentException.class) + public void getNullTest() { + table.get(null); + } + + @Test (expected = IllegalArgumentException.class) + public void putKeyNullTest() { + table.put(null, "smth"); + } + + @Test (expected = IllegalArgumentException.class) + public void putValueNullTest() { + table.put("null", null); + } + + @Test (expected = IllegalArgumentException.class) + public void putBothNullTest() { + table.put(null, null); + } + + @Test + public void removeTest() { + Assert.assertNull(table.remove("nothing")); + table.put("nothing", "something"); + Assert.assertEquals("something", table.remove("nothing")); + } + + @Test(expected = IllegalArgumentException.class) + public void removeNullTest() { + table.remove(null); + } + + @Test + public void rollbackTest() { + table.put("1", "one"); + table.put("2", "two"); + table.remove("3"); + table.remove("1"); + table.put("1", "uno"); + Assert.assertEquals(4, table.rollback()); + } + + @Test + public void commitTest() { + table.put("1", "one"); + table.put("2", "two"); + table.remove("3"); + table.remove("1"); + table.put("1", "uno"); + Assert.assertEquals(4, table.commit()); + } + + @Test + public void sizeTest() { + Assert.assertEquals(0, table.size()); + table.put("1", "one"); + Assert.assertEquals(1, table.size()); + table.remove("1"); + Assert.assertEquals(0, table.size()); + } + + @Test + public void listTest() { + table.put("1", "one"); + table.put("1", "one"); + table.put("1", "one"); + table.put("3", "one"); + table.put("4", "one"); + table.put("5", "one"); + table.remove("1"); + List list = table.list(); + TreeSet keySet = new TreeSet(); + keySet.addAll(list); + Assert.assertEquals(3, keySet.size()); + Assert.assertTrue(keySet.contains("3")); + Assert.assertTrue(keySet.contains("4")); + Assert.assertTrue(keySet.contains("5")); + Assert.assertFalse(keySet.contains("1")); + + } +}