From ac569964b188165523449bf7038622ab9e8fb4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D1=81=D0=BA=D0=B0=D1=80?= Date: Sun, 14 Dec 2014 16:42:43 +0400 Subject: [PATCH 1/2] Storeable added --- .../Storeable/Comands/CmdCommit.java | 27 ++ .../Storeable/Comands/CmdCreate.java | 46 +++ .../Storeable/Comands/CmdDrop.java | 31 ++ .../Storeable/Comands/CmdExit.java | 46 +++ .../Storeable/Comands/CmdGet.java | 32 ++ .../Storeable/Comands/CmdList.java | 27 ++ .../Storeable/Comands/CmdPut.java | 40 +++ .../Storeable/Comands/CmdRemove.java | 31 ++ .../Storeable/Comands/CmdRollback.java | 26 ++ .../Storeable/Comands/CmdShowTables.java | 43 +++ .../Storeable/Comands/CmdUse.java | 41 +++ .../Storeable/Comands/Command.java | 46 +++ .../oscar_nasibullin/Storeable/DataFile.java | 253 +++++++++++++++ .../Storeable/DataFileHasher.java | 53 ++++ .../oscar_nasibullin/Storeable/Main.java | 40 +++ .../oscar_nasibullin/Storeable/Shell.java | 127 ++++++++ .../Storeable/StoreableImpl.java | 103 ++++++ .../oscar_nasibullin/Storeable/TableImpl.java | 299 ++++++++++++++++++ .../Storeable/TableProviderFactoryImpl.java | 34 ++ .../Storeable/TableProviderImpl.java | 236 ++++++++++++++ .../Tests/TableProviderFactoryTest.java | 38 +++ .../Storeable/Tests/TableProviderTest.java | 94 ++++++ .../Storeable/Tests/TableTest.java | 168 ++++++++++ testTableProvider/table1/signature.tsv | 1 + testTableProvider/table2/signature.tsv | 1 + testTableProvider/table3/signature.tsv | 1 + 26 files changed, 1884 insertions(+) create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCommit.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdDrop.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdExit.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdList.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdPut.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRemove.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRollback.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdShowTables.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdUse.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/Command.java create mode 100755 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFile.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFileHasher.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Main.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Shell.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderFactoryImpl.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderFactoryTest.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderTest.java create mode 100644 src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableTest.java create mode 100644 testTableProvider/table1/signature.tsv create mode 100644 testTableProvider/table2/signature.tsv create mode 100644 testTableProvider/table3/signature.tsv diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCommit.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCommit.java new file mode 100644 index 000000000..7786dac4a --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCommit.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; + +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdCommit extends Command { + public CmdCommit() { + setName("commit"); + setArgs(1); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + resultMessage = new Integer(currTable.commit()).toString(); + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java new file mode 100644 index 000000000..306193adb --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java @@ -0,0 +1,46 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableImpl; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdCreate extends Command { + public CmdCreate() { + setName("create"); + //setArgs(); >= 3 + } + + @Override + public String run(List args) throws Exception { + if (3 > args.size()) { + throw new IllegalArgumentException("Not enough arguments for " + getName()); + } + String resultMessage = ""; + Table createTable = tableProvider.getTable(args.get(1)); + + if (createTable != null) { + resultMessage = args.get(1) + " exists"; + } else { + List> types = new ArrayList<>(); + for (String typeName : args.subList(2, args.size())) { + typeName = typeName.replaceAll("[()]", ""); + if (!typeName.equals("")) { + String className = TableImpl.convertClassName(typeName); + try { + types.add(Class.forName(className)); + } catch (ClassNotFoundException e) { + throw new Exception("wrong type (no type with name: " + typeName.replaceAll("[()]", "") + ")", e); + } + } + } + tableProvider.createTable(args.get(1), types); + resultMessage = "created"; + } + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdDrop.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdDrop.java new file mode 100644 index 000000000..d57180e5e --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdDrop.java @@ -0,0 +1,31 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; + + +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdDrop extends Command { + public CmdDrop() { + setName("drop"); + setArgs(2); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage = ""; + Table dropTable = tableProvider.getTable(args.get(1)); + + if (dropTable == null) { + resultMessage = args.get(1) + " not exists"; + } else { + tableProvider.removeTable(args.get(1)); + resultMessage = "dropped"; + } + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdExit.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdExit.java new file mode 100644 index 000000000..f235952ba --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdExit.java @@ -0,0 +1,46 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + + +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableImpl; + +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdExit extends Command { + public CmdExit() { + setName("exit"); + setArgs(1); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + Table currTable = tableProvider.getTable(currentTableName); + if (tableProvider.getTable(currentTableName) != null) { + Integer unsaved = tableProvider.getTable(currentTableName).getNumberOfUncommittedChanges(); + if (unsaved == 0) { + ((TableImpl) tableProvider.getTable(currentTableName)).close(); + } else { + throw new Exception(unsaved.toString() + " unsaved changes"); + } + } + try { + if (tableProvider.getTable(currentTableName) != null) { + ((TableImpl) currTable).close(); + } + } catch (Exception e) { + if (e.getMessage() != null) { + System.err.println("exit error: " + e.getMessage()); + } else { + System.err.println("exit error: something went wrong when I tried to close current table"); + e.printStackTrace(); + } + System.exit(1); + } + System.exit(0); + return null; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java new file mode 100644 index 000000000..5ee0d752d --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java @@ -0,0 +1,32 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdGet extends Command { + public CmdGet() { + setName("get"); + setArgs(2); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage = ""; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + + String value = tableProvider.serialize(currTable ,currTable.get(args.get(1))); + if (value != null) { + resultMessage = "found\n" + value; + } else { + resultMessage = "not found"; + } + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdList.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdList.java new file mode 100644 index 000000000..8ed1d3aa2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdList.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdList extends Command { + public CmdList() { + setName("list"); + setArgs(1); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage = ""; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + List keys = currTable.list(); + resultMessage = String.join(", ", keys); + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdPut.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdPut.java new file mode 100644 index 000000000..054b39584 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdPut.java @@ -0,0 +1,40 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdPut extends Command { + public CmdPut() { + setName("put"); + //setArgs(3); >= 3 + } + + @Override + public String run(List args) throws Exception { + if (3 > args.size()) { + throw new IllegalArgumentException("Not enough arguments for " + getName()); + } + String rezultMessage = ""; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + try { + String arguments = String.join(" ", args.subList(2, args.size())); + Storeable newValue = tableProvider.deserialize(currTable, arguments); + Storeable changed = currTable.put(args.get(1), newValue); + if (changed != null) { + rezultMessage = "overwrite\n" + tableProvider.serialize(currTable, changed); + } else { + rezultMessage = "new"; + } + } catch (Exception e) { + throw new Exception("wrong type (" + e.getMessage() + ")", e); + } + return rezultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRemove.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRemove.java new file mode 100644 index 000000000..d65211e31 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRemove.java @@ -0,0 +1,31 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdRemove extends Command { + public CmdRemove() { + setName("remove"); + setArgs(2); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String rezultMessage = ""; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + + if (currTable.remove(args.get(1)) != null) { + rezultMessage = "removed"; + } else { + rezultMessage = "not found"; + } + return rezultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRollback.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRollback.java new file mode 100644 index 000000000..de98b66dc --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdRollback.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdRollback extends Command { + public CmdRollback() { + setName("rollback"); + setArgs(1); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage; + Table currTable = tableProvider.getTable(currentTableName); + if (currTable == null) { + throw new Exception("no table"); + } + resultMessage = new Integer(currTable.rollback()).toString(); + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdShowTables.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdShowTables.java new file mode 100644 index 000000000..f35aa6ef1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdShowTables.java @@ -0,0 +1,43 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableImpl; +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdShowTables extends Command { + + public CmdShowTables() { + setName("show"); + } + + @Override + public String run(List args) throws Exception { + if (args.size() > 2) { + throw new IllegalArgumentException( + "Illegal arguments for show tables"); + } + if (args.size() < 2 || !args.get(1).equals("tables")) { + throw new Exception("show: no such command"); + } + String resultMessage = ""; + List tableNames = tableProvider.getTableNames(); + + for (String tableName : tableNames) { + if (!tableName.equals(currentTableName)) { + ((TableImpl) tableProvider.getTable(tableName)).open(); + } + resultMessage += tableName + " " + tableProvider.getTable(tableName).size() + "\n"; + if (!tableName.equals(currentTableName)) { + ((TableImpl) tableProvider.getTable(tableName)).close(); + } + } + if (resultMessage.equals("")) { + resultMessage = "no tables found"; + } else { + resultMessage = resultMessage.substring(0, resultMessage.length() - 1); + } + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdUse.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdUse.java new file mode 100644 index 000000000..eb764f68b --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdUse.java @@ -0,0 +1,41 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableImpl; + +import java.util.List; + +/** + * Created by Oskar on 23.11.14. + */ +public class CmdUse extends Command { + + public CmdUse() { + setName("use"); + setArgs(2); + } + + @Override + public String run(List args) throws Exception { + checkArgumentsAmount(args); + String resultMessage; + Table newTable = tableProvider.getTable(args.get(1)); + if (newTable != null) { + if (tableProvider.getTable(currentTableName) != null) { + Integer unsaved = tableProvider.getTable(currentTableName).getNumberOfUncommittedChanges(); + if (unsaved == 0) { + ((TableImpl) tableProvider.getTable(currentTableName)).close(); + } else { + throw new Exception(unsaved.toString() + " unsaved changes"); + } + } + currentTableName = args.get(1); + ((TableImpl) tableProvider.getTable(currentTableName)).open(); + resultMessage = "using " + currentTableName; + } else { + resultMessage = args.get(1) + " not exists"; + } + + return resultMessage; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/Command.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/Command.java new file mode 100644 index 000000000..806ec0090 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/Command.java @@ -0,0 +1,46 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands; + +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableProviderImpl; + +import java.util.List; + +/** + * Created by Oskar on 19.11.14. + */ +public abstract class Command { + + private String name; + private int argumentsAmount = 0; + public static TableProviderImpl tableProvider; + public static String currentTableName; // or even whole Table ? + + public String getName() { + return name; + } + + public void setName(String newName) { + name = newName; + } + + public void setArgs(int amount) { + argumentsAmount = amount; + } + + public void checkArgumentsAmount(List args) throws IllegalArgumentException { + if (argumentsAmount != args.size()) { + throw new IllegalArgumentException("Illegal arguments for " + name); + } + } + + /** + * Выполняет что-то. + * + * @param args Список аргументов. + * @return Сообщение с результатом работы. Если возвращает null, то на экран ничего не печатается. + * + * @throws Exception проброс наверх любого исключения. + */ + public String run(List args) throws Exception { + return null; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFile.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFile.java new file mode 100755 index 000000000..bdff85fb8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFile.java @@ -0,0 +1,253 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +public class DataFile implements Map , AutoCloseable{ + private final Map storage; + private final Map cache; + private final DataFileHasher dataFileHasher ; + private final File datFile; + //boolean loaded = true; + + + + public DataFile(String tablePath, DataFileHasher dataFileHasherObject) throws Exception { + + dataFileHasher = dataFileHasherObject; + Path datFilePath = Paths.get(tablePath). + resolve(dataFileHasher.getDirNum().toString() + ".dir"). + resolve(dataFileHasher.getFileNum().toString() + ".dat"); + datFile = datFilePath.toFile(); + storage = new TreeMap<>(); + cache = new TreeMap<>(); + importData(); + } + + + private boolean dataFileExists() throws Exception { + if (datFile.exists()) { + if (datFile.isDirectory()) { + throw new Exception("Expected .dat file, found directory"); + } + if (!datFile.isFile()) { + throw new Exception(datFile.getName() + "is not a file or corrupted"); + } + return true; + } else { + return false; + } + } + + + + private void importData() throws Exception { + ByteArrayOutputStream bytesBuffer = new ByteArrayOutputStream(); + List offsets = new LinkedList<>(); + List keys = new LinkedList<>(); + byte b; + int bytesCounter = 0; + int firstOffset = -1; + if (!dataFileExists()) { + return; + } + + try (RandomAccessFile datRAFile = new RandomAccessFile(datFile, "r")) { + do { + b = datRAFile.readByte(); + bytesCounter++; + bytesBuffer.write(b); + if (!dataFileHasher.contains(b)) { + throw new Exception(datFile.getName() + " corrupted."); + } + + while ((b = datRAFile.readByte()) != 0) { + bytesCounter++; + bytesBuffer.write(b); + } + bytesCounter++; + if (firstOffset == -1) { + firstOffset = datRAFile.readInt(); + } else { + offsets.add(datRAFile.readInt()); + } + bytesCounter += 4; + keys.add(bytesBuffer.toString(DataFileHasher.ENCODING)); + bytesBuffer.reset(); + } while (bytesCounter < firstOffset); + + offsets.add((int) datRAFile.length()); + Iterator keyIter = keys.iterator(); + for (int nextOffset : offsets) { + while (bytesCounter < nextOffset) { + bytesBuffer.write(datRAFile.readByte()); + bytesCounter++; + } + if (bytesBuffer.size() > 0) { + storage.put(keyIter.next(), bytesBuffer.toString(DataFileHasher.ENCODING)); + bytesBuffer.reset(); + } else { + throw new IOException(); + } + } + bytesBuffer.close(); + } catch (EOFException e) { + throw new IOException("Unexpected end of file: " + datFile.getPath()); + } + } + private void exportData() throws Exception { + int offset = 0; + if (!dataFileExists()) { + datFile.getParentFile().mkdir(); + datFile.createNewFile(); + } + try (RandomAccessFile datRAFile = new RandomAccessFile(datFile, "rw")) { + datRAFile.setLength(0); + for (Entry entry : storage.entrySet()) { + offset += entry.getKey().length() + 5; + } + for (Entry entry : storage.entrySet()) { + datRAFile.write(entry.getKey().getBytes(DataFileHasher.ENCODING)); + datRAFile.write('\0'); + datRAFile.writeInt(offset); + offset += entry.getValue().length(); + } + for (Entry entry : storage.entrySet()) { + datRAFile.write(entry.getValue().getBytes(DataFileHasher.ENCODING)); + } + } + } + + public int commit() { + for (Entry entry : cache.entrySet()) { + if (storage.containsKey(entry.getKey())) { + storage.remove(entry.getKey()); + } + if (!entry.getValue().equals("")) { + storage.put(entry.getKey(), entry.getValue()); + } + } + int saved = cache.size(); + cache.clear(); + return saved; + //exportData(); Todo: should it be here? + } + + public int rollback() { + int changesRolled = cache.size(); + cache.clear(); + return changesRolled; + } + + public int unsavedChangesNum() { + return cache.size(); + } + + @Override + public int size() { + Map merged = new TreeMap<>(storage); + for (Entry entry : cache.entrySet()) { + if (merged.containsKey(entry.getKey())) { + merged.remove(entry.getKey()); + } + if (!entry.getValue().equals("")) { + merged.put(entry.getKey(), entry.getValue()); + } + } + return merged.size(); + } + + @Override + public boolean isEmpty() { + return cache.isEmpty() && storage.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return cache.containsKey(key) || storage.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return cache.containsValue(value) || storage.containsValue(value); + } + + @Override + public String get(Object key) { + if (cache.containsKey(key)) { + if (cache.get(key).equals("")) { + return null; + } + return cache.get(key); + } else if (storage.containsKey(key)) { + return storage.get(key); + } + return null; + } + + @Override + public String put(String key, String value) { + if (cache.containsKey(key) && cache.get(key).equals("")) { + cache.remove(key); + } + return cache.put(key, value); + } + + @Override + public String remove(Object key) { + if (cache.containsKey(key) || storage.containsKey(key)) { + cache.remove(key); + cache.put((String) key, ""); // "" - means deleted + } + return null; + } + + @Override + public void putAll(Map m) { + cache.putAll(m); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public Set keySet() { + return storage.keySet(); + } + + @Override + public Collection values() { + return storage.values(); + } + + @Override + public Set> entrySet() { + Map merged = new TreeMap<>(storage); + for (Entry entry : cache.entrySet()) { + if (merged.containsKey(entry.getKey())) { + merged.remove(entry.getKey()); + } + if (!entry.getValue().equals("")) { + merged.put(entry.getKey(), entry.getValue()); + } + } + return merged.entrySet(); + } + + @Override + public void close() throws Exception { + commit(); + if (!storage.isEmpty()) { + exportData(); + cache.clear(); + storage.clear(); + } else { + datFile.delete(); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFileHasher.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFileHasher.java new file mode 100644 index 000000000..1561fae54 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/DataFileHasher.java @@ -0,0 +1,53 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import java.io.UnsupportedEncodingException; + +class DataFileHasher implements Comparable { + private final int ndir; + private final int nfile; + static final int HASH_NUMBER = 16; + static final String ENCODING = "UTF-8"; + + public DataFileHasher(String key) throws IllegalArgumentException { + try { + ndir = Math.abs(key.getBytes(ENCODING)[0] % HASH_NUMBER); + nfile = Math.abs((key.getBytes(ENCODING)[0] / HASH_NUMBER) % HASH_NUMBER); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("bad key in DataFileHasher constructor"); + } + } + + public DataFileHasher(int dirNum, int fileNum) { + ndir = dirNum; + nfile = fileNum; + } + + public Integer getDirNum() { + return new Integer(ndir); + } + + public Integer getFileNum() { + return new Integer(nfile); + } + + + public boolean contains(Byte key) { + int keyNdir = Math.abs(Byte.valueOf(key) % HASH_NUMBER); + int keyNfile = Math.abs((Byte.valueOf(key) / HASH_NUMBER) % HASH_NUMBER); + return ndir == keyNdir && nfile == keyNfile; + } + + @Override + public int compareTo(DataFileHasher o) { + if (o.getDirNum() > getDirNum()) { + return -1; + } else if (o.getDirNum() < getDirNum()) { + return 1; + } else if (o.getFileNum() > getFileNum()) { + return -1; + } else if (o.getFileNum() < getFileNum()) { + return 1; + } + return 0; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Main.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Main.java new file mode 100644 index 000000000..259d5e8d5 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Main.java @@ -0,0 +1,40 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands.*; + + +/** + * Created by Oskar on 23.11.14. + */ +public class Main { + + private Main() { + // Disable instantiation to this class. + } + + public static void main(final String[] args) { + try { + TableProviderFactoryImpl factory = new TableProviderFactoryImpl(); + TableProviderImpl provider = factory.create(System.getProperty("fizteh.db.dir")); + + + Command[] commands = { + new CmdCommit(), new CmdCreate(), new CmdDrop(), new CmdExit(), + new CmdGet(), new CmdList(), new CmdPut(), new CmdRemove(), + new CmdRollback(), new CmdShowTables(), new CmdUse() + }; + + List commandList = new ArrayList<>(Arrays.asList(commands)); + Command.currentTableName = ""; + Command.tableProvider = provider; + Shell client = new Shell(commandList, args); + } catch (Exception e) { + System.err.println("Fatal error: " + e.getMessage()); + //e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Shell.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Shell.java new file mode 100644 index 000000000..32a06e6dd --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Shell.java @@ -0,0 +1,127 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Comands.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +/** + * Created by Oskar on 23.11.14. + */ +public class Shell { + List commandList; + + public Shell(List commands, String[] args) { + commandList = new ArrayList(commands); + if (args.length > 0) { + runBatch(args); + } else { + runInteractive(); + } + } + + public void runInteractive() { + List> commands = new ArrayList<>(); + try (Scanner in = new Scanner(System.in)) { + // "Try with resources" doesn't have to have a catch block. + while (true) { + System.err.flush(); + System.out.flush(); + System.out.print("$ "); + String input = null; + input = in.nextLine(); + String[] args = new String[1]; + args[0] = input; + commands = parse(args); + activator(commands, false); + } + } + } + + + public void runBatch(final String[] args) { + List> commands = new ArrayList<>(); + + commands = parse(args); + activator(commands, true); + + System.exit(0); + } + + + public void activator(final List> parsed, final boolean batchMode) { + String rezultMessage = null; + try { + for (int i = 0; i < parsed.size(); i++) { + boolean executed = false; + for (Command command : commandList) { + if (command.getName().equals(parsed.get(i).get(0))) { + rezultMessage = command.run(parsed.get(i)); + executed = true; + break; + } + } + if (!executed) { + throw new Exception(parsed.get(i).get(0) + ": no such command"); + } + if (rezultMessage != null) { + System.out.println(rezultMessage); + } + } + } catch (Exception e) { + if (e.getMessage() != null) { + System.err.println(e.getMessage()); + } else { + System.err.println("Undefined Exception: "); + e.printStackTrace(); + } + if (batchMode) { + System.exit(1); + } + } + } + + public static List> parse(final String[] args) { + List> commands = new ArrayList<>(); + ArrayList comAndArgs = new ArrayList<>(); + String[] arguments; + + if (args.length == 1) { // Interactive parse. + arguments = args[0].split(";"); + for (String arg : arguments) { + comAndArgs = new ArrayList(Arrays.asList(arg.split(" "))); + if (comAndArgs.size() != 0) { + commands.add(new ArrayList(comAndArgs)); + } + comAndArgs.clear(); + } + } else { // Batch. + arguments = args; + for (int i = 0; i < arguments.length; i++) { + if (arguments[i].endsWith(";")) { + int start = 0; + int end = arguments[i].length() - 1; + char[] buf = new char[end]; + arguments[i].getChars(start, end, buf, 0); + String lastArg = new String(buf); + + comAndArgs.add(lastArg); + commands.add(new ArrayList<>(comAndArgs)); + comAndArgs.clear(); + } else { + comAndArgs.add(arguments[i]); + if (i == arguments.length - 1) { + commands.add(comAndArgs); + } + } + } + } + return commands; + } + + + +} + + diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java new file mode 100644 index 000000000..6d8d35499 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java @@ -0,0 +1,103 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Oskar on 03.12.14. + */ +public class StoreableImpl implements Storeable { + + private List storage; + private List> types; + + public StoreableImpl(List values, List> valuesTypes) { + storage = values; + types = valuesTypes; + } + + @Override + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + if (value.getClass() != types.get(columnIndex)) { + throw new ColumnFormatException( + "Storable: expected " + types.get(columnIndex).getName() + + ", found " + value.getClass().getName()); + } + storage.set(columnIndex, types.get(columnIndex).cast(value)); // todo: check IndexOutOfBounds + } + + @Override + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + return storage.get(columnIndex); // todo: check IndexOutOfBounds + } + + @Override + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Integer.class) { + throw new ColumnFormatException( + "Storable: reqested Integer, but found " + types.get(columnIndex).getName()); + } + return (Integer) storage.get(columnIndex); //todo: check for null + } + + @Override + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Long.class) { + throw new ColumnFormatException( + "Storable: reqested Long, but found " + types.get(columnIndex).getName()); + } + return (Long) storage.get(columnIndex); //todo: check for null + } + + @Override + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Byte.class) { + throw new ColumnFormatException( + "Storable: reqested Byte, but found " + types.get(columnIndex).getName()); + } + return (Byte) storage.get(columnIndex); //todo: check for null + } + + @Override + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Float.class) { + throw new ColumnFormatException( + "Storable: reqested Float, but found " + types.get(columnIndex).getName()); + } + return (Float) storage.get(columnIndex); //todo: check for null + } + + @Override + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Double.class) { + throw new ColumnFormatException( + "Storable: reqested Double, but found " + types.get(columnIndex).getName()); + } + return (Double) storage.get(columnIndex); //todo: check for null + } + + @Override + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != Boolean.class) { + throw new ColumnFormatException( + "Storable: reqested Boolean, but found " + types.get(columnIndex).getName()); + } + return (Boolean) storage.get(columnIndex); //todo: check for null + } + + @Override + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (types.get(columnIndex) != String.class) { + throw new ColumnFormatException( + "Storable: reqested String, but found " + types.get(columnIndex).getName()); + } + return (String) storage.get(columnIndex); //todo: check for null + } + + public List> getTypes () { + return new ArrayList<>(types); + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java new file mode 100644 index 000000000..adfb1730b --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java @@ -0,0 +1,299 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; + +import java.io.*; +import java.nio.file.Files; +import java.text.ParseException; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +/** + * Created by Oskar on 15.11.14. + */ +public class TableImpl implements Table { + + static final int NUMBER_DIRECTORIES = DataFileHasher.HASH_NUMBER; + static final int NUMBER_FILES_IN_DIRECTORY = DataFileHasher.HASH_NUMBER; + + private Map datFiles; + private TableProvider provider; + private List> types; + public boolean isOpen; + private final File tableRoot; + + public TableImpl(File table, List> columnTypes, TableProvider myProvider) { + provider = myProvider; + types = columnTypes; + tableRoot = table; + isOpen = false; + try { + open(); + close(); + File tsvFile = tableRoot.toPath().resolve("signature.tsv").toFile(); + tsvFile.createNewFile(); + RandomAccessFile sign = new RandomAccessFile( tsvFile, "rw"); + for (Class type : types) { + sign.writeBytes(convertClassName(type.getName()) + " "); + } + sign.close(); + } catch (Exception e) { + if (e.getMessage().endsWith("is not a directory")) { + throw new RuntimeException("Can't create table: " + e.getMessage()); + } + throw new RuntimeException(e); + } + } + + public TableImpl(File table, TableProvider myProvider) throws IOException, ClassNotFoundException { + provider = myProvider; + tableRoot = table; + isOpen = false; + try { + open(); + close(); + types = new ArrayList<>(); + byte[] data = Files.readAllBytes(tableRoot.toPath().resolve("signature.tsv")); + String[] typeNames = new String(data).split(" "); + for (String typeName : typeNames) { + types.add(Class.forName(convertClassName(typeName))); + } + + } catch (Exception e) { + if (e.getMessage().endsWith("is not a directory")) { + throw new RuntimeException("Can't create table: " + e.getMessage()); + } else { + throw new RuntimeException(e); + } + } + } + + + public void open() throws Exception { //todo: + if (isOpen) { + return; + } + datFiles = new TreeMap<>(); + + if (!tableRoot.exists()) { + tableRoot.mkdir(); + } else if (!tableRoot.isDirectory()) { + throw new Exception(tableRoot.getAbsolutePath() + " - is not a directory"); + } + + for (int i = 0; i < NUMBER_DIRECTORIES; i++) { + for (int j = 0; j < NUMBER_FILES_IN_DIRECTORY; j++) { + DataFileHasher hasher = new DataFileHasher(i, j); + DataFile newDataFile = new DataFile(tableRoot.getAbsolutePath(), hasher); + datFiles.put(hasher, newDataFile); + } + } + isOpen = true; + } + + public void close() throws Exception { + if (!datFiles.isEmpty()) { + for (Map.Entry entry : datFiles.entrySet()) { + entry.getValue().close(); + } + + File[] folders = tableRoot.listFiles(); + if (folders != null) { + for (File folder : folders) { + if (folder.exists() && folder.isDirectory() && folder.list().length == 0) { + folder.delete(); + } + } + } + datFiles.clear(); + } + isOpen = false; + } + + private void checkTableIsOpen() { + if (!isOpen) { + throw new RuntimeException("Trying to operate with table <" + getName()+"> which is not open"); + } + } + + public static String convertClassName (String name) { + if (name.startsWith("java.lang.")) { + name = name.replaceAll("(java.lang.)", ""); + if (name.equals("String")) { + return name; + } + } + if (name.equals("int")) { + name = "java.lang.Integer"; + } else if (name.equals("Integer")) { + name = "int"; + } else if (name.equals("String")) { + return "java.lang.String"; + } else if (name.substring(0,1).equals(name.substring(0,1).toUpperCase())) { + return name.toLowerCase(); + } else { + name = "java.lang." + name.substring(0,1).toUpperCase() + name.substring(1, name.length()); + } + return name; + } + + public boolean storeableIsSuitable (StoreableImpl value) { + List> storedTypes = value.getTypes(); + return types.equals(storedTypes); + } + + public List> getTypes () { + return new ArrayList<>(types); + } + + @Override + public Storeable put(String key, Storeable value) throws ColumnFormatException { + checkTableIsOpen(); + if (key == null || value == null) { + throw new IllegalArgumentException("Illegal arguments for put"); + } + if (!storeableIsSuitable((StoreableImpl) value)) { + throw new ColumnFormatException("Can't put: type mismatch"); + } + DataFile data = datFiles.get(new DataFileHasher(key)); + Storeable result = null; + try { + if (data.get(key) != null) { + result = provider.deserialize((Table) this, data.get(key)); + } + } catch (ParseException e) { + throw new RuntimeException(e); //todo: try to catch in shell + } + data.remove(key); + data.put(key,provider.serialize((Table) this, value)); + + return result; + } + + @Override + public Storeable remove(String key) { + checkTableIsOpen(); + if (key == null) { + throw new IllegalArgumentException("Illegal argument for remove"); + } + DataFile data = datFiles.get(new DataFileHasher(key)); + Storeable result = null; + if (data.containsKey(key)) { + try { + result = provider.deserialize((Table) this, data.get(key)); + } catch (ParseException e) { + throw new RuntimeException(e); //todo: try to catch in shell + } + data.remove(key); + } + return result; + } + + @Override + public int size() { + checkTableIsOpen(); + int totalSize = 0; + for (Map.Entry entry : datFiles.entrySet()) { + totalSize += entry.getValue().size(); + } + return totalSize; + } + + @Override + public List list() { + checkTableIsOpen(); + List keys = new ArrayList(); + for (Map.Entry dataEntry : datFiles.entrySet()) { + for (Map.Entry element : dataEntry.getValue().entrySet()) { + keys.add(element.getKey()); + } + } + return keys; + } + + @Override + public int commit() throws IOException { + checkTableIsOpen(); + int saved = 0; + for (Map.Entry entry : datFiles.entrySet()) { + saved += entry.getValue().commit(); + } + return saved; + } + + @Override + public int rollback() { + checkTableIsOpen(); + int changesRolled = 0; + for (Map.Entry entry : datFiles.entrySet()) { + changesRolled += entry.getValue().rollback(); + } + return changesRolled; + } + + @Override + public int getNumberOfUncommittedChanges() { + checkTableIsOpen(); + int unsaved = 0; + for (Map.Entry entry : datFiles.entrySet()) { + unsaved += entry.getValue().unsavedChangesNum(); + } + return unsaved; + } + + @Override + public int getColumnsCount() { + return types.size(); + } + + @Override + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + return types.get(columnIndex); + } + + @Override + public String getName() { + return tableRoot.getName(); + } + + @Override + public Storeable get(String key) { + checkTableIsOpen(); + if (key == null) { + throw new IllegalArgumentException("Illegal argument for get"); + } + DataFile data = datFiles.get(new DataFileHasher(key)); + if (data.containsKey(key)) { + try { + return provider.deserialize((Table) this, data.get(key)); + } catch (ParseException e) { + throw new RuntimeException(e); //todo: try to catch in shell + } + } + return null; + } + + public void clear() { + File[] folders = tableRoot.listFiles(); + if (folders == null) { + return; + } + for (File folder : folders) { + if (folder.isDirectory()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + file.delete(); + } + } + } + folder.delete(); + } + tableRoot.delete(); + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderFactoryImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderFactoryImpl.java new file mode 100644 index 000000000..259a634e5 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderFactoryImpl.java @@ -0,0 +1,34 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +import java.io.File; +import java.nio.file.Paths; + +/** + * Created by Oskar on 15.11.14. + */ +public class TableProviderFactoryImpl implements TableProviderFactory { + @Override + public TableProviderImpl create(String dir) throws IllegalArgumentException { + if (dir == null) { + throw new IllegalArgumentException("TabeProviderFactory: no directory"); + } + + try { + File dbFile = Paths.get(dir).toFile(); + if (!dbFile.exists()) { + if (dbFile.mkdir()) { + // System.err.println("created root folder: " + dir); Todo: add to log in Proxy + } else { + // Todo: add to log in Proxy + throw new IllegalArgumentException("TabeProviderFactory: folder not exist and cannot be created"); + } + } + } catch (Exception e) { + // Todo: add to log in Proxy + throw new IllegalArgumentException("TabeProviderFactory: database root directory error: " + e.getMessage()); + } + return new TableProviderImpl(dir); + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java new file mode 100644 index 000000000..e090d0eef --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java @@ -0,0 +1,236 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; + + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Created by Oskar on 15.11.14. + */ +public class TableProviderImpl implements TableProvider { + + private Map tables; + private String dbDir; + + public TableProviderImpl(String dir) { + tables = new TreeMap<>(); + dbDir = dir; + File[] tableFiles = Paths.get(dbDir).toFile().listFiles(); + for (File newTableFile : tableFiles) { + if (newTableFile.isDirectory()) { + initTable(newTableFile.getName()); + } + } + } + + @Override + public Table getTable(String name) { + if (name == null) { + throw new IllegalArgumentException("Illegal argument for get table"); + } + return tables.get(name); + } + + private void initTable(String name) { + try { + if (!tables.containsKey(name)) { + tables.put(name, new TableImpl(Paths.get(dbDir + "/" + name).toFile(), + (TableProvider) this)); + } + } catch (Exception e) { + throw new RuntimeException("signature.tsv for table <" + name + "> not found or corrupted", e); + } + } + + @Override + public Table createTable(String name, List> columnTypes) throws IOException { + if (name == null) { + throw new IllegalArgumentException("Illegal argument for create"); + } + if (!tables.containsKey(name)) { + tables.put(name, new TableImpl(Paths.get(dbDir + "/" + name).toFile(), columnTypes, + (TableProvider) this)); + return tables.get(name); + } + return null; + } + + @Override + public void removeTable(String name) throws IOException { + if (name == null) { + throw new IllegalArgumentException("Illegal argument for remove"); + } + if (!tables.containsKey(name)) { + throw new IllegalStateException("Table not exist"); + } + tables.get(name).clear(); + tables.remove(name); + } + + @Override + public Storeable deserialize(Table table, String value) throws ParseException { + List strings = parseToList(value); // todo can be "" + List values = new ArrayList<>(); + List> types = new ArrayList<>(); + int i = 0; + try { + for (i = 0; i < table.getColumnsCount(); i++) { + values.add(castToType(new String(strings.get(i)), table.getColumnType(i))); + types.add(table.getColumnType(i)); + } + } catch (Exception e) { + throw new ParseException(e.getMessage(), value.indexOf(strings.get(i))); + } + Storeable result = new StoreableImpl(values, types); + return result; + } + + private Object castToType(String value, Class type) throws Exception { + if (value.replaceAll("[ ]","").equals("null")) { + return null; + } + switch(type.getName()) { + case "java.lang.Integer": + return Integer.parseInt(value); + //break; + case "java.lang.Long": + return Long.parseLong(value); + //break; + case "java.lang.Byte": + return Byte.parseByte(value); + //break; + case "java.lang.Float": + return Float.parseFloat(value); + //break; + case "java.lang.Double": + return Double.parseDouble(value); + //break; + case "java.lang.Boolean": + return Boolean.parseBoolean(value); + //break; + case "java.lang.String": + return value; + //break; + + } + return null; + } + + private List parseToList(String value) throws ParseException { + List result = new ArrayList<>(); + if (value.startsWith("[") && value.endsWith("]")) { + value = value.substring(1, value.length() - 1); + } else { + throw new ParseException("expected [ ... ]", 0); + } + boolean quote = false; + boolean slash = false; + String current = ""; + int position = -1; + for (char c : value.toCharArray()) { + position++; + if (slash) { // todo: /t /r /u ... + current += c; + slash = false; + } else { + switch (c) { + case '\\': + slash = true; + break; + case '\"': + quote = !quote; + break; + case ',': + if (quote) { + current += ","; + } else { + result.add(new String(current)); + current = ""; + } + break; + case ' ': + if (quote) { + current += " "; + } + break; + default: + if (!quote && (c == '[' || c == ']')) { + throw new ParseException("unexpected " + String.valueOf(c), position); + } else { + current += c; + } + break; + } + } + } + result.add(current); + return result; + } + + @Override + public String serialize(Table table, Storeable value) throws ColumnFormatException { + if (!((TableImpl) table).storeableIsSuitable((StoreableImpl) value)) { + throw new ColumnFormatException("Can't serialize: type mismatch"); + } + String result = "[ "; + List values = new ArrayList<>(); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (value.getColumnAt(i) == null) { + values.add("null"); + } else if (table.getColumnType(i) == String.class) { + values.add("\""+ value.getColumnAt(i) + "\""); + } else { + values.add(table.getColumnType(i).cast(value.getColumnAt(i)).toString()); + } + } + result += String.join(", ", values); + result += " ]"; + return result; + } + + @Override + public Storeable createFor(Table table) { + List values = new ArrayList<>(); + for(int i = 0; i < table.getColumnsCount(); i++) { + values.add(null); + } + return new StoreableImpl(values, ((TableImpl) table).getTypes()); + } + + @Override + public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { + if (values.size() != table.getColumnsCount()) { + throw new IndexOutOfBoundsException(); + } + List valuesToPut = new ArrayList<>(); + for(int i = 0; i < table.getColumnsCount(); i++) { + try { + valuesToPut.add(table.getColumnType(i).cast(values.get(i))); + } catch (Exception e) { + throw new ColumnFormatException("Can't cast <" + + values.get(i).toString() + "> to <" + table.getColumnType(i) ); + } + } + return new StoreableImpl(valuesToPut, ((TableImpl) table).getTypes()); + } + + @Override + public List getTableNames() { + List names = new ArrayList(); + for (Map.Entry entry : tables.entrySet()) { + names.add(entry.getKey()); + } + return names; + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderFactoryTest.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderFactoryTest.java new file mode 100644 index 000000000..74ee18e34 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderFactoryTest.java @@ -0,0 +1,38 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableProviderFactoryImpl; + +import java.nio.file.Paths; + +public class TableProviderFactoryTest { + TableProviderFactoryImpl factory; + + @Before + public void setUp() throws Exception { + factory = new TableProviderFactoryImpl(); + } + + @After + public void tearDown() throws Exception { + Paths.get("./factoryTest").toFile().delete(); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateThrowsIllegalArgumentExceptionOnNull() throws Exception { + factory.create(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateThrowsIllegalArgumentException() throws Exception { + factory.create("abc/def\\gh*./-=09()'///../"); + } + + @Test + public void testCreate() throws Exception { + factory.create("./factoryTest"); + } + +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderTest.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderTest.java new file mode 100644 index 000000000..ebf6f3ec5 --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableProviderTest.java @@ -0,0 +1,94 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableProviderImpl; + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TableProviderTest { + TableProviderImpl provider; + List> types; + + @Before + public void setUp() throws Exception { + File tableFile = Paths.get("testTableProvider").toFile(); + if (!tableFile.exists()) { + tableFile.mkdir(); + } + provider = new TableProviderImpl(tableFile.getAbsolutePath()); + types = new ArrayList<>(); + types.add(String.class); + } + + @After + public void tearDown() throws Exception { + File[] files = Paths.get("testTableProvider").toFile().listFiles(); + for (File file : files) { + file.delete(); + } + Paths.get("testTableProvider").toFile().delete(); + } + + @Test + public void testGetTable() throws Exception { + provider.createTable("table", types); + assertEquals("table", provider.getTable("table").getName()); + } + + @Test + public void testGetUnexistTable() throws Exception { + assertEquals(null, provider.getTable("table")); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetTableThrowsIllegalArgumentException() throws Exception { + provider.getTable(null); + } + + @Test + public void testGetTableNames() throws Exception { + provider.createTable("table1", types); + provider.createTable("table2", types); + provider.createTable("table3", types); + List trueResult = new ArrayList<>(Arrays.asList(new String[]{"table1", "table2", "table3"})); + assertEquals(trueResult, provider.getTableNames()); + + } + + @Test + public void testCreateTable() throws Exception { + assertEquals(provider.createTable("table", types), provider.getTable("table")); + assertEquals(null, provider.createTable("table", types)); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateTableThrowsIllegalArgumentException() throws Exception { + provider.createTable(null, types); + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveTableThrowsIllegalArgumentException() throws Exception { + provider.removeTable(null); + } + + @Test(expected = IllegalStateException.class) + public void testRemoveTableThrowsIllegalStateException() throws Exception { + provider.removeTable("unexisting_table"); + } + + @Test + public void testRemoveTable() throws Exception { + provider.createTable("table", types); + assertEquals(1, provider.getTableNames().size()); + provider.removeTable("table"); + assertEquals(0, provider.getTableNames().size()); + } +} diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableTest.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableTest.java new file mode 100644 index 000000000..64c7fcd9d --- /dev/null +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Tests/TableTest.java @@ -0,0 +1,168 @@ +package ru.fizteh.fivt.students.oscar_nasibullin.Storeable.Tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.storage.structured.*; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableProviderImpl; +import ru.fizteh.fivt.students.oscar_nasibullin.Storeable.TableImpl; + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TableTest { + Table testTable; + List> types; + Storeable testValue1; + Storeable testValue2; + Storeable testValue3; + TableProvider provider; + + @Before + public void setUp() throws Exception { + Class[] t = {Integer.class, Long.class, Byte.class, Float.class, Double.class, Boolean.class, String.class}; + types = new ArrayList<>(Arrays.asList(t)); + File tableFile = Paths.get("testTable").toFile(); + if (!tableFile.exists()) { + tableFile.mkdir(); + } + provider = new TableProviderImpl(tableFile.getAbsolutePath()); + provider.createTable("testTable", types); + testTable = provider.getTable("testTable"); + testValue1 = provider.createFor(testTable); + testValue2 = provider.createFor(testTable); + testValue3 = provider.createFor(testTable); + testValue1.setColumnAt(6, "some text for testing, other fields are nulls"); + testValue2.setColumnAt(4, 0.7777777); + ((TableImpl) testTable).open(); + } + + @After + public void tearDown() throws Exception { + File[] files = Paths.get("testTable").resolve("testTable").toFile().listFiles(); + for (File file : files) { + file.delete(); + } + files = Paths.get("testTable").toFile().listFiles(); + for (File file : files) { + file.delete(); + } + Paths.get("testTable").toFile().delete(); + } + + @Test + public void testGetName() throws Exception { + assertEquals("testTable", testTable.getName()); + } + + @Test + public void testGetUnexistElement() throws Exception { + assertEquals(null, testTable.get("key_which_is_not_in_table")); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetThrowsIllegalArgumentException() throws Exception { + testTable.get(null); + } + + @Test + public void testPutNewElement() throws Exception { + assertEquals(null, testTable.put("key", testValue1)); + assertEquals(null, testTable.put("k", testValue2)); + assertEquals(null, testTable.put("x", testValue3)); + } + + @Test + public void testPutOverwritelement() throws Exception { + testTable.put("key", testValue1); + testTable.put("x", testValue3); + testTable.put("k", testValue2); + assertEquals(testValue1, testTable.put("key", testValue2)); + assertEquals(testValue2, testTable.put("k", testValue3)); + assertEquals(testValue3, testTable.put("x", testValue1)); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutThrowsIllegalArgumentException() throws Exception { + testTable.put(null, testValue1); + } + + @Test + public void testGet() throws Exception { + testTable.put("key", testValue1); + testTable.put("k", testValue2); + testTable.put("x", testValue3); + assertEquals(testValue1, testTable.get("key")); + assertEquals(testValue2, testTable.get("k")); + assertEquals(testValue3, testTable.get("x")); + + } + + @Test + public void testRemoveUnexistElement() throws Exception { + assertEquals(null, testTable.remove("key_which_is_not_in_table")); + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveThrowsIllegalArgumentException() throws Exception { + testTable.remove(null); + } + + @Test + public void testRemove() throws Exception { + testTable.put("key", testValue1); + assertEquals(testValue1, testTable.remove("key")); + } + + @Test + public void testSize() throws Exception { + testTable.put("k", testValue1); + testTable.put("x", testValue2); + assertEquals(2, testTable.size()); + } + + @Test + public void testCommitNewElements() throws Exception { + testTable.put("k", testValue1); + testTable.put("x", testValue2); + assertEquals(2, testTable.commit()); + } + + @Test + public void testCommitRemoveElements() throws Exception { + testTable.put("k", testValue1); + testTable.put("x", testValue2); + assertEquals(2, testTable.commit()); + testTable.remove("k"); + testTable.remove("x"); + assertEquals(2, testTable.commit()); + } + + @Test + public void testRollback() throws Exception { + testTable.put("k", testValue1); + assertEquals(1, testTable.rollback()); + } + + @Test + public void testRollbackRemovedElements() throws Exception { + testTable.put("k", testValue1); + testTable.put("x", testValue2); + assertEquals(2, testTable.commit()); + testTable.remove("x"); + assertEquals(1, testTable.rollback()); + } + + @Test + public void testList() throws Exception { + testTable.put("k", testValue1); + testTable.put("x", testValue2); + List trueResult = new ArrayList<>(Arrays.asList(new String[]{"x", "k"})); + assertEquals(trueResult, testTable.list()); + } +} diff --git a/testTableProvider/table1/signature.tsv b/testTableProvider/table1/signature.tsv new file mode 100644 index 000000000..d855855ef --- /dev/null +++ b/testTableProvider/table1/signature.tsv @@ -0,0 +1 @@ +String \ No newline at end of file diff --git a/testTableProvider/table2/signature.tsv b/testTableProvider/table2/signature.tsv new file mode 100644 index 000000000..d855855ef --- /dev/null +++ b/testTableProvider/table2/signature.tsv @@ -0,0 +1 @@ +String \ No newline at end of file diff --git a/testTableProvider/table3/signature.tsv b/testTableProvider/table3/signature.tsv new file mode 100644 index 000000000..d855855ef --- /dev/null +++ b/testTableProvider/table3/signature.tsv @@ -0,0 +1 @@ +String \ No newline at end of file From 12de89342a2223f040c60766e1617b3372bed649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D1=81=D0=BA=D0=B0=D1=80?= Date: Sun, 14 Dec 2014 17:37:46 +0400 Subject: [PATCH 2/2] check-styled --- .../Storeable/Comands/CmdCreate.java | 2 +- .../Storeable/Comands/CmdGet.java | 2 +- .../Storeable/StoreableImpl.java | 6 +++--- .../oscar_nasibullin/Storeable/TableImpl.java | 16 ++++++++-------- .../Storeable/TableProviderImpl.java | 15 ++++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java index 306193adb..19cf4a9ac 100644 --- a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdCreate.java @@ -34,7 +34,7 @@ public String run(List args) throws Exception { try { types.add(Class.forName(className)); } catch (ClassNotFoundException e) { - throw new Exception("wrong type (no type with name: " + typeName.replaceAll("[()]", "") + ")", e); + throw new Exception("wrong type (no type with name: " + typeName + ")", e); } } } diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java index 5ee0d752d..89861c185 100644 --- a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/Comands/CmdGet.java @@ -21,7 +21,7 @@ public String run(List args) throws Exception { throw new Exception("no table"); } - String value = tableProvider.serialize(currTable ,currTable.get(args.get(1))); + String value = tableProvider.serialize(currTable, currTable.get(args.get(1))); if (value != null) { resultMessage = "found\n" + value; } else { diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java index 6d8d35499..3b1bf146d 100644 --- a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/StoreableImpl.java @@ -23,8 +23,8 @@ public StoreableImpl(List values, List> valuesTypes) { public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { if (value.getClass() != types.get(columnIndex)) { throw new ColumnFormatException( - "Storable: expected " + types.get(columnIndex).getName() + - ", found " + value.getClass().getName()); + "Storable: expected " + types.get(columnIndex).getName() + + ", found " + value.getClass().getName()); } storage.set(columnIndex, types.get(columnIndex).cast(value)); // todo: check IndexOutOfBounds } @@ -97,7 +97,7 @@ public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOu return (String) storage.get(columnIndex); //todo: check for null } - public List> getTypes () { + public List> getTypes() { return new ArrayList<>(types); } } diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java index adfb1730b..e7ef12481 100644 --- a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableImpl.java @@ -37,7 +37,7 @@ public TableImpl(File table, List> columnTypes, TableProvider myProvide close(); File tsvFile = tableRoot.toPath().resolve("signature.tsv").toFile(); tsvFile.createNewFile(); - RandomAccessFile sign = new RandomAccessFile( tsvFile, "rw"); + RandomAccessFile sign = new RandomAccessFile(tsvFile, "rw"); for (Class type : types) { sign.writeBytes(convertClassName(type.getName()) + " "); } @@ -117,11 +117,11 @@ public void close() throws Exception { private void checkTableIsOpen() { if (!isOpen) { - throw new RuntimeException("Trying to operate with table <" + getName()+"> which is not open"); + throw new RuntimeException("Trying to operate with table <" + getName() + "> which is not open"); } } - public static String convertClassName (String name) { + public static String convertClassName(String name) { if (name.startsWith("java.lang.")) { name = name.replaceAll("(java.lang.)", ""); if (name.equals("String")) { @@ -134,20 +134,20 @@ public static String convertClassName (String name) { name = "int"; } else if (name.equals("String")) { return "java.lang.String"; - } else if (name.substring(0,1).equals(name.substring(0,1).toUpperCase())) { + } else if (name.substring(0, 1).equals(name.substring(0, 1).toUpperCase())) { return name.toLowerCase(); } else { - name = "java.lang." + name.substring(0,1).toUpperCase() + name.substring(1, name.length()); + name = "java.lang." + name.substring(0, 1).toUpperCase() + name.substring(1, name.length()); } return name; } - public boolean storeableIsSuitable (StoreableImpl value) { + public boolean storeableIsSuitable(StoreableImpl value) { List> storedTypes = value.getTypes(); return types.equals(storedTypes); } - public List> getTypes () { + public List> getTypes() { return new ArrayList<>(types); } @@ -170,7 +170,7 @@ public Storeable put(String key, Storeable value) throws ColumnFormatException { throw new RuntimeException(e); //todo: try to catch in shell } data.remove(key); - data.put(key,provider.serialize((Table) this, value)); + data.put(key, provider.serialize((Table) this, value)); return result; } diff --git a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java index e090d0eef..ce4cfdc5a 100644 --- a/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java +++ b/src/ru/fizteh/fivt/students/oscar_nasibullin/Storeable/TableProviderImpl.java @@ -97,7 +97,7 @@ public Storeable deserialize(Table table, String value) throws ParseException { } private Object castToType(String value, Class type) throws Exception { - if (value.replaceAll("[ ]","").equals("null")) { + if (value.replaceAll("[ ]", "").equals("null")) { return null; } switch(type.getName()) { @@ -122,9 +122,10 @@ private Object castToType(String value, Class type) throws Exception { case "java.lang.String": return value; //break; + default: + return value; } - return null; } private List parseToList(String value) throws ParseException { @@ -189,7 +190,7 @@ public String serialize(Table table, Storeable value) throws ColumnFormatExcepti if (value.getColumnAt(i) == null) { values.add("null"); } else if (table.getColumnType(i) == String.class) { - values.add("\""+ value.getColumnAt(i) + "\""); + values.add("\"" + value.getColumnAt(i) + "\""); } else { values.add(table.getColumnType(i).cast(value.getColumnAt(i)).toString()); } @@ -202,7 +203,7 @@ public String serialize(Table table, Storeable value) throws ColumnFormatExcepti @Override public Storeable createFor(Table table) { List values = new ArrayList<>(); - for(int i = 0; i < table.getColumnsCount(); i++) { + for (int i = 0; i < table.getColumnsCount(); i++) { values.add(null); } return new StoreableImpl(values, ((TableImpl) table).getTypes()); @@ -214,12 +215,12 @@ public Storeable createFor(Table table, List values) throws ColumnFormatExcep throw new IndexOutOfBoundsException(); } List valuesToPut = new ArrayList<>(); - for(int i = 0; i < table.getColumnsCount(); i++) { + for (int i = 0; i < table.getColumnsCount(); i++) { try { valuesToPut.add(table.getColumnType(i).cast(values.get(i))); } catch (Exception e) { - throw new ColumnFormatException("Can't cast <" + - values.get(i).toString() + "> to <" + table.getColumnType(i) ); + throw new ColumnFormatException("Can't cast <" + + values.get(i).toString() + "> to <" + table.getColumnType(i)); } } return new StoreableImpl(valuesToPut, ((TableImpl) table).getTypes());