From ffa311b150a2121b27fa866bed7ef03a4039cd2a Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Sun, 16 Nov 2014 14:21:20 +0000 Subject: [PATCH 1/7] added permissions validator --- LICENSE | 24 --- README.md | 22 -- build.xml | 59 ------ github-workflow.md | 16 -- .../{junit => database}/Command.java | 4 +- .../database/HandlerInterface.java | 7 + .../{junit => database}/Main.java | 28 +-- .../database/PermissionsValidator.java | 73 +++++++ .../{junit => database}/Shell.java | 24 +-- .../{junit => database}/Table.java | 162 +++++++------- .../{junit => database}/TableInterface.java | 2 +- .../andrewzhernov/database/TableManager.java | 121 +++++++++++ .../TableManagerInterface.java} | 4 +- .../{junit => database}/Utils.java | 2 +- .../andrewzhernov/filemap/DataBase.java | 100 --------- .../andrewzhernov/filemap/FileMap.java | 93 -------- .../andrewzhernov/junit/AbstractHandler.java | 7 - .../andrewzhernov/junit/HandlerInterface.java | 7 - .../andrewzhernov/junit/TableProvider.java | 116 ---------- .../andrewzhernov/multifilemap/DataBase.java | 199 ------------------ .../multifilemap/MultiFileMap.java | 109 ---------- .../andrewzhernov/multifilemap/Utils.java | 20 -- .../students/andrewzhernov/shell/Cat.java | 39 ---- .../andrewzhernov/shell/ChangeDir.java | 28 --- .../students/andrewzhernov/shell/Copy.java | 73 ------- .../students/andrewzhernov/shell/List.java | 19 -- .../students/andrewzhernov/shell/MakeDir.java | 16 -- .../students/andrewzhernov/shell/Move.java | 28 --- .../students/andrewzhernov/shell/Pwd.java | 11 - .../students/andrewzhernov/shell/Remove.java | 41 ---- .../students/andrewzhernov/shell/Shell.java | 87 -------- tasks/01-Shell.md | 113 ---------- 32 files changed, 304 insertions(+), 1350 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 build.xml delete mode 100644 github-workflow.md rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/Command.java (82%) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/Main.java (80%) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/Shell.java (75%) rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/Table.java (50%) rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/TableInterface.java (97%) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java rename src/ru/fizteh/fivt/students/andrewzhernov/{junit/TableProviderInterface.java => database/TableManagerInterface.java} (96%) rename src/ru/fizteh/fivt/students/andrewzhernov/{junit => database}/Utils.java (93%) delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/filemap/DataBase.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/filemap/FileMap.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/junit/AbstractHandler.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/junit/HandlerInterface.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProvider.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/DataBase.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/MultiFileMap.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/Utils.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Cat.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/ChangeDir.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Copy.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/List.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/MakeDir.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Move.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Pwd.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Remove.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/shell/Shell.java delete mode 100644 tasks/01-Shell.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f87793701..000000000 --- a/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2014, Dmitriy Komanov -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/README.md b/README.md deleted file mode 100644 index 718085524..000000000 --- a/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Программирование на языке JAVA - -Курс лекций и семинаров для студентов второго курса ФизТеха, факультет -[ФИВТ](http://fivt.fizteh.ru) на 2014 год. - -## Семинары -В папке [tasks](tasks) находятся описания заданий на -семинары. Для написания программ необходимо руководствоваться [соглашениями -по оформлению кода](http://www.oracle.com/technetwork/java/codeconv-138413.html) -от Oracle (Code Conventions). - -В документе [github-workflow](github-workflow.md) описаны -общие правила по ведению задач на github, а также упрощение жизни с помощью утилиты -[checkstyle](http://checkstyle.sourceforge.net/), которая проверяет код на соответствие -Code Conventions. - -## Список литературы -* Брюс Эккель — Философия Java (Thinking in Java) -* Brian Goetz — Java Concurrency in Practive (продвинутая книжка -по многопоточности в Java) -* [Спецификации по JVM и JLS](http://docs.oracle.com/javase/specs/index.html) -* [Code Conventions](http://www.oracle.com/technetwork/java/codeconv-138413.html) diff --git a/build.xml b/build.xml deleted file mode 100644 index f949c9245..000000000 --- a/build.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/github-workflow.md b/github-workflow.md deleted file mode 100644 index e1ae4cab3..000000000 --- a/github-workflow.md +++ /dev/null @@ -1,16 +0,0 @@ -## Workflow работы с github - -Каждый студент делает [fork](https://help.github.com/articles/fork-a-repo) репозитория ```fizteh-java-2014```. Работа -ведётся в собственном репозитории, в директории ```src/ru/fizteh/fivt/students//```. - -После того, как задание выполнено и протестировано в собственном репозитории, необходимо создать -[pull request](https://help.github.com/articles/using-pull-requests) в репозиторий ```fizteh-java-2014```. В заголовке -к pull request необходимо написать: ```Имя Фамилия, № группы, задание``` (например, ```Василий Иванов, 123, Shell```). -Также необходимо назначить pull request на своего семинариста. - -В одном pull request должно быть решение только одной задачи. Если хочется сдавать параллельно несколько заданий, -необходимо создавать бранчи и делать pull request из бранчей. - -Чтобы сократить количество итераций на проверку задания, полезно самостоятельно удостовериться, что Code Conventions -соблюдены. Для этого нужно запустить сборку проекта с помощью ```ant checkstyle```. Вместо этого можно поставить -соответствующий плагин в IDE и настроить этот плагин на конфигурационный файл ```bin/checkstyle-rules.xml```. diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Command.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java similarity index 82% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/Command.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java index b9249d1d8..973d9324f 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Command.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; public class Command { private String name; @@ -15,7 +15,7 @@ public String getName() { return name; } - public void execute(TableProvider database, String[] params) throws Exception { + public void execute(TableManager database, String[] params) throws Exception { if (params.length != numArgs) { throw new Exception(String.format("Invalid number of arguments: %d expected, %d found.", numArgs, params.length)); diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java new file mode 100644 index 000000000..7edda1b27 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java @@ -0,0 +1,7 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +public interface HandlerInterface { + Object execute(TableManager database, String[] args) throws Exception; + void handle(Object object) throws Exception; +} + diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Main.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java similarity index 80% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/Main.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java index 62ea4a85a..6d0e62381 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Main.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.util.List; import java.util.Map; @@ -6,11 +6,11 @@ public class Main { public static void main(String[] args) { try { - TableProvider database = new TableProvider(System.getProperty("fizteh.db.dir")); + TableManager database = new TableManager(System.getProperty("fizteh.db.dir")); Shell shell = new Shell(database, new Command[] { new Command("size", 1, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().size(); } @Override @@ -20,7 +20,7 @@ public void handle(Object object) throws Exception { }), new Command("put", 3, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().put(args[1], args[2]); } @Override @@ -36,7 +36,7 @@ public void handle(Object object) throws Exception { }), new Command("get", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().get(args[1]); } @Override @@ -52,7 +52,7 @@ public void handle(Object object) throws Exception { }), new Command("remove", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().remove(args[1]); } @Override @@ -66,7 +66,7 @@ public void handle(Object object) throws Exception { }), new Command("list", 1, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().list(); } @Override @@ -78,7 +78,7 @@ public void handle(Object object) throws Exception { }), new Command("commit", 1, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().commit(); } @Override @@ -88,7 +88,7 @@ public void handle(Object object) throws Exception { }), new Command("rollback", 1, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().rollback(); } @Override @@ -98,7 +98,7 @@ public void handle(Object object) throws Exception { }), new Command("create", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.createTable(args[1]); } @Override @@ -108,7 +108,7 @@ public void handle(Object object) throws Exception { }), new Command("drop", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { database.removeTable(args[1]); return null; } @@ -119,7 +119,7 @@ public void handle(Object object) throws Exception { }), new Command("use", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.useTable(args[1]); } @Override @@ -129,7 +129,7 @@ public void handle(Object object) throws Exception { }), new Command("show tables", 2, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { return database.showTables(); } @Override @@ -143,7 +143,7 @@ public void handle(Object object) throws Exception { }), new Command("exit", 1, new HandlerInterface() { @Override - public Object execute(TableProvider database, String[] args) throws Exception { + public Object execute(TableManager database, String[] args) throws Exception { database.exit(); return null; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java new file mode 100644 index 000000000..fc857f271 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java @@ -0,0 +1,73 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import java.util.EnumSet; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; +import java.io.File; +import java.nio.file.Files; +import java.io.IOException; + +public class PermissionsValidator { + public enum Permissions { + NOT_NULL, + EXISTS, + CREATE_FILE_IF_NOT_EXISTS, + CREATE_DIRECTORY_IF_NOT_EXISTS, + CAN_READ, + CAN_WRITE, + IS_FILE, + IS_DIRECTORY; + } + + public static boolean validateTableName(String tableName, EnumSet perms) throws IllegalArgumentException { + if (perms.contains(NOT_NULL) && tableName == null) { + throw new IllegalArgumentException("Table name hasn't been specified"); + } + return true; + } + + public static boolean validateDbFolder(String dbFolder, EnumSet perms) throws IllegalArgumentException { + if (perms.contains(NOT_NULL) && dbFolder == null) { + throw new IllegalArgumentException("Database name hasn't been specified"); + } + return true; + } + + public static boolean validate(String fileName, EnumSet perms) throws IllegalArgumentException { + if (perms.contains(NOT_NULL) && fileName == null) { + throw new IllegalArgumentException("File name hasn't been specified"); + } + File file = new File(fileName); + if (perms.contains(EXISTS) && !file.exists()) { + return false; + } + if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) || perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) && !file.exists()) { + try { + File parentFile = file.getCanonicalFile().getParentFile(); + if (!parentFile.canWrite()) { + throw new IllegalArgumentException(fileName + + ": don't have permission to create the directory or normal file"); + } + if (perms.contains(CREATE_FILE_IF_NOT_EXISTS)) { + Files.createFile(file.toPath()); + } else { + Files.createDirectory(file.toPath()); + } + } catch (IOException e) { + System.out.println(file.toString() + ": Input-Output exception"); + } + } + if (perms.contains(CAN_READ) && !file.canRead()) { + throw new IllegalArgumentException(file.getPath() + ": don't have permission to read the file"); + } + if (perms.contains(CAN_WRITE) && !file.canWrite()) { + throw new IllegalArgumentException(file.getPath() + ": don't have permission to write the file"); + } + if (perms.contains(IS_FILE) && !file.isFile()) { + throw new IllegalArgumentException(file.getPath() + ": isn't a normal file"); + } + if (perms.contains(IS_DIRECTORY) && !file.isDirectory()) { + throw new IllegalArgumentException(file.getPath() + ": isn't a directory"); + } + return true; + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Shell.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java similarity index 75% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/Shell.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java index 5e58be33b..03f1d0025 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Shell.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.util.Scanner; import java.util.Map; @@ -9,11 +9,11 @@ public class Shell { private static final String STATEMENT_DELIMITER = ";"; private static final String PARAM_DELIMITER = "\\s+"; - private TableProvider database; + private TableManager manager; private Map commands; - public Shell(TableProvider database, Command[] commands) throws Exception { - this.database = database; + public Shell(TableManager manager, Command[] commands) throws Exception { + this.manager = manager; this.commands = new HashMap<>(); for (Command command : commands) { this.commands.put(command.getName(), command); @@ -56,18 +56,18 @@ public void executeLine(String line) throws Exception { for (String statement : statements) { String[] params = statement.trim().split(PARAM_DELIMITER); - String cmdName = null; - if (params.length > 0) { - cmdName = params[0]; - if (cmdName.equals("show") && params.length > 1) { - cmdName += (" " + params[1]); - } + String cmdName = params[0]; + for (int i = 1; i < params.length && commands.get(cmdName) == null; ++i) { + cmdName += " " + params[i]; } + Command command = commands.get(cmdName); if (command == null) { - throw new Exception("Command not found: " + cmdName); + if (!cmdName.isEmpty()) { + throw new Exception(cmdName + ": command not found"); + } } else { - command.execute(database, params); + command.execute(manager, params); } } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java similarity index 50% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/Table.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 466006b7e..10c5ed93e 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -1,51 +1,40 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.io.*; +import java.lang.Integer; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.io.RandomAccessFile; import java.util.List; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + public class Table implements TableInterface { private static final int DIRECTORIES_COUNT = 16; private static final int FILES_COUNT = 16; - private Path path; + private TableManager manager; + private String name; private int size; private Map disk; private Map diff; - public Table(Path tablePath) throws IllegalArgumentException { - if (tablePath == null) { - throw new IllegalArgumentException("The table directory wasn't specified"); - } + public Table(TableManager tableManager, String tableName) { + PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); + manager = tableManager; + name = tableName; + size = 0; disk = new HashMap<>(); diff = new HashMap<>(); - path = tablePath; - try { - if (Files.notExists(path)) { - if (!path.getParent().toFile().canWrite()) { - throw new Exception(path.toString() + ": don't have permission to create the directory"); - } - Files.createDirectory(path); - } else if (!path.toFile().canRead()) { - throw new Exception(path.toString() + ": don't have permission to read the directory"); - } else if (!Files.isDirectory(path)) { - throw new Exception(path.toString() + ": isn't a directory"); - } else { - loadTable(); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } } public String getName() { - return path.toFile().getName(); + return name; } public int unsavedSize() { @@ -120,12 +109,7 @@ public int commit() { } } diff.clear(); - try { - saveTable(); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } + saveTable(); return amount; } @@ -136,6 +120,18 @@ public int rollback() { return amount; } + public String getTableFolder() { + return Paths.get(manager.getDbFolder(), name).toString(); + } + + public String getTableBucket(int bucket) { + return Paths.get(getTableFolder(), new Integer(bucket).toString() + ".dir").toString(); + } + + public String getTableSubBucket(String bucket, int subBucket) { + return Paths.get(bucket, new Integer(subBucket).toString() + ".dat").toString(); + } + private static String readItem(RandomAccessFile file) throws Exception { int wordSize = file.readInt(); byte[] word = new byte[wordSize]; @@ -143,50 +139,44 @@ private static String readItem(RandomAccessFile file) throws Exception { return new String(word, "UTF-8"); } - private void loadFile(String filename) throws Exception { - RandomAccessFile file = new RandomAccessFile(filename, "r"); + private void loadSubBucket(String subBucket) throws Exception { + RandomAccessFile file = new RandomAccessFile(subBucket, "r"); while (file.getFilePointer() < file.length()) { try { String key = readItem(file); String value = readItem(file); disk.put(key, value); } catch (Exception | OutOfMemoryError e) { - throw new Exception(filename + ": invalid file format"); + throw new Exception(subBucket + ": invalid file format"); } } file.close(); } - private void loadDirectory(Path dir) throws Exception { - for (int i = 0; i < FILES_COUNT; ++i) { - Path file = dir.resolve(Integer.toString(i) + ".dat"); - if (Files.exists(file)) { - if (!file.toFile().canRead()) { - throw new Exception(file.toString() + ": don't have permission to read the file"); - } else if (!file.toFile().isFile()) { - throw new Exception(file.toString() + ": isn't a normal file"); - } else { - loadFile(file.toString()); - } + private void loadBucket(String bucket) throws Exception { + for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { + String subBucket = getTableSubBucket(bucket, subBucketIndex); + if (PermissionsValidator.validate(subBucket, of(NOT_NULL, EXISTS, CAN_READ, IS_FILE))) { + loadSubBucket(subBucket); } } } - private void loadTable() throws Exception { - disk.clear(); - for (int i = 0; i < DIRECTORIES_COUNT; ++i) { - Path dir = path.resolve(Integer.toString(i) + ".dir"); - if (Files.exists(dir)) { - if (!dir.toFile().canRead()) { - throw new Exception(dir.toString() + ": don't have permission to read the directory"); - } else if (!Files.isDirectory(dir)) { - throw new Exception(dir.toString() + ": isn't a directory"); - } else { - loadDirectory(dir); + public void loadTable() { + try { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); + disk.clear(); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { + loadBucket(bucket); } } + size = disk.size(); + } catch (Exception e) { + System.err.println(e.getMessage()); + System.exit(1); } - size = disk.size(); } private static void writeItem(RandomAccessFile file, String word) throws Exception { @@ -195,61 +185,51 @@ private static void writeItem(RandomAccessFile file, String word) throws Excepti file.write(byteWord); } - private boolean saveFile(String filename, int dirIndex, int fileIndex) throws Exception { - boolean hasWritten = false; - RandomAccessFile file = new RandomAccessFile(filename, "rw"); + private boolean saveSubBucket(String subBucket, int bucketIndex, int subBucketIndex) throws Exception { + boolean isWritten = false; + RandomAccessFile file = new RandomAccessFile(subBucket, "rw"); for (String key : disk.keySet()) { int hashcode = key.hashCode(); int dirNumber = hashcode % DIRECTORIES_COUNT; int fileNumber = hashcode / DIRECTORIES_COUNT % FILES_COUNT; - if (dirIndex == dirNumber && fileIndex == fileNumber) { + if (bucketIndex == dirNumber && subBucketIndex == fileNumber) { writeItem(file, key); writeItem(file, disk.get(key)); - hasWritten = true; + isWritten = true; } } file.close(); - return hasWritten; + return isWritten; } - private void saveDirectory(Path dir, int dirIndex) throws Exception { + private void saveBucket(String bucket, int bucketIndex) throws Exception { int usedFiles = FILES_COUNT; - for (int i = 0; i < FILES_COUNT; ++i) { - Path file = dir.resolve(Integer.toString(i) + ".dat"); - if (Files.notExists(file)) { - if (!file.getParent().toFile().canWrite()) { - throw new Exception(file.toString() + ": don't have permission to create the file"); + for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { + String subBucket = getTableSubBucket(bucket, subBucketIndex); + if (PermissionsValidator.validate(subBucket, of(NOT_NULL, CREATE_FILE_IF_NOT_EXISTS, CAN_WRITE, IS_FILE))) { + if (!saveSubBucket(subBucket, bucketIndex, subBucketIndex)) { + new File(subBucket).delete(); + --usedFiles; } - Files.createFile(file); - } else if (!file.toFile().canWrite()) { - throw new Exception(file.toString() + ": don't have permission to write the file"); - } else if (!file.toFile().isFile()) { - throw new Exception(file.toString() + ": isn't a normal file"); - } - if (!saveFile(file.toString(), dirIndex, i)) { - Files.deleteIfExists(file); - --usedFiles; } } if (usedFiles == 0) { - Files.deleteIfExists(dir); + new File(bucket).delete(); } } - private void saveTable() throws Exception { - for (int i = 0; i < DIRECTORIES_COUNT; ++i) { - Path dir = path.resolve(Integer.toString(i) + ".dir"); - if (Files.notExists(dir)) { - if (!dir.getParent().toFile().canWrite()) { - throw new Exception(dir.toString() + ": don't have permission to create the directory"); + private void saveTable() { + try { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { + saveBucket(bucket, bucketIndex); } - Files.createDirectory(dir); - } else if (!dir.toFile().canWrite()) { - throw new Exception(dir.toString() + ": don't have permission to write the directory"); - } else if (!Files.isDirectory(dir)) { - throw new Exception(dir.toString() + ": isn't a directory"); } - saveDirectory(dir, i); + } catch (Exception e) { + System.err.println(e.getMessage()); + System.exit(1); } } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java similarity index 97% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/TableInterface.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java index 447d98b61..958b2f693 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableInterface.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.util.List; diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java new file mode 100644 index 000000000..4db9b0c7c --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java @@ -0,0 +1,121 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.HashMap; +import java.lang.IllegalArgumentException; +import java.lang.IllegalStateException; + +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + +public class TableManager implements TableManagerInterface { + private String dbFolder; + private Map nameToTableMap; + private String currentTable; + + public TableManager(String folder) throws IllegalArgumentException { + PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); + PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); + dbFolder = folder; + nameToTableMap = new HashMap<>(); + currentTable = null; + loadDatabase(); + } + + private boolean isValidName(String tableName) { + String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/"}; + for (String character : invalidCharacters) { + if (tableName.contains(character)) { + return false; + } + } + return true; + } + + private void loadDatabase() throws IllegalArgumentException { + for (String tableName : new File(dbFolder).list()) { + if (!isValidName(tableName)) { + throw new IllegalArgumentException(tableName + ": incorrect table name"); + } + Table table = new Table(this, tableName); + nameToTableMap.put(tableName, table); + table.loadTable(); + } + } + + private void checkUnsavedChanges() throws IllegalStateException { + int count = getCurrentTable().unsavedSize(); + if (count > 0) { + throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); + } + } + + public String getDbFolder() { + return dbFolder; + } + + public Table getTable(String name) throws IllegalArgumentException { + if (name == null) { + throw new IllegalArgumentException("no table"); + } + return nameToTableMap.get(name); + } + + public Table getCurrentTable() throws IllegalArgumentException { + return getTable(currentTable); + } + + public Table createTable(String name) throws IllegalArgumentException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect tablename"); + } else if (nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException("tablename exists"); + } + Table table = new Table(this, name); + nameToTableMap.put(name, table); + PermissionsValidator.validate(table.getTableFolder(), of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); + return table; + } + + public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalStateException("tablename not exist"); + } else if (name.equals(currentTable)) { + currentTable = null; + } + Utils.removeDir(Paths.get(dbFolder, name)); + nameToTableMap.remove(name); + } + + public String useTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException("tablename not exist"); + } else if (currentTable != null && !name.equals(currentTable)) { + checkUnsavedChanges(); + } + currentTable = name; + return currentTable; + } + + public Map showTables() { + Map tables = new HashMap<>(); + for (String tablename : nameToTableMap.keySet()) { + tables.put(tablename, nameToTableMap.get(tablename).size()); + } + return tables; + } + + public void exit() throws IllegalStateException { + if (currentTable != null) { + checkUnsavedChanges(); + } + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProviderInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java similarity index 96% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProviderInterface.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java index adf089a3c..e06eb17b2 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProviderInterface.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java @@ -1,8 +1,8 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.util.Map; -public interface TableProviderInterface { +public interface TableManagerInterface { /** * Возвращает таблицу с указанным названием. diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Utils.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java similarity index 93% rename from src/ru/fizteh/fivt/students/andrewzhernov/junit/Utils.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java index b909daeed..9378f45a6 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/Utils.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; +package ru.fizteh.fivt.students.andrewzhernov.database; import java.io.*; import java.nio.file.Files; diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/filemap/DataBase.java b/src/ru/fizteh/fivt/students/andrewzhernov/filemap/DataBase.java deleted file mode 100644 index 090404832..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/filemap/DataBase.java +++ /dev/null @@ -1,100 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.filemap; - -import java.io.File; -import java.io.RandomAccessFile; -import java.util.Map; -import java.util.HashMap; - -public class DataBase { - private Map dataBase; - private String dbPath; - - public DataBase(String name) throws Exception { - if (name == null) { - throw new Exception("Usage: java -Ddb.file= ..."); - } - dataBase = new HashMap(); - File dbFile = openFile(name); - if (dbFile.isDirectory()) { - throw new Exception("Can't create file, " + name + " is a directory"); - } else { - dbPath = dbFile.getCanonicalPath(); - if (dbFile.exists()) { - loadFromDisk(); - } - } - } - - private static File openFile(String name) throws Exception { - if (name.charAt(0) == File.separatorChar) { - return new File(name); - } else { - return new File(System.getProperty("user.dir") + File.separator + name); - } - } - - private String readFromDataBase(RandomAccessFile file) throws Exception { - int wordSize = file.readInt(); - byte[] word = new byte[wordSize]; - file.read(word, 0, wordSize); - return new String(word); - } - - private void writeToDataBase(RandomAccessFile file, String word) throws Exception { - file.writeInt(word.getBytes("UTF-8").length); - file.write(word.getBytes("UTF-8")); - } - - public void loadFromDisk() throws Exception { - RandomAccessFile file = new RandomAccessFile(dbPath, "r"); - while (file.getFilePointer() < file.length()) { - String key = readFromDataBase(file); - String value = readFromDataBase(file); - dataBase.put(key, value); - } - file.close(); - } - - public void saveToDisk() throws Exception { - RandomAccessFile file = new RandomAccessFile(dbPath, "rw"); - for (String key : dataBase.keySet()) { - writeToDataBase(file, key); - writeToDataBase(file, dataBase.get(key)); - } - file.close(); - } - - public void put(String key, String value) { - if (dataBase.containsKey(key)) { - System.out.println("overwrite"); - System.out.println(dataBase.get(key)); - dataBase.remove(key); - dataBase.put(key, value); - } else { - System.out.println("new"); - dataBase.put(key, value); - } - } - - public void get(String key) { - if (dataBase.containsKey(key)) { - System.out.println("found"); - System.out.println(dataBase.get(key)); - } else { - System.out.println("not found"); - } - } - - public void remove(String key) { - if (dataBase.containsKey(key)) { - System.out.println("removed"); - dataBase.remove(key); - } else { - System.out.println("not found"); - } - } - - public void list() { - System.out.println(String.join(", ", dataBase.keySet())); - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/filemap/FileMap.java b/src/ru/fizteh/fivt/students/andrewzhernov/filemap/FileMap.java deleted file mode 100644 index 1f24b8fe4..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/filemap/FileMap.java +++ /dev/null @@ -1,93 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.filemap; - -import java.util.Scanner; - -public class FileMap { - public static void main(String[] args) { - try { - DataBase dataBase = new DataBase(System.getProperty("db.file")); - if (args.length == 0) { - interactiveMode(dataBase); - } else { - batchMode(args, dataBase); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } - - public static void interactiveMode(DataBase dataBase) throws Exception { - Scanner input = new Scanner(System.in); - System.out.print("$ "); - while (input.hasNextLine()) { - try { - executeCommand(parseCommand(input.nextLine()), dataBase); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - System.out.print("$ "); - } - dataBase.saveToDisk(); - input.close(); - } - - public static void batchMode(String[] args, DataBase dataBase) throws Exception { - String[] input = parseInput(args); - for (String cmd : input) { - executeCommand(parseCommand(cmd), dataBase); - } - dataBase.saveToDisk(); - } - - private static String[] parseInput(String[] args) throws Exception { - StringBuilder input = new StringBuilder(); - for (String cmd : args) { - input.append(cmd).append(' '); - } - return input.toString().split("\\s*;\\s*"); - } - - private static String[] parseCommand(String cmd) throws Exception { - return cmd.trim().split("\\s+"); - } - - public static void executeCommand(String[] cmd, DataBase dataBase) throws Exception { - if (cmd.length > 0 && cmd[0].length() > 0) { - if (cmd[0].equals("put")) { - if (cmd.length != 3) { - throw new Exception("Usage: put "); - } - dataBase.put(cmd[1], cmd[2]); - } else if (cmd[0].equals("get")) { - if (cmd.length != 2) { - throw new Exception("Usage: get "); - } - dataBase.get(cmd[1]); - } else if (cmd[0].equals("remove")) { - if (cmd.length != 2) { - throw new Exception("Usage: remove "); - } - dataBase.remove(cmd[1]); - } else if (cmd[0].equals("list")) { - if (cmd.length != 1) { - throw new Exception("Usage: list"); - } - dataBase.list(); - } else if (cmd[0].equals("exit")) { - if (cmd.length != 1) { - throw new Exception("Usage: exit"); - } - try { - dataBase.saveToDisk(); - System.exit(0); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } else { - throw new Exception(cmd[0] + ": no such command"); - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/AbstractHandler.java b/src/ru/fizteh/fivt/students/andrewzhernov/junit/AbstractHandler.java deleted file mode 100644 index d8dd9ec8a..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/AbstractHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; - -interface AbstractHandler { - Object exec(TableProvider database, String[] args) throws Exception; - void print(Object returnValue) throws Exception; -} - diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/HandlerInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/junit/HandlerInterface.java deleted file mode 100644 index e82c9c9b7..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/HandlerInterface.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; - -public interface HandlerInterface { - Object execute(TableProvider database, String[] args) throws Exception; - void handle(Object object) throws Exception; -} - diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProvider.java b/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProvider.java deleted file mode 100644 index 604152863..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/junit/TableProvider.java +++ /dev/null @@ -1,116 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.junit; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; -import java.util.HashMap; - -public class TableProvider implements TableProviderInterface { - private Path path; - private Map database; - private String currentTable; - - private void load() throws Exception { - for (String tablename : path.toFile().list()) { - if (tablename.indexOf('/') != -1) { - throw new Exception(tablename + ": incorrect tablename"); - } - database.put(tablename, new Table(path.resolve(tablename))); - } - } - - private void checkUnsavedChanges() throws IllegalStateException { - int count = getCurrentTable().unsavedSize(); - if (count > 0) { - throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); - } - } - - public TableProvider(String dir) throws IllegalArgumentException { - if (dir == null) { - throw new IllegalArgumentException("The database directory wasn't specified"); - } - currentTable = null; - database = new HashMap(); - path = Paths.get(dir); - try { - if (Files.notExists(path)) { - if (!path.toFile().getCanonicalFile().getParentFile().canWrite()) { - throw new Exception(path.toString() + ": don't have permission to create the directory"); - } - Files.createDirectory(path); - } else if (!path.toFile().canRead()) { - throw new Exception(path.toString() + ": don't have permission to read the directory"); - } else if (!Files.isDirectory(path)) { - throw new IllegalArgumentException(dir + ": isn't a directory"); - } else { - load(); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } - - public Table getTable(String name) throws IllegalArgumentException { - if (name == null) { - throw new IllegalArgumentException("no table"); - } - return database.get(name); - } - - public Table getCurrentTable() throws IllegalArgumentException { - return getTable(currentTable); - } - - public Table createTable(String name) throws IllegalArgumentException { - if (name == null || name.indexOf('/') != -1) { - throw new IllegalArgumentException(name + ": incorrect tablename"); - } else if (database.containsKey(name)) { - throw new IllegalArgumentException("tablename exists"); - } - Table newTable = new Table(path.resolve(name)); - database.put(name, newTable); - return newTable; - } - - public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || name.indexOf('/') != -1) { - throw new IllegalArgumentException(name + ": incorrect tablename"); - } else if (!database.containsKey(name)) { - throw new IllegalStateException("tablename not exist"); - } else if (name.equals(currentTable)) { - currentTable = null; - } - Utils.removeDir(path.resolve(name)); - database.remove(name); - } - - public String useTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || name.indexOf('/') != -1) { - throw new IllegalArgumentException(name + ": incorrect tablename"); - } else if (!database.containsKey(name)) { - throw new IllegalArgumentException("tablename not exist"); - } else if (currentTable != null && !name.equals(currentTable)) { - checkUnsavedChanges(); - } - currentTable = name; - return currentTable; - } - - public Map showTables() { - Map tables = new HashMap<>(); - for (String tablename : database.keySet()) { - tables.put(tablename, database.get(tablename).size()); - } - return tables; - } - - public void exit() throws IllegalStateException { - if (currentTable != null) { - checkUnsavedChanges(); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/DataBase.java b/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/DataBase.java deleted file mode 100644 index 271a0ac00..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/DataBase.java +++ /dev/null @@ -1,199 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.multifilemap; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.io.RandomAccessFile; -import java.util.Map; -import java.util.HashMap; - -public class DataBase { - private static final int COUNT = 16; - - private Map recordsCount; - private Map table; - private String name; - private Path dir; - - public DataBase(String directory) throws Exception { - if (directory == null) { - throw new Exception("Usage: java -Dfizteh.db.dir= ..."); - } - recordsCount = new HashMap(); - table = new HashMap(); - dir = Paths.get(directory); - if (!Files.exists(dir)) { - Files.createDirectory(dir); - } else { - for (String tablename : dir.toFile().list()) { - reloadTable(tablename); - recordsCount.put(tablename, table.size()); - } - table.clear(); - } - name = null; - } - - private static String readString(RandomAccessFile file) throws Exception { - int wordSize = file.readInt(); - byte[] word = new byte[wordSize]; - file.read(word, 0, wordSize); - return new String(word); - } - - private static void writeString(RandomAccessFile file, String word) throws Exception { - file.writeInt(word.getBytes("UTF-8").length); - file.write(word.getBytes("UTF-8")); - } - - public void reloadTable(String tablename) throws Exception { - table.clear(); - name = tablename; - Path tablePath = dir.resolve(tablename); - for (int i = 0; i < COUNT; ++i) { - Path tableDir = tablePath.resolve(Integer.toString(i) + ".dir"); - if (Files.isDirectory(tableDir)) { - for (int j = 0; j < COUNT; ++j) { - Path tableFile = tableDir.resolve(Integer.toString(j) + ".dat"); - if (Files.exists(tableFile)) { - RandomAccessFile file = new RandomAccessFile(tableFile.toString(), "r"); - while (file.getFilePointer() < file.length()) { - String key = readString(file); - String value = readString(file); - table.put(key, value); - } - file.close(); - } - } - } - } - } - - public void saveTable() throws Exception { - if (name == null) { - return; - } - Path tablePath = dir.resolve(name); - for (int i = 0; i < COUNT; ++i) { - Path tableDir = tablePath.resolve(Integer.toString(i) + ".dir"); - if (!Files.isDirectory(tableDir)) { - Files.createDirectory(tableDir); - } - for (int j = 0; j < COUNT; ++j) { - Path tableFile = tableDir.resolve(Integer.toString(j) + ".dat"); - if (!Files.exists(tableFile)) { - Files.createFile(tableFile); - } - RandomAccessFile file = new RandomAccessFile(tableFile.toString(), "rw"); - boolean written = false; - for (String key : table.keySet()) { - int first = key.hashCode() % COUNT; - int second = key.hashCode() / COUNT % COUNT; - if (i == first && j == second) { - writeString(file, key); - writeString(file, table.get(key)); - written = true; - } - } - file.close(); - if (!written) { - Files.deleteIfExists(tableFile); - } - } - if (tableDir.toFile().list().length == 0) { - Files.deleteIfExists(tableDir); - } - } - } - - public void create(String tablename) throws Exception { - Path tablePath = dir.resolve(tablename); - if (Files.isDirectory(tablePath)) { - System.out.println("tablename exists"); - } else { - Files.createDirectory(tablePath); - recordsCount.put(tablename, 0); - System.out.println("created"); - } - } - - public void drop(String tablename) throws Exception { - Path tablePath = dir.resolve(tablename); - if (Files.isDirectory(tablePath)) { - Utils.remove(tablePath); - recordsCount.remove(tablename); - if (name != null && name.equals(tablename)) { - name = null; - } - System.out.println("dropped"); - } else { - System.out.println("tablename not exists"); - } - } - - public void use(String tablename) throws Exception { - Path tablePath = dir.resolve(tablename); - if (Files.isDirectory(tablePath)) { - if (name != null) { - saveTable(); - } - reloadTable(tablename); - System.out.println("using tablename"); - } else { - System.out.println("tablename not exists"); - } - } - - public void showTables() throws Exception { - for (String tablename : recordsCount.keySet()) { - System.out.format("%s %d\n", tablename, recordsCount.get(tablename)); - } - } - - public void put(String key, String value) throws Exception { - if (name == null) { - throw new Exception("The table is not selected"); - } - if (table.containsKey(key)) { - System.out.println("overwrite"); - System.out.println(table.get(key)); - table.put(key, value); - } else { - table.put(key, value); - recordsCount.put(name, recordsCount.get(name) + 1); - System.out.println("new"); - } - } - - public void get(String key) throws Exception { - if (name == null) { - throw new Exception("The table is not selected"); - } - if (table.containsKey(key)) { - System.out.println("found"); - System.out.println(table.get(key)); - } else { - System.out.println("not found"); - } - } - - public void remove(String key) throws Exception { - if (name == null) { - throw new Exception("The table is not selected"); - } - if (table.containsKey(key)) { - table.remove(key); - recordsCount.put(name, recordsCount.get(name) - 1); - System.out.println("removed"); - } else { - System.out.println("not found"); - } - } - - public void list() throws Exception { - if (name == null) { - throw new Exception("The table is not selected"); - } - System.out.println(String.join(", ", table.keySet())); - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/MultiFileMap.java b/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/MultiFileMap.java deleted file mode 100644 index bff38e279..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/MultiFileMap.java +++ /dev/null @@ -1,109 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.multifilemap; - -import java.util.Scanner; - -public class MultiFileMap { - public static void main(String[] args) { - try { - DataBase dataBase = new DataBase(System.getProperty("fizteh.db.dir")); - if (args.length == 0) { - interactiveMode(dataBase); - } else { - batchMode(args, dataBase); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } - - public static void interactiveMode(DataBase dataBase) throws Exception { - Scanner input = new Scanner(System.in); - System.out.print("$ "); - while (input.hasNextLine()) { - try { - executeCommand(parseCommand(input.nextLine()), dataBase); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - System.out.print("$ "); - } - dataBase.saveTable(); - input.close(); - } - - public static void batchMode(String[] args, DataBase dataBase) throws Exception { - String[] input = parseInput(args); - for (String cmd : input) { - executeCommand(parseCommand(cmd), dataBase); - } - dataBase.saveTable(); - } - - private static String[] parseInput(String[] args) throws Exception { - StringBuilder input = new StringBuilder(); - for (String cmd : args) { - input.append(cmd).append(';'); - } - return input.toString().split("\\s*;\\s*"); - } - - private static String[] parseCommand(String cmd) throws Exception { - return cmd.trim().split("\\s+"); - } - - public static void executeCommand(String[] cmd, DataBase dataBase) throws Exception { - if (cmd.length > 0 && cmd[0].length() > 0) { - if (cmd[0].equals("create")) { - if (cmd.length != 2) { - throw new Exception("Usage: create "); - } - dataBase.create(cmd[1]); - } else if (cmd[0].equals("drop")) { - if (cmd.length != 2) { - throw new Exception("Usage: drop "); - } - dataBase.drop(cmd[1]); - } else if (cmd[0].equals("use")) { - if (cmd.length != 2) { - throw new Exception("Usage: use "); - } - dataBase.use(cmd[1]); - } else if (cmd[0].equals("show")) { - if (cmd.length != 2) { - throw new Exception("Usage: show tables"); - } else if (cmd[1].equals("tables")) { - dataBase.showTables(); - } - } else if (cmd[0].equals("put")) { - if (cmd.length != 3) { - throw new Exception("Usage: put "); - } - dataBase.put(cmd[1], cmd[2]); - } else if (cmd[0].equals("get")) { - if (cmd.length != 2) { - throw new Exception("Usage: get "); - } - dataBase.get(cmd[1]); - } else if (cmd[0].equals("remove")) { - if (cmd.length != 2) { - throw new Exception("Usage: remove "); - } - dataBase.remove(cmd[1]); - } else if (cmd[0].equals("list")) { - if (cmd.length != 1) { - throw new Exception("Usage: list"); - } - dataBase.list(); - } else if (cmd[0].equals("exit")) { - if (cmd.length != 1) { - throw new Exception("Usage: exit"); - } - dataBase.saveTable(); - System.exit(0); - } else { - throw new Exception(cmd[0] + ": no such command"); - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/Utils.java b/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/Utils.java deleted file mode 100644 index 0c6d3ec9d..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/multifilemap/Utils.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.multifilemap; - -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; - -public class Utils { - static void remove(Path directory) throws Exception { - if (Files.isDirectory(directory)) { - try (DirectoryStream stream = Files.newDirectoryStream(directory)) { - for (Path entry : stream) { - remove(entry); - } - } - } - if (!directory.toFile().delete()) { - throw new Exception("Cannot delete " + directory.toString()); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Cat.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Cat.java deleted file mode 100644 index 1aaac475d..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Cat.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.OutputStream; - -public class Cat { - public static void execute(String[] args) throws Exception { - if (args.length != 2) { - throw new Exception("Usage: cat "); - } else { - File file = ChangeDir.openFile(args[1]); - if (file.isFile()) { - InputStream input = null; - try { - input = new FileInputStream(file); - printFile(input, System.out); - } catch (Exception e) { - throw new Exception("cat: can't read file"); - } finally { - input.close(); - } - } else if (file.isDirectory()) { - throw new Exception("cat: " + args[1] + " is a directory"); - } else { - throw new Exception("cat: " + args[1] + ": no such file or directory"); - } - } - } - - public static void printFile(InputStream input, OutputStream output) throws Exception { - int index; - byte[] buffer = new byte[4096]; - while ((index = input.read(buffer)) != -1) { - output.write(buffer, 0, index); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/ChangeDir.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/ChangeDir.java deleted file mode 100644 index d98dcd88d..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/ChangeDir.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; - -public class ChangeDir { - public static File openFile(String file) { - if (file.charAt(0) == File.separatorChar) { - return new File(file); - } else { - return new File(System.getProperty("user.dir") + File.separator + file); - } - } - - public static void execute(String[] args) throws Exception { - if (args.length != 2) { - throw new Exception("Usage: cd "); - } else { - File file = openFile(args[1]); - if (file.isDirectory()) { - System.setProperty("user.dir", file.getCanonicalPath()); - } else if (file.exists()) { - throw new Exception("cd: " + args[1] + ": isn't a directory"); - } else { - throw new Exception("cd: " + args[1] + ": no such file or directory"); - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Copy.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Copy.java deleted file mode 100644 index 4e9ddf2c4..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Copy.java +++ /dev/null @@ -1,73 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; - -public class Copy { - public static void execute(String[] args) throws Exception { - File source = null; - File destination = null; - if (args.length == 3) { - source = ChangeDir.openFile(args[1]); - destination = ChangeDir.openFile(args[2]); - if (source.isDirectory()) { - throw new Exception("cp: " + args[1] + " is a directory"); - } - } else if (args.length == 4 && args[1].equals("-r")) { - source = ChangeDir.openFile(args[2]); - destination = ChangeDir.openFile(args[3]); - } else { - throw new Exception("Usage: cp [-r] "); - } - if (source.equals(destination)) { - throw new Exception("cp: '" + source.getPath() + "' and '" + destination.getPath() + "' are the same"); - } - if (!source.exists()) { - throw new Exception("cp: " + source.getPath() + ": no such file or directory"); - } - if (destination.exists() && destination.isDirectory()) { - destination = new File(destination, source.getName()); - } - if (!destination.exists()) { - if (source.isDirectory() && !destination.mkdir() || source.isFile() && !destination.createNewFile()) { - throw new Exception("cp: " + destination.getPath() + ": no such file or directory"); - } - } - copy(source, destination); - } - - private static void copy(File source, File destination) throws Exception { - if (source.isFile()) { - copyFile(source, destination); - } else { - copyDir(source, destination); - } - } - - private static void copyFile(File source, File destination) throws Exception { - InputStream input = null; - OutputStream output = null; - - try { - input = new FileInputStream(source); - output = new FileOutputStream(destination); - Cat.printFile(input, output); - } catch (Exception e) { - throw new Exception("cp: can't read file"); - } finally { - input.close(); - output.close(); - } - } - - private static void copyDir(File source, File destination) throws Exception { - destination.mkdir(); - String[] list = source.list(); - for (String fileName : list) { - copy(new File(source, fileName), new File(destination, fileName)); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/List.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/List.java deleted file mode 100644 index 6574ffe42..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/List.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; - -public class List { - public static void execute(String[] args) throws Exception { - if (args.length != 1) { - throw new Exception("Usage: ls"); - } else { - File currentPath = new File(System.getProperty("user.dir")); - String[] list = currentPath.list(); - for (String fileName : list) { - if (fileName.charAt(0) != '.') { - System.out.println(fileName); - } - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/MakeDir.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/MakeDir.java deleted file mode 100644 index 2839a9fc5..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/MakeDir.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; - -public class MakeDir { - public static void execute(String[] args) throws Exception { - if (args.length != 2) { - throw new Exception("Usage: mkdir "); - } else { - File dir = ChangeDir.openFile(args[1]); - if (!dir.mkdir()) { - throw new Exception("mkdir: " + args[1] + ": can't create directory"); - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Move.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Move.java deleted file mode 100644 index 93e641a3b..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Move.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -public class Move { - public static void execute(String[] args) throws Exception { - if (args.length != 3) { - throw new Exception("Usage: mv "); - } else { - try { - String[] copyArgs = new String[4]; - copyArgs[0] = args[0]; - copyArgs[1] = "-r"; - copyArgs[2] = args[1]; - copyArgs[3] = args[2]; - Copy.execute(copyArgs); - - String[] removeArgs = new String[3]; - removeArgs[0] = args[0]; - removeArgs[1] = "-r"; - removeArgs[2] = args[1]; - Remove.execute(removeArgs); - } catch (Exception e) { - String newException = e.getMessage(); - newException = newException.replaceFirst("(cp|rm):", "mv:"); - throw new Exception(newException); - } - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Pwd.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Pwd.java deleted file mode 100644 index 704767b0a..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Pwd.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -public class Pwd { - public static void execute(String[] args) throws Exception { - if (args.length != 1) { - throw new Exception("Usage: pwd"); - } else { - System.out.println(System.getProperty("user.dir")); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Remove.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Remove.java deleted file mode 100644 index 431c1da03..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Remove.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.io.File; - -public class Remove { - public static void execute(String[] args) throws Exception { - File file = null; - if (args.length == 2) { - file = ChangeDir.openFile(args[1]); - if (file.isDirectory()) { - throw new Exception("rm: " + args[1] + " is a directory"); - } - } else if (args.length == 3 && args[1].equals("-r")) { - file = ChangeDir.openFile(args[2]); - } else { - throw new Exception("Usage: rm [-r] "); - } - - if (!file.exists()) { - throw new Exception("rm: " + file.getPath() + ": no such file or directory"); - } - - removeFile(file); - } - - public static void removeFile(File file) throws Exception { - boolean isRemoved = false; - if (file.isFile()) { - isRemoved = file.delete(); - } else { - String[] list = file.list(); - for (String son : list) { - removeFile(new File(file, son)); - } - isRemoved = file.delete(); - } - if (!isRemoved) { - throw new Exception("rm: " + file.getCanonicalPath() + ": can't remove"); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Shell.java b/src/ru/fizteh/fivt/students/andrewzhernov/shell/Shell.java deleted file mode 100644 index 0ea475c55..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/shell/Shell.java +++ /dev/null @@ -1,87 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.shell; - -import java.util.Scanner; - -public class Shell { - public static void main(String[] args) { - if (args.length == 0) { - interactiveMode(); - } else { - packageMode(args); - } - } - - public static void interactiveMode() { - Scanner input = null; - try { - input = new Scanner(System.in); - System.out.print("$ "); - while (input.hasNextLine()) { - try { - executeCommand(parseCommand(input.nextLine())); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - System.out.print("$ "); - } - } catch (Exception e) { - System.err.println(e.getMessage()); - } finally { - System.out.println(); - input.close(); - } - } - - public static void packageMode(String[] args) { - String[] input = parseInput(args); - int isError = 0; - for (String cmd : input) { - try { - executeCommand(parseCommand(cmd)); - } catch (Exception e) { - System.err.println(e.getMessage()); - isError = 1; - } - } - System.exit(isError); - } - - private static String[] parseInput(String[] args) { - StringBuilder input = new StringBuilder(); - for (String cmd : args) { - input.append(cmd).append(' '); - } - return input.toString().split("\\s*;\\s*"); - } - - private static String[] parseCommand(String cmd) { - return cmd.split("\\s+"); - } - - private static void executeCommand(String[] cmd) throws Exception { - if (cmd.length > 0 && cmd[0].length() > 0) { - if (cmd[0].equals("cd")) { - ChangeDir.execute(cmd); - } else if (cmd[0].equals("mkdir")) { - MakeDir.execute(cmd); - } else if (cmd[0].equals("pwd")) { - Pwd.execute(cmd); - } else if (cmd[0].equals("rm")) { - Remove.execute(cmd); - } else if (cmd[0].equals("cp")) { - Copy.execute(cmd); - } else if (cmd[0].equals("mv")) { - Move.execute(cmd); - } else if (cmd[0].equals("ls")) { - List.execute(cmd); - } else if (cmd[0].equals("cat")) { - Cat.execute(cmd); - } else if (cmd[0].equals("exit")) { - System.exit(0); - } else { - throw new Exception("Shell: " + cmd[0] + ": no such command"); - } - } - } - -} diff --git a/tasks/01-Shell.md b/tasks/01-Shell.md deleted file mode 100644 index a4c6dbda2..000000000 --- a/tasks/01-Shell.md +++ /dev/null @@ -1,113 +0,0 @@ -## Shell (Оболочка) -Консольное приложение, частично эмулирующее оболочку -[shell](http://en.wikipedia.org/wiki/Unix_shell). - -``` -java Shell [COMMAND1 [; COMMAND2 ...]] -``` - -Если Shell запускается без параметров, то запускается интерактивный режим, -в котором пользователь может ввести команду (или команды) прямо в консоли. - -Список команд: -* ```cd ``` — change directory, смена -текущей директории. Поддерживаются ```.```, ```..```, относительные и абсолютные -пути -* ```mkdir ``` — создание директории в текущей директории -* ```pwd``` — print working directory, печатает абсолютный путь к текущей -директории -* ```rm [-r] ``` — удаляет указанную в параметрах файл или папку. Если указать параметр ```-r``` то удаляется рекурсивно. -* ```cp [-r] ``` — копирует указанную в параметрах -папку/файл в указанное место. Параметр ```-r``` позволяет копировать рекурсивно. -* ```mv ``` — переносит указанный файл/папку в -новое место (файл на прежнем месте удаляется). В частности переименовывает -файл/папку, если ```source``` и ```destination``` находятся в одной папке -* ```ls``` — печатает содержимое текущей директории -* ```exit``` — выход из приложения -* ```cat ``` — выводит содержимое файла на экран. - -За один раз можно написать несколько команд. Разделителем команд является -```;``` (точка с запятой). - -### Интерактивный режим -В интерактивном режиме должно отображаться "приглашение" — ```$ ``` (знак -доллара и пробел), после которого производится ввод команд. Также в интерактивном -режиме допускается вывод текущей директории перед "приглашением". - -### Пакетный режим -Если запустить консольное приложение с параметрами, то параметры должны -интерпретироваться как команды (все команды должны склеиваться через пробел). -Приложение должно выполнить последовательно все команды и завершиться. - -В случае ошибки команды, приложение должно выводить ошибку в stderr. - -В случае ошибки в любой из команд, приложение должно написать сообщение об -ошибке и завершиться с ненулевым кодом. - -### Вывод команд -Вывод команд имеет строгий формат. Никакого дополнительного вывода в stdout быть -не должно (в финальном коде вся debug-информация должна отсутствовать). - -```(bash) -$ cd /noexistingdir -cd: '/noexistingdir': No such file or directory -$ cd /home/user -$ pwd -/home/user -$ ls -Folder -file.txt -$ mkdir MyFolder -$ ls -Folder -MyFolder -file.txt -$ cp file.txt MyFolder -$ cd MyFolder -$ ls -file.txt -$ mv file.txt file2.txt -$ ls -file2.txt -$ rm file.txt -rm: cannot remove 'file.txt': No such file or directory -$ rm file2.txt -$ ls -$ cd .. -$ ls -Folder -MyFolder -file.txt -$ mv Folder MyFolder -$ ls -MyFolder -file.txt -$ cp MyFolder NewFolder -cp: MyFolder is a directory (not copied). -$ ls -Folder -MyFolder -file.txt -$ cp -r MyFolder NewFolder -$ ls -Folder -MyFolder -NewFolder -file.txt -$ rm NewFolder -rm: NewFolder: is a directory -$ ls -Folder -MyFolder -NewFolder -file.txt -$ rm -r NewFolder -$ ls -Folder -MyFolder -file.txt -$ cat file.text -Hello World! -$ cat wrongfile.txt -cat: wrongfile.txt: No such file or directory -$ exit From 45ade3bd8453be68ad147f808920067647e1a921 Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Tue, 18 Nov 2014 13:01:38 +0300 Subject: [PATCH 2/7] fixed bugs and added Table tests --- .../andrewzhernov/database/Command.java | 4 +- .../{HandlerInterface.java => Handler.java} | 2 +- .../students/andrewzhernov/database/Main.java | 26 ++-- .../database/PermissionsValidator.java | 23 +++- .../andrewzhernov/database/Table.java | 9 +- .../{TableInterface.java => TableImpl.java} | 2 +- .../andrewzhernov/database/TableManager.java | 13 +- ...erInterface.java => TableManagerImpl.java} | 26 +--- .../database/tests/TableTest.java | 129 ++++++++++++++++++ 9 files changed, 176 insertions(+), 58 deletions(-) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{HandlerInterface.java => Handler.java} (83%) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{TableInterface.java => TableImpl.java} (98%) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{TableManagerInterface.java => TableManagerImpl.java} (58%) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java index 973d9324f..df7b8c53c 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java @@ -3,9 +3,9 @@ public class Command { private String name; private int numArgs; - private HandlerInterface processor; + private Handler processor; - public Command(String name, int numArgs, HandlerInterface processor) { + public Command(String name, int numArgs, Handler processor) { this.name = name; this.numArgs = numArgs; this.processor = processor; diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java similarity index 83% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java index 7edda1b27..a23b50489 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HandlerInterface.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java @@ -1,6 +1,6 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -public interface HandlerInterface { +public interface Handler { Object execute(TableManager database, String[] args) throws Exception; void handle(Object object) throws Exception; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java index 6d0e62381..e72a0d6a0 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java @@ -8,7 +8,7 @@ public static void main(String[] args) { try { TableManager database = new TableManager(System.getProperty("fizteh.db.dir")); Shell shell = new Shell(database, new Command[] { - new Command("size", 1, new HandlerInterface() { + new Command("size", 1, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().size(); @@ -18,7 +18,7 @@ public void handle(Object object) throws Exception { System.out.println((Integer) object); } }), - new Command("put", 3, new HandlerInterface() { + new Command("put", 3, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().put(args[1], args[2]); @@ -34,7 +34,7 @@ public void handle(Object object) throws Exception { } } }), - new Command("get", 2, new HandlerInterface() { + new Command("get", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().get(args[1]); @@ -50,7 +50,7 @@ public void handle(Object object) throws Exception { } } }), - new Command("remove", 2, new HandlerInterface() { + new Command("remove", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().remove(args[1]); @@ -64,7 +64,7 @@ public void handle(Object object) throws Exception { } } }), - new Command("list", 1, new HandlerInterface() { + new Command("list", 1, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().list(); @@ -76,7 +76,7 @@ public void handle(Object object) throws Exception { System.out.println(String.join(", ", list)); } }), - new Command("commit", 1, new HandlerInterface() { + new Command("commit", 1, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().commit(); @@ -86,7 +86,7 @@ public void handle(Object object) throws Exception { System.out.println((Integer) object); } }), - new Command("rollback", 1, new HandlerInterface() { + new Command("rollback", 1, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.getCurrentTable().rollback(); @@ -96,7 +96,7 @@ public void handle(Object object) throws Exception { System.out.println((Integer) object); } }), - new Command("create", 2, new HandlerInterface() { + new Command("create", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.createTable(args[1]); @@ -106,7 +106,7 @@ public void handle(Object object) throws Exception { System.out.println("created"); } }), - new Command("drop", 2, new HandlerInterface() { + new Command("drop", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { database.removeTable(args[1]); @@ -117,17 +117,17 @@ public void handle(Object object) throws Exception { System.out.println("dropped"); } }), - new Command("use", 2, new HandlerInterface() { + new Command("use", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.useTable(args[1]); } @Override public void handle(Object object) throws Exception { - System.out.println("using tablename"); + System.out.println("using " + (String) object); } }), - new Command("show tables", 2, new HandlerInterface() { + new Command("show tables", 2, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { return database.showTables(); @@ -141,7 +141,7 @@ public void handle(Object object) throws Exception { } } }), - new Command("exit", 1, new HandlerInterface() { + new Command("exit", 1, new Handler() { @Override public Object execute(TableManager database, String[] args) throws Exception { database.exit(); diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java index fc857f271..02ebecbde 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java @@ -18,21 +18,27 @@ public enum Permissions { IS_DIRECTORY; } - public static boolean validateTableName(String tableName, EnumSet perms) throws IllegalArgumentException { + public static boolean validateTableName(String tableName, EnumSet perms) + throws IllegalArgumentException + { if (perms.contains(NOT_NULL) && tableName == null) { throw new IllegalArgumentException("Table name hasn't been specified"); } return true; } - public static boolean validateDbFolder(String dbFolder, EnumSet perms) throws IllegalArgumentException { + public static boolean validateDbFolder(String dbFolder, EnumSet perms) + throws IllegalArgumentException + { if (perms.contains(NOT_NULL) && dbFolder == null) { throw new IllegalArgumentException("Database name hasn't been specified"); } return true; } - public static boolean validate(String fileName, EnumSet perms) throws IllegalArgumentException { + public static boolean validate(String fileName, EnumSet perms) + throws IllegalArgumentException + { if (perms.contains(NOT_NULL) && fileName == null) { throw new IllegalArgumentException("File name hasn't been specified"); } @@ -40,7 +46,10 @@ public static boolean validate(String fileName, EnumSet perms) thro if (perms.contains(EXISTS) && !file.exists()) { return false; } - if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) || perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) && !file.exists()) { + if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) || + perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) && + !file.exists()) + { try { File parentFile = file.getCanonicalFile().getParentFile(); if (!parentFile.canWrite()) { @@ -57,10 +66,12 @@ public static boolean validate(String fileName, EnumSet perms) thro } } if (perms.contains(CAN_READ) && !file.canRead()) { - throw new IllegalArgumentException(file.getPath() + ": don't have permission to read the file"); + throw new IllegalArgumentException(file.getPath() + + ": don't have permission to read the file"); } if (perms.contains(CAN_WRITE) && !file.canWrite()) { - throw new IllegalArgumentException(file.getPath() + ": don't have permission to write the file"); + throw new IllegalArgumentException(file.getPath() + + ": don't have permission to write the file"); } if (perms.contains(IS_FILE) && !file.isFile()) { throw new IllegalArgumentException(file.getPath() + ": isn't a normal file"); diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 10c5ed93e..74bbb3f2a 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -1,9 +1,11 @@ package ru.fizteh.fivt.students.andrewzhernov.database; +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + import java.io.*; import java.lang.Integer; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.io.RandomAccessFile; import java.util.List; @@ -11,10 +13,7 @@ import java.util.Map; import java.util.HashMap; -import static java.util.EnumSet.of; -import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; - -public class Table implements TableInterface { +public class Table implements TableImpl { private static final int DIRECTORIES_COUNT = 16; private static final int FILES_COUNT = 16; diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java similarity index 98% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java index 958b2f693..3ea178425 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableInterface.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java @@ -2,7 +2,7 @@ import java.util.List; -public interface TableInterface { +public interface TableImpl { /** * Возвращает название базы данных. diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java index 4db9b0c7c..81c480a98 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java @@ -1,5 +1,8 @@ package ru.fizteh.fivt.students.andrewzhernov.database; +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + import java.io.*; import java.nio.file.Files; import java.nio.file.Path; @@ -12,7 +15,7 @@ import static java.util.EnumSet.of; import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; -public class TableManager implements TableManagerInterface { +public class TableManager implements TableManagerImpl { private String dbFolder; private Map nameToTableMap; private String currentTable; @@ -71,9 +74,9 @@ public Table getCurrentTable() throws IllegalArgumentException { public Table createTable(String name) throws IllegalArgumentException { if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect tablename"); + throw new IllegalArgumentException(name + ": incorrect table name"); } else if (nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException("tablename exists"); + throw new IllegalArgumentException(name + " exists"); } Table table = new Table(this, name); nameToTableMap.put(name, table); @@ -85,7 +88,7 @@ public void removeTable(String name) throws IllegalArgumentException, IllegalSta if (name == null || !isValidName(name)) { throw new IllegalArgumentException(name + ": incorrect table name"); } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalStateException("tablename not exist"); + throw new IllegalStateException(name + " not exist"); } else if (name.equals(currentTable)) { currentTable = null; } @@ -97,7 +100,7 @@ public String useTable(String name) throws IllegalArgumentException, IllegalStat if (name == null || !isValidName(name)) { throw new IllegalArgumentException(name + ": incorrect table name"); } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException("tablename not exist"); + throw new IllegalArgumentException(name + " not exist"); } else if (currentTable != null && !name.equals(currentTable)) { checkUnsavedChanges(); } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java similarity index 58% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java index e06eb17b2..f0e6fbe6c 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerInterface.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java @@ -2,7 +2,7 @@ import java.util.Map; -public interface TableManagerInterface { +public interface TableManagerImpl { /** * Возвращает таблицу с указанным названием. @@ -30,28 +30,4 @@ public interface TableManagerInterface { * @throws IllegalStateException Если таблицы с указанным названием не существует. */ void removeTable(String name); - - /** - * Устанавливает таблицу с указанным названием в качестве текущей. - * - * @param name Название таблицы. - * @return Имя текущей таблицы. Если таблица не выбрана, возвращает null. - * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. - * @throws IllegalStateException Если предыдущая таблица имеет несохранённые изменения. - */ - String useTable(String name); - - /** - * Список таблиц с их размером. - * - * @return Список пар ключ-значение: имя таблицы, количество ключей. - */ - Map showTables(); - - /** - * Выход. - * - * @throws IllegalStateException Если текущая таблица имеет несохранённые изменения. - */ - void exit(); } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java new file mode 100644 index 000000000..308bed629 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java @@ -0,0 +1,129 @@ +package ru.fizteh.fivt.students.andrewzhernov.database.tests; + +import ru.fizteh.fivt.students.andrewzhernov.database.*; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.After; +import org.junit.Test; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.util.List; +import java.util.LinkedList; + +public class TableTest { + TableManager manager; + Table table; + + @Before + public void setUp() throws Exception { + manager = new TableManager("data"); + table = manager.createTable("test_table"); + } + + @After + public void tearDown() throws Exception { + manager.removeTable("test_table"); + } + + @Test(expected = IllegalArgumentException.class) + public void testTableConstructorThrowsExceptionLoadedInvalidDirectory() { + new Table(manager, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testTablePutInvalidKey() { + table.put(null, ""); + } + + @Test(expected = IllegalArgumentException.class) + public void testTablePutInvalidValue() { + table.put("", null); + } + + @Test(expected = IllegalArgumentException.class) + public void testTableGetInvalidKey() { + table.get(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testTableRemoveInvalidKey() { + table.remove(null); + } + + @Test + public void testTableSize() { + table.put("1", "xyz"); + assertEquals(1, table.size()); + table.put("1", "abc"); + assertEquals(1, table.size()); + table.put("2", "bca"); + assertEquals(2, table.size()); + table.remove("1"); + assertEquals(1, table.size()); + table.remove("2"); + assertEquals(0, table.size()); + } + + @Test + public void testTablePut() { + table.put("1", "xyz"); + assertNull(table.put("2", "abc")); + assertEquals("xyz", table.put("1", "zyx")); + assertEquals("abc", table.put("2", "cba")); + } + + @Test + public void testTableGet() { + table.put("1", "xyz"); + assertEquals("xyz", table.get("1")); + assertNull(table.get("2")); + table.put("2", "abc"); + assertEquals("abc", table.get("2")); + } + + @Test + public void testTableRemove() { + table.put("1", "xyz"); + table.put("2", "abc"); + assertEquals("abc", table.remove("2")); + assertEquals("xyz", table.remove("1")); + assertNull("1-error", table.remove("1")); + assertNull("2-error", table.remove("2")); + } + + @Test + public void testTableList() { + table.put("1", "xyz"); + List list = new LinkedList<>(); + list.add("1"); + assertEquals(list, table.list()); + table.put("2", "abc"); + list.add("2"); + assertEquals(list, table.list()); + } + + @Test + public void testTableCommit() { + table.put("1", "abc"); + assertEquals(1, table.commit()); + table.put("2", "efg"); + table.put("1", "bca"); + table.remove("1"); + assertEquals(2, table.commit()); + } + + @Test + public void testTableRollback() { + table.put("1", "xyz"); + table.commit(); + table.put("1", "abc"); + assertEquals(1, table.rollback()); + + table.put("2", "efg"); + table.remove("1"); + assertEquals(2, table.rollback()); + } +} From 7f94a10fd1b5c01522d302fed04831f44fe0f151 Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Wed, 19 Nov 2014 19:00:33 +0300 Subject: [PATCH 3/7] fixed checkstyle warnings --- LICENSE | 24 +++ README.md | 35 ++++ build.xml | 60 +++++++ github-workflow.md | 16 ++ lib/checkstyle-rules.xml | 157 ++++++++++++++++++ lib/checkstyle-suppress.xml | 7 + .../database/PermissionsValidator.java | 28 ++-- .../andrewzhernov/database/Table.java | 5 +- .../andrewzhernov/database/TableManager.java | 12 +- .../database/TableManagerImpl.java | 2 - .../database/tests/TableTest.java | 3 - 11 files changed, 316 insertions(+), 33 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.xml create mode 100644 github-workflow.md create mode 100644 lib/checkstyle-rules.xml create mode 100644 lib/checkstyle-suppress.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f87793701 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2014, Dmitriy Komanov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 000000000..9508b8ea4 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Программирование на языке JAVA + +Курс лекций и семинаров для студентов второго курса ФизТеха, факультет +[ФИВТ](http://fivt.fizteh.ru) на 2014 год. + +## Семинары +В папке [tasks](tasks) находятся описания заданий на +семинары. Для написания программ необходимо руководствоваться [соглашениями +по оформлению кода](http://www.oracle.com/technetwork/java/codeconvtoc-136057.html) +от Oracle (Code Conventions). + +В документе [github-workflow](github-workflow.md) описаны +общие правила по ведению задач на github, а также упрощение жизни с помощью утилиты +[checkstyle](http://checkstyle.sourceforge.net/), которая проверяет код на соответствие +Code Conventions. + +## Лекции +1. [Java, JVM, примитивные типы](https://yadi.sk/d/_ZgbGf9NbXraZ) +2. [Object, String, исключения](https://yadi.sk/d/MCu6krbtbXrgZ) +3. [IO Stream, инициализация объектов, enum](https://yadi.sk/d/MinWJhG0bteEr) +4. [Лямбды, аннотации, обобщения, коллекции](https://yadi.sk/d/hxkoP81rbteTp) +5. [Reflection, Proxy, JUnit](https://yadi.sk/i/Ku-C6VYOc4icJ) +6. [Сериализация, XML, JSON](https://yadi.sk/d/8upuEG2ecRknU) +7. [Многопоточность](https://yadi.sk/i/FsBB-AXVcRkoH) +8. [Многопоточность](https://yadi.sk/i/IY2kKLoacgaBr) + +## Список литературы +* Брюс Эккель — Философия Java (Thinking in Java) +* Brian Goetz — Java Concurrency in Practive (продвинутая книжка +по многопоточности в Java) +* [Спецификации по JVM и JLS](http://docs.oracle.com/javase/specs/index.html) +* [Code Conventions](http://www.oracle.com/technetwork/java/codeconv-138413.html) + +## Успеваемость +[Таблица на Google Docs](https://docs.google.com/spreadsheet/ccc?key=0Ag7dwrMmZBKedGk1Q1RfSXh1RzZyeEJhaE94dW1CaGc&usp=sharing) diff --git a/build.xml b/build.xml new file mode 100644 index 000000000..3fd09f7f4 --- /dev/null +++ b/build.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/github-workflow.md b/github-workflow.md new file mode 100644 index 000000000..41a464c2c --- /dev/null +++ b/github-workflow.md @@ -0,0 +1,16 @@ +## Workflow работы с github + +Каждый студент делает [fork](https://help.github.com/articles/fork-a-repo) репозитория ```fizteh-java-2014```. Работа +ведётся в собственном репозитории, в директории ```src/ru/fizteh/fivt/students//```. + +После того, как задание выполнено и протестировано в собственном репозитории, необходимо создать +[pull request](https://help.github.com/articles/using-pull-requests) в репозиторий ```fizteh-java-2014```. В заголовке +к pull request необходимо написать: ```Имя Фамилия, № группы, задание``` (например, ```Василий Иванов, 123, Shell```). +Также необходимо назначить pull request на своего семинариста. + +В одном pull request должно быть решение только одной задачи. Если хочется сдавать параллельно несколько заданий, +необходимо создавать бранчи и делать pull request из бранчей. + +Чтобы сократить количество итераций на проверку задания, полезно самостоятельно удостовериться, что Code Conventions +соблюдены. Для этого нужно запустить сборку проекта с помощью ```ant checkstyle```. Вместо этого можно поставить +соответствующий плагин в IDE и настроить этот плагин на конфигурационный файл ```lib/checkstyle-rules.xml```. diff --git a/lib/checkstyle-rules.xml b/lib/checkstyle-rules.xml new file mode 100644 index 000000000..c73c18923 --- /dev/null +++ b/lib/checkstyle-rules.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/checkstyle-suppress.xml b/lib/checkstyle-suppress.xml new file mode 100644 index 000000000..2608395c6 --- /dev/null +++ b/lib/checkstyle-suppress.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java index 02ebecbde..c1989fdbe 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java @@ -19,8 +19,7 @@ public enum Permissions { } public static boolean validateTableName(String tableName, EnumSet perms) - throws IllegalArgumentException - { + throws IllegalArgumentException { if (perms.contains(NOT_NULL) && tableName == null) { throw new IllegalArgumentException("Table name hasn't been specified"); } @@ -28,8 +27,7 @@ public static boolean validateTableName(String tableName, EnumSet p } public static boolean validateDbFolder(String dbFolder, EnumSet perms) - throws IllegalArgumentException - { + throws IllegalArgumentException { if (perms.contains(NOT_NULL) && dbFolder == null) { throw new IllegalArgumentException("Database name hasn't been specified"); } @@ -37,8 +35,7 @@ public static boolean validateDbFolder(String dbFolder, EnumSet per } public static boolean validate(String fileName, EnumSet perms) - throws IllegalArgumentException - { + throws IllegalArgumentException { if (perms.contains(NOT_NULL) && fileName == null) { throw new IllegalArgumentException("File name hasn't been specified"); } @@ -46,15 +43,14 @@ public static boolean validate(String fileName, EnumSet perms) if (perms.contains(EXISTS) && !file.exists()) { return false; } - if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) || - perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) && - !file.exists()) - { + if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) + || perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) + && !file.exists()) { try { File parentFile = file.getCanonicalFile().getParentFile(); if (!parentFile.canWrite()) { - throw new IllegalArgumentException(fileName + - ": don't have permission to create the directory or normal file"); + throw new IllegalArgumentException(fileName + + ": don't have permission to create the directory or normal file"); } if (perms.contains(CREATE_FILE_IF_NOT_EXISTS)) { Files.createFile(file.toPath()); @@ -66,12 +62,12 @@ public static boolean validate(String fileName, EnumSet perms) } } if (perms.contains(CAN_READ) && !file.canRead()) { - throw new IllegalArgumentException(file.getPath() + - ": don't have permission to read the file"); + throw new IllegalArgumentException(file.getPath() + + ": don't have permission to read the file"); } if (perms.contains(CAN_WRITE) && !file.canWrite()) { - throw new IllegalArgumentException(file.getPath() + - ": don't have permission to write the file"); + throw new IllegalArgumentException(file.getPath() + + ": don't have permission to write the file"); } if (perms.contains(IS_FILE) && !file.isFile()) { throw new IllegalArgumentException(file.getPath() + ": isn't a normal file"); diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 74bbb3f2a..78ffe6cd8 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -4,8 +4,6 @@ import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; import java.io.*; -import java.lang.Integer; -import java.nio.file.Files; import java.nio.file.Paths; import java.io.RandomAccessFile; import java.util.List; @@ -222,7 +220,8 @@ private void saveTable() { PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { String bucket = getTableBucket(bucketIndex); - if (PermissionsValidator.validate(bucket, of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { + if (PermissionsValidator.validate(bucket, + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { saveBucket(bucket, bucketIndex); } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java index 81c480a98..013593d0b 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java @@ -1,21 +1,15 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -import static java.util.EnumSet.of; -import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; - import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.HashMap; -import java.lang.IllegalArgumentException; -import java.lang.IllegalStateException; import static java.util.EnumSet.of; import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; public class TableManager implements TableManagerImpl { + private final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; private String dbFolder; private Map nameToTableMap; private String currentTable; @@ -30,7 +24,6 @@ public TableManager(String folder) throws IllegalArgumentException { } private boolean isValidName(String tableName) { - String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/"}; for (String character : invalidCharacters) { if (tableName.contains(character)) { return false; @@ -80,7 +73,8 @@ public Table createTable(String name) throws IllegalArgumentException { } Table table = new Table(this, name); nameToTableMap.put(name, table); - PermissionsValidator.validate(table.getTableFolder(), of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); + PermissionsValidator.validate(table.getTableFolder(), + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); return table; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java index f0e6fbe6c..138b386c4 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java @@ -1,7 +1,5 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -import java.util.Map; - public interface TableManagerImpl { /** diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java index 308bed629..add9fe320 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/tests/TableTest.java @@ -7,9 +7,6 @@ import org.junit.After; import org.junit.Test; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.Path; import java.util.List; import java.util.LinkedList; From 7a24c9dfa0d402c7d681dbbc0434c6344bcaa372 Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Sun, 21 Dec 2014 15:19:32 +0300 Subject: [PATCH 4/7] renamed files --- build.xml | 3 + .../andrewzhernov/database/ArrayStorable.java | 82 +++++ .../database/ColumnFormatException.java | 23 ++ .../andrewzhernov/database/Handler.java | 1 - .../andrewzhernov/database/HashMapTable.java | 234 +++++++++++++ .../database/HashMapTableManager.java | 125 +++++++ .../andrewzhernov/database/Index.java | 26 ++ .../andrewzhernov/database/IndexManager.java | 30 ++ .../students/andrewzhernov/database/Main.java | 52 +-- .../database/PermissionsValidator.java | 53 ++- .../andrewzhernov/database/Storable.java | 96 ++++++ .../andrewzhernov/database/Table.java | 315 +++++------------- .../andrewzhernov/database/TableImpl.java | 71 ---- .../andrewzhernov/database/TableManager.java | 224 ++++++------- .../database/TableManagerImpl.java | 31 -- 15 files changed, 861 insertions(+), 505 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/Index.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java diff --git a/build.xml b/build.xml index 3fd09f7f4..e1a79f0f2 100644 --- a/build.xml +++ b/build.xml @@ -29,6 +29,9 @@ + + + diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java new file mode 100644 index 000000000..e4ae46453 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java @@ -0,0 +1,82 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import java.util.List; +import java.util.ArrayList; + +public class ArrayStorable implements Storable { + private Object[] columnValues; + + public ArrayStorable(List values) { + columnValues = values.toArray(); + } + + @Override + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + if (value.getClass() != columnValues[columnIndex].getClass()) { + throw new ColumnFormatException("Invalid column format: expected " + columnValues[columnIndex].getClass().getName() ", got " value.getClass().getName()); + } + columnValues[columnIndex] = value; + } + + @Override + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + return columnValues[columnIndex]; + } + + @Override + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Integer)) { + throw new ColumnFormatException("Column is not Integer"); + } + return (Integer) columnValues[columnIndex]; + } + + @Override + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Long)) { + throw new ColumnFormatException("Column is not Long"); + } + return (Long) columnValues[columnIndex]; + } + + @Override + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Byte)) { + throw new ColumnFormatException("Column is not Byte"); + } + return (Byte) columnValues[columnIndex]; + } + + @Override + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Float)) { + throw new ColumnFormatException("Column is not Float"); + } + return (Float) columnValues[columnIndex]; + } + + @Override + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Double)) { + throw new ColumnFormatException("Column is not Double"); + } + return (Double) columnValues[columnIndex]; + } + + @Override + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof Boolean)) { + throw new ColumnFormatException("Column is not Boolean"); + } + return (Boolean) columnValues[columnIndex]; + } + + @Override + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + if (!(columnValues[columnIndex] instanceof String)) { + throw new ColumnFormatException("Column is not String"); + } + return (String) columnValues[columnIndex]; + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java new file mode 100644 index 000000000..72a550dfd --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java @@ -0,0 +1,23 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +/** + * Бросается при попытке извлечь из колонки {@link Storeable} значение не соответствующего типа, + * либо подставить в колонку значение несоответствующего типа. + */ +public class ColumnFormatException extends IllegalArgumentException { + + public ColumnFormatException() { + } + + public ColumnFormatException(String s) { + super(s); + } + + public ColumnFormatException(String message, Throwable cause) { + super(message, cause); + } + + public ColumnFormatException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java index a23b50489..da7cdb6c7 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java @@ -4,4 +4,3 @@ public interface Handler { Object execute(TableManager database, String[] args) throws Exception; void handle(Object object) throws Exception; } - diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java new file mode 100644 index 000000000..0b7fb5c2a --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java @@ -0,0 +1,234 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + +import java.io.*; +import java.nio.file.Paths; +import java.io.RandomAccessFile; +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; + +public class HashMapTable implements Table { + private static final int DIRECTORIES_COUNT = 16; + private static final int FILES_COUNT = 16; + + private TableManager manager; + private String name; + private int size; + private Map disk; + private Map diff; + private List> columnTypes; + + public HashMapTable(TableManager tableManager, String tableName) { + PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); + manager = tableManager; + name = tableName; + size = 0; + disk = new HashMap<>(); + diff = new HashMap<>(); + } + + public String getName() { + return name; + } + + public int getNumberOfUncommittedChanges() { + return diff.size(); + } + + public int size() { + return size; + } + + public Storable put(String key, Storable value) throws IllegalArgumentException { + if (key == null || value == null) { + throw new IllegalArgumentException("Invalid key/value"); + } + Storable diffValue = diff.put(key, value); + String result = diffValue != null ? diffValue : disk.get(key); + if (result == null) { + ++size; + } + return result; + } + + public Storable get(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("Invalid key"); + } + Storable diffValue = diff.get(key); + return diffValue != null ? diffValue : disk.get(key); + } + + public Storable remove(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("Invalid key"); + } + Storable result = null; + Storable diskValue = disk.get(key); + if (diskValue != null) { + Storable diffValue = diff.put(key, null); + result = diffValue != null ? diffValue : diskValue; + } else { + result = diff.remove(key); + } + if (result != null) { + --size; + } + return result; + } + + public List list() { + List list = new LinkedList(); + for (String key : disk.keySet()) { + if (!diff.containsKey(key)) { + list.add(key); + } + } + for (String key : diff.keySet()) { + if (diff.get(key) != null) { + list.add(key); + } + } + return list; + } + + public int commit() { + int amount = diff.size(); + for (String key : diff.keySet()) { + Storable value = diff.get(key); + if (value != null) { + disk.put(key, value); + } else { + disk.remove(key); + } + } + diff.clear(); + saveTable(); + return amount; + } + + public int rollback() { + int amount = diff.size(); + diff.clear(); + size = disk.size(); + return amount; + } + + public String getTableFolder() { + return Paths.get(manager.getDbFolder(), name).toString(); + } + + public String getTableBucket(int bucket) { + return Paths.get(getTableFolder(), new Integer(bucket).toString() + ".dir").toString(); + } + + public String getTableSubBucket(String bucket, int subBucket) { + return Paths.get(bucket, new Integer(subBucket).toString() + ".dat").toString(); + } + + private static String readItem(RandomAccessFile file) throws Exception { + int wordSize = file.readInt(); + byte[] word = new byte[wordSize]; + file.read(word, 0, wordSize); + return new String(word, "UTF-8"); + } + + private void loadSubBucket(String subBucket) throws Exception { + RandomAccessFile file = new RandomAccessFile(subBucket, "r"); + while (file.getFilePointer() < file.length()) { + try { + String key = readItem(file); + String value = readItem(file); + disk.put(key, value); + } catch (Exception | OutOfMemoryError e) { + throw new Exception(subBucket + ": invalid file format"); + } + } + file.close(); + } + + private void loadBucket(String bucket) throws Exception { + for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { + String subBucket = getTableSubBucket(bucket, subBucketIndex); + if (PermissionsValidator.validate(subBucket, of(NOT_NULL, EXISTS, CAN_READ, IS_FILE))) { + loadSubBucket(subBucket); + } + } + } + + public void loadTable() { + try { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); + disk.clear(); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { + loadBucket(bucket); + } + } + size = disk.size(); + } catch (Exception e) { + System.err.println(e.getMessage()); + System.exit(1); + } + } + + private static void writeItem(RandomAccessFile file, String word) throws Exception { + byte[] byteWord = word.getBytes("UTF-8"); + file.writeInt(byteWord.length); + file.write(byteWord); + } + + private boolean saveSubBucket(String subBucket, int bucketIndex, int subBucketIndex) throws Exception { + boolean isWritten = false; + RandomAccessFile file = new RandomAccessFile(subBucket, "rw"); + for (String key : disk.keySet()) { + int hashcode = key.hashCode(); + int dirNumber = hashcode % DIRECTORIES_COUNT; + int fileNumber = hashcode / DIRECTORIES_COUNT % FILES_COUNT; + if (bucketIndex == dirNumber && subBucketIndex == fileNumber) { + writeItem(file, key); + writeItem(file, disk.get(key)); + isWritten = true; + } + } + file.close(); + return isWritten; + } + + private void saveBucket(String bucket, int bucketIndex) throws Exception { + int usedFiles = FILES_COUNT; + for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { + String subBucket = getTableSubBucket(bucket, subBucketIndex); + if (PermissionsValidator.validate(subBucket, of(NOT_NULL, CREATE_FILE_IF_NOT_EXISTS, CAN_WRITE, IS_FILE))) { + if (!saveSubBucket(subBucket, bucketIndex, subBucketIndex)) { + new File(subBucket).delete(); + --usedFiles; + } + } + } + if (usedFiles == 0) { + new File(bucket).delete(); + } + } + + private void saveTable() { + try { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { + saveBucket(bucket, bucketIndex); + } + } + } catch (Exception e) { + System.err.println(e.getMessage()); + System.exit(1); + } + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java new file mode 100644 index 000000000..ef41e9b4c --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java @@ -0,0 +1,125 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import java.io.*; +import java.nio.file.Paths; +import java.lang.Long; +import java.util.Arrays; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.LinkedList; + +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; + +public class HashMapTableManager implements TableManager { + private static final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; + private static final List> types = Arrays.asList(new Integer().getClass(), new Long().getClass(), Byte, Float, Double, Boolean, String); + + private String dbFolder; + private Map nameToTableMap; + private String currentTable; + + public HashMapTableManager(String folder) throws IllegalArgumentException { + PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); + PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); + dbFolder = folder; + nameToTableMap = new HashMap<>(); + currentTable = null; + loadDatabase(); + } + + private boolean isValidName(String tableName) { + for (String character : invalidCharacters) { + if (tableName.contains(character)) { + return false; + } + } + return true; + } + + private void loadDatabase() throws IllegalArgumentException { + for (String tableName : new File(dbFolder).list()) { + if (!isValidName(tableName)) { + throw new IllegalArgumentException(tableName + ": incorrect table name"); + } + Table table = new HashMapTable(this, tableName); + nameToTableMap.put(tableName, table); + table.loadTable(); + } + } + + private void checkUnsavedChanges() throws IllegalStateException { + int count = getCurrentTable().getNumberOfUncommittedChanges(); + if (count > 0) { + throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); + } + } + + public String getDbFolder() { + return dbFolder; + } + + public Table getTable(String name) throws IllegalArgumentException { + if (name == null) { + throw new IllegalArgumentException("no table"); + } + return nameToTableMap.get(name); + } + + public Table getCurrentTable() throws IllegalArgumentException { + return getTable(currentTable); + } + + public Table createTable(String name, List> columnTypes) throws IllegalArgumentException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException(name + " exists"); + } + + Table table = new Table(this, name); + nameToTableMap.put(name, table); + PermissionsValidator.validate(table.getTableFolder(), + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); + return table; + } + + public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalStateException(name + " not exist"); + } else if (name.equals(currentTable)) { + currentTable = null; + } + Utils.removeDir(Paths.get(dbFolder, name)); + nameToTableMap.remove(name); + } + + public String useTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException(name + " not exist"); + } else if (currentTable != null && !name.equals(currentTable)) { + checkUnsavedChanges(); + } + currentTable = name; + return currentTable; + } + + public List getTableNames() { + return new LinkedList<>().addAll(nameToTableMap.keySet()); + } + + public createFor(Table table) { + + } + + public void exit() throws IllegalStateException { + if (currentTable != null) { + checkUnsavedChanges(); + } + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Index.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Index.java new file mode 100644 index 000000000..c986fd97d --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Index.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +/** + * Представляет базу для интерфейс для работы с таблицей, содержащей ключи-значения. + * Используется также для доступа к поисковому индексу. + */ +public interface Index { + /** + * Возвращает название таблицы или индекса. + * + * @return Название таблицы. + */ + String getName(); + + /** + * Получает значение по указанному ключу. + * + * @param key Ключ для поиска значения. Не может быть null. + * Для индексов по не-строковым полям аргумент представляет собой сериализованное значение колонки. + * Его потребуется распарсить. + * @return Значение. Если не найдено, возвращает null. + * + * @throws IllegalArgumentException Если значение параметра key является null. + */ + Storeable get(String key); +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java new file mode 100644 index 000000000..fd112a114 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java @@ -0,0 +1,30 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +/** + * Дополнительный интерфейс к {@link ru.fizteh.fivt.storage.structured.TableProvider}, позволяющий создавать индексы на + * основе имеющейся таблицы. + */ +public interface IndexManager extends TableManager { + /** + * + * Возвращает индекс с таким именем, если он существует. + * + * @param name Имя индекса. Не должно быть null. + * @return Объект индекса + * @throws IllegalArgumentException Если имя индекса невалидно. + */ + Index getIndex(String name) throws IllegalArgumentException; + + /** + * Создает индекс. + * + * @param table Таблица, для которой нужно создать индекс. + * @param column Номер колонки. + * @param name Имя для будущего индекса. + * @return Свежесозданный индекс. null, если индекс уже был создан. + * @throws IllegalArgumentException Если таблица невалидна, указана неверная колонка или имя индекса, + * а также, если сущность с таким именем уже существует. + * @throws IllegalStateException Если индекс содержит невалидные элементы. + */ + Index createIndex(Table table, int column, String name) throws IllegalArgumentException, IllegalStateException; +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java index e72a0d6a0..f3f5d88db 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java @@ -6,12 +6,12 @@ public class Main { public static void main(String[] args) { try { - TableManager database = new TableManager(System.getProperty("fizteh.db.dir")); - Shell shell = new Shell(database, new Command[] { + TableManager manager = new HashMapTableManager(System.getProperty("fizteh.db.dir")); + Shell shell = new Shell(manager, new Command[] { new Command("size", 1, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().size(); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().size(); } @Override public void handle(Object object) throws Exception { @@ -20,8 +20,8 @@ public void handle(Object object) throws Exception { }), new Command("put", 3, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().put(args[1], args[2]); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().put(args[1], args[2]); } @Override public void handle(Object object) throws Exception { @@ -36,8 +36,8 @@ public void handle(Object object) throws Exception { }), new Command("get", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().get(args[1]); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().get(args[1]); } @Override public void handle(Object object) throws Exception { @@ -52,8 +52,8 @@ public void handle(Object object) throws Exception { }), new Command("remove", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().remove(args[1]); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().remove(args[1]); } @Override public void handle(Object object) throws Exception { @@ -66,8 +66,8 @@ public void handle(Object object) throws Exception { }), new Command("list", 1, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().list(); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().list(); } @Override public void handle(Object object) throws Exception { @@ -78,8 +78,8 @@ public void handle(Object object) throws Exception { }), new Command("commit", 1, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().commit(); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().commit(); } @Override public void handle(Object object) throws Exception { @@ -88,8 +88,8 @@ public void handle(Object object) throws Exception { }), new Command("rollback", 1, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.getCurrentTable().rollback(); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.getCurrentTable().rollback(); } @Override public void handle(Object object) throws Exception { @@ -98,8 +98,8 @@ public void handle(Object object) throws Exception { }), new Command("create", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.createTable(args[1]); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.createTable(args[1]); } @Override public void handle(Object object) throws Exception { @@ -108,8 +108,8 @@ public void handle(Object object) throws Exception { }), new Command("drop", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - database.removeTable(args[1]); + public Object execute(TableManager manager, String[] args) throws Exception { + manager.removeTable(args[1]); return null; } @Override @@ -119,8 +119,8 @@ public void handle(Object object) throws Exception { }), new Command("use", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.useTable(args[1]); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.useTable(args[1]); } @Override public void handle(Object object) throws Exception { @@ -129,8 +129,8 @@ public void handle(Object object) throws Exception { }), new Command("show tables", 2, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - return database.showTables(); + public Object execute(TableManager manager, String[] args) throws Exception { + return manager.showTables(); } @Override public void handle(Object object) throws Exception { @@ -143,8 +143,8 @@ public void handle(Object object) throws Exception { }), new Command("exit", 1, new Handler() { @Override - public Object execute(TableManager database, String[] args) throws Exception { - database.exit(); + public Object execute(TableManager manager, String[] args) throws Exception { + manager.exit(); return null; } @Override diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java index c1989fdbe..bc511cc43 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java @@ -1,7 +1,8 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -import java.util.EnumSet; import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; +import java.util.EnumSet; + import java.io.File; import java.nio.file.Files; import java.io.IOException; @@ -19,61 +20,53 @@ public enum Permissions { } public static boolean validateTableName(String tableName, EnumSet perms) - throws IllegalArgumentException { + throws RuntimeException { if (perms.contains(NOT_NULL) && tableName == null) { - throw new IllegalArgumentException("Table name hasn't been specified"); + throw new RuntimeException("Table name hasn't been specified"); } return true; } public static boolean validateDbFolder(String dbFolder, EnumSet perms) - throws IllegalArgumentException { + throws RuntimeException { if (perms.contains(NOT_NULL) && dbFolder == null) { - throw new IllegalArgumentException("Database name hasn't been specified"); + throw new RuntimeException("Database name hasn't been specified"); } return true; } - public static boolean validate(String fileName, EnumSet perms) - throws IllegalArgumentException { + public static boolean validate(String fileName, + EnumSet perms) throws RuntimeException, IOException { if (perms.contains(NOT_NULL) && fileName == null) { - throw new IllegalArgumentException("File name hasn't been specified"); + throw new RuntimeException("File name hasn't been specified"); } File file = new File(fileName); if (perms.contains(EXISTS) && !file.exists()) { return false; } - if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) - || perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) - && !file.exists()) { - try { - File parentFile = file.getCanonicalFile().getParentFile(); - if (!parentFile.canWrite()) { - throw new IllegalArgumentException(fileName - + ": don't have permission to create the directory or normal file"); - } - if (perms.contains(CREATE_FILE_IF_NOT_EXISTS)) { - Files.createFile(file.toPath()); - } else { - Files.createDirectory(file.toPath()); - } - } catch (IOException e) { - System.out.println(file.toString() + ": Input-Output exception"); + if ((perms.contains(CREATE_FILE_IF_NOT_EXISTS) || perms.contains(CREATE_DIRECTORY_IF_NOT_EXISTS)) + && !file.exists()) { + File parentFile = file.getCanonicalFile().getParentFile(); + if (!parentFile.canWrite()) { + throw new RuntimeException(fileName + ": don't have permission to create the file"); + } + if (perms.contains(CREATE_FILE_IF_NOT_EXISTS)) { + Files.createFile(file.toPath()); + } else { + Files.createDirectory(file.toPath()); } } if (perms.contains(CAN_READ) && !file.canRead()) { - throw new IllegalArgumentException(file.getPath() - + ": don't have permission to read the file"); + throw new RuntimeException(file.getPath() + ": don't have permission to read the file"); } if (perms.contains(CAN_WRITE) && !file.canWrite()) { - throw new IllegalArgumentException(file.getPath() - + ": don't have permission to write the file"); + throw new RuntimeException(file.getPath() + ": don't have permission to write the file"); } if (perms.contains(IS_FILE) && !file.isFile()) { - throw new IllegalArgumentException(file.getPath() + ": isn't a normal file"); + throw new RuntimeException(file.getPath() + ": isn't a normal file"); } if (perms.contains(IS_DIRECTORY) && !file.isDirectory()) { - throw new IllegalArgumentException(file.getPath() + ": isn't a directory"); + throw new RuntimeException(file.getPath() + ": isn't a directory"); } return true; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java new file mode 100644 index 000000000..a983cd847 --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java @@ -0,0 +1,96 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +/** + * Список фиксированной структуры, строка таблицы {@link Table}. + * + * Нумерация колонок с нуля. Позиция в списке соответствует колонке таблицы под тем же номером. + * + * С помощью {@link TableProvider} может быть сериализован или десериализован. + * + * Для получения объекта из нужной колонки воспользуйтесь соответствующим геттером. + * Для установки объекта а колонку воспользуйтесь {@link #setColumnAt(int, Object)} . + */ +public interface Storable { + + /** + * Установить значение в колонку + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @param value - значение, которое нужно установить. + * Может быть null. + * Тип значения должен соответствовать декларированному типу колонки. + * @throws ColumnFormatException - Тип значения не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, не приводя его к конкретному типу. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, без приведения типа. Может быть null. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Integer. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Integer. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Long. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Long. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Byte. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Byte. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Float. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Float. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Double. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Double. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к Boolean. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к Boolean. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает значение из данной колонки, приведя его к String. + * @param columnIndex - индекс колонки в таблице, начиная с нуля + * @return - значение в этой колонке, приведенное к String. Может быть null. + * @throws ColumnFormatException - Запрошенный тип не соответствует типу колонки. + * @throws IndexOutOfBoundsException - Неверный индекс колонки. + */ + String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException; +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 78ffe6cd8..44fa34493 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -1,233 +1,92 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -import static java.util.EnumSet.of; -import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; - -import java.io.*; -import java.nio.file.Paths; -import java.io.RandomAccessFile; +import java.io.IOException; import java.util.List; -import java.util.LinkedList; -import java.util.Map; -import java.util.HashMap; - -public class Table implements TableImpl { - private static final int DIRECTORIES_COUNT = 16; - private static final int FILES_COUNT = 16; - - private TableManager manager; - private String name; - private int size; - private Map disk; - private Map diff; - - public Table(TableManager tableManager, String tableName) { - PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); - manager = tableManager; - name = tableName; - size = 0; - disk = new HashMap<>(); - diff = new HashMap<>(); - } - - public String getName() { - return name; - } - - public int unsavedSize() { - return diff.size(); - } - - public int size() { - return size; - } - - public String put(String key, String value) throws IllegalArgumentException { - if (key == null || value == null) { - throw new IllegalArgumentException("Invalid key/value"); - } - String diffValue = diff.put(key, value); - String result = diffValue != null ? diffValue : disk.get(key); - if (result == null) { - ++size; - } - return result; - } - - public String get(String key) throws IllegalArgumentException { - if (key == null) { - throw new IllegalArgumentException("Invalid key"); - } - String diffValue = diff.get(key); - return diffValue != null ? diffValue : disk.get(key); - } - - public String remove(String key) throws IllegalArgumentException { - if (key == null) { - throw new IllegalArgumentException("Invalid key"); - } - String result = null; - String diskValue = disk.get(key); - if (diskValue != null) { - String diffValue = diff.put(key, null); - result = diffValue != null ? diffValue : diskValue; - } else { - result = diff.remove(key); - } - if (result != null) { - --size; - } - return result; - } - - public List list() { - List list = new LinkedList(); - for (String key : disk.keySet()) { - if (!diff.containsKey(key)) { - list.add(key); - } - } - for (String key : diff.keySet()) { - if (diff.get(key) != null) { - list.add(key); - } - } - return list; - } - - public int commit() { - int amount = diff.size(); - for (String key : diff.keySet()) { - String value = diff.get(key); - if (value != null) { - disk.put(key, value); - } else { - disk.remove(key); - } - } - diff.clear(); - saveTable(); - return amount; - } - - public int rollback() { - int amount = diff.size(); - diff.clear(); - size = disk.size(); - return amount; - } - - public String getTableFolder() { - return Paths.get(manager.getDbFolder(), name).toString(); - } - - public String getTableBucket(int bucket) { - return Paths.get(getTableFolder(), new Integer(bucket).toString() + ".dir").toString(); - } - - public String getTableSubBucket(String bucket, int subBucket) { - return Paths.get(bucket, new Integer(subBucket).toString() + ".dat").toString(); - } - - private static String readItem(RandomAccessFile file) throws Exception { - int wordSize = file.readInt(); - byte[] word = new byte[wordSize]; - file.read(word, 0, wordSize); - return new String(word, "UTF-8"); - } - - private void loadSubBucket(String subBucket) throws Exception { - RandomAccessFile file = new RandomAccessFile(subBucket, "r"); - while (file.getFilePointer() < file.length()) { - try { - String key = readItem(file); - String value = readItem(file); - disk.put(key, value); - } catch (Exception | OutOfMemoryError e) { - throw new Exception(subBucket + ": invalid file format"); - } - } - file.close(); - } - - private void loadBucket(String bucket) throws Exception { - for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { - String subBucket = getTableSubBucket(bucket, subBucketIndex); - if (PermissionsValidator.validate(subBucket, of(NOT_NULL, EXISTS, CAN_READ, IS_FILE))) { - loadSubBucket(subBucket); - } - } - } - - public void loadTable() { - try { - PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); - disk.clear(); - for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { - String bucket = getTableBucket(bucketIndex); - if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { - loadBucket(bucket); - } - } - size = disk.size(); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } - - private static void writeItem(RandomAccessFile file, String word) throws Exception { - byte[] byteWord = word.getBytes("UTF-8"); - file.writeInt(byteWord.length); - file.write(byteWord); - } - - private boolean saveSubBucket(String subBucket, int bucketIndex, int subBucketIndex) throws Exception { - boolean isWritten = false; - RandomAccessFile file = new RandomAccessFile(subBucket, "rw"); - for (String key : disk.keySet()) { - int hashcode = key.hashCode(); - int dirNumber = hashcode % DIRECTORIES_COUNT; - int fileNumber = hashcode / DIRECTORIES_COUNT % FILES_COUNT; - if (bucketIndex == dirNumber && subBucketIndex == fileNumber) { - writeItem(file, key); - writeItem(file, disk.get(key)); - isWritten = true; - } - } - file.close(); - return isWritten; - } - - private void saveBucket(String bucket, int bucketIndex) throws Exception { - int usedFiles = FILES_COUNT; - for (int subBucketIndex = 0; subBucketIndex < FILES_COUNT; ++subBucketIndex) { - String subBucket = getTableSubBucket(bucket, subBucketIndex); - if (PermissionsValidator.validate(subBucket, of(NOT_NULL, CREATE_FILE_IF_NOT_EXISTS, CAN_WRITE, IS_FILE))) { - if (!saveSubBucket(subBucket, bucketIndex, subBucketIndex)) { - new File(subBucket).delete(); - --usedFiles; - } - } - } - if (usedFiles == 0) { - new File(bucket).delete(); - } - } - private void saveTable() { - try { - PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); - for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { - String bucket = getTableBucket(bucketIndex); - if (PermissionsValidator.validate(bucket, - of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { - saveBucket(bucket, bucketIndex); - } - } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); - } - } +/** + * Представляет интерфейс для работы с таблицей, содержащей ключи-значения. Ключи должны быть уникальными. + * + * Транзакционность: изменения фиксируются или откатываются с помощью методов {@link #commit()} или {@link #rollback()}, + * соответственно. Предполагается, что между вызовами этих методов никаких операций ввода-вывода не происходит. + * + * Данный интерфейс не является потокобезопасным. + */ +public interface Table extends Index { + + /** + * Устанавливает значение по указанному ключу. + * + * @param key Ключ для нового значения. Не может быть null. + * @param value Новое значение. Не может быть null. + * @return Значение, которое было записано по этому ключу ранее. Если ранее значения не было записано, + * возвращает null. + * + * @throws IllegalArgumentException Если значение параметров key или value является null. + * @throws ColumnFormatException - при попытке передать Storable с колонками другого типа. + */ + Storable put(String key, Storable value) throws ColumnFormatException; + + /** + * Удаляет значение по указанному ключу. + * + * @param key Ключ для поиска значения. Не может быть null. + * @return Предыдущее значение. Если не найдено, возвращает null. + * + * @throws IllegalArgumentException Если значение параметра key является null. + */ + Storable remove(String key); + + /** + * Возвращает количество ключей в таблице. Возвращает размер текущей версии, с учётом незафиксированных изменений. + * + * @return Количество ключей в таблице. + */ + int size(); + + /** + * Выводит список ключей таблицы, с учётом незафиксированных изменений. + * + * @return Список ключей. + */ + List list(); + + /** + * Выполняет фиксацию изменений. + * + * @return Число записанных изменений. + * + * @throws java.io.IOException если произошла ошибка ввода/вывода. Целостность таблицы не гарантируется. + */ + int commit() throws IOException; + + /** + * Выполняет откат изменений с момента последней фиксации. + * + * @return Число откаченных изменений. + */ + int rollback(); + + /** + * Возвращает количество изменений, ожидающих фиксации. + * + * @return Количество изменений, ожидающих фиксации. + */ + int getNumberOfUncommittedChanges(); + + /** + * Возвращает количество колонок в таблице. + * + * @return Количество колонок в таблице. + */ + int getColumnsCount(); + + /** + * Возвращает тип значений в колонке. + * + * @param columnIndex Индекс колонки. Начинается с нуля. + * @return Класс, представляющий тип значения. + * + * @throws IndexOutOfBoundsException - неверный индекс колонки + */ + Class getColumnType(int columnIndex) throws IndexOutOfBoundsException; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java deleted file mode 100644 index 3ea178425..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.database; - -import java.util.List; - -public interface TableImpl { - - /** - * Возвращает название базы данных. - */ - String getName(); - - /** - * Возвращает количество ключей в таблице. - * - * @return Количество ключей в таблице. - */ - int size(); - - /** - * Устанавливает значение по указанному ключу. - * - * @param key Ключ. - * @param value Значение. - * @return Значение, которое было записано по этому ключу ранее. Если ранее значения не было записано, - * возвращает null. - * - * @throws IllegalArgumentException Если значение параметров key или value является null. - */ - String put(String key, String value); - - /** - * Получает значение по указанному ключу. - * - * @param key Ключ. - * @return Значение. Если не найдено, возвращает null. - * - * @throws IllegalArgumentException Если значение параметра key является null. - */ - String get(String key); - - /** - * Удаляет значение по указанному ключу. - * - * @param key Ключ. - * @return Значение. Если не найдено, возвращает null. - * - * @throws IllegalArgumentException Если значение параметра key является null. - */ - String remove(String key); - - /** - * Выводит список ключей таблицы - * - * @return Список ключей. - */ - List list(); - - /** - * Выполняет фиксацию изменений. - * - * @return Количество сохранённых ключей. - */ - int commit(); - - /** - * Выполняет откат изменений с момента последней фиксации. - * - * @return Количество отменённых ключей. - */ - int rollback(); -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java index 013593d0b..0c44be4cd 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java @@ -1,118 +1,106 @@ -package ru.fizteh.fivt.students.andrewzhernov.database; - -import java.io.*; -import java.nio.file.Paths; -import java.util.Map; -import java.util.HashMap; - -import static java.util.EnumSet.of; -import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; - -public class TableManager implements TableManagerImpl { - private final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; - private String dbFolder; - private Map nameToTableMap; - private String currentTable; - - public TableManager(String folder) throws IllegalArgumentException { - PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); - PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); - dbFolder = folder; - nameToTableMap = new HashMap<>(); - currentTable = null; - loadDatabase(); - } - - private boolean isValidName(String tableName) { - for (String character : invalidCharacters) { - if (tableName.contains(character)) { - return false; - } - } - return true; - } - - private void loadDatabase() throws IllegalArgumentException { - for (String tableName : new File(dbFolder).list()) { - if (!isValidName(tableName)) { - throw new IllegalArgumentException(tableName + ": incorrect table name"); - } - Table table = new Table(this, tableName); - nameToTableMap.put(tableName, table); - table.loadTable(); - } - } - - private void checkUnsavedChanges() throws IllegalStateException { - int count = getCurrentTable().unsavedSize(); - if (count > 0) { - throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); - } - } - - public String getDbFolder() { - return dbFolder; - } - - public Table getTable(String name) throws IllegalArgumentException { - if (name == null) { - throw new IllegalArgumentException("no table"); - } - return nameToTableMap.get(name); - } - - public Table getCurrentTable() throws IllegalArgumentException { - return getTable(currentTable); - } - - public Table createTable(String name) throws IllegalArgumentException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException(name + " exists"); - } - Table table = new Table(this, name); - nameToTableMap.put(name, table); - PermissionsValidator.validate(table.getTableFolder(), - of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); - return table; - } - - public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalStateException(name + " not exist"); - } else if (name.equals(currentTable)) { - currentTable = null; - } - Utils.removeDir(Paths.get(dbFolder, name)); - nameToTableMap.remove(name); - } - - public String useTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException(name + " not exist"); - } else if (currentTable != null && !name.equals(currentTable)) { - checkUnsavedChanges(); - } - currentTable = name; - return currentTable; - } - - public Map showTables() { - Map tables = new HashMap<>(); - for (String tablename : nameToTableMap.keySet()) { - tables.put(tablename, nameToTableMap.get(tablename).size()); - } - return tables; - } - - public void exit() throws IllegalStateException { - if (currentTable != null) { - checkUnsavedChanges(); - } - } -} +package ru.fizteh.fivt.students.andrewzhernov.database; + +import java.io.IOException; +import java.text.ParseException; +import java.util.List; + +/** + * Управляющий класс для работы с {@link Table таблицами} + * + * Предполагает, что актуальная версия с устройства хранения, сохраняется при создании + * экземпляра объекта. Далее ввод-вывод выполняется только в момент создания и удаления + * таблиц. + * + * Данный интерфейс не является потокобезопасным. + */ +public interface TableManager { + + /** + * Возвращает таблицу с указанным названием. + * + * Последовательные вызовы метода с одинаковыми аргументами должны возвращать один и тот же объект таблицы, + * если он не был удален с помощью {@link #removeTable(String)}. + * + * @param name Название таблицы. + * @return Объект, представляющий таблицу. Если таблицы с указанным именем не существует, возвращает null. + * + * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. + */ + Table getTable(String name); + + /** + * Создаёт таблицу с указанным названием. + * Создает новую таблицу. Совершает необходимые дисковые операции. + * + * @param name Название таблицы. + * @param columnTypes Типы колонок таблицы. Не может быть пустой. + * @return Объект, представляющий таблицу. Если таблица с указанным именем существует, возвращает null. + * + * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. Если список типов + * колонок null или содержит недопустимые значения. + * @throws java.io.IOException При ошибках ввода/вывода. + */ + Table createTable(String name, List> columnTypes) throws IOException; + + /** + * Удаляет существующую таблицу с указанным названием. + * + * Объект удаленной таблицы, если был кем-то взят с помощью {@link #getTable(String)}, + * с этого момента должен бросать {@link IllegalStateException}. + * + * @param name Название таблицы. + * + * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. + * @throws IllegalStateException Если таблицы с указанным названием не существует. + * @throws java.io.IOException - при ошибках ввода/вывода. + */ + void removeTable(String name) throws IOException; + + /** + * Преобразовывает строку в объект {@link Storable}, соответствующий структуре таблицы. + * + * @param table Таблица, которой должен принадлежать {@link Storable}. + * @param value Строка, из которой нужно прочитать {@link Storable}. + * @return Прочитанный {@link Storable}. + * + * @throws ParseException - при каких-либо несоответстиях в прочитанных данных. + */ + Storable deserialize(Table table, String value) throws ParseException; + + /** + * Преобразовывает объект {@link Storable} в строку. + * + * @param table Таблица, которой должен принадлежать {@link Storable}. + * @param value {@link Storable}, который нужно записать. + * @return Строка с записанным значением. + * + * @throws ColumnFormatException При несоответствии типа в {@link Storable} и типа колонки в таблице. + */ + String serialize(Table table, Storable value) throws ColumnFormatException; + + /** + * Создает новый пустой {@link Storable} для указанной таблицы. + * + * @param table Таблица, которой должен принадлежать {@link Storable}. + * @return Пустой {@link Storable}, нацеленный на использование с этой таблицей. + */ + Storable createFor(Table table); + + /** + * Создает новый {@link Storable} для указанной таблицы, подставляя туда переданные значения. + * + * @param table Таблица, которой должен принадлежать {@link Storable}. + * @param values Список значений, которыми нужно проинициализировать поля Storable. + * @return {@link Storable}, проинициализированный переданными значениями. + * @throws ColumnFormatException При несоответствии типа переданного значения и колонки. + * @throws IndexOutOfBoundsException При несоответствии числа переданных значений и числа колонок. + */ + Storable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает имена существующих таблиц, которые могут быть получены с помощью {@link #getTable(String)}. + * + * @return Имена существующих таблиц. + */ + List getTableNames(); +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java deleted file mode 100644 index 138b386c4..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManagerImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.database; - -public interface TableManagerImpl { - - /** - * Возвращает таблицу с указанным названием. - * - * @param name Название таблицы. - * @return Объект, представляющий таблицу. Если таблицы с указанным именем не существует, возвращает null. - * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. - */ - Table getTable(String name); - - /** - * Создаёт таблицу с указанным названием. - * - * @param name Название таблицы. - * @return Объект, представляющий таблицу. Если таблица уже существует, возвращает null. - * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. - */ - Table createTable(String name); - - /** - * Удаляет таблицу с указанным названием. - * - * @param name Название таблицы. - * @throws IllegalArgumentException Если название таблицы null или имеет недопустимое значение. - * @throws IllegalStateException Если таблицы с указанным названием не существует. - */ - void removeTable(String name); -} From 7d34c864a8dd131ec3d5588c552aac5cd698d763 Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Thu, 25 Dec 2014 05:07:46 +0300 Subject: [PATCH 5/7] storeable added --- .../database/ColumnFormatException.java | 4 +- .../andrewzhernov/database/Command.java | 6 +- .../andrewzhernov/database/Handler.java | 4 +- .../andrewzhernov/database/HashMapTable.java | 137 ++++---- .../database/HashMapTableManager.java | 125 -------- .../database/HashMapTableProvider.java | 297 ++++++++++++++++++ .../{IndexManager.java => IndexProvider.java} | 2 +- ...{ArrayStorable.java => JsonStoreable.java} | 13 +- .../students/andrewzhernov/database/Main.java | 103 +++--- .../database/PermissionsValidator.java | 12 +- .../andrewzhernov/database/Shell.java | 14 +- .../{Storable.java => Storeable.java} | 2 +- .../andrewzhernov/database/Table.java | 12 +- .../{TableManager.java => TableProvider.java} | 50 +-- .../andrewzhernov/database/Utils.java | 86 +++++ 15 files changed, 590 insertions(+), 277 deletions(-) delete mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java rename src/ru/fizteh/fivt/students/andrewzhernov/database/{IndexManager.java => IndexProvider.java} (96%) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{ArrayStorable.java => JsonStoreable.java} (92%) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{Storable.java => Storeable.java} (99%) rename src/ru/fizteh/fivt/students/andrewzhernov/database/{TableManager.java => TableProvider.java} (74%) diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java index 72a550dfd..7bb1cc858 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/ColumnFormatException.java @@ -6,7 +6,9 @@ */ public class ColumnFormatException extends IllegalArgumentException { - public ColumnFormatException() { + private static final long serialVersionUID = 1L; + + public ColumnFormatException() { } public ColumnFormatException(String s) { diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java index df7b8c53c..4f7f043be 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Command.java @@ -15,12 +15,12 @@ public String getName() { return name; } - public void execute(TableManager database, String[] params) throws Exception { + public void execute(TableProvider database, String[] params) throws Exception { if (params.length != numArgs) { - throw new Exception(String.format("Invalid number of arguments: %d expected, %d found.", + throw new IllegalArgumentException(String.format(name + ": invalid number of arguments: %d expected, %d found.", numArgs, params.length)); } else { - processor.handle(processor.execute(database, params)); + processor.handle(database, processor.execute(database, params)); } } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java index da7cdb6c7..14ec003fc 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Handler.java @@ -1,6 +1,6 @@ package ru.fizteh.fivt.students.andrewzhernov.database; public interface Handler { - Object execute(TableManager database, String[] args) throws Exception; - void handle(Object object) throws Exception; + Object execute(TableProvider database, String[] args) throws Exception; + void handle(TableProvider database, Object object) throws Exception; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java index 0b7fb5c2a..0f0928949 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java @@ -15,111 +15,122 @@ public class HashMapTable implements Table { private static final int DIRECTORIES_COUNT = 16; private static final int FILES_COUNT = 16; - private TableManager manager; - private String name; - private int size; - private Map disk; - private Map diff; - private List> columnTypes; - - public HashMapTable(TableManager tableManager, String tableName) { + private TableProvider provider_; + private String name_; + private int size_; + private Map disk_; + private Map diff_; + private List> columnTypes_; + + public HashMapTable(TableProvider tableProvider, String tableName, List> columnTypes) { PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); - manager = tableManager; - name = tableName; - size = 0; - disk = new HashMap<>(); - diff = new HashMap<>(); + provider_ = tableProvider; + name_ = tableName; + columnTypes_ = columnTypes; + size_ = 0; + disk_ = new HashMap<>(); + diff_ = new HashMap<>(); } + @Override public String getName() { - return name; + return name_; } + @Override public int getNumberOfUncommittedChanges() { - return diff.size(); + return diff_.size(); } + @Override public int size() { - return size; + return size_; } - public Storable put(String key, Storable value) throws IllegalArgumentException { + @Override + public Storeable put(String key, Storeable value) throws IllegalArgumentException { if (key == null || value == null) { throw new IllegalArgumentException("Invalid key/value"); } - Storable diffValue = diff.put(key, value); - String result = diffValue != null ? diffValue : disk.get(key); + Storeable diffValue = diff_.put(key, value); + Storeable result = diffValue != null ? diffValue : disk_.get(key); if (result == null) { - ++size; + ++size_; } return result; } - public Storable get(String key) throws IllegalArgumentException { + @Override + public Storeable get(String key) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("Invalid key"); } - Storable diffValue = diff.get(key); - return diffValue != null ? diffValue : disk.get(key); + Storeable diffValue = diff_.get(key); + return diffValue != null ? diffValue : disk_.get(key); } - public Storable remove(String key) throws IllegalArgumentException { + @Override + public Storeable remove(String key) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("Invalid key"); } - Storable result = null; - Storable diskValue = disk.get(key); + Storeable result = null; + Storeable diskValue = disk_.get(key); if (diskValue != null) { - Storable diffValue = diff.put(key, null); + Storeable diffValue = diff_.put(key, null); result = diffValue != null ? diffValue : diskValue; } else { - result = diff.remove(key); + result = diff_.remove(key); } if (result != null) { - --size; + --size_; } return result; } + @Override public List list() { - List list = new LinkedList(); - for (String key : disk.keySet()) { - if (!diff.containsKey(key)) { + List list = new LinkedList<>(); + for (String key : disk_.keySet()) { + if (!diff_.containsKey(key)) { list.add(key); } } - for (String key : diff.keySet()) { - if (diff.get(key) != null) { + for (String key : diff_.keySet()) { + if (diff_.get(key) != null) { list.add(key); } } return list; } + @Override public int commit() { - int amount = diff.size(); - for (String key : diff.keySet()) { - Storable value = diff.get(key); + int amount = diff_.size(); + for (String key : diff_.keySet()) { + Storeable value = diff_.get(key); if (value != null) { - disk.put(key, value); + disk_.put(key, value); } else { - disk.remove(key); + disk_.remove(key); } } - diff.clear(); + diff_.clear(); saveTable(); return amount; } + @Override public int rollback() { - int amount = diff.size(); - diff.clear(); - size = disk.size(); + int amount = diff_.size(); + diff_.clear(); + size_ = disk_.size(); return amount; } + @Override public String getTableFolder() { - return Paths.get(manager.getDbFolder(), name).toString(); + return Paths.get(provider_.getDbFolder(), name_).toString(); } public String getTableBucket(int bucket) { @@ -138,17 +149,15 @@ private static String readItem(RandomAccessFile file) throws Exception { } private void loadSubBucket(String subBucket) throws Exception { - RandomAccessFile file = new RandomAccessFile(subBucket, "r"); - while (file.getFilePointer() < file.length()) { - try { - String key = readItem(file); + try (RandomAccessFile file = new RandomAccessFile(subBucket, "r")) { + while (file.getFilePointer() < file.length()) { + String key = readItem(file); String value = readItem(file); - disk.put(key, value); - } catch (Exception | OutOfMemoryError e) { - throw new Exception(subBucket + ": invalid file format"); + disk_.put(key, provider_.deserialize(provider_.getTable(name_), value)); } + } catch (Exception | OutOfMemoryError e) { + throw new Exception(subBucket + ": invalid file format"); } - file.close(); } private void loadBucket(String bucket) throws Exception { @@ -160,17 +169,18 @@ private void loadBucket(String bucket) throws Exception { } } + @Override public void loadTable() { try { PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); - disk.clear(); + disk_.clear(); for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { String bucket = getTableBucket(bucketIndex); if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { loadBucket(bucket); } } - size = disk.size(); + size_ = disk_.size(); } catch (Exception e) { System.err.println(e.getMessage()); System.exit(1); @@ -186,13 +196,14 @@ private static void writeItem(RandomAccessFile file, String word) throws Excepti private boolean saveSubBucket(String subBucket, int bucketIndex, int subBucketIndex) throws Exception { boolean isWritten = false; RandomAccessFile file = new RandomAccessFile(subBucket, "rw"); - for (String key : disk.keySet()) { + for (String key : disk_.keySet()) { int hashcode = key.hashCode(); - int dirNumber = hashcode % DIRECTORIES_COUNT; + int directoryNumber = hashcode % DIRECTORIES_COUNT; int fileNumber = hashcode / DIRECTORIES_COUNT % FILES_COUNT; - if (bucketIndex == dirNumber && subBucketIndex == fileNumber) { + if (bucketIndex == directoryNumber && subBucketIndex == fileNumber) { + String value = provider_.serialize(provider_.getTable(name_), disk_.get(key)); writeItem(file, key); - writeItem(file, disk.get(key)); + writeItem(file, value); isWritten = true; } } @@ -231,4 +242,14 @@ private void saveTable() { System.exit(1); } } + + @Override + public int getColumnsCount() { + return columnTypes_.size(); + } + + @Override + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + return columnTypes_.get(columnIndex); + } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java deleted file mode 100644 index ef41e9b4c..000000000 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableManager.java +++ /dev/null @@ -1,125 +0,0 @@ -package ru.fizteh.fivt.students.andrewzhernov.database; - -import java.io.*; -import java.nio.file.Paths; -import java.lang.Long; -import java.util.Arrays; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.LinkedList; - -import static java.util.EnumSet.of; -import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; - -public class HashMapTableManager implements TableManager { - private static final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; - private static final List> types = Arrays.asList(new Integer().getClass(), new Long().getClass(), Byte, Float, Double, Boolean, String); - - private String dbFolder; - private Map nameToTableMap; - private String currentTable; - - public HashMapTableManager(String folder) throws IllegalArgumentException { - PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); - PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); - dbFolder = folder; - nameToTableMap = new HashMap<>(); - currentTable = null; - loadDatabase(); - } - - private boolean isValidName(String tableName) { - for (String character : invalidCharacters) { - if (tableName.contains(character)) { - return false; - } - } - return true; - } - - private void loadDatabase() throws IllegalArgumentException { - for (String tableName : new File(dbFolder).list()) { - if (!isValidName(tableName)) { - throw new IllegalArgumentException(tableName + ": incorrect table name"); - } - Table table = new HashMapTable(this, tableName); - nameToTableMap.put(tableName, table); - table.loadTable(); - } - } - - private void checkUnsavedChanges() throws IllegalStateException { - int count = getCurrentTable().getNumberOfUncommittedChanges(); - if (count > 0) { - throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); - } - } - - public String getDbFolder() { - return dbFolder; - } - - public Table getTable(String name) throws IllegalArgumentException { - if (name == null) { - throw new IllegalArgumentException("no table"); - } - return nameToTableMap.get(name); - } - - public Table getCurrentTable() throws IllegalArgumentException { - return getTable(currentTable); - } - - public Table createTable(String name, List> columnTypes) throws IllegalArgumentException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException(name + " exists"); - } - - Table table = new Table(this, name); - nameToTableMap.put(name, table); - PermissionsValidator.validate(table.getTableFolder(), - of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); - return table; - } - - public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalStateException(name + " not exist"); - } else if (name.equals(currentTable)) { - currentTable = null; - } - Utils.removeDir(Paths.get(dbFolder, name)); - nameToTableMap.remove(name); - } - - public String useTable(String name) throws IllegalArgumentException, IllegalStateException { - if (name == null || !isValidName(name)) { - throw new IllegalArgumentException(name + ": incorrect table name"); - } else if (!nameToTableMap.containsKey(name)) { - throw new IllegalArgumentException(name + " not exist"); - } else if (currentTable != null && !name.equals(currentTable)) { - checkUnsavedChanges(); - } - currentTable = name; - return currentTable; - } - - public List getTableNames() { - return new LinkedList<>().addAll(nameToTableMap.keySet()); - } - - public createFor(Table table) { - - } - - public void exit() throws IllegalStateException { - if (currentTable != null) { - checkUnsavedChanges(); - } - } -} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java new file mode 100644 index 000000000..4fac0211a --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java @@ -0,0 +1,297 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +import static java.util.EnumSet.of; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.CAN_READ; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.CAN_WRITE; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.CREATE_DIRECTORY_IF_NOT_EXISTS; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.EXISTS; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.IS_DIRECTORY; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.IS_FILE; +import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.NOT_NULL; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class HashMapTableProvider implements TableProvider { + private static final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; + + private static final String SIGNATURE_FILE = "signature.tsv"; + + private String dbFolder; + private Map nameToTableMap; + private Map>> nameToSignatureMap; + private String currentTable; + + public HashMapTableProvider(String folder) throws Exception { + PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); + PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); + dbFolder = folder; + nameToTableMap = new HashMap<>(); + nameToSignatureMap = new HashMap<>(); + currentTable = null; + loadDatabase(); + } + + private boolean isValidName(String tableName) { + for (String character : invalidCharacters) { + if (tableName.contains(character)) { + return false; + } + } + return true; + } + + private String getSignatureFilename() { + return dbFolder + File.separator + SIGNATURE_FILE; + } + + private void loadSignature() throws Exception { + String signatureFilename = getSignatureFilename(); + if (!PermissionsValidator.validate(signatureFilename, of(NOT_NULL, EXISTS, IS_FILE))) { + File signatureFile = new File(signatureFilename); + signatureFile.createNewFile(); + return; + } + + if (PermissionsValidator.validateFileEmpty(signatureFilename)) { + return; + } + + BufferedReader in = new BufferedReader(new FileReader(signatureFilename)); + + while (in.ready()) { + String s = in.readLine(); + String[] tokens = s.split("\t"); + + if (tokens.length != 2) { + throw new Exception("Wrong line in signatures.tsv: " + s + "\n"); + } + nameToSignatureMap.put(tokens[0], Utils.parseSignature(tokens[1])); + } + in.close(); + } + + public void saveSignature() { + try (PrintWriter out = new PrintWriter(getSignatureFilename())) { + for (String tableName : nameToSignatureMap.keySet()) { + String signature = Utils.makeSignature(nameToSignatureMap.get(tableName)); + out.printf("%s\t%s", tableName, signature); + } + } catch (FileNotFoundException e) { + System.err.println("Coutldn't save signature: " + e.getMessage()); + } + } + + private void loadDatabase() throws Exception { + loadSignature(); + for (String tableName : new File(dbFolder).list()) { + if (tableName.equals(SIGNATURE_FILE)) { + continue; + } + if (!isValidName(tableName)) { + throw new IllegalArgumentException(tableName + ": incorrect table name"); + } + Table table = new HashMapTable(this, tableName, nameToSignatureMap.get(tableName)); + nameToTableMap.put(tableName, table); + table.loadTable(); + } + } + + private void checkUnsavedChanges() throws IllegalStateException { + int count = getCurrentTable().getNumberOfUncommittedChanges(); + if (count > 0) { + throw new IllegalStateException(Integer.toString(count) + " unsaved changes"); + } + } + + @Override + public String getDbFolder() { + return dbFolder; + } + + @Override + public Table getTable(String name) throws IllegalArgumentException { + if (name == null) { + throw new IllegalArgumentException("no table"); + } + return nameToTableMap.get(name); + } + + @Override + public Table getCurrentTable() throws IllegalArgumentException { + return getTable(currentTable); + } + + @Override + public Table createTable(String name, List> columnTypes) throws RuntimeException, IOException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException(name + " exists"); + } + + Table table = new HashMapTable(this, name, columnTypes); + nameToTableMap.put(name, table); + nameToSignatureMap.put(name, columnTypes); + PermissionsValidator.validate(table.getTableFolder(), + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY)); + return table; + } + + @Override + public void removeTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalStateException(name + " not exist"); + } else if (name.equals(currentTable)) { + currentTable = null; + } + Utils.removeDir(Paths.get(dbFolder, name)); + nameToTableMap.remove(name); + } + + @Override + public String useTable(String name) throws IllegalArgumentException, IllegalStateException { + if (name == null || !isValidName(name)) { + throw new IllegalArgumentException(name + ": incorrect table name"); + } else if (!nameToTableMap.containsKey(name)) { + throw new IllegalArgumentException(name + " not exist"); + } else if (currentTable != null && !name.equals(currentTable)) { + checkUnsavedChanges(); + } + currentTable = name; + return currentTable; + } + + @Override + public List getTableNames() { + return new LinkedList<>(nameToTableMap.keySet()); + } + + @Override + public void exit() throws IllegalStateException { + if (currentTable != null) { + checkUnsavedChanges(); + } + saveSignature(); + } + + @Override + public Storeable deserialize(Table table, String value) throws ParseException { + if (value.length() < 2) { + throw new ParseException(value + ": wrong value length", 0); + } + if (value.charAt(0) != '(') { + throw new ParseException(value + ": value must start with '('", 0); + } + if (value.charAt(value.length() - 1) != ')') { + throw new ParseException(value + ": value must end with ')'", value.length() - 1); + } + + value = value.substring(1, value.length() - 1); + String[] elements = value.split(","); + + List expectedTypes = nameToSignatureMap.get(table.getName()); + if (elements.length != expectedTypes.size()) { + throw new ParseException( + String.format("%s: value must contain %d element(s)", value, expectedTypes.size()), 0); + } + + List objects = new ArrayList<>(); + + int i = 0; + for (String element : elements) { + try { + if (expectedTypes.get(i).equals(Integer.class)) { + objects.add(Integer.parseInt(element)); + } else if (expectedTypes.get(i).equals(Long.class)) { + objects.add(Long.parseLong(element)); + } else if (expectedTypes.get(i).equals(Byte.class)) { + objects.add(Byte.parseByte(element)); + } else if (expectedTypes.get(i).equals(Float.class)) { + objects.add(Float.parseFloat(element)); + } else if (expectedTypes.get(i).equals(Double.class)) { + objects.add(Double.parseDouble(element)); + } else if (expectedTypes.get(i).equals(Boolean.class)) { + objects.add(Boolean.parseBoolean(element)); + } else if (expectedTypes.get(i).equals(String.class)) { + objects.add(element); + } else { + throw new ParseException("Wrong element type: " + element, 0); + } + } catch (NumberFormatException e) { + throw new ParseException( + String.format("Couldn't parse value: %s", e.getMessage()), 0); + } + i++; + } + + Storeable result = new JsonStoreable(objects); + + return result; + } + + @Override + public String serialize(Table table, Storeable value) throws ColumnFormatException { + List> expectedTypes = nameToSignatureMap.get(table.getName()); + + String result = "("; + + int i = 0; + for (Class type : expectedTypes) { + if (type.equals(Integer.class)) { + result += value.getIntAt(i); + } else if (type.equals(Long.class)) { + result += value.getLongAt(i); + } else if (type.equals(Byte.class)) { + result += value.getByteAt(i); + } else if (type.equals(Float.class)) { + result += value.getFloatAt(i); + } else if (type.equals(Double.class)) { + result += value.getDoubleAt(i); + } else if (type.equals(Boolean.class)) { + result += value.getBooleanAt(i); + } else if (type.equals(String.class)) { + result += value.getStringAt(i); + } + i++; + + if (i != expectedTypes.size()) { + result += ","; + } + } + + result += ")"; + + return result; + } + + @Override + public Storeable createFor(Table table, List values) throws ColumnFormatException, + IndexOutOfBoundsException { + return null; + } + + @Override + public Storeable createFor(Table table) { + return null; + } + + @Override + public Object showTables() { + return null; + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexProvider.java similarity index 96% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/IndexProvider.java index fd112a114..8eff65218 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexManager.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/IndexProvider.java @@ -4,7 +4,7 @@ * Дополнительный интерфейс к {@link ru.fizteh.fivt.storage.structured.TableProvider}, позволяющий создавать индексы на * основе имеющейся таблицы. */ -public interface IndexManager extends TableManager { +public interface IndexProvider extends TableProvider { /** * * Возвращает индекс с таким именем, если он существует. diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/JsonStoreable.java similarity index 92% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/JsonStoreable.java index e4ae46453..ae94fd2aa 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/ArrayStorable.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/JsonStoreable.java @@ -1,20 +1,19 @@ package ru.fizteh.fivt.students.andrewzhernov.database; import java.util.List; -import java.util.ArrayList; -public class ArrayStorable implements Storable { +public class JsonStoreable implements Storeable { private Object[] columnValues; - public ArrayStorable(List values) { + public JsonStoreable(List values) { columnValues = values.toArray(); } @Override public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { if (value.getClass() != columnValues[columnIndex].getClass()) { - throw new ColumnFormatException("Invalid column format: expected " - columnValues[columnIndex].getClass().getName() ", got " value.getClass().getName()); + throw new ColumnFormatException("Invalid column format: expected " + + columnValues[columnIndex].getClass().getName() + ", got " + value.getClass().getName()); } columnValues[columnIndex] = value; } @@ -79,4 +78,8 @@ public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOu } return (String) columnValues[columnIndex]; } + + public int size() { + return columnValues.length; + } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java index f3f5d88db..736ccc6ee 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java @@ -6,58 +6,59 @@ public class Main { public static void main(String[] args) { try { - TableManager manager = new HashMapTableManager(System.getProperty("fizteh.db.dir")); - Shell shell = new Shell(manager, new Command[] { + TableProvider provider = new HashMapTableProvider(System.getProperty("fizteh.db.dir")); + Shell shell = new Shell(provider, new Command[] { new Command("size", 1, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().size(); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().size(); } @Override - public void handle(Object object) throws Exception { - System.out.println((Integer) object); + public void handle(TableProvider provider, Object object) throws Exception { + System.out.println(object); } }), new Command("put", 3, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().put(args[1], args[2]); + public Object execute(TableProvider provider, String[] args) throws Exception { + Table table = provider.getCurrentTable(); + return table.put(args[1], provider.deserialize(table, args[2])); } @Override - public void handle(Object object) throws Exception { - String value = (String) object; - if (value == null) { + public void handle(TableProvider provider, Object object) throws Exception { + if (object == null) { System.out.println("new"); } else { System.out.println("overwrite"); - System.out.println(value); + System.out.println(provider.serialize( + provider.getCurrentTable(), (Storeable) object)); } } }), new Command("get", 2, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().get(args[1]); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().get(args[1]); } @Override - public void handle(Object object) throws Exception { - String value = (String) object; - if (value == null) { + public void handle(TableProvider provider, Object object) throws Exception { + if (object == null) { System.out.println("not found"); } else { System.out.println("found"); - System.out.println(value); + System.out.println(provider.serialize( + provider.getCurrentTable(), (Storeable) object)); } } }), new Command("remove", 2, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().remove(args[1]); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().remove(args[1]); } @Override - public void handle(Object object) throws Exception { - if ((String) object == null) { + public void handle(TableProvider provider, Object object) throws Exception { + if (object == null) { System.out.println("not found"); } else { System.out.println("removed"); @@ -66,74 +67,74 @@ public void handle(Object object) throws Exception { }), new Command("list", 1, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().list(); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().list(); } @Override - public void handle(Object object) throws Exception { + public void handle(TableProvider provider, Object object) throws Exception { @SuppressWarnings("unchecked") List list = (List) object; - System.out.println(String.join(", ", list)); + System.out.println(Utils.join(", ", list.toArray(new String[list.size()]))); } }), new Command("commit", 1, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().commit(); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().commit(); } @Override - public void handle(Object object) throws Exception { - System.out.println((Integer) object); + public void handle(TableProvider provider, Object object) throws Exception { + System.out.println(object); } }), new Command("rollback", 1, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.getCurrentTable().rollback(); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.getCurrentTable().rollback(); } @Override - public void handle(Object object) throws Exception { - System.out.println((Integer) object); + public void handle(TableProvider provider, Object object) throws Exception { + System.out.println(object); } }), - new Command("create", 2, new Handler() { + new Command("create", 3, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.createTable(args[1]); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.createTable(args[1], Utils.parseSignature(args[2])); } - @Override - public void handle(Object object) throws Exception { + @Override + public void handle(TableProvider provider, Object object) throws Exception { System.out.println("created"); } }), new Command("drop", 2, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - manager.removeTable(args[1]); + public Object execute(TableProvider provider, String[] args) throws Exception { + provider.removeTable(args[1]); return null; } @Override - public void handle(Object object) throws Exception { + public void handle(TableProvider provider, Object object) throws Exception { System.out.println("dropped"); } }), new Command("use", 2, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.useTable(args[1]); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.useTable(args[1]); } @Override - public void handle(Object object) throws Exception { + public void handle(TableProvider provider, Object object) throws Exception { System.out.println("using " + (String) object); } }), new Command("show tables", 2, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - return manager.showTables(); + public Object execute(TableProvider provider, String[] args) throws Exception { + return provider.showTables(); } @Override - public void handle(Object object) throws Exception { + public void handle(TableProvider provider, Object object) throws Exception { @SuppressWarnings("unchecked") Map tables = (Map) object; for (String tablename : tables.keySet()) { @@ -143,12 +144,12 @@ public void handle(Object object) throws Exception { }), new Command("exit", 1, new Handler() { @Override - public Object execute(TableManager manager, String[] args) throws Exception { - manager.exit(); + public Object execute(TableProvider provider, String[] args) throws Exception { + provider.exit(); return null; } @Override - public void handle(Object object) throws Exception { + public void handle(TableProvider provider, Object object) throws Exception { System.exit(0); } }) diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java index bc511cc43..6790a67c2 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/PermissionsValidator.java @@ -1,9 +1,11 @@ package ru.fizteh.fivt.students.andrewzhernov.database; import static ru.fizteh.fivt.students.andrewzhernov.database.PermissionsValidator.Permissions.*; -import java.util.EnumSet; +import java.util.EnumSet; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.nio.file.Files; import java.io.IOException; @@ -16,7 +18,8 @@ public enum Permissions { CAN_READ, CAN_WRITE, IS_FILE, - IS_DIRECTORY; + IS_DIRECTORY, + IS_FILE_EMPTY; } public static boolean validateTableName(String tableName, EnumSet perms) @@ -35,6 +38,11 @@ public static boolean validateDbFolder(String dbFolder, EnumSet per return true; } + public static boolean validateFileEmpty(String fileName) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(fileName)); + return br.readLine() == null; + } + public static boolean validate(String fileName, EnumSet perms) throws RuntimeException, IOException { if (perms.contains(NOT_NULL) && fileName == null) { diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java index 03f1d0025..f7bae00c0 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Shell.java @@ -1,18 +1,18 @@ package ru.fizteh.fivt.students.andrewzhernov.database; -import java.util.Scanner; -import java.util.Map; import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; public class Shell { private static final String PROMPT = "$ "; private static final String STATEMENT_DELIMITER = ";"; private static final String PARAM_DELIMITER = "\\s+"; - private TableManager manager; + private TableProvider manager; private Map commands; - public Shell(TableManager manager, Command[] commands) throws Exception { + public Shell(TableProvider manager, Command[] commands) throws Exception { this.manager = manager; this.commands = new HashMap<>(); for (Command command : commands) { @@ -31,7 +31,7 @@ public void run(String[] args) throws Exception { } } - public void interactiveMode() throws Exception { + public void interactiveMode() { Scanner input = new Scanner(System.in); while (true) { System.out.print(PROMPT); @@ -48,7 +48,7 @@ public void interactiveMode() throws Exception { } public void batchMode(String[] args) throws Exception { - executeLine(String.join(";", args)); + executeLine(Utils.join(";", args)); } public void executeLine(String line) throws Exception { @@ -64,7 +64,7 @@ public void executeLine(String line) throws Exception { Command command = commands.get(cmdName); if (command == null) { if (!cmdName.isEmpty()) { - throw new Exception(cmdName + ": command not found"); + throw new IllegalArgumentException(cmdName + ": command not found"); } } else { command.execute(manager, params); diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Storeable.java similarity index 99% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/Storeable.java index a983cd847..658e8b8fb 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Storable.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Storeable.java @@ -10,7 +10,7 @@ * Для получения объекта из нужной колонки воспользуйтесь соответствующим геттером. * Для установки объекта а колонку воспользуйтесь {@link #setColumnAt(int, Object)} . */ -public interface Storable { +public interface Storeable { /** * Установить значение в колонку diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 44fa34493..2550d0210 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -24,7 +24,7 @@ public interface Table extends Index { * @throws IllegalArgumentException Если значение параметров key или value является null. * @throws ColumnFormatException - при попытке передать Storable с колонками другого типа. */ - Storable put(String key, Storable value) throws ColumnFormatException; + Storeable put(String key, Storeable value) throws ColumnFormatException; /** * Удаляет значение по указанному ключу. @@ -34,7 +34,7 @@ public interface Table extends Index { * * @throws IllegalArgumentException Если значение параметра key является null. */ - Storable remove(String key); + Storeable remove(String key); /** * Возвращает количество ключей в таблице. Возвращает размер текущей версии, с учётом незафиксированных изменений. @@ -89,4 +89,12 @@ public interface Table extends Index { * @throws IndexOutOfBoundsException - неверный индекс колонки */ Class getColumnType(int columnIndex) throws IndexOutOfBoundsException; + + + String getTableFolder(); + + /** + * + */ + void loadTable(); } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableProvider.java similarity index 74% rename from src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java rename to src/ru/fizteh/fivt/students/andrewzhernov/database/TableProvider.java index 0c44be4cd..6ecb6cc59 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/TableManager.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/TableProvider.java @@ -13,7 +13,7 @@ * * Данный интерфейс не является потокобезопасным. */ -public interface TableManager { +public interface TableProvider { /** * Возвращает таблицу с указанным названием. @@ -57,45 +57,45 @@ public interface TableManager { void removeTable(String name) throws IOException; /** - * Преобразовывает строку в объект {@link Storable}, соответствующий структуре таблицы. + * Преобразовывает строку в объект {@link Storeable}, соответствующий структуре таблицы. * - * @param table Таблица, которой должен принадлежать {@link Storable}. - * @param value Строка, из которой нужно прочитать {@link Storable}. - * @return Прочитанный {@link Storable}. + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @param value Строка, из которой нужно прочитать {@link Storeable}. + * @return Прочитанный {@link Storeable}. * * @throws ParseException - при каких-либо несоответстиях в прочитанных данных. */ - Storable deserialize(Table table, String value) throws ParseException; + Storeable deserialize(Table table, String value) throws ParseException; /** - * Преобразовывает объект {@link Storable} в строку. + * Преобразовывает объект {@link Storeable} в строку. * - * @param table Таблица, которой должен принадлежать {@link Storable}. - * @param value {@link Storable}, который нужно записать. + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @param value {@link Storeable}, который нужно записать. * @return Строка с записанным значением. * - * @throws ColumnFormatException При несоответствии типа в {@link Storable} и типа колонки в таблице. + * @throws ColumnFormatException При несоответствии типа в {@link Storeable} и типа колонки в таблице. */ - String serialize(Table table, Storable value) throws ColumnFormatException; + String serialize(Table table, Storeable value) throws ColumnFormatException; /** - * Создает новый пустой {@link Storable} для указанной таблицы. + * Создает новый пустой {@link Storeable} для указанной таблицы. * - * @param table Таблица, которой должен принадлежать {@link Storable}. - * @return Пустой {@link Storable}, нацеленный на использование с этой таблицей. + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @return Пустой {@link Storeable}, нацеленный на использование с этой таблицей. */ - Storable createFor(Table table); + Storeable createFor(Table table); /** - * Создает новый {@link Storable} для указанной таблицы, подставляя туда переданные значения. + * Создает новый {@link Storeable} для указанной таблицы, подставляя туда переданные значения. * - * @param table Таблица, которой должен принадлежать {@link Storable}. + * @param table Таблица, которой должен принадлежать {@link Storeable}. * @param values Список значений, которыми нужно проинициализировать поля Storable. - * @return {@link Storable}, проинициализированный переданными значениями. + * @return {@link Storeable}, проинициализированный переданными значениями. * @throws ColumnFormatException При несоответствии типа переданного значения и колонки. * @throws IndexOutOfBoundsException При несоответствии числа переданных значений и числа колонок. */ - Storable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException; + Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException; /** * Возвращает имена существующих таблиц, которые могут быть получены с помощью {@link #getTable(String)}. @@ -103,4 +103,16 @@ public interface TableManager { * @return Имена существующих таблиц. */ List getTableNames(); + + Table getCurrentTable(); + + String getDbFolder(); + + String useTable(String string); + + void exit(); + + Object showTables(); + + void saveSignature(); } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java index 9378f45a6..d39874889 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java @@ -4,6 +4,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.DirectoryStream; +import java.util.ArrayList; +import java.util.List; public class Utils { public static void removeDir(Path directory) throws IllegalStateException { @@ -23,4 +25,88 @@ public static void removeDir(Path directory) throws IllegalStateException { System.exit(1); } } + + // for java 7 + public static String join(String join, String... strings) { + if (strings == null || strings.length == 0) { + return ""; + } else if (strings.length == 1) { + return strings[0]; + } else { + StringBuilder sb = new StringBuilder(); + sb.append(strings[0]); + for (int i = 1; i < strings.length; i++) { + sb.append(join).append(strings[i]); + } + return sb.toString(); + } + } + + public static List> parseSignature(String signature) throws Exception { + if (signature.length() < 2) { + throw new Exception("Wrong signature length"); + } + if (signature.charAt(0) != '(' || signature.charAt(signature.length() - 1) != ')') { + throw new Exception("Signature must start and end with paranthesis"); + } + + signature = signature.substring(1, signature.length() - 1); + + String[] types = signature.split(","); + + List> result = new ArrayList<>(); + for (String type : types) { + if (type.equals("int")) { + result.add(Integer.class); + } else if (type.equals("long")) { + result.add(Long.class); + } else if (type.equals("byte")) { + result.add(Byte.class); + } else if (type.equals("float")) { + result.add(Float.class); + } else if (type.equals("double")) { + result.add(Double.class); + } else if (type.equals("boolean")) { + result.add(Boolean.class); + } else if (type.equals("String")) { + result.add(String.class); + } else { + throw new Exception("Wrong type (" + type + ")"); + } + } + + return result; + } + + public static String makeSignature(List> types) { + String result = "("; + + int i = 0; + for (Class type : types) { + if (type.equals(Integer.class)) { + result += "int"; + } else if (type.equals(Long.class)) { + result += "long"; + } else if (type.equals(Byte.class)) { + result += "byte"; + } else if (type.equals(Float.class)) { + result += "float"; + } else if (type.equals(Double.class)) { + result += "double"; + } else if (type.equals(Boolean.class)) { + result += "boolean"; + } else if (type.equals(String.class)) { + result += "String"; + } + + i++; + if (i != types.size()) { + result += ","; + } + } + + result += ")"; + return result; + } + } From d460b123094a065c77e4cadd289684df7865f1e4 Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Thu, 25 Dec 2014 12:27:29 +0300 Subject: [PATCH 6/7] parallel --- .../andrewzhernov/database/HashMapTable.java | 57 ++++++++++++------- .../database/HashMapTableProvider.java | 21 ++++++- .../students/andrewzhernov/database/Main.java | 7 ++- 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java index 0f0928949..e8e1929c3 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java @@ -11,6 +11,9 @@ import java.util.Map; import java.util.HashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + public class HashMapTable implements Table { private static final int DIRECTORIES_COUNT = 16; private static final int FILES_COUNT = 16; @@ -19,9 +22,11 @@ public class HashMapTable implements Table { private String name_; private int size_; private Map disk_; - private Map diff_; + private ThreadLocal> diff_; private List> columnTypes_; - + + private ReadWriteLock rwlLock = new ReentrantReadWriteLock(); + public HashMapTable(TableProvider tableProvider, String tableName, List> columnTypes) { PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); provider_ = tableProvider; @@ -29,7 +34,7 @@ public HashMapTable(TableProvider tableProvider, String tableName, List columnTypes_ = columnTypes; size_ = 0; disk_ = new HashMap<>(); - diff_ = new HashMap<>(); + diff_ = ThreadLocal.withInitial(() -> new HashMap()); } @Override @@ -39,7 +44,7 @@ public String getName() { @Override public int getNumberOfUncommittedChanges() { - return diff_.size(); + return diff_.get().size(); } @Override @@ -52,12 +57,15 @@ public Storeable put(String key, Storeable value) throws IllegalArgumentExceptio if (key == null || value == null) { throw new IllegalArgumentException("Invalid key/value"); } - Storeable diffValue = diff_.put(key, value); - Storeable result = diffValue != null ? diffValue : disk_.get(key); - if (result == null) { - ++size_; + if (!value.equals(disk_.get(key))) { + Storeable diffValue = diff_.get().put(key, value); + Storeable result = diffValue != null ? diffValue : disk_.get(key); + if (result == null) { + ++size_; + } + return result; } - return result; + return value; } @Override @@ -65,7 +73,7 @@ public Storeable get(String key) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("Invalid key"); } - Storeable diffValue = diff_.get(key); + Storeable diffValue = diff_.get().get(key); return diffValue != null ? diffValue : disk_.get(key); } @@ -77,10 +85,10 @@ public Storeable remove(String key) throws IllegalArgumentException { Storeable result = null; Storeable diskValue = disk_.get(key); if (diskValue != null) { - Storeable diffValue = diff_.put(key, null); + Storeable diffValue = diff_.get().put(key, null); result = diffValue != null ? diffValue : diskValue; } else { - result = diff_.remove(key); + result = diff_.get().remove(key); } if (result != null) { --size_; @@ -92,12 +100,12 @@ public Storeable remove(String key) throws IllegalArgumentException { public List list() { List list = new LinkedList<>(); for (String key : disk_.keySet()) { - if (!diff_.containsKey(key)) { + if (!diff_.get().containsKey(key)) { list.add(key); } } - for (String key : diff_.keySet()) { - if (diff_.get(key) != null) { + for (String key : diff_.get().keySet()) { + if (diff_.get().get(key) != null) { list.add(key); } } @@ -106,24 +114,29 @@ public List list() { @Override public int commit() { - int amount = diff_.size(); - for (String key : diff_.keySet()) { - Storeable value = diff_.get(key); + int amount = diff_.get().size(); + for (String key : diff_.get().keySet()) { + Storeable value = diff_.get().get(key); if (value != null) { disk_.put(key, value); } else { disk_.remove(key); } } - diff_.clear(); - saveTable(); + diff_.get().clear(); + rwlLock.writeLock().lock(); + try { + saveTable(); + } finally { + rwlLock.writeLock().unlock(); + } return amount; } @Override public int rollback() { - int amount = diff_.size(); - diff_.clear(); + int amount = diff_.get().size(); + diff_.get().clear(); size_ = disk_.size(); return amount; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java index 4fac0211a..1e2c60ab4 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java @@ -24,6 +24,9 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + public class HashMapTableProvider implements TableProvider { private static final String[] invalidCharacters = {".", "|", "\\", "*", "\"", "\'", ":", "/", "?", "<", ">"}; @@ -34,6 +37,8 @@ public class HashMapTableProvider implements TableProvider { private Map>> nameToSignatureMap; private String currentTable; + private ReadWriteLock rwlLock = new ReentrantReadWriteLock(); + public HashMapTableProvider(String folder) throws Exception { PermissionsValidator.validateDbFolder(folder, of(NOT_NULL)); PermissionsValidator.validate(folder, of(CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_READ, IS_DIRECTORY)); @@ -105,7 +110,12 @@ private void loadDatabase() throws Exception { } Table table = new HashMapTable(this, tableName, nameToSignatureMap.get(tableName)); nameToTableMap.put(tableName, table); - table.loadTable(); + rwlLock.readLock().lock(); + try { + table.loadTable(); + } finally { + rwlLock.readLock().unlock(); + } } } @@ -159,7 +169,12 @@ public void removeTable(String name) throws IllegalArgumentException, IllegalSta } else if (name.equals(currentTable)) { currentTable = null; } - Utils.removeDir(Paths.get(dbFolder, name)); + rwlLock.writeLock().lock(); + try { + Utils.removeDir(Paths.get(dbFolder, name)); + } finally { + rwlLock.writeLock().unlock(); + } nameToTableMap.remove(name); } @@ -292,6 +307,6 @@ public Storeable createFor(Table table) { @Override public Object showTables() { - return null; + return nameToTableMap; } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java index 736ccc6ee..c935351b0 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Main.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; public class Main { public static void main(String[] args) { @@ -136,9 +137,9 @@ public Object execute(TableProvider provider, String[] args) throws Exception { @Override public void handle(TableProvider provider, Object object) throws Exception { @SuppressWarnings("unchecked") - Map tables = (Map) object; - for (String tablename : tables.keySet()) { - System.out.printf("%s %d\n", tablename, tables.get(tablename)); + Map map = (Map) object; + for (String tablename : map.keySet()) { + System.out.println(tablename); } } }), From a06a200df193893258d846270e847c2b1d6e93db Mon Sep 17 00:00:00 2001 From: Andrew Zhernov Date: Thu, 25 Dec 2014 19:35:34 +0300 Subject: [PATCH 7/7] fixed --- .../ConnectionInterruptException.java | 7 + .../andrewzhernov/database/HashMapTable.java | 143 +++++++++--------- .../database/HashMapTableProvider.java | 58 ++++--- .../andrewzhernov/database/Table.java | 4 +- .../andrewzhernov/database/Utils.java | 66 ++++---- 5 files changed, 135 insertions(+), 143 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/andrewzhernov/database/ConnectionInterruptException.java diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/ConnectionInterruptException.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/ConnectionInterruptException.java new file mode 100644 index 000000000..30c8804db --- /dev/null +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/ConnectionInterruptException.java @@ -0,0 +1,7 @@ +package ru.fizteh.fivt.students.andrewzhernov.database; + +public class ConnectionInterruptException extends Exception { + public ConnectionInterruptException(String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java index e8e1929c3..c7834f629 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTable.java @@ -17,39 +17,42 @@ public class HashMapTable implements Table { private static final int DIRECTORIES_COUNT = 16; private static final int FILES_COUNT = 16; + private static final String DIR_EXTENSION = ".dir"; + private static final String FILE_EXTENSION = ".dat"; + private static final String ENCODING = "UTF-8"; - private TableProvider provider_; - private String name_; - private int size_; - private Map disk_; - private ThreadLocal> diff_; - private List> columnTypes_; + private TableProvider provider; + private String name; + private int size; + private Map disk; + private ThreadLocal> diff; + private List> columnTypes; private ReadWriteLock rwlLock = new ReentrantReadWriteLock(); public HashMapTable(TableProvider tableProvider, String tableName, List> columnTypes) { PermissionsValidator.validateTableName(tableName, of(NOT_NULL)); - provider_ = tableProvider; - name_ = tableName; - columnTypes_ = columnTypes; - size_ = 0; - disk_ = new HashMap<>(); - diff_ = ThreadLocal.withInitial(() -> new HashMap()); + provider = tableProvider; + name = tableName; + columnTypes = columnTypes; + size = 0; + disk = new HashMap<>(); + diff = ThreadLocal.withInitial(() -> new HashMap()); } @Override public String getName() { - return name_; + return name; } @Override public int getNumberOfUncommittedChanges() { - return diff_.get().size(); + return diff.get().size(); } @Override public int size() { - return size_; + return size; } @Override @@ -57,11 +60,11 @@ public Storeable put(String key, Storeable value) throws IllegalArgumentExceptio if (key == null || value == null) { throw new IllegalArgumentException("Invalid key/value"); } - if (!value.equals(disk_.get(key))) { - Storeable diffValue = diff_.get().put(key, value); - Storeable result = diffValue != null ? diffValue : disk_.get(key); + if (!value.equals(disk.get(key))) { + Storeable diffValue = diff.get().put(key, value); + Storeable result = diffValue != null ? diffValue : disk.get(key); if (result == null) { - ++size_; + ++size; } return result; } @@ -73,8 +76,8 @@ public Storeable get(String key) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("Invalid key"); } - Storeable diffValue = diff_.get().get(key); - return diffValue != null ? diffValue : disk_.get(key); + Storeable diffValue = diff.get().get(key); + return diffValue != null ? diffValue : disk.get(key); } @Override @@ -83,15 +86,15 @@ public Storeable remove(String key) throws IllegalArgumentException { throw new IllegalArgumentException("Invalid key"); } Storeable result = null; - Storeable diskValue = disk_.get(key); + Storeable diskValue = disk.get(key); if (diskValue != null) { - Storeable diffValue = diff_.get().put(key, null); + Storeable diffValue = diff.get().put(key, null); result = diffValue != null ? diffValue : diskValue; } else { - result = diff_.get().remove(key); + result = diff.get().remove(key); } if (result != null) { - --size_; + --size; } return result; } @@ -99,13 +102,13 @@ public Storeable remove(String key) throws IllegalArgumentException { @Override public List list() { List list = new LinkedList<>(); - for (String key : disk_.keySet()) { - if (!diff_.get().containsKey(key)) { + for (String key : disk.keySet()) { + if (!diff.get().containsKey(key)) { list.add(key); } } - for (String key : diff_.get().keySet()) { - if (diff_.get().get(key) != null) { + for (String key : diff.get().keySet()) { + if (diff.get().get(key) != null) { list.add(key); } } @@ -113,17 +116,17 @@ public List list() { } @Override - public int commit() { - int amount = diff_.get().size(); - for (String key : diff_.get().keySet()) { - Storeable value = diff_.get().get(key); + public int commit() throws Exception { + int amount = diff.get().size(); + for (String key : diff.get().keySet()) { + Storeable value = diff.get().get(key); if (value != null) { - disk_.put(key, value); + disk.put(key, value); } else { - disk_.remove(key); + disk.remove(key); } } - diff_.get().clear(); + diff.get().clear(); rwlLock.writeLock().lock(); try { saveTable(); @@ -135,30 +138,30 @@ public int commit() { @Override public int rollback() { - int amount = diff_.get().size(); - diff_.get().clear(); - size_ = disk_.size(); + int amount = diff.get().size(); + diff.get().clear(); + size = disk.size(); return amount; } @Override public String getTableFolder() { - return Paths.get(provider_.getDbFolder(), name_).toString(); + return Paths.get(provider.getDbFolder(), name).toString(); } public String getTableBucket(int bucket) { - return Paths.get(getTableFolder(), new Integer(bucket).toString() + ".dir").toString(); + return Paths.get(getTableFolder(), new Integer(bucket).toString() + DIR_EXTENSION).toString(); } public String getTableSubBucket(String bucket, int subBucket) { - return Paths.get(bucket, new Integer(subBucket).toString() + ".dat").toString(); + return Paths.get(bucket, new Integer(subBucket).toString() + FILE_EXTENSION).toString(); } private static String readItem(RandomAccessFile file) throws Exception { int wordSize = file.readInt(); byte[] word = new byte[wordSize]; file.read(word, 0, wordSize); - return new String(word, "UTF-8"); + return new String(word, ENCODING); } private void loadSubBucket(String subBucket) throws Exception { @@ -166,10 +169,10 @@ private void loadSubBucket(String subBucket) throws Exception { while (file.getFilePointer() < file.length()) { String key = readItem(file); String value = readItem(file); - disk_.put(key, provider_.deserialize(provider_.getTable(name_), value)); + disk.put(key, provider.deserialize(provider.getTable(name), value)); } } catch (Exception | OutOfMemoryError e) { - throw new Exception(subBucket + ": invalid file format"); + throw new ConnectionInterruptException(subBucket + ": invalid file format"); } } @@ -183,25 +186,20 @@ private void loadBucket(String bucket) throws Exception { } @Override - public void loadTable() { - try { - PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); - disk_.clear(); - for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { - String bucket = getTableBucket(bucketIndex); - if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { - loadBucket(bucket); - } + public void loadTable() throws Exception { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY)); + disk.clear(); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, of(NOT_NULL, EXISTS, CAN_READ, IS_DIRECTORY))) { + loadBucket(bucket); } - size_ = disk_.size(); - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); } + size = disk.size(); } private static void writeItem(RandomAccessFile file, String word) throws Exception { - byte[] byteWord = word.getBytes("UTF-8"); + byte[] byteWord = word.getBytes(ENCODING); file.writeInt(byteWord.length); file.write(byteWord); } @@ -209,12 +207,12 @@ private static void writeItem(RandomAccessFile file, String word) throws Excepti private boolean saveSubBucket(String subBucket, int bucketIndex, int subBucketIndex) throws Exception { boolean isWritten = false; RandomAccessFile file = new RandomAccessFile(subBucket, "rw"); - for (String key : disk_.keySet()) { + for (String key : disk.keySet()) { int hashcode = key.hashCode(); int directoryNumber = hashcode % DIRECTORIES_COUNT; int fileNumber = hashcode / DIRECTORIES_COUNT % FILES_COUNT; if (bucketIndex == directoryNumber && subBucketIndex == fileNumber) { - String value = provider_.serialize(provider_.getTable(name_), disk_.get(key)); + String value = provider.serialize(provider.getTable(name), disk.get(key)); writeItem(file, key); writeItem(file, value); isWritten = true; @@ -240,29 +238,24 @@ private void saveBucket(String bucket, int bucketIndex) throws Exception { } } - private void saveTable() { - try { - PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); - for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { - String bucket = getTableBucket(bucketIndex); - if (PermissionsValidator.validate(bucket, - of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { - saveBucket(bucket, bucketIndex); - } + private void saveTable() throws Exception { + PermissionsValidator.validate(getTableFolder(), of(NOT_NULL, EXISTS, CAN_WRITE, IS_DIRECTORY)); + for (int bucketIndex = 0; bucketIndex < DIRECTORIES_COUNT; ++bucketIndex) { + String bucket = getTableBucket(bucketIndex); + if (PermissionsValidator.validate(bucket, + of(NOT_NULL, CREATE_DIRECTORY_IF_NOT_EXISTS, CAN_WRITE, IS_DIRECTORY))) { + saveBucket(bucket, bucketIndex); } - } catch (Exception e) { - System.err.println(e.getMessage()); - System.exit(1); } } @Override public int getColumnsCount() { - return columnTypes_.size(); + return columnTypes.size(); } @Override public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { - return columnTypes_.get(columnIndex); + return columnTypes.get(columnIndex); } } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java index 1e2c60ab4..79c5a5f1d 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/HashMapTableProvider.java @@ -261,37 +261,33 @@ public Storeable deserialize(Table table, String value) throws ParseException { @Override public String serialize(Table table, Storeable value) throws ColumnFormatException { - List> expectedTypes = nameToSignatureMap.get(table.getName()); - - String result = "("; - - int i = 0; - for (Class type : expectedTypes) { - if (type.equals(Integer.class)) { - result += value.getIntAt(i); - } else if (type.equals(Long.class)) { - result += value.getLongAt(i); - } else if (type.equals(Byte.class)) { - result += value.getByteAt(i); - } else if (type.equals(Float.class)) { - result += value.getFloatAt(i); - } else if (type.equals(Double.class)) { - result += value.getDoubleAt(i); - } else if (type.equals(Boolean.class)) { - result += value.getBooleanAt(i); - } else if (type.equals(String.class)) { - result += value.getStringAt(i); - } - i++; - - if (i != expectedTypes.size()) { - result += ","; - } - } - - result += ")"; - - return result; + List> expectedTypes = nameToSignatureMap.get(table.getName()); + + String result = "("; + + List types = new LinkedList<>(); + int i = 0; + for (Class type : expectedTypes) { + if (type.equals(Integer.class)) { + types.add(value.getIntAt(i).toString()); + } else if (type.equals(Long.class)) { + types.add(value.getLongAt(i).toString()); + } else if (type.equals(Byte.class)) { + types.add(value.getByteAt(i).toString()); + } else if (type.equals(Float.class)) { + types.add(value.getFloatAt(i).toString()); + } else if (type.equals(Double.class)) { + types.add(value.getDoubleAt(i).toString()); + } else if (type.equals(Boolean.class)) { + types.add(value.getBooleanAt(i).toString()); + } else if (type.equals(String.class)) { + types.add(value.getStringAt(i)); + } + i++; + } + result += String.join(",", types) + ")"; + + return result; } @Override diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java index 2550d0210..1a2665293 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Table.java @@ -57,7 +57,7 @@ public interface Table extends Index { * * @throws java.io.IOException если произошла ошибка ввода/вывода. Целостность таблицы не гарантируется. */ - int commit() throws IOException; + int commit() throws Exception; /** * Выполняет откат изменений с момента последней фиксации. @@ -96,5 +96,5 @@ public interface Table extends Index { /** * */ - void loadTable(); + void loadTable() throws Exception; } diff --git a/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java index d39874889..311975727 100644 --- a/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java +++ b/src/ru/fizteh/fivt/students/andrewzhernov/database/Utils.java @@ -4,10 +4,33 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.DirectoryStream; -import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.HashMap; public class Utils { + private static Map, String> ctsMap = new HashMap<>(); + private static Map> stcMap = new HashMap<>(); + + static { + ctsMap.put(Integer.class, "int"); + ctsMap.put(Long.class, "long"); + ctsMap.put(Float.class, "float"); + ctsMap.put(Double.class, "double"); + ctsMap.put(Byte.class, "byte"); + ctsMap.put(Boolean.class, "boolean"); + ctsMap.put(String.class, "String"); + + stcMap.put("int", Integer.class); + stcMap.put("long", Long.class); + stcMap.put("float", Float.class); + stcMap.put("double", Double.class); + stcMap.put("byte", Byte.class); + stcMap.put("boolean", Boolean.class); + stcMap.put("String", String.class); + } + public static void removeDir(Path directory) throws IllegalStateException { try { if (Files.isDirectory(directory)) { @@ -54,22 +77,10 @@ public static List> parseSignature(String signature) throws Exception { String[] types = signature.split(","); - List> result = new ArrayList<>(); + List> result = new LinkedList<>(); for (String type : types) { - if (type.equals("int")) { - result.add(Integer.class); - } else if (type.equals("long")) { - result.add(Long.class); - } else if (type.equals("byte")) { - result.add(Byte.class); - } else if (type.equals("float")) { - result.add(Float.class); - } else if (type.equals("double")) { - result.add(Double.class); - } else if (type.equals("boolean")) { - result.add(Boolean.class); - } else if (type.equals("String")) { - result.add(String.class); + if (stcMap.containsKey(type)) { + result.add(stcMap.get(type)); } else { throw new Exception("Wrong type (" + type + ")"); } @@ -81,31 +92,16 @@ public static List> parseSignature(String signature) throws Exception { public static String makeSignature(List> types) { String result = "("; + List typeList = new LinkedList<>(); int i = 0; for (Class type : types) { - if (type.equals(Integer.class)) { - result += "int"; - } else if (type.equals(Long.class)) { - result += "long"; - } else if (type.equals(Byte.class)) { - result += "byte"; - } else if (type.equals(Float.class)) { - result += "float"; - } else if (type.equals(Double.class)) { - result += "double"; - } else if (type.equals(Boolean.class)) { - result += "boolean"; - } else if (type.equals(String.class)) { - result += "String"; + if (ctsMap.containsKey(type)) { + typeList.add(ctsMap.get(type)); } - i++; - if (i != types.size()) { - result += ","; - } } - result += ")"; + result += String.join(",", typeList) + ")"; return result; }